Skip to content

Commit 9598eb8

Browse files
authored
Fixes for ACR usage and Front Door export Azure#2494 Azure#2668 (Azure#2669)
1 parent dc728ad commit 9598eb8

File tree

6 files changed

+147
-68
lines changed

6 files changed

+147
-68
lines changed

docs/CHANGELOG-v1.md

+5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ What's changed since pre-release v1.33.0-B0126:
4949
- Policies that duplicate built-in rules can now be exported by using the `-KeepDuplicates` parameter by @BernieWhite.
5050
[#2482](https://github.com/Azure/PSRule.Rules.Azure/issues/2482)
5151
- For details see [Policy as rules](./concepts/policy-as-rules.md#duplicate-policies).
52+
- Bug fixes:
53+
- Fixed inconclusive result reported for `Azure.ACR.Usage` by @BernieWhite.
54+
[#2494](https://github.com/Azure/PSRule.Rules.Azure/issues/2494)
55+
- Fixed export of Front Door resource data is incomplete by @BernieWhite.
56+
[#2668](https://github.com/Azure/PSRule.Rules.Azure/issues/2668)
5257

5358
## v1.33.0-B0126 (pre-release)
5459

docs/en/rules/Azure.ACR.Usage.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
2-
reviewed: 2022-01-22
2+
reviewed: 2024-02-04
33
severity: Important
44
pillar: Cost Optimization
5-
category: Reports
5+
category: CO:07 Component costs
66
resource: Container Registry
77
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.ACR.Usage/
88
---
@@ -32,8 +32,8 @@ This rule applies when analyzing resources deployed (in-flight) to Azure.
3232

3333
## LINKS
3434

35-
- [Generate cost reports](https://learn.microsoft.com/azure/architecture/framework/cost/monitor-reports)
36-
- [Azure Container Registry service tiers](https://docs.microsoft.com/azure/container-registry/container-registry-skus)
37-
- [Scalable storage](https://docs.microsoft.com/azure/container-registry/container-registry-storage#scalable-storage)
38-
- [Manage registry size](https://docs.microsoft.com/azure/container-registry/container-registry-best-practices#manage-registry-size)
39-
- [Delete container images in Azure Container Registry using the Azure CLI](https://docs.microsoft.com/azure/container-registry/container-registry-delete)
35+
- [CO:07 Component costs](https://learn.microsoft.com/azure/well-architected/cost-optimization/optimize-component-costs)
36+
- [Azure Container Registry service tiers](https://learn.microsoft.com/azure/container-registry/container-registry-skus)
37+
- [Scalable storage](https://learn.microsoft.com/azure/container-registry/container-registry-storage#scalable-storage)
38+
- [Manage registry size](https://learn.microsoft.com/azure/container-registry/container-registry-best-practices#manage-registry-size)
39+
- [Delete container images in Azure Container Registry using the Azure CLI](https://learn.microsoft.com/azure/container-registry/container-registry-delete)

src/PSRule.Rules.Azure/Pipeline/Export/ResourceExportVisitor.cs

+44-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ internal sealed class ResourceExportVisitor
3535

3636
private const string TYPE_CONTAINERSERVICE_MANAGEDCLUSTERS = "Microsoft.ContainerService/managedClusters";
3737
private const string TYPE_CONTAINERREGISTRY_REGISTRIES = "Microsoft.ContainerRegistry/registries";
38+
private const string TYPE_CONTAINERREGISTRY_REGISTRIES_LISTUSAGES = "Microsoft.ContainerRegistry/registries/listUsages";
39+
private const string TYPE_CDN_PROFILES = "Microsoft.Cdn/profiles";
3840
private const string TYPE_CDN_PROFILES_ENDPOINTS = "Microsoft.Cdn/profiles/endpoints";
41+
private const string TYPE_CDN_PROFILES_AFDENDPOINTS = "Microsoft.Cdn/profiles/afdEndpoints";
3942
private const string TYPE_AUTOMATION_ACCOUNTS = "Microsoft.Automation/automationAccounts";
4043
private const string TYPE_APIMANAGEMENT_SERVICE = "Microsoft.ApiManagement/service";
4144
private const string TYPE_SQL_SERVERS = "Microsoft.Sql/servers";
@@ -75,7 +78,6 @@ internal sealed class ResourceExportVisitor
7578
private const string APIVERSION_2016_09_01 = "2016-09-01";
7679
private const string APIVERSION_2017_12_01 = "2017-12-01";
7780
private const string APIVERSION_2021_05_01_PREVIEW = "2021-05-01-preview";
78-
private const string APIVERSION_2021_06_01 = "2021-06-01";
7981
private const string APIVERSION_2021_11_01 = "2021-11-01";
8082
private const string APIVERSION_2022_07_01 = "2022-07-01";
8183
private const string APIVERSION_2022_08_01 = "2022-08-01";
@@ -84,6 +86,7 @@ internal sealed class ResourceExportVisitor
8486
private const string APIVERSION_2022_09_10 = "2022-09-10";
8587
private const string APIVERSION_2022_05_01 = "2022-05-01";
8688
private const string APIVERSION_2023_04_01 = "2023-04-01";
89+
private const string APIVERSION_2023_05_01 = "2023-05-01";
8790

8891
private readonly ProviderData _ProviderData;
8992

@@ -146,6 +149,8 @@ private async Task<bool> ExpandResource(IResourceExportContext context, JObject
146149
await VisitAPIManagement(resourceContext, resource, resourceType, resourceId) ||
147150
await VisitAutomationAccount(resourceContext, resource, resourceType, resourceId) ||
148151
await VisitCDNEndpoint(resourceContext, resource, resourceType, resourceId) ||
152+
await VisitCDNProfile(resourceContext, resource, resourceType, resourceId) ||
153+
await VisitFrontDoorEndpoint(resourceContext, resource, resourceType, resourceId) ||
149154
await VisitContainerRegistry(resourceContext, resource, resourceType, resourceId) ||
150155
await VisitAKSCluster(resourceContext, resource, resourceType, resourceId) ||
151156
await VisitSqlServers(resourceContext, resource, resourceType, resourceId) ||
@@ -237,7 +242,8 @@ private static async Task<bool> VisitDevCenterProject(ResourceContext context, J
237242
var pools = await GetSubResourcesByType(context, resourceId, "pools", APIVERSION_2023_04_01);
238243
foreach (var pool in pools)
239244
{
240-
AddSubResource(pool, await GetSubResourcesByType(context, resourceId, "schedules", APIVERSION_2023_04_01));
245+
if (pool.TryStringProperty(PROPERTY_ID, out var poolId))
246+
AddSubResource(pool, await GetSubResourcesByType(context, poolId, "schedules", APIVERSION_2023_04_01));
241247
}
242248
AddSubResource(resource, pools);
243249

@@ -297,6 +303,15 @@ private static async Task<bool> VisitFrontDoorClassic(ResourceContext context, J
297303
return true;
298304
}
299305

306+
private static async Task<bool> VisitFrontDoorEndpoint(ResourceContext context, JObject resource, string resourceType, string resourceId)
307+
{
308+
if (!string.Equals(resourceType, TYPE_CDN_PROFILES_AFDENDPOINTS, StringComparison.OrdinalIgnoreCase))
309+
return false;
310+
311+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "routes", APIVERSION_2023_05_01));
312+
return true;
313+
}
314+
300315
private static async Task<bool> VisitKeyVault(ResourceContext context, JObject resource, string resourceType, string resourceId)
301316
{
302317
if (!string.Equals(resourceType, TYPE_KEYVAULT_VAULT, StringComparison.OrdinalIgnoreCase))
@@ -454,9 +469,16 @@ private static async Task<bool> VisitContainerRegistry(ResourceContext context,
454469
if (!string.Equals(resourceType, TYPE_CONTAINERREGISTRY_REGISTRIES, StringComparison.OrdinalIgnoreCase))
455470
return false;
456471

457-
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "replications", "2022-02-01-preview"));
458-
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "webhooks", "2022-02-01-preview"));
459-
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "tasks", "2019-06-01-preview"));
472+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "replications", "2023-01-01-preview"));
473+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "webhooks", "2023-01-01-preview"));
474+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "tasks", "2019-04-01"));
475+
476+
// Handle usage information that does not include a strong type.
477+
foreach (var usage in await GetSubResourcesByType(context, resourceId, "listUsages", "2023-01-01-preview"))
478+
{
479+
usage[PROPERTY_TYPE] = TYPE_CONTAINERREGISTRY_REGISTRIES_LISTUSAGES;
480+
AddSubResource(resource, usage);
481+
}
460482
return true;
461483
}
462484

@@ -465,7 +487,23 @@ private static async Task<bool> VisitCDNEndpoint(ResourceContext context, JObjec
465487
if (!string.Equals(resourceType, TYPE_CDN_PROFILES_ENDPOINTS, StringComparison.OrdinalIgnoreCase))
466488
return false;
467489

468-
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "customDomains", APIVERSION_2021_06_01));
490+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "customDomains", APIVERSION_2023_05_01));
491+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "originGroups", APIVERSION_2023_05_01));
492+
493+
await GetDiagnosticSettings(context, resource, resourceId);
494+
return true;
495+
}
496+
497+
private static async Task<bool> VisitCDNProfile(ResourceContext context, JObject resource, string resourceType, string resourceId)
498+
{
499+
if (!string.Equals(resourceType, TYPE_CDN_PROFILES, StringComparison.OrdinalIgnoreCase))
500+
return false;
501+
502+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "customDomains", APIVERSION_2023_05_01));
503+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "originGroups", APIVERSION_2023_05_01));
504+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "ruleSets", APIVERSION_2023_05_01));
505+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "secrets", APIVERSION_2023_05_01));
506+
AddSubResource(resource, await GetSubResourcesByType(context, resourceId, "securityPolicies", APIVERSION_2023_05_01));
469507
return true;
470508
}
471509

src/PSRule.Rules.Azure/rules/Azure.ACR.Rule.ps1

+9-6
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99

1010
# Synopsis: Consider freeing up registry space.
1111
Rule 'Azure.ACR.Usage' -Ref 'AZR-000001' -Type 'Microsoft.ContainerRegistry/registries' -If { IsExport } -Tag @{ release = 'GA'; ruleSet = '2020_12'; 'Azure.WAF/pillar' = 'Cost Optimization'; method = 'in-flight'; } {
12-
$usages = @(GetSubResources -ResourceType 'Microsoft.ContainerRegistry/registries/listUsages' | ForEach-Object {
13-
$_.value | Where-Object { $_.Name -eq 'Size' }
12+
$usages = @(GetSubResources -ResourceType 'Microsoft.ContainerRegistry/registries/listUsages' | Where-Object {
13+
$_.Name -eq 'Size'
1414
});
15-
if ($usages.Length -gt 0) {
16-
foreach ($usage in $usages) {
17-
$Assert.LessOrEqual([int]($usage.currentValue/$usage.limit*100), '.', 90);
18-
}
15+
16+
if ($usages.Length -eq 0) {
17+
return $Assert.Pass();
18+
}
19+
20+
foreach ($usage in $usages) {
21+
$Assert.LessOrEqual([int]($usage.currentValue/$usage.limit*100), '.', 90);
1922
}
2023
}
2124

tests/PSRule.Rules.Azure.Tests/Azure.ACR.Tests.ps1

+4-4
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,15 @@ Describe 'Azure.ACR' -Tag 'ACR' {
140140
# Fail
141141
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
142142
$ruleResult | Should -Not -BeNullOrEmpty;
143-
$ruleResult.Length | Should -Be 9;
144-
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-B', 'registry-D', 'registry-E', 'registry-F', 'registry-G', 'registry-H', 'registry-I', 'registry-J';
143+
$ruleResult.Length | Should -Be 2;
144+
$ruleResult.TargetName | Should -BeIn 'registry-B', 'registry-E';
145145
# TODO: $ruleResult.Detail.Reason.Path | Should -BeIn '';
146146

147147
# Pass
148148
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
149149
$ruleResult | Should -Not -BeNullOrEmpty;
150-
$ruleResult.Length | Should -Be 1;
151-
$ruleResult.TargetName | Should -BeIn 'registry-C';
150+
$ruleResult.Length | Should -Be 8;
151+
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-C', 'registry-D', 'registry-F', 'registry-G', 'registry-H', 'registry-I', 'registry-J';
152152
}
153153

154154
It 'Azure.ACR.ContainerScan' {

tests/PSRule.Rules.Azure.Tests/Resources.ACR.json

+78-45
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,32 @@
126126
"ETag": null
127127
},
128128
{
129-
"value": [
130-
{
131-
"name": "Size",
132-
"limit": 536870912000,
133-
"currentValue": 531502202880,
134-
"unit": "Bytes"
135-
},
136-
{
137-
"name": "Webhooks",
138-
"limit": 500,
139-
"currentValue": 0,
140-
"unit": "Count"
141-
}
142-
],
143-
"ResourceType": "Microsoft.ContainerRegistry/registries/listUsages"
129+
"name": "Size",
130+
"limit": 536870912000,
131+
"currentValue": 531502202880,
132+
"unit": "Bytes",
133+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
134+
},
135+
{
136+
"name": "Webhooks",
137+
"limit": 500,
138+
"currentValue": 0,
139+
"unit": "Count",
140+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
141+
},
142+
{
143+
"name": "ScopeMaps",
144+
"limit": 100,
145+
"currentValue": 0,
146+
"unit": "Count",
147+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
148+
},
149+
{
150+
"name": "Tokens",
151+
"limit": 100,
152+
"currentValue": 0,
153+
"unit": "Count",
154+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
144155
},
145156
{
146157
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.ContainerRegistry/registries/registry-B/providers/Microsoft.Security/assessments/00000000-0000-0000-0000-000000000000",
@@ -279,21 +290,32 @@
279290
"ETag": null
280291
},
281292
{
282-
"value": [
283-
{
284-
"name": "Size",
285-
"limit": 536870912000,
286-
"currentValue": 1073741824,
287-
"unit": "Bytes"
288-
},
289-
{
290-
"name": "Webhooks",
291-
"limit": 500,
292-
"currentValue": 0,
293-
"unit": "Count"
294-
}
295-
],
296-
"ResourceType": "Microsoft.ContainerRegistry/registries/listUsages"
293+
"name": "Size",
294+
"limit": 536870912000,
295+
"currentValue": 1073741824,
296+
"unit": "Bytes",
297+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
298+
},
299+
{
300+
"name": "Webhooks",
301+
"limit": 500,
302+
"currentValue": 0,
303+
"unit": "Count",
304+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
305+
},
306+
{
307+
"name": "ScopeMaps",
308+
"limit": 100,
309+
"currentValue": 0,
310+
"unit": "Count",
311+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
312+
},
313+
{
314+
"name": "Tokens",
315+
"limit": 100,
316+
"currentValue": 0,
317+
"unit": "Count",
318+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
297319
},
298320
{
299321
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.ContainerRegistry/registries/registry-C/providers/Microsoft.Security/assessments/00000000-0000-0000-0000-000000000000",
@@ -468,21 +490,32 @@
468490
"ETag": null
469491
},
470492
{
471-
"value": [
472-
{
473-
"name": "Size",
474-
"limit": 536870912000,
475-
"currentValue": 531502202880,
476-
"unit": "Bytes"
477-
},
478-
{
479-
"name": "Webhooks",
480-
"limit": 500,
481-
"currentValue": 0,
482-
"unit": "Count"
483-
}
484-
],
485-
"ResourceType": "Microsoft.ContainerRegistry/registries/listUsages"
493+
"name": "Size",
494+
"limit": 536870912000,
495+
"currentValue": 531502202880,
496+
"unit": "Bytes",
497+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
498+
},
499+
{
500+
"name": "Webhooks",
501+
"limit": 500,
502+
"currentValue": 0,
503+
"unit": "Count",
504+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
505+
},
506+
{
507+
"name": "ScopeMaps",
508+
"limit": 100,
509+
"currentValue": 0,
510+
"unit": "Count",
511+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
512+
},
513+
{
514+
"name": "Tokens",
515+
"limit": 100,
516+
"currentValue": 0,
517+
"unit": "Count",
518+
"type": "Microsoft.ContainerRegistry/registries/listUsages"
486519
},
487520
{
488521
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.ContainerRegistry/registries/registry-E/providers/Microsoft.Security/assessments/00000000-0000-0000-0000-000000000000",

0 commit comments

Comments
 (0)