Skip to main content

PowerShell + Azure Automation : Deploy a Windows 10 VM (Server Tech Preview) & domain join

Recently VM running Server Technical Preview were added Azure Gallery and I thought of deploying a VM running it joined to my test domain (see my post on using PowerShell to deploy a DC on Azure ), But I wanted to do that using Azure Automation.

Azure Automation is a highly scalable workflow engine offering where we can have Runbooks (PowerShell workflows) to automate long running , error prone tasks.

I first saw this in action at one of the sessions by Ravi Sir [PowerShell MVP] at one of the PowerShell Bangalore User Group meet where he used Runbooks to demonstrate really cool stuff.

Note - Azure Automation is still in preview so you might have to sign up for it by navigating to https://account.windowsazure.com/PreviewFeatures

Divided the post into 3 steps:
1. Get the Ground work ready
2. Create the Assets
3. Create a RunBook to deploy the VM (and add it to the domain)


Get the Ground work ready


First of all create an Azure Automation account from the portal (no PowerShell cmdlet for that at the moment). Once done it should show up in the Azure portal like below :








So before we start doing anything else we need a mechanism in place to authenticate to Azure against my subscription. There are two ways to achieve that (links in Resource section) :

  1. Using Azure Active Directory
  2. Using Certificate Based Authentication

In my opinion using Azure AD for the authentication is better and easier to explain (am gonna use that for this post) . The below steps of using Azure AD for Authentication are borrowed from the Azure blog found here.

Now Let's head to our Azure Active Directory > Default Directory > Users > Add User.






Then after this you will see a wizard to add a User, Key in the UserName:
Click Next, Be careful and do not enable Multi-Factor auth.



On next screen , Click "Create Temporary password".  Make a note of Complete Username and temporary password. We will change it in next step.



Now logout of the browser or open another web browser and navigate to https://manage.windowsazure.com/ .

Now on the Sign-in page , you have to specify the username of the User you created above (that's why we had to make a note of the Username and Password).
After this you will be asked to enter password (key in the temporary password). Log-in and you will be asked to change your password, do that.


Create the Assets

Let's look at what we are trying to do here, We are going to author a PowerShell Workflow which will deploy a VM with Server Technical Preview on Azure and then add the machine to my domain :)

But before we get ahead of ourselves we have few important questions to answer here.

  1. How does my workflow authenticate to Azure to add a new VM to my Cloud service ?
  2. How to set Local Admin Username and Password for the VM deployed ? (We can hardcode these). 
  3. Once the VM is up how to add it to the domain using another set of Credentials ?


This is where the Assets kick in (no Cmdlets as of now ).


We will have to create 3 Credential assets (stores Username and Password) to tackle this problem at hand. Creating Assets is very straight ahead :

Navigate to Azure Automation > Your Automation Account > Assets > Click on "Add Setting" (at the bottom). After this you will be presented with a page like below :

Select "Add Credential"

Give a Name to the Asset, have kept the Asset name same as the Azure AD User name.


 In the next screen give the User name and Password of the User we added in the first step to Azure AD.



The first asset is created, Now I will similarly add 2 mores credentials asset named "DomainDexterPOSH" of a User in my Domain dex.com which has permissions to add a Machine to my domain and another set of credential called "LocalDexterPOSH" to set the local admin username and password for the New VM which our workflow will provision.



Create a RunBook to deploy the VM



Under the Automation account in Azure portal, I can create a new RunBook from Scratch (supports authoring workflows) or create a workflow locally and upload it using the Azure Automation cmdlets. The Azure Automation cmdlets are evolving at the moment , So for this post let's focus more on using the portal to author RunBooks.

Let's get familiarize with how a  runbook looks like in the Azure portal.
To create a new runbook in portal you can click "New" and give it a name (see below)



Your runbook name should be unique among your runbooks.
Once done you will see the Runbook sitting in your Azure Automation account.





Click on it and it lands you to the editing area for the Runbook. See the below screenshot :



Notice that the Workflow Name and the Runbook name has to be the same.

The Workflow editor is pretty cool, you can play with that. This is one way of authoring things up in the Web browser.

Now the Runbooks in Azure Automation are essentially PowerShell workflows and they have few differences from the workflows which we author locally (if you have done that ). The whole workflow is available at the below link: 
https://gist.github.com/DexterPOSH/e9dceb72a6f171bd3d97

Basically you copy the whole workflow and paste it in your runbook. 
Once you have your workflow authored and tested well, you can Publish it. This way you can use that Runbook inside another runbooks too.




Once the workflow is Published, you can run it by clicking on the "Start" button at the end (this only shows up after you publish it...If you want to run a workflow while authoring it then there is "Test" button see above pic):



Clicking on "Start" will prompt you to supply arguments to the parameters, like below:

After this you can view the Job




Note - The workflow is very specific to my test LAB on Azure and you will have to substitute values and tweak the workflow for your own environment.

Below is an attempt to explain what the workflow does.

the parameters our Runbook will take:


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
workflow New-TestVM
{
    param(
        [parameter(Mandatory)]
        [String]
        $AzureConnectionName,
  
        [parameter(Mandatory)]
        [String]
        $ServiceName,
   
        [parameter(Mandatory)]
        [String]
        $VMName,
                   
        [parameter()]
        [String]
        $InstanceSize = "Medium"
  
    )

So the Workflow named New-TestVM (Note here that the workflow name and the Runbook name should be the same) will take 4 parameters for specifying the below :


  1. Subscription Name ($AzureConnectionName)
  2. Cloud Service Name ($ServiceName)
  3. Name of the new VM to provision ($VMname)
  4. Instance size of the VM ($InstanceSize which by default is Medium)


The first step in a Runbook is to authenticate to Azure this is where we use the below code snippet :


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
$verbosepreference = 'continue'

    #Get the Credentials to authenticate agains Azure
    Write-Verbose -Message "Getting the Credentials"
    $Cred = Get-AutomationPSCredential -Name "AuthAzure"
    $LocalCred = Get-AutomationPSCredential -Name "LocalDexterPOSH"
    $DomainCred = Get-AutomationPSCredential -Name "DomainDexterPOSH"



    #Add the Account to the Workflow
    Write-Verbose -Message "Adding the AuthAzure Account to Authenticate"
    Add-AzureAccount -Credential $Cred

    #select the Subscription
    Write-Verbose -Message "Selecting the $AzureConnectionName Subscription"
    Select-AzureSubscription -SubscriptionName $AzureConnectionName

    #Set the Storage for the Subscrption
    Write-Verbose -Message "Setting the Storage Account for the Subscription"
    Set-AzureSubscription -SubscriptionName $AzureConnectionName -CurrentStorageAccountName "dexterposhstorage"

In the above code snippet we retrieve all of the Assets we created then we use one of them $cred to authenticate to Azure. After that we select the Subscription against which our workflow will run (& automate stuff). One more thing as we are going to create a new VM we will need to specify the storage account as well.


Now the below code snippet will :

  • Get the Image details using Get-AzureVMImage and store the ImageName property in a variable
  • Then we derive the Username and Password from the LocalCred which stores one of the Credential Asset
  • Finally we specify all the configurations to the cmdlet New-AzureQuickVM (e.g SubnetName, Username, Password, ImageName etc.). Note the user of -WaitForBoot switch will pass on the control to next activity once the VM is up and running.
  • After this let's do a checkpoint so that if something fails in our workflow past this point, it should be able to resume from here.



001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
#Select the most recent Server 2012 R2 Image
    Write-Verbose -Message  "Getting the Image details"
    $imagename = Get-AzureVMImage |
                     where-object -filterscript { $_.ImageFamily -eq “Windows Server Technical Preview” } |
                     Sort-Object -Descending -Property PublishedDate |
                     Select-Object -First 1 |
                     select -ExpandProperty ImageName

    #use the above Image selected to build a new VM and wait for it to Boot
    $Username = $LocalCred.UserName
    $Password = $LocalCred.GetNetworkCredential().Password

    New-AzureQuickVM -Windows -ServiceName $ServiceName -Name $VMName -ImageName $imagename -Password $Password -AdminUsername $Username -SubnetNames "Rest_LAB" -InstanceSize $InstanceSize  -WaitForBoot
    Write-Verbose -Message "The VM is created and booted up now..Doing a checkpoint"

    #CheckPoint the workflow
    CheckPoint-WorkFlow
    Write-Verbose -Message "Reached CheckPoint"

This will take some time and after the machine is provisioned, we have another task at hand of adding the new VM to our domain. This can be achieved by opening a PSSession to the new VM and performing the action.

Below is the code which will open a PSSession to the machine using the LocalCred Asset and perform the domain join passing the DomainCred as argument to the Scriptblock:


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028

    #Call the Function Connect-VM to import the Certificate and give back the WinRM uri
    $WinRMURi = Get-AzureWinRMUri -ServiceName $ServiceName -Name $VMName | Select-Object -ExpandProperty AbsoluteUri

    InlineScript
    {
        do
        {
            #open a PSSession to the VM
            $Session = New-PSSession -ConnectionUri $Using:WinRMURi -Credential $Using:LocalCred -Name $using:VMName -SessionOption (New-PSSessionOption -SkipCACheck ) -ErrorAction SilentlyContinue
            Write-Verbose -Message "Trying to open a PSSession to the VM $VMName "
        } While (! $Session)
 
        #Once the Session is opened, first step is to join the new VM to the domain
        if ($Session)
        {
            Write-Verbose -Message "Found a Session opened to VM $using:VMname. Now will try to add it to the domain"
                              
            Invoke-command -Session $Session -ArgumentList $Using:DomainCred -ScriptBlock {
                param($cred)
                Add-Computer -DomainName "dex.com" -DomainCredential $cred
                Restart-Computer -Force
            }
        }  
    }
#Workflow end



Resources:

Use Azure AD to authenticate to Azure
http://azure.microsoft.com/blog/2014/08/27/azure-automation-authenticating-to-azure-using-azure-active-directory/

Popular posts from this blog

Test connectivity via a specific network interface

Recently while working on a Private cloud implementation, I came across a scenario where I needed to test connectivity of a node to the AD/DNS via multiple network adapters.  Many of us would know that having multiple network routes is usually done to take care of redundancy. So that if a network adapter goes down, one can use the other network interface to reach out to the node. In order to make it easy for everyone to follow along, below is an analogy for the above scenario: My laptop has multiple network adapters (say Wi-Fi and Ethernet) connected to the same network. Now how do I test connectivity to a Server on the network only over say Wi-Fi network adapter?

PowerShell + SCCM : Run CM cmdlets remotely

Today I saw a tweet about using implicit remoting to load the Configuration Manager on my machine by Justin Mathews . It caught my eye as I have never really tried it, but theoretically it can be done. Note - The second tweet says "Cannot find a provider with the name CMSite", resolution to which is in the Troubleshooting section at the end.

PowerShell : Trust network share to load modules & ps1

Problem Do you have a central network share, where you store all the scripts or PowerShell modules ? What happens if you try to run the script from a network share ? or if you have scripts (local) which invoke scripts or import PowerShell modules stored on this network share ? Well you would see a security warning like below (Note - I have set execution policy as 'Unrestricted' not 'bypass' here): Run a .ps1 from the network share Well this is a similar warning, which you get when you download scripts from Internet. As the message says run Unblock-File cmdlet to unblock the script and then run it, let's try it.