65.9K
CodeProject 正在变化。 阅读更多。
Home

通过运行手册进行 Azure VM 操作

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (3投票s)

2016 年 9 月 9 日

CPOL

5分钟阅读

viewsIcon

13220

本文介绍了如何配置 Azure Automation 的 Runbook 功能,以及如何利用该功能管理和优化 Azure 中的基础结构资源的成本。

Runbook

本文将引导您完成在 Azure Automation 中创建 Runbook 的过程。我们将从创建一个简单的 Runbook 来启动 VM 开始,然后进行测试和发布。之后,我们将指导您如何创建用于停止 VM 的 Runbook,以实际管理 Azure 资源。

什么是 Runbook?

可以通过在 Azure 门户中实现 Runbook 来实现 PowerShell 脚本执行的自动化。Runbook 可以被计划为任务,以实现 PowerShell 脚本执行的原子化。此外,还可以为 Runbook 创建 Webhook。Webhook 可以作为 HTTP 端点,从应用程序触发 Runbook。

通过 Runbook,可以在 Azure 门户中实现大多数资源管理过程的自动化,例如 VM/Vnet/NSG/Public IP/NIC 等的创建、删除、启动、停止。

通过在 VM 不使用时自动启动和停止 VM 来实现成本优化。

一些组织需要在每月特定日期启动 VM 以完成某些活动。在其余时间,VM 可能不会被使用,此时可以将 VM 停止(取消分配)/删除。每当需要 VM 或收到第一个用户的请求时,就可以通过 Runbook Webhook 实现动态创建/启动 VM。我们将在本文中探讨此用例。

Runbook 与 PowerShell

Azure 自动化支持两种选项:Windows PowerShell 和工作流,后者称为 Runbook。即使 Runbook 内部使用 PowerShell 脚本,但主要优势在于 Runbook 本身在 Azure 门户的上下文中执行。这种执行是安全的,并且不易出错。

要从本地计算机在 Azure 中执行 PowerShell 脚本,用户必须通过证书发布或 Azure 登录 cmdlet 进行身份验证。用户必须提供用户名、密码和订阅 ID。

而 Runbook 在 Azure 门户中以服务帐户的形式执行。服务帐户可以通过自动化帐户中的凭据对象以及分配给该服务帐户的 Active Directory 中的联合管理员角色来创建。

在 PowerShell 脚本中,可以在 cmdlet 中指定“**Verbose**”开关以进行用户确认,而在 Runbook 中,无法指定“**Verbose**”开关,也无法在执行期间进行用户交互式确认。

在 Runbook 的 PowerShell 脚本执行中需要用户确认的地方,可以指定“Force”开关来覆盖 Runbook 中 cmdlet 的执行。

创建 Runbook

在 Azure 门户中创建和执行 Runbook 的步骤

创建新的自动化帐户

在 Azure 新门户的右上角单击“**New**”,然后在文本框中键入“automation”。它将按照下图显示 Azure 自动化帐户资源。

选择“Automation”选项。它将重定向到以下屏幕。

单击“Create”按钮。它将重定向到以下屏幕。

提供自动化帐户名称,选择要从中创建自动化帐户的订阅和资源组。位置将根据资源组的选择默认填充。在“Create Azure Run as Account”处选择“Yes”。自动化帐户创建完成后,它将显示在资源列表窗格中,如下图所示。

单击自动化帐户名称会显示所有可供创建的资源以及已创建资源的列表。

添加凭据验证

要执行脚本(无论是 PowerShell 脚本、工作流、DSC 还是 Runbook),都需要凭据来执行脚本。

在这里,我们将使用 Azure Active Directory 服务主体用户。首先,从 Azure 经典门户在 Azure Active Directory 中创建用户。然后,将该用户分配到订阅级别的联合管理员角色。

单击上述屏幕中的“Assets”窗格将显示以下屏幕。

单击“Credentials”窗格将显示以下屏幕,我们可以在其中添加我们在经典门户中的 Active Directory 中创建的服务主体用户。

单击“Add a credential”按钮将显示以下屏幕。在此屏幕上,需要添加我们在经典门户的 Active Directory 中创建的服务主体用户。需要在此处添加创建用户时使用的相同详细信息,例如用户名和密码。

单击“Create”按钮将在“Assets”中的“Credential”资源下创建服务主体用户。

Runbook 1:启动 VM

从上述屏幕的“Runbooks”选项卡中单击。它将显示以下屏幕以创建新的 Runbook。

单击“Add a Runbook”图标以添加新的 Runbook。它将显示一个新屏幕以添加 Runbook。单击“Quick Create”。

输入 Runbook 名称“**VMStart**”,从下拉列表中选择 Runbook 类型为“**PowerShell**”,然后单击“Create”按钮。它将创建并打开一个新的 Runbook 草稿窗口以添加脚本,如下所示。

在“Edit PowerShell Runbook”窗口中添加以下代码。单击上栏中的“Save”按钮并保存 Runbook。

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))))"
    }

测试 Runbook

要测试 Runbook,请单击“Test Pane”图标。它将打开一个新窗口,如下所示。

单击“Start”按钮。查看结果。如果 Runbook 执行成功,请关闭“Test Pane”窗口。

发布 Runbook

现在,在下面的屏幕中的“Edit PowerShell Runbook”窗口中单击“Publish”图标。它将发布 Runbook。

VMStart Runbook 已成功发布,如下所示。

执行 Runbook

此 Runbook 已准备好执行。单击下面的屏幕中的“Start”图标。它将执行 Runbook。

此 Runbook 可以计划为任务。此外,还可以创建 Webhook 作为 HTTP 端点,从外部应用程序触发 Runbook,这将在将来的文章中介绍。

Runbook 2:停止 VM

从下面的自动化帐户屏幕中单击“Runbooks”选项卡。

它将显示以下屏幕以创建新的 Runbook。

单击“Add a Runbook”图标以添加新的 Runbook。它将显示一个新屏幕以添加 Runbook。单击“Quick Create”。

输入 Runbook 名称“**VMStop**”,从下拉列表中选择 Runbook 类型为“**PowerShell**”,然后单击“Create”按钮。它将创建并打开一个新的 Runbook 草稿窗口以添加脚本,如下所示。

在“Edit PowerShell Runbook”窗口中添加以下代码。

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))))"
}

此“VMStop”Runbook 可以像 Runbook-1“VMStart”中提到的步骤一样进行测试、发布和执行。

摘要

因此,我们已经了解了 Azure 自动化最重要的功能之一是 Runbook。这是一个可重用组件,可用于自动化 Azure 资源的管理。由于它在 Azure 环境的上下文中以服务主体用户身份执行,因此与从远程环境执行 PowerShell 脚本相比,在安全上下文方面非常安全可靠。此功能使我们能够有效地管理 Azure 资源。

参考文献

历史

  • 2016 年 9 月 9 日:初始版本
© . All rights reserved.