Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / Azure

Azure VM Operations through Runbooks

4.00/5 (3 votes)
9 Sep 2016CPOL5 min read 12.7K  
This article describes how to configure Runbook feature of Azure Automation and utilization of the same to manage and optimize cost of infrastructure resources in Azure.

Runbook

This article walks you through the creation of a Runbook in Azure Automation. We'll start with creating a simple Runbook to Start VM that we'll test and publish. Then, we'll walk you through how to create the Runbook for Stop VM to actually manage Azure resources.

What is Runbook?

Automation of PowerShell scripts execution can be achieved through implementation of Runbook in Azure Portal. Runbook can be scheduled as a job to atomized PowerShell script execution. Also, Webhook can be created on Runbook. Webhook can be used as a http endpoint to trigger Runbook from application.

Through Runbook, most of resources management process automation is possible in Azure portal, i.e., creation, deletion, start, stop of VM/ Vnet/NSG/Public IP/NIC, etc.

Cost optimization can be achieved by automatically spinning up and spinning off VMs through Runbook execution when VMs are not in use.

Some organizations required VMs up on some specific dates on month to achieve some activities to finish. Rest of the month VM are not in use, at that time VM can be stopped(deallocated)/deleted. Whenever VM is required or first request received from first user, at that time VM can be automatically created/started dynamically through Runbook Webhook implementation. Will see this use case in this article.

Runbook vs PowerShell

Azure automation is supported using two options, Windows PowerShell and workflow which is known as a Runbook. Even Runbook internally uses PowerShell scripts. But the major benefit is Runbook itself executes in Azure portal context. This execution is secure and less error prone.

To execute PowerShell script in Azure from local machine, the user has to authenticate through either certificate publish or Azure login cmdlets. The user has to provide username, password and subscription id.

Where Runbook executes with service account on Azure portal. Service account can be created through Credential object in automation account and co-administrator role level user in active directory assigned to this service account.

In PowerShell script, “Verbose” switch can be specified with cmdlets for user confirmation, whereas in Runbook, you can’t specify “Verbose” switch and couldn’t make user interactive confirmation while execution.

Wherever confirmation is required from user in PowerShell script execution in Runbook, “Force” switch can be specified to override execution of cmdlets in Runbook.

Create Runbook

Steps to create Runbook and execute in Azure Portal:

Create new automation account

Click on New in upper right corner on Azure new portal and type automation in textbox. It will populate Azure automation account resource as per below.

Image 1

Select Automation option. It will redirect to the below screen.

Image 2

Click on Create button. It will redirect to the below screen.

Image 3

Image 4Provide Automation account name, select subscription, resource group in which you want to create automation account. Location will be populated by default based on resource group selection. Select Yes on Create Azure Run as Account. Once automation account will be created, it will be shown on resources list blade as below:

Image 5

Clicking on Automation account name shows all resources available to create as well as a list of already created resources.

Image 6

Add Credential Validation

To execute scripts whether it is a PowerShell script, Workflow, DSC or Runbook, it requires credential to execute scripts.

Here, we will use Azure active directory Service Principal user. First of all, create user in Azure active directory from Azure classic portal. Then, assign Co-administrator role to this user at subscription level.

Clicking on Assets blade on the above screen shows the below screen.

Image 7

Clicking on Credentials blade shows the below screen where we can add Service principal user which we have created in Active Directory in classic portal.

Image 8

Clicking on Add a credential button shows the below screen. From this screen, Service Principal user which created in classic portal under active directory needs to be added. Same details which were used when creating user like username, password need to be added here.

Image 9

Click on Create button will create Service Principal user under Credential resources in Assets.

Image 10

Runbook 1: Start VM

Click on Runbooks tab from the above screen. It shows the below screen to create new Runbook.

Image 11

Click on Add a Runbook icon to add new Runbook. It shows new screen as below to add Runbook. Click Quick Create.

Image 12

Enter Runbook name “VMStart”, select Runbook type as “PowerShell” from dropdown and click Create button. It creates and open new window for Runbook draft to add scripts as below.

Image 13

Add below code in Edit PowerShell Runbook window. Click on Save button from upper bar and save the Runbook.

C#
param(

    [parameter(Mandatory=$false)]
    [String] $AzureCredentialName = "Use *Default Automation Credential* Asset",

    [parameter(Mandatory=$false)]
    [String] $AzureSubscriptionName = "Use *Default Azure Subscription* Variable Value",

    [parameter(Mandatory=$false)]
    [String] $VMName = "YourVMName",

    [parameter(Mandatory=$false)]
    [String] $ResourceGroupName = "YourResourceGroupName"
)

# Main Runbook content

try
{
    # Retrieve subscription name from variable asset if not specified

    if($AzureSubscriptionName -eq "Use *Default Azure Subscription* Variable Value")
    {
        $AzureSubscriptionName = Get-AutomationVariable -Name "Default Azure Subscription"

        if($AzureSubscriptionName.length -gt 0)
        {
            Write-Output "Specified subscription name/ID: [$AzureSubscriptionName]"
        }
        else
        {
            throw "No subscription name was specified, and no variable asset 
            with name 'Default Azure Subscription' was found. 
            Either specify an Azure subscription name or define the default using a variable setting"
        }
    }

    # Retrieve credential

    write-output "Specified credential asset name: [$AzureCredentialName]"

    if($AzureCredentialName -eq "Use *Default Automation Credential* asset")
    {

        # By default, look for "Default Automation Credential" asset

        $azureCredential = Get-AutomationPSCredential -Name "Default Automation Credential"

        if($azureCredential -ne $null)
        {
             Write-Output "Attempting to authenticate as: [$($azureCredential.UserName)]"
        }
        else
        {
            throw "No automation credential name was specified, 
            and no credential asset with name 'Default Automation Credential' was found. 
            Either specify a stored credential name or define the default using a credential asset"
        }
    }
    else
    {
        # A different credential name was specified, attempt to load it
        $azureCredential = Get-AutomationPSCredential -Name $AzureCredentialName

        if($azureCredential -eq $null)
        {
            throw "Failed to get credential with name [$AzureCredentialName]"
        }
    }

    write-output "Test: [$azureCredential]"

    # Connect to Azure using credential asset (classic API)
    $account = Add-AzureAccount -Credential $azureCredential
              
    # Check for returned userID, indicating successful authentication
    if(Get-AzureAccount -Name $azureCredential.UserName)
    {
        Write-Output "Successfully authenticated as user: [$($azureCredential.UserName)]"
    }
    else
    {
        throw "Authentication failed for credential [$($azureCredential.UserName)]. 
        Ensure a valid Azure Active Directory user account is specified which is configured 
        as a co-administrator (using classic portal) and subscription owner (modern portal) 
        on the target subscription. Verify you can log into the Azure portal using these credentials."
    }

    # Validate subscription

    $subscriptions = @(Get-AzureSubscription | where {$_.SubscriptionName 
         -eq $AzureSubscriptionName -or $_.SubscriptionId -eq $AzureSubscriptionName})

    if($subscriptions.Count -eq 1)
    {
        # Set working subscription

        $targetSubscription = $subscriptions | select -First 1
        $targetSubscription | Select-AzureSubscription


        # Connect via Azure Resource Manager
        $resourceManagerContext = Add-AzureRmAccount -Credential $azureCredential 
                                    -SubscriptionId $targetSubscription.SubscriptionId
        $currentSubscription = Get-AzureSubscription -Current
        Write-Output "Working against subscription: $($currentSubscription.SubscriptionName) 
                         ($($currentSubscription.SubscriptionId))"
    }
    else
    {
        if($subscription.Count -eq 0)
        {
            throw "No accessible subscription found with name or ID [$AzureSubscriptionName]. 
             Check the Runbook parameters and ensure user is a co-administrator 
             on the target subscription."
        }
        elseif($subscriptions.Count -gt 1)
        {
            throw "More than one accessible subscription found with name or 
               ID [$AzureSubscriptionName]. Please ensure your subscription names are unique, 
               or specify the ID instead"
        }
    }

        Write-Output "[($VMName)]: Starting VM."
        Start-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VMName
    }
    catch
    {
        $errorMessage = $_.Exception.Message
        throw "Unexpected exception: $errorMessage"
    }
    finally
    {
        Write-Output "Runbook finished (Duration: $(("{0:hh\:mm\:ss}" 
                      -f ((Get-Date).ToUniversalTime() - $currentTime))))"
    }

Test Runbook

To test Runbook, click on Test Pane icon. It will open new window as shown below.

Image 14

Click on Start button. See result. If Runbook executes successfully, close the Test Pane window.

Publish Runbook

Now click on Publish icon in Edit PowerShell Runbook window from the below screen. It will publish Runbook.

Image 15

VMStart Runbook is successfully published as below:

Image 16

Execute Runbook

This Runbook is ready to execute. Click on Start icon from the below screen. It will execute Runbook.

Image 17

This Runbook can be scheduled as a job. Also, Webhook can be created as an http endpoint to trigger Runbook from outside application which we will see in future articles.

Runbook 2: Stop VM

Click on Runbooks tab from the below Automation Account screen.

Image 18

It shows the below screen to create new Runbook.

Image 19

Click on Add a Runbook icon to add new Runbook. It shows new screen to add Runbook. Click Quick Create.

Image 20

Enter Runbook name “VMStop”, select Runbook type as “PowerShell” from dropdown and click Create button. It creates and opens a new window for Runbook draft to add scripts as below.

Image 21

Add the below code in Edit PowerShell Runbook window.

C#
param(
    [parameter(Mandatory=$false)]
    [String] $AzureCredentialName = "Use *Default Automation Credential* Asset",

    [parameter(Mandatory=$false)]
    [String] $AzureSubscriptionName = "Use *Default Azure Subscription* Variable Value",

    [parameter(Mandatory=$false)]
    [String] $VMName = "YourVMName",

    [parameter(Mandatory=$false)]
    [String] $ResourceGroupName = "YourResourceGroupName"
)

# Main Runbook content
try
{
    # Retrieve subscription name from variable asset if not specified
    if($AzureSubscriptionName -eq "Use *Default Azure Subscription* Variable Value")
    {
        $AzureSubscriptionName = Get-AutomationVariable -Name "Default Azure Subscription"
        if($AzureSubscriptionName.length -gt 0)
        {
            Write-Output "Specified subscription name/ID: [$AzureSubscriptionName]"
        }
        else
        {
            throw "No subscription name was specified, and no variable asset with name 
            'Default Azure Subscription' was found. 
            Either specify an Azure subscription name or define the default using a variable setting"
        }
    }

    # Retrieve credential
    write-output "Specified credential asset name: [$AzureCredentialName]"
    if($AzureCredentialName -eq "Use *Default Automation Credential* asset")
    {
        # By default, look for "Default Automation Credential" asset
        $azureCredential = Get-AutomationPSCredential -Name "Default Automation Credential"
        if($azureCredential -ne $null)
        {
             Write-Output "Attempting to authenticate as: [$($azureCredential.UserName)]"
        }
        else
        {
            throw "No automation credential name was specified, 
            and no credential asset with name 'Default Automation Credential' was found. 
            Either specify a stored credential name or define the default using a credential asset"
        }
    }
    else
    {
        # A different credential name was specified, attempt to load it
        $azureCredential = Get-AutomationPSCredential -Name $AzureCredentialName
        if($azureCredential -eq $null)
        {
            throw "Failed to get credential with name [$AzureCredentialName]"
        }
    }

    write-output "Test: [$azureCredential]"

    # Connect to Azure using credential asset (classic API)
    $account = Add-AzureAccount -Credential $azureCredential

               

    # Check for returned userID, indicating successful authentication
    if(Get-AzureAccount -Name $azureCredential.UserName)
    {
        Write-Output "Successfully authenticated as user: [$($azureCredential.UserName)]"
    }
    else
    {
        throw "Authentication failed for credential [$($azureCredential.UserName)]. 
        Ensure a valid Azure Active Directory user account is specified which is configured 
        as a co-administrator (using classic portal) and subscription owner (modern portal) 
        on the target subscription. Verify you can log into the Azure portal using these credentials."
    }

    # Validate subscription
    $subscriptions = @(Get-AzureSubscription | where {$_.SubscriptionName 
    -eq $AzureSubscriptionName -or $_.SubscriptionId -eq $AzureSubscriptionName})
    if($subscriptions.Count -eq 1)
    {

        # Set working subscription
        $targetSubscription = $subscriptions | select -First 1
        $targetSubscription | Select-AzureSubscription

        # Connect via Azure Resource Manager
        $resourceManagerContext = Add-AzureRmAccount 
        -Credential $azureCredential -SubscriptionId $targetSubscription.SubscriptionId
        $currentSubscription = Get-AzureSubscription -Current

        Write-Output "Working against subscription: 
        $($currentSubscription.SubscriptionName) ($($currentSubscription.SubscriptionId))"
    }
    else
    {
        if($subscription.Count -eq 0)
        {
            throw "No accessible subscription found with name or ID [$AzureSubscriptionName]. 
            Check the runbook parameters and ensure user is a co-administrator 
                    on the target subscription."
        }
        elseif($subscriptions.Count -gt 1)
        {
            throw "More than one accessible subscription found with name or ID [$AzureSubscriptionName]. 
            Please ensure your subscription names are unique, or specify the ID instead"
        }
    }

        Write-Output "[($VMName)]: Stopping VM."
        Stop-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VMName
}
catch
{
        $errorMessage = $_.Exception.Message
        throw "Unexpected exception: $errorMessage"
}
finally
{
        Write-Output "Runbook finished 
        (Duration: $(("{0:hh\:mm\:ss}" -f ((Get-Date).ToUniversalTime() - $currentTime))))"
}

This “VMStop” Runbook can be Tested, Published and Executed same as steps mentioned above for Runbook-1 “VMStart”.

Summary

So, we have seen one of the most important features of Azure Automation is Runbook. This is a reusable component that can be used for automation of Azure resources management. Since it executes on Azure environment context with service principal user, it is very safe and secure in point of security context compared to PowerShell script execution from remote environment. This feature enables us to manage the Azure resources efficiently.

References

History

  • 9th September, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)