Today I’m working on creating and updating Azure Network Security Group rules from the Az PowerShell module. However I hit a bit of a bump.
I’m trying to do something simple like add 1 rule to an existing NSG that has other rules:
# Find the security group where it matches a variable
$nsgs = Get-AzNetworkSecurityGroup -ResourceGroupName $rgname | where-object { $_.Name -like "$nsgsuffix" }
# For the selected security group, add a rule, and then apply it
$nsgs | add-aznetworksecurityruleconfig -Access Allow -DestinationAddressPrefix $ips -DestinationPortRange 443 `
-Direction Outbound -name allowOut-To3rdParty_443 -Priority 400 `
-SourceAddressPrefix * -SourcePortRange * -Protocol * | Set-AzNetworkSecurityGroup |
# Find the security group where it matches a variable
$nsgs = Get-AzNetworkSecurityGroup -ResourceGroupName $rgname | where-object { $_.Name -like "$nsgsuffix" }
# For the selected security group, add a rule, and then apply it
$nsgs | add-aznetworksecurityruleconfig -Access Allow -DestinationAddressPrefix $ips -DestinationPortRange 443 `
-Direction Outbound -name allowOut-To3rdParty_443 -Priority 400 `
-SourceAddressPrefix * -SourcePortRange * -Protocol * | Set-AzNetworkSecurityGroup
This NSG and other rules were previously deployed through TerraForm, and I want to add an “out-of-band” rule that isn’t tracked in the Terraform state.
When I run these commands, I get this error:
Required security rule parameters are missing for security rule with Id:
Security rule must specify either DestinationPortRange or DestinationPortRanges.
That’s super strange; when I look at that rule in the Portal, it has ports listed:
So I check my $nsgs variable that was previously populated, looking specifically for the SecurityRules property:
What I find is that for any rule that the portal displays having multiple ports, it lists them just fine (see Destination Port Range property below):
Name : test_test.test
Id : /subscriptions/1/resourceGroups/rg/providers/Microsoft.Network
/networkSecurityGroups/db-nsg/securityRules/test_test.test
Etag : W/"3a98e199-3bf6-4308-806c-8d84bc03723e"
ProvisioningState : Succeeded
Description :
Protocol : *
SourcePortRange : {*}
DestinationPortRange : {123, 456, 789}
SourceAddressPrefix : {10.8.1.}
DestinationAddressPrefix : {10.8.1.}
SourceApplicationSecurityGroups : []
DestinationApplicationSecurityGroups : []
Access : Allow
Priority : 109
Direction : Inbound |
Name : test_test.test
Id : /subscriptions/1/resourceGroups/rg/providers/Microsoft.Network
/networkSecurityGroups/db-nsg/securityRules/test_test.test
Etag : W/"3a98e199-3bf6-4308-806c-8d84bc03723e"
ProvisioningState : Succeeded
Description :
Protocol : *
SourcePortRange : {*}
DestinationPortRange : {123, 456, 789}
SourceAddressPrefix : {10.8.1.}
DestinationAddressPrefix : {10.8.1.}
SourceApplicationSecurityGroups : []
DestinationApplicationSecurityGroups : []
Access : Allow
Priority : 109
Direction : Inbound
But for any rule that only has a SINGLE port shown in the portal, it is blank for that property:
Name : web_.db
Id : /subscriptions/1/resourceGroups/rg/providers/Microsoft.Network
/networkSecurityGroups/db-nsg/securityRules/web_.db
Etag : W/"3a98e199-3bf6-4308-806c-8d84bc03723e"
ProvisioningState : Succeeded
Description :
Protocol : *
SourcePortRange : {*}
DestinationPortRange : {}
SourceAddressPrefix : {10.0.0.1, 10.0.0.2}
DestinationAddressPrefix : {10.1.1.100}
SourceApplicationSecurityGroups : []
DestinationApplicationSecurityGroups : []
Access : Allow
Priority : 107
Direction : Inbound |
Name : web_.db
Id : /subscriptions/1/resourceGroups/rg/providers/Microsoft.Network
/networkSecurityGroups/db-nsg/securityRules/web_.db
Etag : W/"3a98e199-3bf6-4308-806c-8d84bc03723e"
ProvisioningState : Succeeded
Description :
Protocol : *
SourcePortRange : {*}
DestinationPortRange : {}
SourceAddressPrefix : {10.0.0.1, 10.0.0.2}
DestinationAddressPrefix : {10.1.1.100}
SourceApplicationSecurityGroups : []
DestinationApplicationSecurityGroups : []
Access : Allow
Priority : 107
Direction : Inbound
My first thought was “how is this possible”?
I think this is because for these single port rules, we’re using the following Terraform syntax:
destination_port_ranges = ["${var.port-https}"] |
destination_port_ranges = ["${var.port-https}"]
Terraform is expecting a list, but we’re only passing a single value. Terraform doesn’t consider this invalid, and it is applied to Azure successfully. However the flaw is revealed when trying to use the “Set-AzNetworkSecurityGroup” cmdlet because that attempts to re-validate ALL rules on the NSG, not just the one that I added or modified.
If I change that Terraform property to destination_port_range (note the singular) then everything appears to work properly when using Az PowerShell afterwards.