Automating VM Deployment in Azure with PowerShell

Published:18 October 2024 - 4 min. read

Automating tasks in Azure can save significant time and effort, especially when deploying resources like virtual machines (VMs). In this blog post, we’ll walk through automating the deployment of a Windows VM in Azure using PowerShell. We’ll configure networking, security, and install IIS for a web server all in one script.

Why Automate VM Deployment?

Automating tasks such as virtual machine deployment ensures consistency, reduces errors, and accelerates the setup process. With PowerShell, you can define all the necessary parameters and let the script handle the deployment, instead of manually creating each resource via the Azure Portal.

Here’s a step-by-step breakdown of the PowerShell script used to deploy an Azure VM, configure networking, set security rules, and install IIS.

Here’s the script if you just want to download it.

[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [string]$ResourceGroupName = 'Admissions',

    [Parameter(Mandatory)]
    [string]$Location = 'East US',

    [Parameter(Mandatory)]
    [string]$VMName = 'ADMISSIONSWEB',

    [Parameter(Mandatory)]
    [string]$VMSize = 'Standard_DS3_v2',

    [Parameter(Mandatory)]
    [string]$PublicIPName = 'Admissions-PubIp',

    [Parameter(Mandatory)]
    [string]$NICName = 'Admissions-vNIC',

    [Parameter(Mandatory)]
    [string]$OSDiskName = 'Admissions-OSDisk',

    [Parameter(Mandatory)]
    [string]$NSGName = 'ADMISSIONSWEB-NSG',

    [Parameter(Mandatory)]
    [string]$AdminUsername = 'adam',

    [Parameter(Mandatory)]
    [SecureString]$AdminPassword = (ConvertTo-SecureString 'P@$$w0rd12' -AsPlainText -Force)
)

$ErrorActionPreference = 'Stop'

function CreateAzResource {
    param(
        [Parameter(Mandatory)]
        [string]$ResourceType,
        
        [Parameter(Mandatory)]
        [string]$Name,
        
        [Parameter(Mandatory)]
        [hashtable]$NewParameter
    )
    
    try {
        $resource = & "Get-Az$ResourceType" -Name $Name -ResourceGroupName $NewParameter.ResourceGroupName
    } catch {
        if ($_.Exception.Message -match "under resource group '.*' was not found") {
            $resource = & "New-Az$ResourceType" @NewParameter
        } else {
            throw $_
        }
    } finally {
        $resource
    }
}

#region VM Configuration
$vmconfig = New-AzVMConfig -VMName $VMName -VMSize $VMSize
#endregion

#region Public IP Creation
$newPublicIpParams = @{
    Name              = $PublicIPName
    ResourceGroupName = $ResourceGroupName
    AllocationMethod  = 'Static'
    Location          = $Location
}
$publicIp = CreateAzResource -ResourceType PublicIpAddress -Name $PublicIPName -NewParameter $newPublicIpParams
#endregion

#region Network Interface Configuration
$vNet = Get-AzVirtualNetwork -ResourceGroupName $ResourceGroupName
$subnetId = $vNet.Subnets[0].Id

$newVNicParams = @{
    Name              = $NICName
    ResourceGroupName = $ResourceGroupName
    Location          = $Location
    SubnetId          = $subnetId
    PublicIpAddressId = $publicIp.Id
}
$vNic = CreateAzResource -ResourceType NetworkInterface -Name $NICName -NewParameter $newVNicParams
#endregion

#region OS Configuration
$cred = New-Object System.Management.Automation.PSCredential ($AdminUsername, $AdminPassword)
$newVmOsParams = @{
    Windows          = $true
    ComputerName     = $VMName
    Credential       = $cred
    EnableAutoUpdate = $true
    VM               = $vmconfig
}
$vm = Set-AzVMOperatingSystem @newVmOsParams
#endregion

#region Image Configuration
$newSourceImageParams = @{
    PublisherName = 'MicrosoftWindowsServer'
    Offer         = 'WindowsServer'
    Skus          = '2019-Datacenter'
    Version       = 'latest'
    VM            = $vm
}
$vm = Set-AzVMSourceImage @newSourceImageParams
#endregion

#region Disk Configuration
$vm = Set-AzVMOSDisk -VM $vm -Name $OSDiskName -CreateOption FromImage
#endregion

#region Network Interface Attachment
$vm = Add-AzVMNetworkInterface -VM $vm -Id $vNic.Id
#endregion

#region NSG Configuration
$newNsgParams = @{
    ResourceGroupName = $ResourceGroupName
    Location          = $Location
    Name              = $NSGName
}
$nsg = CreateAzResource -ResourceType NetworkSecurityGroup -Name $NSGName -NewParameter $newNsgParams

$ruleExists = $nsg.SecurityRules.Name -contains "Allow-RDP"
if (-not $ruleExists) {
    $ip = Invoke-RestMethod -Uri "https://api.ipify.org"
    $rdpRule = New-AzNetworkSecurityRuleConfig -Name "Allow-RDP" -Description "Allow RDP" -Access Allow -Protocol Tcp -Direction Inbound -Priority 1000 -SourceAddressPrefix $ip -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 3389
    $nsg.SecurityRules.Add($rdpRule)
    $nsg | Set-AzNetworkSecurityGroup
}
#endregion

#region Web Traffic Rule Configuration
$webRuleExists = $nsg.SecurityRules.Name -contains "Allow-Web"
if (-not $webRuleExists) {
    $webRule = New-AzNetworkSecurityRuleConfig -Name "Allow-Web" -Description "Allow HTTP" -Access Allow -Protocol Tcp -Direction Inbound -Priority 200 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 80
    $nsg.SecurityRules.Add($webRule)
    $nsg | Set-AzNetworkSecurityGroup
}
#endregion

$vNic.NetworkSecurityGroup = $nsg
$vNic | Set-AzNetworkInterface

#region VM Creation
$newVmParams = @{
    ResourceGroupName = $ResourceGroupName
    VM                = $vm
    Location          = $Location
}
CreateAzResource -ResourceType VM -Name $VMName -NewParameter $newVmParams
#endregion

#region IIS Installation
$installIIS = {
    Install-WindowsFeature -Name Web-Server -IncludeManagementTools
}
$installScript = [scriptblock]::create($installIIS)
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroupName -VMName $VMName -CommandId 'RunPowerShellScript' -ScriptString $installScript
#endregion

#region Output Public IP
$publicIp.IpAddress

Define Parameters

The script starts by defining parameters that will be used throughout the deployment. These include basic information such as resource group, VM name, location, and admin credentials. Using parameters ensures the script is flexible and can be reused for different environments.

param(
    [Parameter(Mandatory)]
    [string]$ResourceGroupName = 'Admissions',
    [Parameter(Mandatory)]
    [string]$Location = 'East US',
    [Parameter(Mandatory)]
    [string]$VMName = 'ADMISSIONSWEB',
    [Parameter(Mandatory)]
    [string]$VMSize = 'Standard_DS3_v2',
    [Parameter(Mandatory)]
    [string]$PublicIPName = 'Admissions-PubIp',
    [Parameter(Mandatory)]
    [string]$NICName = 'Admissions-vNIC',
    [Parameter(Mandatory)]
    [string]$OSDiskName = 'Admissions-OSDisk',
    [Parameter(Mandatory)]
    [string]$NSGName = 'ADMISSIONSWEB-NSG',
    [Parameter(Mandatory)]
    [string]$AdminUsername = 'adam',
    [Parameter(Mandatory)]
    [SecureString]$AdminPassword = (ConvertTo-SecureString 'P@$$w0rd12' -AsPlainText -Force)
)

Creating Azure Resources

The `CreateAzResource` function helps create various Azure resources like public IPs, network interfaces, and network security groups (NSGs). It attempts to get a resource and creates it if it doesn’t exist, making the process more efficient.

function CreateAzResource {
    param(
        [Parameter(Mandatory)]
        [string]$ResourceType,
        [Parameter(Mandatory)]
        [string]$Name,
        [Parameter(Mandatory)]
        [hashtable]$NewParameter
    )
    
    try {
        $resource = & "Get-Az$ResourceType" -Name $Name -ResourceGroupName $NewParameter.ResourceGroupName
    } catch {
        if ($_.Exception.Message -match "not found") {
            $resource = & "New-Az$ResourceType" @NewParameter
        } else {
            throw $_
        }
    } finally {
        $resource
    }
}

VM Configuration and Networking Setup

Once resources like public IP and virtual networks are created, the script configures a virtual machine (VM) using `New-AzVMConfig`, attaches a network interface, and sets up the operating system and disk.

$vmconfig = New-AzVMConfig -VMName $VMName -VMSize $VMSize

$vNic = CreateAzResource -ResourceType NetworkInterface -Name $NICName -NewParameter $newVNicParams
$vm = Set-AzVMOperatingSystem @newVmOsParams
$vm = Set-AzVMSourceImage @newSourceImageParams
$vm = Set-AzVMOSDisk -VM $vm -Name $OSDiskName -CreateOption FromImage

Configuring Network Security Rules

The script adds security rules to the NSG to allow inbound RDP (Remote Desktop) and web traffic on port 80 (HTTP). This is critical for managing the VM and hosting web applications.

$ruleExists = $nsg.SecurityRules.Name -contains "Allow-RDP"
if (-not $ruleExists) {
    $ip = Invoke-RestMethod -Uri "https://api.ipify.org"
    $rdpRule = New-AzNetworkSecurityRuleConfig -Name "Allow-RDP" -Access Allow -Protocol Tcp -SourceAddressPrefix $ip -DestinationPortRange 3389
    $nsg.SecurityRules.Add($rdpRule)
    $nsg | Set-AzNetworkSecurityGroup
}

Installing IIS

After the VM is created, the script installs IIS (Internet Information Services), which will allow the VM to act as a web server. This is done by executing a PowerShell command on the VM.

$installIIS = {
    Install-WindowsFeature -Name Web-Server -IncludeManagementTools
}
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroupName -VMName $VMName -CommandId 'RunPowerShellScript' -ScriptString $installIIS

Output the Public IP

Finally, the public IP of the VM is outputted for easy access to the web server.

$publicIp.IpAddress

Conclusion

With this PowerShell script, deploying a Windows VM on Azure with configured networking, security, and IIS setup becomes seamless. This level of automation is invaluable for repetitive tasks and ensures that your infrastructure is set up consistently.

By following this guide, you can deploy VMs quickly and focus more on configuring applications rather than spending time manually setting up resources.

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!