Optimize Powershell script for Azure NSG - azure

I have a script for finding all inbound, allow rules in Azure NSG which source is any. It is doing his job but it takes just an enormous amount of time to iterate every rule in every available NSG in Azure.
Question is there is any way to optimize it, so it could work faster? Thanks!
function nsg {
# List of default rules which are skipped
$array =
'Default rules'
# Get all RG with NSG
$RGS = (Get-AzureRmResource -ODataQuery "`$filter=resourcetype eq 'Microsoft.Network/networkSecurityGroups'").ResourceGroupName | Sort-Object -Unique
foreach ($RG in $RGS) {
# List of all NSG names
$NSG_Names = (Get-AzureRmNetworkSecurityGroup -ResourceGroupName $RG).Name
# Get NSG rules
foreach ($NSG_Name in $NSG_Names){
$Rules = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig | Select-Object -ExpandProperty Name
# Check if rule is not default
foreach ($Rule in $Rules){
if ($array.contains($Rule)){
Write-Verbose "$Rule excluded because it is default!"
}
else {
Write-Verbose "$NSG_Name - $Rule"
#$DestinationAddressPrefix = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig -Name $Rule | Select-Object -ExpandProperty DestinationAddressPrefix
$DestinationPortRange = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig -Name $Rule | Select-Object -ExpandProperty DestinationPortRange
$SourceAddrPref = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig -Name $Rule | Select-Object -ExpandProperty SourceAddressPrefix
$SourcePortRange = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig -Name $Rule | Select-Object -ExpandProperty SourcePortRange
$Access = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig -Name $Rule | Select-Object -ExpandProperty Access
$Direction = Get-AzureRmNetworkSecurityGroup -Name $NSG_Name -ResourceGroupName $RG | Get-AzureRmNetworkSecurityRuleConfig -Name $Rule | Select-Object -ExpandProperty Direction
# Chek rule for every criterion, access type, etc.
if ($Direction -eq "Inbound" -and $SourceAddrPref -eq "*" -and $Access -eq "Allow"){ #-and $DestinationAddressPrefix -eq "*" -and $DestinationPortRange -eq "*") {
$message = "Warning! RG: $RG; NSG: $NSG_Name has SOURCE ANY Rule: $Rule to Destionation Port: $DestinationPortRange"
$message
}}}}}}

I don't think you need to iterate all that data. All those nested loops + sorting is whats causing the slowdown.
It seems to me that you just want to output all your NSGs with direction Inbound and access is Allow and source address prefix as *. If this is the case, you can do this:
$nsgs = Get-AzureRmNetworkSecurityGroup
foreach ($nsg in $nsgs.SecurityRules)
{
if ($nsg.Direction -eq "Inbound" -and $nsg.Access -eq "Allow" -and $nsg.SourceAddressPrefix -eq "*")
{
$nsg
}
}
Here we just iterate the security rules you have made from $nsgs.SecurityRules. The default ones are in $nsgs.DefaultSecurityRules. You can pipe to Get-Member to find these properties.

Related

Automate NSG rule creation for NSGs within azure: Type error from Get-AzNetworkSecurityGroup

I am trying to write a script to automate creating NSG rules for our production NSGs. I am pretty sure I have something close to working but the issue I run into is that the Get-AZNetworkSecurityGroup command returns a string and so I cant feed it into the Add-AzNetworkSecurityRuleConfig command.
Import-Module Az.network
Connect-AzAccount
$tcpports = #(22,53,80,135,137,161,427,443,515,548,5060,5480,5985,5986,5989,9100,9443)
$udpports = #(53,161,427,515,548)
$solservers = #Server IP here
$file = Import-Csv C:\Users\temp\Downloads\AzureNSGs.csv
foreach ($NSG in $file){
$RGname=$NSG.'RESOURCE GROUP'
$nsgname=$NSG.NAME
$NSGObj = Get-AzNetworkSecurityGroup | Where-Object -Property Name -Like $RGname | Select-Object -Property Name
$name = "AllowSolarWinds"
if($NSGObj){
$name = $name + 1
$NSGObj | Add-AzNetworkSecurityRuleConfig -Name $name -NetworkSecurityGroup $NSGObj -Protocol Icmp -SourceAddressPrefix $solservers -DestinationPortRange "*" -Priority 555
$NSGObj | Set-AzNetworkSecurityGroup
}
}
Whenever I run this I get two kinds of returns. It either looks like it ran successfully with no errors but the rule is never created in azure. Or powershell spits out one of the following errors.
Add-AzNetworkSecurityRuleConfig : Cannot bind argument to parameter 'NetworkSecurityGroup' because it is null.
or
Add-AzNetworkSecurityRuleConfig : Cannot bind parameter 'NetworkSecurityGroup'. Cannot convert the value of type "System.String" to type
"Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup".
I tried to reproduce the same in my environment I got the same error like below:
To resolve the error, try to modify the code like below:
Connect-AzAccount
Import-Module Az.network
$tcpports = #(22,53,80,135,137,161,427,443,515,548,5060,5480,5985,5986,5989,9100,9443)
$udpports = #(53,161,427,515,548)
$solservers = "112.121.61.196"
$file = Import-Csv C:\Users\v-khanimran\Downloads\AzureNSGs.csv
foreach ($NSG in $file){
$RGname=$NSG.RESOURCEGROUPNAME
$nsgname=$NSG.NAME
$NSGObj =Get-AzNetworkSecurityGroup -Name $nsgname -ResourceGroupName $RGname
#Get-AzNetworkSecurityGroup | Where-Object {$_.Name -Like $nsgname} | Select-Object -Property Name
$name = "AllowSolarWinds"
if($NSGObj){
$name = $name + 1
$NSGObj | Add-AzNetworkSecurityRuleConfig -Name $name -Protocol Icmp -SourceAddressPrefix $solservers -DestinationPortRange "*" -SourcePortRange "*" -DestinationAddressPrefix "*" -Priority 555 -Access Allow -Direction Inbound
$NSGObj | Set-AzNetworkSecurityGroup
}
}
Output:
In the portal NSG rule got added successfully like below:

How to split different values in powershell by a line

With this script i am able to fetch all the Tags that a VM has but i want that in output the each key and its value should be separated by a line in the way that each key and its value appears on different lines like this
reference image
# Sign into Azure Portal
connect-azaccount
# Fetch the Virtual Machines from the subscription
$azureVMDetails = get-azvm
# Fetch the NIC details from the subscription
$azureNICDetails = Get-AzNetworkInterface | ?{ $_.VirtualMachine -NE $null}
#Fetching Virtual Machine Details
$virtual_machine_object = $null
$virtual_machine_object = #()
#Iterating over the NIC Interfaces under the subscription
foreach($azureNICDetail in $azureNICDetails){
#Fetching the VM Name
$azureVMDetail = $azureVMDetails | ? -Property Id -eq $azureNICDetail.VirtualMachine.id
#Fetching the VM Tags
foreach($azureDetail in $azureVMDetails) {
$vm_tags = $azureVMDetail| Select-Object -Property (
#{name='Tags'; expression = {($_.tags.GetEnumerator().ForEach({ '{0} : {1}' -f $_.key, $_.value }) -join ';')}}
)
}
#VM Details export
$virtual_machine_object_temp = new-object PSObject
$virtual_machine_object_temp | add-member -membertype NoteProperty -name "name" -Value $azureVMDetail.Name
$virtual_machine_object_temp | add-member -membertype NoteProperty -name "comments" -Value ($vm_tags.Tags -join ';')
$virtual_machine_object += $virtual_machine_object_temp
}
#Report format and path
$virtual_machine_object | Export-Csv "C:\Users\JOHN\Desktop\Inventory\Final Scripts\VM_details_$(get-date -f dd.MM.yyyy).csv" -NoTypeInformation -Force
I tried to reproduce the same in my environment and got the results successfully by using the below PowerShell script:
$vmdeatil = Get-AzVm -Name testvm | Select -ExpandProperty Tags
$value = $vmdeatil
foreach($i in 0..($value.Count -1))
{
$ErrorActionPreference = ‘SilentlyContinue’
[array]$report += [pscustomobject] #{
key = $key[$i]
name = $value[$i]
}
}
$report | Export-Csv -Path "C:\Users\ruk1.csv" -NoTypeInformation
Response:
The output is successfully exported in the csv file like below:

How to loop through mulitple azure subscriptions parallelly

I am trying with nested ForEach-Object -Parallel to loop through multiple azure subscriptions parallelly and get data from all VMs in one go. I am using below code:
$SubsJob = Get-AzSubscription -WarningAction SilentlyContinue | Where-Object {$_.Name -match 'abc'} | ForEach-Object -Parallel {
$Context = Set-AzContext -Tenant $_.TenantId -SubscriptionId $_.SubscriptionId
[System.String]$ScriptBlock = {Get-Process}
$VMsJob = Get-AzVM | ForEach-Object -Parallel {
$FileName = $using:Context.Subscription.Name + "_$($_.Name)_" + (Get-Random) + ".ps1"
Out-File -FilePath $FileName -InputObject $using:ScriptBlock -NoNewline
$Output = Invoke-AzVMRunCommand -Name $_.Name -ResourceGroupName $_.ResourceGroupName -CommandId 'RunPowerShellScript' -ScriptPath $FileName
$PSCustomObject = [PSCustomObject]#{Subscription = $using:Context.Subscription.Name; ServerName = $_.Name; Output = $Output}
#Remove-Item -Path $FileName -Force -ErrorAction SilentlyContinue
Write-Output $PSCustomObject
} -ThrottleLimit 200 -AsJob
Write-Output $VMsJob
} -ThrottleLimit 200 -AsJob
I am not able to get it work, not sure what is wrong. One thing I observed while debugging is that, Get-AzVM command is getting VMs list from all the subscriptions rather than specific subscription. I came to know that by looking at the Out-File -FilePath $FileName which are generated.
sub1_server1_980337551.ps1
sub2_server1_42701325.ps1
server1 is only present in sub1 but it is being picked in sub2 as well.

Azure-Powershell Script error (ip address increment )

I hope you all doing well
can you please help me to solve this problem?
$ipnumber = 3
$RgName = "test"
$Location = "test"
for ($i = 0; $i -lt $ipnumber; $i++) {
$res1 = -join ((1..10) | ForEach-Object { get-random -minimum 97 -maximum 122 | ForEach-Object { [char]$_ } })
$res2 = -join ((1..10) | ForEach-Object { get-random -minimum 97 -maximum 122 | ForEach-Object { [char]$_ } })
$PublicIP = '$PublicIP'
$IpConfigName = '$IpConfigName'
$IpConfig = '$IpConfig'
$ip = '10.0.0.'
$x = 25
$PublicIP = $PublicIP + $i
$IpConfigName = $IpConfigName + $i
$IpConfig = $IpConfig + $i
$x = 25 + $i
$ip = $ip + $x
#####################################
# Create a public IP address
$PublicIP = New-AzPublicIpAddress `
-Name "$res1" `
-ResourceGroupName $RgName `
-Location $Location `
-AllocationMethod dynamic
#Create an IP configuration with a dynamic private IP address and assign the public IP address to it
$IpConfigName = "$res2"
$IpConfig = New-AzNetworkInterfaceIpConfig `
-Name $IpConfigName `
-Subnet $Subnet `
-PrivateIpAddress $ip `
-PublicIpAddress $PublicIP
}
$rand6 = -join ((1..10) | ForEach-Object { get-random -minimum 97 -maximum 122 | ForEach-Object { [char]$_ } })
$NIC = New-AzNetworkInterface `
-Name $rand6 `
-ResourceGroupName $RgName `
-Location $Location `
-NetworkSecurityGroupId $NSG.Id `
-IpConfiguration $IpConfig0, $IpConfig1, $IpConfig2
Error1 :
enter image description here
Error2 :
New-AzNetworkInterface: C:\Users\Marouane\Desktop\testpowershell\test2.ps1:95:22
Line |
95 | -IpConfiguration $IpConfig, $IpConfig0, $IpConfig1
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Cannot bind parameter 'IpConfiguration'. Cannot convert the "$IpConfig1" value of type "System.String" to type
| "Microsoft.Azure.Commands.Network.Models.PSNetworkInterfaceIPConfiguration".
I want to increment the IP by 1
Ex : 10.0.0.25 next 10.0.0.26 next 10.0.0.27
but it says that you can't put in 'IpConfiguration' String ips
So what should i do ?
please help me!!
Thank you
I think Mainich you are trying do the array operation.
If I understand correctly, you are trying to create to create an array of the below variable :
$PublicIP = $PublicIP + $i
$IpConfigName = $IpConfigName + $i
$IpConfig = $IpConfig + $i
For instance,
IpConfig1,Ipconfig2,Ipconfig3 - and store the value accordingly.
Unfortunately, the implementation is slightly wrong
Please note : I have detailed the operation only for the $ipconfig variable. You can extend the same to other variables like $IpConfigName,$PublicIP
What Exactly is Happening
For all the iteration, $IpConfig gets updated & the variables you intended IpConfig1,Ipconfig2,Ipconfig3 are not created through the above logic.
You are initializing the variable ipConfig as a string
Overwriting the value of the variable ipconfig with the string value
$ipconfig1 (for the first iteration)
Overwriting the string value of the variable ipconfig with object.
AzNetworkInterfaceIpConfig
End of the iteration 1,
$ipconfig is with the value AzNetworkInterfaceIpConfig
ipconfig1 is non existant.
Suggestion :
The below creates an empty array with a fixed number - in your case 3 as $ipnumber = 3
$ipconfig = #() * $ipnumber
$ipconfig.append(New-AzNetworkInterfaceIpConfig `
-Name <>`
-Subnet<>`
-PrivateIpAddress <> `
-PublicIpAddress <>)
( or )
$ipconfig[0] = New-AzNetworkInterfaceIpConfig `
-Name <>`
-Subnet<>`
-PrivateIpAddress <> `
-PublicIpAddress <>
Now you can access the values through ipconfig[0], ipconfig[1],ipconfig[2]
Or you can pass the $ipconfig directly - as the parameter Ip configuration accepts the array value
-IpConfiguration <PSNetworkInterfaceIPConfiguration[]>
https://learn.microsoft.com/en-us/powershell/module/az.network/new-aznetworkinterface?view=azps-5.0.0
Suggestion 2:
if you would prefer creating the variable on the fly like ipconfig1,ipconfig2,ipconfig3, then you could do the below :
New-Variable -Name "Ipconfig$i" -Value <>
New-Variable -Name "Ipconfig$i" -Value <>
Get-Variable -Name "Ipconfig$i" -ValueOnly

Azure Route Table Modification With Powershell

We have over 20 route tables that we need to be able to quickly modify.
We have 2 NVAs that could act as the next hop but we are not able to find the correct solution.
We are stuck at this:
$groupname = Get-AzResourceGroup | Out-GridView –PassThru | Select -ExpandProperty ResourceGroupName
$rt = Get-AzRouteTable -ResourceGroupName $groupname | Out-GridView -PassThru
$oldroutes = Get-AzRouteTable -ResourceGroupName $groupname | Get-AzRouteConfig | Where-Object -Property NextHopIpAddress -Like 1.1.1.1 | Select -ExpandProperty Name
foreach ($oldroutes in $oldroutes)
{
Set-AzRouteConfig -RouteTable $rt -Name $oldroute -NextHopIpAddress 2.2.2.2 | Set-AzRoutetable }
This works to the part that is able to go through all Route Tables and identify all routes that have next hop as 1.1.1.1 and store them in a variable called $oldroutes and only selecting the route Name which is needed by the Set-AZRouteConfig command.
It runs correct up until the Set part. Instead of Modifying it just sets the NextHopIpAddress as 2.2.2.2 and deletes all other values, so when we try to commit the changes we get an error.
Set-AzRoutetable : Address prefix string for resource ...... cannot be null
Has anyone done this or are we missing anything maybe?
Thank you in advance!
I can reproduce your issue, when using Set-AzRouteConfig | Set-AzRoutetable , the parameter -AddressPrefix is needed, if you don't want change it, you could specify it with the original one. And if you want to set -NextHopIpAddress, you need to specify the -NextHopType only with VirtualAppliance. And this part $oldroutes in $oldroutes is also has a mistake, it should be $oldroute in $oldroutes.
So in summary, your script should be like below.
$groupname = Get-AzResourceGroup | Out-GridView –PassThru | Select -ExpandProperty ResourceGroupName
$rt = Get-AzRouteTable -ResourceGroupName $groupname | Out-GridView -PassThru
$oldroutes = Get-AzRouteTable -ResourceGroupName $groupname | Get-AzRouteConfig | Where-Object -Property NextHopIpAddress -Like 1.1.1.1
foreach ($oldroute in $oldroutes)
{
Set-AzRouteConfig -RouteTable $rt -Name $oldroute.Name -AddressPrefix $oldroute.AddressPrefix -NextHopType VirtualAppliance -NextHopIpAddress 2.2.2.2 | Set-AzRoutetable
}

Resources