Skip to content

Commit

Permalink
Updated Azure.VNET.UseNSGs (#3008)
Browse files Browse the repository at this point in the history
* Updated Azure.VNET.UseNSGs

* Updated Azure.VNET.UseNSGs

* Fix

---------

Co-authored-by: Bernie White <bewhite@microsoft.com>
  • Loading branch information
BenjaminEngeset and BernieWhite authored Aug 5, 2024
1 parent f694a7c commit a8850b7
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 13 deletions.
4 changes: 4 additions & 0 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
- Azure Kubernetes Service:
- Verify that clusters have the customer-controlled maintenance windows 'aksManagedAutoUpgradeSchedule' and 'aksManagedNodeOSUpgradeSchedule' configured by @BenjaminEngeset.
[#2444](https://github.com/Azure/PSRule.Rules.Azure/issues/2444)
- Updated rules:
- Virtual Network:
- Updated `Azure.VNET.UseNSGs` to correctly handle cases for special purpose and customer-excluded subnets by @BenjaminEngeset.
[#3007](https://github.com/Azure/PSRule.Rules.Azure/issues/3007)

What's changed since pre-release v1.39.0-B0009:

Expand Down
6 changes: 2 additions & 4 deletions src/PSRule.Rules.Azure/rules/Azure.VNET.Rule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ Rule 'Azure.VNET.UseNSGs' -Ref 'AZR-000263' -Type 'Microsoft.Network/virtualNetw
if ($PSRule.TargetType -eq 'Microsoft.Network/virtualNetworks') {
# Get subnets
$subnet = @($TargetObject.properties.subnets | Where-Object {
$_.Name -notin $excludedSubnets -and $_.Name -notin $customExcludedSubnets -and @($_.properties.delegations | Where-Object { $_.properties.serviceName -eq 'Microsoft.HardwareSecurityModules/dedicatedHSMs' }).Length -eq 0
[PSRule.Rules.Azure.Runtime.Helper]::GetSubResourceName($_.Name) -notin $excludedSubnets -and [PSRule.Rules.Azure.Runtime.Helper]::GetSubResourceName($_.Name) -notin $customExcludedSubnets -and @($_.properties.delegations | Where-Object { $_.properties.serviceName -eq 'Microsoft.HardwareSecurityModules/dedicatedHSMs' }).Length -eq 0
});
if ($subnet.Length -eq 0 -or !$Assert.HasFieldValue($TargetObject, 'properties.subnets').Result) {
return $Assert.Pass();
}
}
elseif ($PSRule.TargetType -eq 'Microsoft.Network/virtualNetworks/subnets' -and
($PSRule.TargetName -in $excludedSubnets -or $PSRule.TargetName -in $customExcludedSubnets -or @($TargetObject.properties.delegations | Where-Object { $_.properties.serviceName -eq 'Microsoft.HardwareSecurityModules/dedicatedHSMs' }).Length -gt 0)) {
([PSRule.Rules.Azure.Runtime.Helper]::GetSubResourceName($PSRule.TargetName) -in $excludedSubnets -or [PSRule.Rules.Azure.Runtime.Helper]::GetSubResourceName($PSRule.TargetName) -in $customExcludedSubnets -or @($TargetObject.properties.delegations | Where-Object { $_.properties.serviceName -eq 'Microsoft.HardwareSecurityModules/dedicatedHSMs' }).Length -gt 0)) {
return $Assert.Pass();
}
foreach ($sn in $subnet) {
Expand All @@ -32,8 +32,6 @@ Rule 'Azure.VNET.UseNSGs' -Ref 'AZR-000263' -Type 'Microsoft.Network/virtualNetw
}
}

# TODO: Check that NSG on GatewaySubnet is not defined

# Synopsis: VNETs should have at least two DNS servers assigned.
Rule 'Azure.VNET.SingleDNS' -Ref 'AZR-000264' -Type 'Microsoft.Network/virtualNetworks' -Tag @{ release = 'GA'; ruleSet = '2020_06'; 'Azure.WAF/pillar' = 'Reliability'; } {
# If DNS servers are customized, at least two IP addresses should be defined
Expand Down
61 changes: 52 additions & 9 deletions tests/PSRule.Rules.Azure.Tests/Azure.VNET.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ Describe 'Azure.VNET' -Tag 'Network', 'VNET' {
Module = 'PSRule.Rules.Azure'
WarningAction = 'Ignore'
ErrorAction = 'Stop'
Option = @{
'Configuration.AZURE_VNET_SUBNET_EXCLUDED_FROM_NSG' = @('subnet-ZZ')
}
}
$dataPath = Join-Path -Path $here -ChildPath 'Resources.VirtualNetwork.json';
$result = Invoke-PSRule @invokeParams -InputPath $dataPath -Outcome All;
Expand All @@ -45,8 +42,8 @@ Describe 'Azure.VNET' -Tag 'Network', 'VNET' {
# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 3;
$ruleResult.TargetName | Should -BeIn 'vnet-B', 'vnet-C', 'vnet-D';
$ruleResult.Length | Should -Be 5;
$ruleResult.TargetName | Should -BeIn 'vnet-B', 'vnet-C', 'vnet-D', 'vnet-G', 'vnet-H/excludedSubnet';

$ruleResult[0].Reason | Should -Not -BeNullOrEmpty;
$ruleResult[0].Reason | Should -HaveCount 4;
Expand All @@ -70,12 +67,18 @@ Describe 'Azure.VNET' -Tag 'Network', 'VNET' {
"The subnet (subnet-C) has no NSG associated.",
"The subnet (subnet-D) has no NSG associated."
);
$ruleResult[3].Reason | Should -Not -BeNullOrEmpty;
$ruleResult[3].Reason | Should -HaveCount 1;
$ruleResult[3].Reason | Should -Be "The subnet (subnet-ZZ) has no NSG associated.";
$ruleResult[4].Reason | Should -Not -BeNullOrEmpty;
$ruleResult[4].Reason | Should -HaveCount 1;
$ruleResult[4].Reason | Should -Be "The subnet (vnet-H/excludedSubnet) has no NSG associated.";

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 4;
$ruleResult.TargetName | Should -BeIn 'vnet-A', 'vnet-E', 'vnet-F', 'vnet-G';
$ruleResult.TargetName | Should -BeIn 'vnet-A', 'vnet-E', 'vnet-F', 'vnet-H/AzureFirewallSubnet';
}

It 'Azure.VNET.SingleDNS' {
Expand Down Expand Up @@ -156,8 +159,8 @@ Describe 'Azure.VNET' -Tag 'Network', 'VNET' {
# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 7;
$ruleResult.TargetName | Should -BeIn 'vnet-A', 'vnet-B', 'vnet-C', 'vnet-D', 'vnet-E', 'vnet-F', 'vnet-G';
$ruleResult.Length | Should -Be 9;
$ruleResult.TargetName | Should -BeIn 'vnet-A', 'vnet-B', 'vnet-C', 'vnet-D', 'vnet-E', 'vnet-F', 'vnet-G', 'vnet-H/AzureFirewallSubnet', 'vnet-H/excludedSubnet';
}

It 'Azure.VNET.BastionSubnet' {
Expand Down Expand Up @@ -387,10 +390,11 @@ Describe 'Azure.VNET' -Tag 'Network', 'VNET' {
ErrorAction = 'Stop'
Option = @{
'Configuration.AZURE_VNET_DNS_WITH_IDENTITY' = $true
'Configuration.AZURE_VNET_SUBNET_EXCLUDED_FROM_NSG' = @('subnet-ZZ', 'excludedSubnet')
}
}
$dataPath = Join-Path -Path $here -ChildPath 'Resources.VirtualNetwork.json';
$result = Invoke-PSRule @invokeParams -InputPath $dataPath -Outcome All -Name 'Azure.VNET.LocalDNS';
$result = Invoke-PSRule @invokeParams -InputPath $dataPath -Outcome All -Name 'Azure.VNET.LocalDNS', 'Azure.VNET.UseNSGs';
}

It 'Azure.VNET.LocalDNS' {
Expand All @@ -404,5 +408,44 @@ Describe 'Azure.VNET' -Tag 'Network', 'VNET' {
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -BeNullOrEmpty;
}

It 'Azure.VNET.UseNSGs' {
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.VNET.UseNSGs' };

# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 3;
$ruleResult.TargetName | Should -BeIn 'vnet-B', 'vnet-C', 'vnet-D';

$ruleResult[0].Reason | Should -Not -BeNullOrEmpty;
$ruleResult[0].Reason | Should -HaveCount 4;
$ruleResult[0].Reason | Should -Be @(
"The subnet (AzureBastionSubnet) has no NSG associated.",
"The subnet (subnet-B) has no NSG associated.",
"The subnet (subnet-C) has no NSG associated.",
"The subnet (subnet-D) has no NSG associated."
);
$ruleResult[1].Reason | Should -Not -BeNullOrEmpty;
$ruleResult[1].Reason | Should -HaveCount 3;
$ruleResult[1].Reason | Should -Be @(
"The subnet (subnet-B) has no NSG associated.",
"The subnet (subnet-C) has no NSG associated.",
"The subnet (subnet-D) has no NSG associated."
);
$ruleResult[2].Reason | Should -Not -BeNullOrEmpty;
$ruleResult[2].Reason | Should -HaveCount 3;
$ruleResult[2].Reason | Should -Be @(
"The subnet (subnet-B) has no NSG associated.",
"The subnet (subnet-C) has no NSG associated.",
"The subnet (subnet-D) has no NSG associated."
);

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 6;
$ruleResult.TargetName | Should -BeIn 'vnet-A', 'vnet-E', 'vnet-F', 'vnet-G', 'vnet-H/AzureFirewallSubnet', 'vnet-H/excludedSubnet';
}
}
}
50 changes: 50 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/Resources.VirtualNetwork.json
Original file line number Diff line number Diff line change
Expand Up @@ -2999,5 +2999,55 @@
}
]
}
},
{
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/vnet-H/subnets/AzureFirewallSubnet",
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/vnet-H/subnets/AzureFirewallSubnet",
"Identity": null,
"Kind": null,
"Location": "region",
"ResourceName": "vnet-H/AzureFirewallSubnet",
"Name": "vnet-H/AzureFirewallSubnet",
"Plan": null,
"Properties": {
"addressPrefix": "10.5.0.0/26",
"routeTable": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/routeTables/route-A"
},
"serviceEndpoints": [],
"delegations": []
},
"ResourceGroupName": "test-rg",
"Type": "Microsoft.Network/virtualNetworks/subnets",
"ResourceType": "Microsoft.Network/virtualNetworks/subnets",
"ExtensionResourceType": null,
"Sku": null,
"Tags": null,
"SubscriptionId": "00000000-0000-0000-0000-000000000000"
},
{
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/vnet-H/subnets/excludedSubnet",
"Id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/vnet-H/subnets/excludedSubnet",
"Identity": null,
"Kind": null,
"Location": "region",
"ResourceName": "vnet-H/excludedSubnet",
"Name": "vnet-H/excludedSubnet",
"Plan": null,
"Properties": {
"addressPrefix": "10.0.0.0/29",
"routeTable": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/routeTables/route-A"
},
"serviceEndpoints": [],
"delegations": []
},
"ResourceGroupName": "test-rg",
"Type": "Microsoft.Network/virtualNetworks/subnets",
"ResourceType": "Microsoft.Network/virtualNetworks/subnets",
"ExtensionResourceType": null,
"Sku": null,
"Tags": null,
"SubscriptionId": "00000000-0000-0000-0000-000000000000"
}
]

0 comments on commit a8850b7

Please sign in to comment.