Automate the Deployment of Azure Firewall as a Network Virtual Appliance (NVA)
Published Jan 19 2021 06:02 AM 3,604 Views
Microsoft

Purpose:

 

The purpose of this post is to demonstrate how to automate the deployment of Azure Firewall to be used as an Network Virtual Appliance (NVA) in a Hub & Spoke architecture.  Our previous post on this subject (Using Azure Firewall as a Network Virtual Appliance (NVA) (microsoft.com) walked through this process as it would be done in the Azure Portal.  In this post we will show how to deploy this entire solution with one Powershell script.

 

Assumptions: Knowledge of creating Azure virtual machines and Azure virtual networks, as well as user-defined routes and peering is assumed.  The firewall rules described in this writing will allow all outbound traffic from resources in Spoke1 and Spoke2.  This configuration is for demonstration purposes only.  Depending on the security posture needed for a production environment, this configuration would likely be more tightly controlled from the firewall.  For our demonstration purposes, this configuration is being used for functionality and convenience.

 

Here are the items that are deployed with this automated solution:

 

Resource Group:

  • Just one named AzureFW

 

Virtual Networks:

Vnet

Address Space

Hub

10.200.0.0/16

Spoke1

10.201.0.0/16

Spoke2

10.020.0.0/16

 

Subnets:

Subnet

CIDR

Hub-Subnet

10.200.0.0/24

AzureFirewallSubnet

10.200.1.0/24

Spoke1-Subnet

10.201.0.0/24

Spoke2-Subnet

20.202.0.0/24

 

Virtual Network Peering:

  • Hub peers with Spoke1 (bi-directional)
    • Forwarding must be enabled
  • Hub peers with Spoke2 (bi-directional)
    • Forwarding must be enabled

 

Route Tables: 

  • RT-Spoke1
    • Attached to default subnet in Spoke1-Vnet.
    • Routes:
      • 0.0.0.0/0: Next Hop: <<Azure Firewall Private IP>>
    • RT-Spoke2
      • Attached to default subnet in Spoke2-Vnet.
      • Routes:
        • 0.0.0.0/0: Next Hop: <<Azure Firewall Private IP>>

Azure Firewall:

  • NAT Rule Collection:
    • Rule 1, priority 1000 allow:
      • Spoke1-RDP, allow traffic from any source to destination firewall public IP address on port 3389 which is translated to Spoke1 VM private IP address on port 3389
    • Network Rule Collections:
      • Rule 1, priority 2000, allow:
        • Spoke1-Outbound, allow all traffic from source 10.201.0.0/24 to any destination, all ports
        • Spoke2-Outbound, allow all traffic from source 10.202.0.0/24 to any destination, all ports

Virtual Machines:

  • (3) Windows VM’s
    • (1) VM in Hub VNet, hub-subnet
    • (1) VM in Spoke1 VNet, spoke1-subnet
    • (1) VM in Spoke2 Vnet, spoke2-subnet

 

 

Powershell Code Parameters/Notes:

 

The Powershell code used to deploy this solution can be easily modified to suit your needs.  This sample code was written to deploy the solution as a lab environment meant for testing.  Here are a few of the parameters that can be input or modified.

 

Param

Default

Options

Note

SubscriptionName

My-Subscription

 

 

RGBase

AzureFw

 

Base of Resource Group Name

AzureEnvironment

MAC

MAC, MAG, *Any*

Feeds a function to translate Azure Env

RG

$RGBase-RG

 

Adds "-RG" to RGBase for Resource Group Name

Location

EastUS

 

Azure Location

Script

c:\temp\Disable-WindowsFW.ps1

 

Temp location of script created to disable Windows Firewall on all VM's after creation

VMUser

AzureAdmin

 

Admin user for VM's if not using VMPWPrompt flag

VMPw

My@zurePW010203

 

Admin user password for VM's if not using VMPWPrompt

VMPwPrompt

$false

$true,$false

Flag if you want to use VM admin name/password from variables or if you want to be prompted to enter for each VM

HubNvetName

Hub-Vnet

 

Name of Hub Vnet

Spoke1VnetName

Spoke1-Vnet

 

Name Spoke1 Vnet

Spoke2VnetName

Spoke2-Vnet

 

Name of Spoke2 Vnet

HubSubnetName

HubSubnet

 

Name of Hub Subnet

Spoke1SubnetName

Spoke1Subnet

 

Name of Spoke1 Subnet

Spoke2SubnetName

Spoke2Subnet

 

Name of Spoke2 Subnet

HubVnetAddSpace

10.200.0.0/16

 

Hub Vnet Address Space

Spoke1VnetAddSpace

10.201.0.0/16

 

Spoke1 Vnet Address Space

Spoke2VnetAddSpace

10.202.0.0/16

 

Spoke2 Vnet Address Space

HubSubnetCIDR

10.200.0.0/24

 

Hub Subnet CIDR

HubAzFwSubnetCIDR

10.200.1.0/24

 

Azure Firewall Subnet CIDR

Spoke1SubnetCIDR

10.201.0.0/24

 

Spoke1 Subnet CIDR

Spoke2SubnetCIDR

10.202.0.0/24

 

Spoke2 Subnet CIDR

HubVMName

Hub-VM-01

 

Hub VM Name

Spoke1VMName

Spoke1-VM-01

 

Spoke1 VM Name

Spoke2VMName

Spoke2-VM-01

 

Spoke2 VM Name

HubVMIP

10.200.0.10

 

Hub VM IP

Spoke1VMIP

10.201.0.10

 

Spoke1 VM IP

Spoke2VMIP

10.202.0.10

 

Spoke2 VM IP

VMSize

Standard_B2ms

 

VM Sku Family (VM Size)

ConvertStorage

$true

$true, $false

Flag to run convert storage function.  Function that converts from Premium SSD disks to Standard SSD disks on all created VM's

VMPIP

$false

$true, $false

Flag for assigning Public IP to each VM created

OutFile

c:\temp\Create-AzureFW-As-NVA-FINAL_LOG.log

 

Path to output log file

VMStorageType

StandardSSD_LRS

 

Disk storage type used if $ConvertStorage is set to $true

 

 

Powershell Code:

 

 

 

 

 

 

 

 

param (
    [string]$SubscriptionName = "My-Subscription",
    [string]$RGBase = "AzureFW",
    [string]$AzureEnvironment = "MAC",
    [string]$RG = "$RGBase-RG",
    [string]$Location="EastUS",
    [string]$Script = "C:\temp\Disable-WindowsFW.ps1",
    [string]$VMUser = "AzureAdmin",
    [string]$VMPW = 'Azure@dmin010203',
    [switch]$VMPWPrompt = $false,
    [string]$HubVnetName = "Hub-vnet",
    [string]$Spoke1VnetName = "Spoke1-vnet",
    [string]$Spoke2VnetName = "Spoke2-vnet",
    [string]$HubSubnetName = "HubSubnet",
    [string]$Spoke1SubnetName = "Spoke1Subnet",
    [string]$Spoke2SubnetName = "Spoke2Subnet",
    [string]$HubVnetAddSpace = "10.200.0.0/16",
    [string]$Spoke1VnetAddSpace = "10.201.0.0/16",
    [string]$Spoke2VnetAddSpace = "10.202.0.0/16",
    [string]$HubSubnetCIDR = "10.200.0.0/24",
    [string]$HubAzFWSubnetCIDR = "10.200.1.0/24",
    [string]$Spoke1SubnetCIDR = "10.201.0.0/24",
    [string]$Spoke2SubnetCIDR = "10.202.0.0/24",
    [string]$HubVMName = "Hub-VM-01",
    [string]$Spoke1VMName = "Spoke1-VM-01",
    [string]$Spoke2VMName = "Spoke2-VM-01",
    [string]$HubVMIP = "10.200.0.10",
    [string]$Spoke1VMIP = "10.201.0.10",
    [string]$Spoke2VMIP = "10.202.0.10",
    [string]$VMSize = "Standard_B2ms",
    [switch]$ConvertStorage = $false,
    [switch]$VMPIP = $false,
    [string]$Outputfile = "c:\temp\Create-Azure-Firewall-As-NVA_Final_LOG.log",
    [string]$VMStorageType = "StandardSSD_LRS"
)

#Logging/Output Function

Function Log($out) {
    $t = [System.DateTime]::Now.ToString("yyyy.MM.dd hh:mm:ss")
    set-variable -Name Now -Value $t -scope Script
    $Out = $Now +" ---- "+$out
    $Out | add-content $Outputfile
    Write-Host $Out -ForegroundColor "Green"
}

#Begin Processing
$Start = Get-Date
Log "Starting Process:  $Start"

#Convert necessary parts for MAG
Switch ($AzureEnvironment)
{
    "MAG" {$AzureEnvironment = "AzureUSGovernment"}
    "MAC" {$AzureEnvironment = "AzureCloud"}
    "AzureUSGovernment" {$AzureEnvironment = "AzureUSGovernment"}
    "AzureCloud" {$AzureEnvironment = "AzureCloud"}
    Default {$AzureEnvironment = "AzureCloud"}
}

#Connect to Azure
Connect-AzAccount -Environment $AzureEnvironment
Select-AzSubscription -SubscriptionName $SubscriptionName

#Validate Locations
$AzLocations = ((Get-AzLocation).Location)
If ($AzLocations -inotcontains $Location)
{
    Log "Creating error because Location: $Location is not found in Connected Environment: $AzureEnvironment Locations: $AzLocations"
    Log "Stopping Script"
    Log "ERROR: Selected Azure Environment and Selected Azure Region Do Not Match.  Change Region or Azure Environment.  (eg MAG vs MAC in the -AzureEnvironment Parameter)"
    Write-Error "Selected Azure Environment and Selected Azure Region Do Not Match.  Change Region or Azure Environment.  (eg MAG vs MAC in the -AzureEnvironment Parameter)" -ErrorAction Stop
}

#Create new RG
Log "Creating Resource Group"
New-AzResourceGroup -Name $RG -Location $Location

#Create Virtual Network and Subnets
Log "Creating VNets"
$s1 = New-AzVirtualNetworkSubnetConfig -Name $HubSubnetName -AddressPrefix $HubSubnetCIDR
$s2 = New-AzVirtualNetworkSubnetConfig -Name "AzureFirewallSubnet" -AddressPrefix $HubAzFWSubnetCIDR
$s3 = New-AzVirtualNetworkSubnetConfig -Name $Spoke1SubnetName -AddressPrefix "$Spoke1SubnetCIDR"
$s4 = New-AzVirtualNetworkSubnetConfig -Name $Spoke2SubnetName -AddressPrefix "$Spoke2SubnetCIDR"
$Hubvnet = New-AzVirtualNetwork -Name $HubVnetName -Location $Location -ResourceGroupName $RG -AddressPrefix "$HubVnetAddSpace" -Subnet $s1,$s2
$Spoke1vnet = New-AzVirtualNetwork -Name $Spoke1VnetName -Location $Location -ResourceGroupName $RG -AddressPrefix "$Spoke1VnetAddSpace" -Subnet $s3
$Spoke2vnet = New-AzVirtualNetwork -Name $Spoke2VnetName -Location $Location -ResourceGroupName $RG -AddressPrefix "$Spoke2VnetAddSpace" -Subnet $s4

#Setup Peering Between Hub and Each Spoke
Log "Setting Up Peering"
Add-AzVirtualNetworkPeering -Name Hub-Spoke1 -VirtualNetwork $Hubvnet -RemoteVirtualNetworkId $Spoke1vnet.Id -AllowForwardedTraffic
Add-AzVirtualNetworkPeering -Name Spoke1-Hub -VirtualNetwork $Spoke1vnet -RemoteVirtualNetworkId $Hubvnet.Id -AllowForwardedTraffic
Add-AzVirtualNetworkPeering -Name Hub-Spoke2 -VirtualNetwork $Hubvnet -RemoteVirtualNetworkId $Spoke2vnet.Id -AllowForwardedTraffic
Add-AzVirtualNetworkPeering -Name Spoke2-Hub -VirtualNetwork $Spoke2vnet -RemoteVirtualNetworkId $Hubvnet.Id -AllowForwardedTraffic

#create Public IP for Firewall
Log "Setting up PIP for FW"
$FWPipName = $RGBase + "-FW-PIP"
$FWPip = New-AzPublicIpAddress -Name $FWPipName -ResourceGroupName $RG -Location $Location -AllocationMethod Static -Sku Standard

#Create AZFW
Log "Creating Firewall"
$FWName = $RGBase + "-AzFW"
$Azfw = New-AzFirewall -Name $FWName -ResourceGroupName $RG -Location $Location -VirtualNetwork $Hubvnet -PublicIpAddress $FWPip

#Add a rule to allow internal traffic
Log "Creating Rules to Firewall"
$Azfw = Get-AzFirewall -ResourceGroupName $RG
$FWPrivIP = $Azfw.IpConfigurations[0].PrivateIPAddress
$FWAssignedPIP = $FWPip.IPAddress
$NetRule3 = New-AzFirewallNetworkRule -Name "Spoke1-All" -Protocol "Any" -SourceAddress $Spoke1SubnetCIDR -DestinationAddress "*" -DestinationPort "*"
$NetRule4 = New-AzFirewallNetworkRule -Name "Spoke2-All" -Protocol "Any" -SourceAddress $Spoke2SubnetCIDR -DestinationAddress "*" -DestinationPort "*"
$NatRule1 = New-AzFirewallNatRule -Name "RDP-In" -Protocol "TCP" -SourceAddress "*" -DestinationAddress $FWAssignedPIP -DestinationPort "3389" -TranslatedAddress $Spoke1VMIP -TranslatedPort "3389"
Log "Creating Rule Collections for FW"
$NetRuleCollection2 = New-AzFirewallNetworkRuleCollection -Name "Allow-Outbound" -Priority 2000 -Rule $NetRule3,$NetRule4 -ActionType "Allow"
$NatRuleCollection1 = New-AzFirewallNatRuleCollection -Name "Inbound-RDP-Nat" -Priority 1000 -Rule $NatRule1
Log "Assigning Rule Collections to FW and Saving"
$Azfw.NetworkRuleCollections.Add($NetRuleCollection2)
$Azfw.NatRuleCollections.Add($NatRuleCollection1)
Set-AzFirewall -AzureFirewall $Azfw

#Create Route Tables and Routes
Log "Creating Route Tables"

#Spoke1 to Spoke2
$Spoke1RouteName = $RGBase + "Spoke1-AllTraffic"
$Spoke1RouteTableName = $RGBase + "Spoke1-RT"
$Spoke1Route = New-AzRouteConfig -Name $Spoke1RouteName -AddressPrefix "0.0.0.0/0" -NextHopType VirtualAppliance -NextHopIpAddress $FWPrivIP
$Spoke1RouteTable = New-AzRouteTable -Name $Spoke1RouteTableName -ResourceGroupName $RG -location $Location -Route $Spoke1Route -DisableBgpRoutePropagation

#Spoke2 to Spoke1
$Spoke2RouteName = $RGBase + "Spoke2-AllTraffic"
$Spoke2RouteTableName = $RGBase + "Spoke2-RT"
$Spoke2Route = New-AzRouteConfig -Name $Spoke2RouteName -AddressPrefix "0.0.0.0/0" -NextHopType VirtualAppliance -NextHopIpAddress $FWPrivIP
$Spoke2RouteTable = New-AzRouteTable -Name $Spoke2RouteTableName -ResourceGroupName $RG -location $Location -Route $Spoke2Route -DisableBgpRoutePropagation

#Associate route tables to Subnets
Log "Associating Route Tables to Subnets"
Set-AzVirtualNetworkSubnetConfig -VirtualNetwork $Spoke1Vnet -Name $Spoke1SubnetName -AddressPrefix $Spoke1SubnetCIDR -RouteTable $Spoke1RouteTable | Set-AzVirtualNetwork
Set-AzVirtualNetworkSubnetConfig -VirtualNetwork $Spoke2Vnet -Name $Spoke2SubnetName -AddressPrefix $Spoke2SubnetCIDR -RouteTable $Spoke2RouteTable | Set-AzVirtualNetwork

#Create an IP configuration with a static private IP address
Log "Creating IP Configs with Private IP for VMs"
$HubIpConfigName = "Hub-IPConfig"
$HubSubnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $Hubvnet -Name $HubSubnetName
$Spoke1IpConfigName = "Spoke1-IPConfig"
$Spoke1Subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $Spoke1vnet -Name $Spoke1SubnetName
$Spoke2IpConfigName = "Spoke2-IPConfig"
$Spoke2Subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $Spoke2vnet -Name $Spoke2SubnetName

#Check if PIP is required, get PIP, and set Ip Configurations accordingly
If ($VMPIP){
    #PIP Required.  Create new and set Ip Configs
    Log "VMPIP Flag Found.  Creating PIP and Adding to Each VM"
    $HubPIP1 = New-AzPublicIpAddress -Name "$HubVMName-NIC-PIP" -ResourceGroupName $RG -Location $Location -AllocationMethod Static -Sku Standard
    $Spoke1PIP1 = New-AzPublicIpAddress -Name "$Spoke1VMName-NIC-PIP" -ResourceGroupName $RG -Location $Location -AllocationMethod Static -Sku Standard
    $Spoke2PIP1 = New-AzPublicIpAddress -Name "$SPoke2VMName-NIC-PIP" -ResourceGroupName $RG -Location $Location -AllocationMethod Static -Sku Standard
    $HubIpConfig = New-AzNetworkInterfaceIpConfig -Name $HubIpConfigName -Subnet $HubSubnet -PrivateIpAddress $HubVMIP -PublicIpAddress $HubPIP1 -Primary
    $Spoke1IpConfig = New-AzNetworkInterfaceIpConfig -Name $Spoke1IpConfigName -Subnet $Spoke1Subnet -PrivateIpAddress $Spoke1VMIP -PublicIpAddress $Spoke1PIP1 -Primary
    $Spoke2IpConfig = New-AzNetworkInterfaceIpConfig -Name $Spoke2IpConfigName -Subnet $Spoke2Subnet -PrivateIpAddress $SPoke2VMIP -PublicIpAddress $Spoke2PIP1 -Primary
}
Else 
{
    #PIP NOT Required.  Set IP Config without PIP
    Log "VMPIP is NOT Found.  Creating IPConfig with NO PIP"
    $HubIpConfig = New-AzNetworkInterfaceIpConfig -Name $HubIpConfigName -Subnet $HubSubnet -PrivateIpAddress $HubVMIP -Primary
    $Spoke1IpConfig = New-AzNetworkInterfaceIpConfig -Name $Spoke1IpConfigName -Subnet $Spoke1Subnet -PrivateIpAddress $Spoke1VMIP -Primary
    $Spoke2IpConfig = New-AzNetworkInterfaceIpConfig -Name $Spoke2IpConfigName -Subnet $Spoke2Subnet -PrivateIpAddress $SPoke2VMIP -Primary
}

#Create & Configure NSG's for the NIC's
Log "Create NSG Rules"
$NSGRule = New-AzNetworkSecurityRuleConfig -Name "Allow-RDP" -Protocol Tcp -Direction Inbound -Priority 1000 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 3389 -Access Allow

# Create a network security group
Log "Create NSGs for VMs"
$HubNSG = New-AzNetworkSecurityGroup -ResourceGroupName $RG -Location $Location -Name "HubNetworkSecurityGroup" -SecurityRules $NSGRule
$Spoke1NSG = New-AzNetworkSecurityGroup -ResourceGroupName $RG -Location $Location -Name "Spoke1NetworkSecurityGroup" -SecurityRules $NSGRule
$Spoke2NSG = New-AzNetworkSecurityGroup -ResourceGroupName $RG -Location $Location -Name "Spoke2NetworkSecurityGroup" -SecurityRules $NSGRule

#Create the NICs
Log "Create NICs for VMs"
$HubNIC = New-AzNetworkInterface -Name "$HubVMName-NIC" -ResourceGroupName $RG -Location $Location -IpConfiguration $HubIpConfig -NetworkSecurityGroupId $HubNSG.Id
$Spoke1NIC = New-AzNetworkInterface -Name "$Spoke1VMName-NIC" -ResourceGroupName $RG -Location $Location -IpConfiguration $Spoke1IpConfig -NetworkSecurityGroupId $Spoke1NSG.Id
$Spoke2NIC = New-AzNetworkInterface -Name "$Spoke2VMName-NIC" -ResourceGroupName $RG -Location $Location -IpConfiguration $Spoke2IpConfig -NetworkSecurityGroupId $Spoke2NSG.Id

#Setup Credentials for VM's
If ($VMPWPrompt)
{
    Log "VMPWPrompt Found.  Prompting for VM Name/PW"
    $Cred = Get-Credential -UserName $VMUser -Message "Specify Credentials for VM OS"
} Else 
{
    Log "NO VMPWPrompt Found.  Using Specified Creds for VMs"
    $SecurePassword = ConvertTo-SecureString $VMPW -AsPlainText -Force
    $Cred = New-Object System.Management.Automation.PSCredential ($VMUser, $securePassword)
}

#Define the virtual machines
Log "Building Config for VMs"
$HubVirtualMachine = New-AzVMConfig -VMName $HubVMName -VMSize $VMSize
$HubVirtualMachine = Set-AzVMOperatingSystem -VM $HubVirtualMachine -Windows -ComputerName $HubVMName -ProvisionVMAgent -EnableAutoUpdate -Credential $Cred
$HubVirtualMachine = Add-AzVMNetworkInterface -VM $HubVirtualMachine -Id $HUbNIC.Id
$HubVirtualMachine = Set-AzVMSourceImage -VM $HubVirtualMachine -PublisherName 'MicrosoftWindowsServer' -Offer 'WindowsServer' -Skus '2016-Datacenter' -Version latest
$Spoke1VirtualMachine = New-AzVMConfig -VMName $Spoke1VMName -VMSize $VMSize
$Spoke1VirtualMachine = Set-AzVMOperatingSystem -VM $Spoke1VirtualMachine -Windows -ComputerName $Spoke1VMName -ProvisionVMAgent -EnableAutoUpdate -Credential $Cred
$Spoke1VirtualMachine = Add-AzVMNetworkInterface -VM $Spoke1VirtualMachine -Id $Spoke1NIC.Id
$Spoke1VirtualMachine = Set-AzVMSourceImage -VM $Spoke1VirtualMachine -PublisherName 'MicrosoftWindowsServer' -Offer 'WindowsServer' -Skus '2016-Datacenter' -Version latest
$Spoke2VirtualMachine = New-AzVMConfig -VMName $Spoke2VMName -VMSize $VMSize
$Spoke2VirtualMachine = Set-AzVMOperatingSystem -VM $Spoke2VirtualMachine -Windows -ComputerName $Spoke2VMName -ProvisionVMAgent -EnableAutoUpdate -Credential $Cred
$Spoke2VirtualMachine = Add-AzVMNetworkInterface -VM $Spoke2VirtualMachine -Id $Spoke2NIC.Id
$Spoke2VirtualMachine = Set-AzVMSourceImage -VM $Spoke2VirtualMachine -PublisherName 'MicrosoftWindowsServer' -Offer 'WindowsServer' -Skus '2016-Datacenter' -Version latest

#Create the virtual machine
Log "Creating VMs"
New-AzVM -ResourceGroupName $RG -Location $Location -VM $HubVirtualMachine -Verbose
New-AzVM -ResourceGroupName $RG -Location $Location -VM $Spoke1VirtualMachine -Verbose
New-AzVM -ResourceGroupName $RG -Location $Location -VM $Spoke2VirtualMachine -Verbose
Start-Sleep -Seconds 15

#Create Script to Disable Windows Firewall
Log "Creating Script to Disable Windows FW in VM OS"
'Get-netfirewallprofile | Set-netfirewallprofile -enabled "false"' | out-file -FilePath $Script -Force

#Run Script Inside VM's to Disable Windows Firewall
Log "Execute Script to Disable Windows FW Inside VMs"

Invoke-AzVMRunCommand -ResourceGroupName $RG -Name $HubVMName -CommandId "RunPowerShellScript" -ScriptPath $Script

Invoke-AzVMRunCommand -ResourceGroupName $RG -Name $Spoke1VMName -CommandId "RunPowerShellScript" -ScriptPath $Script

Invoke-AzVMRunCommand -ResourceGroupName $RG -Name $Spoke2VMName -CommandId "RunPowerShellScript" -ScriptPath $Script

#Remove Script to Disable Windows Firewall
Log "Remove Generated Script"
Remove-Item -Path $Script -Force

#If ConvertSTorage is $True, convert storage of all VM's in RG.  Includes Shutdown and Startup.
If ($ConvertStorage)
{

    #Get the VM's in the RG, Stop the VM's, Get the Disks for the VM's, Set the Disk to SSD Standard, Start the VM's
    Log "ConvertStorage Flag Found.  Changing Storage to $StorageType"
    $VMs = Get-AzVM -ResourceGroupName $RG
    Foreach ($VM in $VMs)
    {
        #Wait for VM's to Finish Provisioning
        $Count = 0
        Do
        {
            $Count++
            Log "Sleeping for VM Provisioning...Trying to Convert Storage for $($VM.Name)"
            Log "Loop Iteration Count: $Count"
            Start-Sleep -Seconds 10
            $V = $null
            $V = Get-AzVM -ResourceGroupName $RG -Name $VM.Name
            Log "VM: $($V.Name) is in Provisioning State: $($V.ProvisioningState)"
        } Until ($V.ProvisioningState -eq "Succeeded")
        Log "Stopping VM $($VM.Name) for Storage Conversion"
        Stop-AzVM -ResourceGroupName $RG -Name $VM.name -Force
        Start-Sleep -Seconds 15
        $VMID = $null
        $VMID = $VM.Id
        $vmDisks = $null
        $vmDisks = Get-azDisk -ResourceGroupName $RG | Where-Object {$_.managedby -eq $VMID}
    
        Foreach ($Disk in $vmDisks)
        {
            Log "Converting Storage for Disk"
            $disk.Sku = [Microsoft.Azure.Management.Compute.Models.DiskSku]::new($VMStorageType)
           $disk | Update-AzDisk
        }
    }
 
#Start VMs
    Foreach ($VM in $VMs)
    {
        Log "Starting VM $($VM.Name) After Disk Conversion"
        Start-AzVM -ResourceGroupName $RG -Name $VM.name -NoWait
    }
}

$Stop = Get-Date
Log "Output Log File Stored to: $Outputfile"
Log "Total Duration: $(($Stop-$Start).Minutes) Minutes"

 

 

 

 

 

 

 

 

 

Version history
Last update:
‎Nov 07 2022 10:30 AM
Updated by: