Friday, October 10, 2014

PowerShell + Azure Automation : Unleash the Automation Cmdlets

In my previous post, I blogged about using Azure Automation to deploy a Windows 10  Server Technical Preview VM in my test domain running on Azure.

But if you noticed there were lot of things that we needed to do from the Azure portal. Being an advocate of doing all things from PowerShell I gave it another shot and below is what I found (notice the colors based on what I was able to do with PowerShell)  :

1. Get the Ground work ready (few bits possible)
2. Create the Assets  (not possible at the moment)
3. Create a RunBook to deploy the VM (and add it to the domain) (PowerShell Rocks !)

PowerShell + Get the Ground work ready

As already mentioned at the moment you can't create an Azure Automation account with PowerShell, but what you can do is add a new User to Azure Active directory :)

You will have to review Software Requirements and then install the Azure AD module 

Once the module named "Msonline" is installed you can very well go ahead and create a new user.

First step is connect to the AD by supplying the credentials.

Note that the User credentials you are supplying should be sourced from Azure AD (from what I understood ). So the first user has to be created in the portal manually.

Once that is done we can use the credentials of the User like below to authenticate :
$cred = Get-Credential -UserName "" -Message "The Username is not the real one ;)"
Connect-MsolService -Credential $cred

The prompt will return which means you have successfully connected :)
Before adding a new User wanted to show what is the domain name for the User you create (this is needed to specify UPN for the User).

Run the cmdlet below to view the domain:

Name                                                                                      Status          Authentication
----                                                                                      ------          --------------                                                                Verified        Managed

Make a note of the name property.

Now let's spin up a new User :

PS>New-MsolUser -UserPrincipalName -DisplayName "TestPSUser" -FirstName "Test"  -LastName "PSUser"

Password                       UserPrincipalName              DisplayName                   isLicensed
--------                       -----------------              -----------                   ----------
Yama6969                       TestPSUser@dexterposh....       TestPSUser                    False

If you don't specify a password for the User account then you will get a temporary password account for the User back. 

After this the process of changing the password for the User and adding that user to manage the subscription is manual process.

Create the Assets  

From what I have explored so far this is not possible at the moment with PowerShell cmdlets or via REST APIs.

Once you have created the assets you should be good to go and from my observation it's more like of a one time activity. Once created you can play with assets inside runbooks.

PowerShell + Create a RunBook to deploy the VM

Once the Automation Account is set up (you have to do that from Azure Web portal ), then you can pretty much do everything done in the earlier post.

Let's start with creating a new Runbook named "testPS"

PS> $Automation = Get-AzureAutomationAccount
PS> New-AzureAutomationRunbook -Name testPS -Description 'RunBook created from PowerShell' -Tags Automation -AutomationAccountName $Automation.AutomationAccountName -Verbose

AccountId                 : 8f25145f-1f75-476e-a364-cfcd5d785843
Id                        : a6d49243-3862-4a03-93e0-76907b6d6d26
Name                      : testPS
CreationTime              : 10/10/2014 3:56:16 PM
LastModifiedTime          : 10/10/2014 3:56:22 PM
LastModifiedBy            :
Description               : RunBook created from PowerShell
IsApiOnly                 : False
IsGlobal                  : False
PublishedRunbookVersionId :
DraftRunbookVersionId     : 4a15116d-5961-4950-b08c-9d172c777467
Tags                      : {Automation}
LogDebug                  : False
LogVerbose                : False
LogProgress               : False
ScheduleNames             : {}

Now this is an empty runbook at the moment, I have to set the content of the runbook with the PowerShell workflow, am going to use the same workflow used in previous post but have to change the workflow name to testPS as the workflow name and runbook name should be the same.

Now below is a screenshot of the Workflow definition in my local machine (Notice I have super cool ISE Steriods post coming in future about that )

I have saved the Workflow as test.ps1 and now time to set this as my Runbook definition.

PS>Set-AzureAutomationRunbookDefinition -Name TestPS -Path 'C:\Users\Dexter\Documents\WindowsPowerShell\TestPS.ps1' -AutomationAccountName $Automation.AutomationAccountName -Verbose
Set-AzureAutomationRunbookDefinition : Runbook already has a draft. Specify the parameter to force an overwrite of this
At line:1 char:1
+ Set-AzureAutomationRunbookDefinition -Name TestPS -Path C:\Users\Dexter\Document ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Set-AzureAutomationRunbookDefinition], InvalidOperationException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.Automation.Cmdlet.SetAzureAutomationRunbookDefinition

PS>Set-AzureAutomationRunbookDefinition -Name TestPS -Path 'C:\Users\Dexter\Documents\WindowsPowerShell\TestPS.ps1' -AutomationAccountName $Automation.AutomationAccountName -Verbose -Overwrite

RunbookVersion                                               Content
--------------                                               -------
Microsoft.Azure.Commands.Automation.Model.RunbookVersion     workflow TestPS...

Note - If you want to log verbose messages for this Now go ahead and check the same in the Azure portal the runbook definition would have updated but it will be still in draft.

With PowerShell cmdlet you can't start a runbook which is in draft (with portal there is an option to test it ) . So we will publish the runbook.

PS>Publish-AzureAutomationRunbook -Name TestPS -AutomationAccountName $Automation.AutomationAccountName -Verbose

AccountId                 : 8f25134f-1f75-476e-a364-cfcd5d785843
Id                        : a6d49243-3862-4a03-93e0-76907b6d6d26
Name                      : testPS
CreationTime              : 10/10/2014 3:56:16 PM
LastModifiedTime          : 10/10/2014 5:07:17 PM
LastModifiedBy            : PowerShell
Description               : RunBook created from PowerShell
IsApiOnly                 : False
IsGlobal                  : False
PublishedRunbookVersionId : 6eb4f038-a086-4585-892e-f82af9c95396
DraftRunbookVersionId     :
Tags                      : {Automation}
LogDebug                  : False
LogVerbose                : False
LogProgress               : False
ScheduleNames             : {}

Now Let's invoke the runbook :

PS>Start-AzureAutomationRunbook -Name TestPS -AutomationAccountName $Automation.AutomationAccountName
Start-AzureAutomationRunbook : The runbook parameter "AzureConnectionName" is mandatory.
At line:1 char:1
+ Start-AzureAutomationRunbook -Name TestPS -AutomationAccountName $Automation.Aut ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Start-AzureAutomationRunbook], ArgumentException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.Automation.Cmdlet.StartAzureAutomationRunbook

If you don't specify arguments to mandatory parameters then you will get the error as above.

Now let's run the Runbook supplying proper Parameters and Arguments to it.
Note that with parameter named -Parameters we specify a Hashtable with Keys being the Paramter names of the Runbook and values are the arguments to those params.

PS>Start-AzureAutomationRunbook -Name TestPS -AutomationAccountName $Automation.AutomationAccountName  -Parameters @{Azure
ConnectionName="Visual Studio Ultimate with MSDN";ServiceName="DexCloudService";VMName="DexWindows10"} -Verbose

Id                     : 662707b5-fa8a-4798-8e09-6e4df09dc333
AccountId              : 8f25dadf-1f75-476e-a364-cfcd5d785843
Status                 : Activating
StatusDetails          : None
StartTime              :
EndTime                :
CreationTime           : 10/10/2014 5:53:09 PM
LastModifiedTime       : 10/10/2014 5:53:18 PM
LastStatusModifiedTime : 10/10/2014 5:53:18 PM
Exception              :
RunbookId              : a6d49243-3862-4a03-93e0-76907b6d6d26
RunbookName            : testPS
ScheduleName           :
JobParameters          : {AzureConnectionName, ServiceName, VMName}

We get back a Automation.Model.Job Object. We can probably script a loop which sleeps for few seconds and checks if the Status is Completed, something like below :
    Start-Sleep -Seconds 60 #sleep for 1 minute and query the Job
    Write-Verbose -Message 'Sleeping for 60 seconds' -Verbose
while((Get-AzureAutomationJob -RunbookName TestPS -AutomationAccountName $Automation.AutomationAccountName).Status -eq 'Completed')

Now let's see how much time the Runbook took to provision the VM ( this was asked by my fellow PowerShell MVP Chendrayan ):

PS>$Job = Get-AzureAutomationJob -RunbookName TestPS -AutomationAccountName $Automation.AutomationAccountName
PS>$Job.EndTime - $Job.StartTime

Days              : 0
Hours             : 0
Minutes           : 8
Seconds           : 53
Milliseconds      : 803
Ticks             : 5338030000
TotalDays         : 0.00617827546296296
TotalHours        : 0.148278611111111
TotalMinutes      : 8.89671666666667
TotalSeconds      : 533.803
TotalMilliseconds : 533803

WoW, Just in around 9 minutes I have a VM running the latest Server Tech preview in my test domain ;) 

Take a look at the job output by using :

Get-AzureAutomationJobOutput -Id (Get-AzureAutomationJob -RunbookName TestPS -AutomationAccountName $Automation.AutomationAccountName).Id -Stream Any -AutomationAccountName $Automation.AutomationAccountName

It surely beats downloading ISO / VHD and creating a VM in my own lab..Ohh ! Wait my LAB laptop was stolen that's why am on Azure :D

As you would know you can open a PSSession to the remote machine and verify that it was added to domain by following below code:

$WinRMURi = (Get-AzureWinRMUri -ServiceName DexCloudService -Name DexWindows10).AbsoluteUri
$Session = New-PSSession -ConnectionUri $WinRMURi -Credential (Get-Credential-Name DexWindows10 -SessionOption (New-PSSessionOption -SkipCACheck )

Invoke-Command -Session $Session -ScriptBlock {"{0} is in domain {1}" -f $env:COMPUTERNAME, $env:USERDOMAIN}

Now below is how it looks on my Console:

That's it play with the Azure PowerShell Cmdlets + Azure Automation feature they should give you ideas on doing some really cool stuff like scheduling a Runbook to run at a given time. I will leave that to your imagination :)

Until next post