PowerShell – Automating Server Builds In Azure – Pt. 2 – Rolling Servers To Their Silos

During this scripting session, I’ll be working on a system that is running PowerShell 5.0 (February 2015 release).

So now that I’ve put together a basic script for building out a server in Azure, I want to do even more by making the script and my environment more versatile.  So we’re going to go ahead and add some parameterization that will build a naming standard for the hostname and cloud service, as well as give our script the ability to deploy the server into a preconfigured VLAN.

Ultimately, my goal will be to deploy a system and apply a DSC configuration using a single script that will configure the system using standardized settings based on the role that I’ve selected initially.  But let’s concentrate on the basics first.

You might have noticed, using the previous script, that there are some things that are configured (or not) with your Azure server.  For example, the VM is built as a Standard A1 Standard system (1 core, 1.75 GB RAM).  It also deposits the machine in your root network instead of any subnets you may have configured.  We’re going to remedy that today.

Before I start with updating my script, I’m creating some virtual networks in my Azure environment to assign systems to.  At the time of this writing, handling this in my PowerShell script requires a little more work than what I’d like to do, so I’m creating my virtual networks through the Azure UI.  These newly created subnets will use a naming convention that will be recognizable to my script with minimal work.

Azure2-6

Now on to the scripting!  First, I’m going to create a parameterization for the server role.  This will be the core variable that will determine a number of settings for us.  Second, I’m going to create a switch for creating an availability group (more on this later).  I’m also adding the cmdletbinding function for use later.

[cmdletbinding()]
Param (

    [Parameter(Mandatory=$True)]
    [ValidateSet('IIS','PSWA','PRT')]
    [string]$Purpose,

    [switch]$Availability

)#End Param

Now I’m going to create the switch to work with my Purpose parameter.  The purpose of this switch will be to determine the future role of the server, its naming convention, what subnet to add the system to, as well as the DSC configuration to apply to it at a later time.  So basically, everything.

Switch ($Purpose){
    'IIS' {$_Purpose = 'IIS'};
    'PSWA' {$_Purpose = 'PSWA'};
    'PRT' {$_Purpose = 'PRT'}
    }#Switch

Now we’ll also add our standardized naming prefix, set up our server naming, and the default Azure location we want to use.

$RootName = "LWIN"
$ConvServerName = ($RootName + $_Purpose)
$Location = "West US"

So when our script executes and we specify the PSWA parameter, we’ll get this:

Azure2-1

And I’m also going to add some Write-Verbose data here for troubleshooting purposes if anything goes south later on.

Write-Verbose "Environment is $_Purpose"
Write-Verbose "Root name is $RootName"
Write-Verbose "Service will be $ConvServerName"
Write-Verbose "Datacenter location will be $Location"
If($Availability.IsPresent){Write-Verbose "Server will be assigned to $ConvServerName availability group."}

Since I’m building a fairly basic environment for now, I’m going to silo things by server role.  But before we deploy the machine, we’re going to check to see if a cloud service exists, and if not, create it using our $ConvServerName label.  For some reason, if you attempt to retrieve a service name that doesn’t exist, Azure throws a terminating error.  So we’re going to handle this with a Try/Catch statement, and leverage that to create the cloud service if it runs into this error.

Try 
    {Write-Verbose "Checking to see if cloud service $ConvServerName exists."
    Get-AzureService -ServiceName $ConvServerName -ErrorAction Stop 
    }#EndTry

Catch [System.Exception]
    {Write-Verbose "Cloud service $ConvServerName does not exist.  Creating new cloud service."
    New-AzureService $ConvServerName -Location $Location
    }#EndCatch

Now that we’ve created the cloud service, we’ll go ahead and create the host name that we’ll be using.  Since I’ll be rolling out servers in numerical order, I’m going to add some logic in to count the number of existing servers in the cloud service (if any) and create the next instance based on count.

$CountInstance = (Get-AzureVM -ServiceName $ConvServerName).where({$PSItem.InstanceName -like "*$ConvServerName*"}) | Measure-Object
$ServerNumber = ($CountInstance.Count + 1)
$NewServer = ($ConvServerName + ("{00:00}" -f $ServerNumber))
Write-Verbose "Server name $NewServer generated.  Executing VM creation."

So let’s add in our arguments table from last week and our boot image location.  We’re making some modifications over last week’s script to accomodate for some of the automation we’re performing.  We’re specifying the Basic_A1 instance size, as well as assigning the machine to a pre-configured subnet, and using our $ConvServerName variable to determine the service to put the machine into.

$BaseImage = (Get-AzureVMImage).where({$PSItem.Label -like "*Windows Server 2012 R2 Datacenter*" -and $PSItem.PublishedDate -eq "2/11/2015 8:00:00 AM" })

$AzureArgs = @{

    'ServiceName' = $ConvServerName
    'Name' = $NewServer
    'InstanceSize' = 'Basic_A1'
    'SubnetNames' = $_Purpose
    'VNetName' = 'LWIN.Azure'
    'ImageName' = $BaseImage.ImageName
    'AdminUserName' = 'LWINAdmin'
    'Password' = 'b0b$yerUncl3'
}

Now for our VM creation, we’re going to add some logic in to verify whether or not the machine already exists in the service (just in case!), and add a little error handling in case things get a little ugly.  We’ll also wrap this in an If statement for handling the build with and without the availability parameter selected.

If($Availability.IsPresent){
    Write-Verbose "Availability set requested.  Building VM with availability set configured."
    Try{
        Write-Verbose "Verifying if server name $NewServer exists in service $ConvServerName"
        $AzureService = Get-AzureVM -ServiceName $ConvServerName -Name $NewServer
            If (($AzureService.InstanceName) -ne $NewServer){
                New-AzureQuickVM -Windows @AzureArgs -AvailabilitySetName $ConvServerName
            }#EndIf
            Else {Write-Output "$NewServer already exists in the Azure service $ConvServerName"}#EndElse
        }
    Catch [System.Exception]{$ErrorMsg = $Error | Select-Object -First 1
                                Write-Verbose "VM Creation failed.  The error was $ErrorMsg"}#EndCatch
}#EndIf
Else{
        Write-Verbose "No availability set requested.  Building VM."
    Try{
        Write-Verbose "Verifying if server name $NewServer exists in service $ConvServerName"
        $AzureService = Get-AzureVM -ServiceName $ConvServerName -Name $NewServer
            If (($AzureService.InstanceName) -ne $NewServer){
                New-AzureQuickVM -Windows @AzureArgs
            }#EndIf
            Else {Write-Output "$NewServer already exists in the Azure service $ConvServerName"}#EndElse
        }
    Catch [System.Exception]{$ErrorMsg = $Error | Select-Object -First 1
                                Write-Verbose "VM Creation failed.  The error was $ErrorMsg"}#EndCatch
}#EndIf

So now we’ll go ahead and save our script and execute…

.\ServerDepl.ps1 -Purpose IIS -Availability -Verbose

Pt2PicA

And success!  So let’s check and verify that we have our service:

Pt2PicB

And that we have our VM.

Pt2PicC

And now we can check our VM config and verify that we have an availability group and the correct network.

Pt2PicD

Tada!

Next week I’ll be putting some of the finishing touches on this script to make it a bit more versatile.  And hopefully in the following week after that, I’ll be able to show off a little of what I’ve learned of DSC before heading out to the PowerShell Summit this April.  Stay tuned!

  • DexterPOSH

    Nice Scripted logic ! Liked the part where you place the Servers on the Subnets dedicated for the purpose they serve 🙂

    May I suggest exploring Azure Automation for this.

  • Thanks Dexter! I’ll be doing just that with the automation eventually if it’s not too costly (my subscription will entirely be out of pocket). Regardless, I’ll probably start exploring just that after I get my environment readily deployable and destroyable. 🙂