Статьи

Автоматическая подготовка виртуальных сетей Azure с помощью PowerShell и XML

Несколько недель назад я писал об автоматической инициализации нового контроллера домена Active Directory в облаке Azure с использованием нового пользовательского расширения сценария агента VM через портал управления Azure. С тех пор несколько человек в сообществе спрашивали также об автоматизации шагов для подготовки виртуальных сетей в Azure.

Нажмите, чтобы увеличить ...

В этой статье мы расскажем о подходе к автоматической инициализации виртуальных сетей Azure с помощью PowerShell, а также о некоторых полезных советах по расширению ваших навыков PowerShell от простых сценариев до создания инструментов многократного использования.

Используя «Set-AzureVNetConfig»…

Как писал мой друг и коллега Кевин Ремде в одной из своих статей — мы можем использовать командлет Set-AzureVNetConfig из модуля Azure PowerShell для предоставления конфигурации виртуальной сети в Azure. 

Но… этот командлет использует подход « все или ничего », что означает, что он ограничен предоставлением всех виртуальных сетей в подписке Azure как единого модуля с использованием файла конфигурации XML. Это может быть хорошо при настройке начальных виртуальных сетей в новых подписках Azure, но может оказаться бесполезным при необходимости поэтапного предоставления дополнительных виртуальных сетей внутри существующей подписки. 

Что нам действительно нужно, так это «New-AzureVNet»

В этих случаях нам может понадобиться командлет «New-AzureVNet», который может постепенно добавлять новые виртуальные сети, DNS-серверы и подсети в существующую подписку Azure. Этот подход особенно полезен в крупных организациях, которые управляют несколькими подписками Azure в рабочих нагрузках dev, test, staging и production, поскольку позволяют им поддерживать согласованные конфигурации для отдельных виртуальных сетей в каждой подписке.

Давайте создадим нашу собственную функцию «New-AzureVNet»!

Привет! PowerShell предоставляет нам возможность легко определять пользовательские функции, которые можно использовать подобно встроенным командлетам. Мы можем использовать эту возможность для создания нашего собственного инструмента New-AzureVNet! После того как наша функция будет определена, мы сможем легко добавлять новые виртуальные сети Azure постепенно, вызывая функцию в виде одной командной строки PowerShell, например:

New-AzureVNet `
  -newDnsServerName labdns01 `
  -newDnsServerIP 10.0.0.5 `
  -newVNetName labnet01 `
  -newVNetAddressRange 10.2.0.0/16 `
  -newSubnetAddressRange 10.2.1.0/24 `
  -newVNetLocation "West US"

Попутно мы также изучим некоторые полезные навыки написания сценариев, которые вы можете использовать для повышения своих возможностей PowerShell, от создания простых сценариев до создания инструментов многократного использования, которые другие могут использовать в вашей организации.

Начиная …

Чтобы следовать этой пошаговой статье по созданию функции New-AzureVNet, вам потребуется активная подписка Azure. Если у вас еще нет активной подписки Azure, вы можете подписаться на БЕСПЛАТНУЮ подписку по следующей ссылке…

Получив активную подписку Azure, вам также необходимо загрузить и установить последний модуль Azure PowerShell…

Задача 1: Какова ваша функция?

Сначала нам нужно создать новый файл сценария PowerShell с именем myAzureLibrary.ps1 и определить нашу новую функцию внутри этого файла с помощью блока кода ниже.

    function New-AzureVNet {
    }

Обратите внимание, что я использую соглашение об именах для этой функции, которое соответствует типичному соглашению об именовании PowerShell, используемому с командлетами. Следуя этому соглашению об именах, другие могут легко понять общее назначение этой функции и использовать ее способами, совместимыми с другими командлетами.

Хотя в этой статье мы определяем только одну функцию в myAzureLibrary.ps1 , вы, безусловно, можете включить несколько определений функций в один файл сценария, который вы используете в других сценариях, как функцию «библиотека».

Задача 2: Расширение возможностей самопознания

При определении функций в PowerShell мне нравится делать их максимально самодокументированными. Это дает другим возможность самостоятельно обнаруживать и быстро понимать, как определенная функция предназначена для работы и какие параметры, если таковые имеются, ожидаются. Включение справки на основе комментариев в функцию — отличный способ предоставить эту информацию другим. 

    function New-AzureVNet {

    <#
    .SYNOPSIS
    New-AzureVNet provisions new Azure Virtual Networks in an existing Azure Subscription
    .DESCRIPTION
    New-AzureVNet defines new Azure Virtual Network and DNS Server information,
    merges with an existing Azure Virtual Network configuration,
    and then provisions the resulting final configuration in an existing Azure subscription.
    For demonstration purposes only.
    No support or warranty is supplied or inferred.
    Use at your own risk.
    .PARAMETER newDnsServerName
    The name of a new DNS Server to provision
    .PARAMETER newDnsServerIP
    The IPv4 address for the new DNS Server
    .PARAMETER newVNetName
    The name of a new Azure Virtual Network to provision
    .PARAMETER newVNetLocation
    The name of the Azure datacenter region in which to provision the new Azure Virtual Network
    .PARAMETER newVNetAddressRange
    The IPv4 address range for the new Azure Virtual Network in CIDR format. Ex) 10.1.0.0/16
    .PARAMETER newSubnetName
    The name of a new subnet within the Azure Virtual Network
    .PARAMETER newSubnetAddressRange
    The IPv4 address range for the subnet in the new Azure Virtual Network in CIDR format. Ex) 10.1.0.0/24
    .PARAMETER configFile
    Specify file location for writing finalized Azure Virtual Network configuration in XML format.
    .INPUTS
    Parameters above.
    .OUTPUTS
    Final Azure Virtual Network XML configuration that was successfully provisioned.
    .NOTES
    Version: 1.0
    Creation Date: Aug 1, 2014
    Author: Keith Mayer ( http://KeithMayer.com )
    Change: Initial function development
    .EXAMPLE
    New-AzureVNet -Verbose
    Provision a new Azure Virtual Network using default values.
    .EXAMPLE
    New-AzureVNet -newDnsServerName labdns01 -newDnsServerIP 10.0.0.5 -newVNetName labnet01 -newVNetLocation "West US"
    Provision a new Azure Virtual Network using specific values.
    #>

    }

После определения справки на основе комментариев вы сможете указать источник myAzureLibrary.ps1 из другого сеанса или сценария PowerShell, а затем использовать стандартный командлет Get-Help для отображения справки по вашей функции.

    . .\myAzureLibrary.ps1

    Get-Help New-AzureVNet –Full

Я считаю, что включение справки на основе комментариев в качестве начального шага в процессе определения новой функции также является отличным способом помочь мне охватить ожидаемую цель, параметры, ввод и вывод новой функции, прежде чем углубляться в « сорняки »кодирования функции.

Задача 3: Хорошо, я заявляю! (Параметры то есть)

Наш следующий шаг в определении функции — это объявление входных параметров для виртуальной сети, которую будет предоставлять наша функция. Вместо того, чтобы жестко кодировать значения в теле функции, использование входных параметров делает нашу функцию более универсальной, позволяя легко вызывать ее с уникальными значениями для каждого сценария, в котором она используется. 

Как часть объявления параметров, мы также можем определить значения по умолчанию для каждого параметра, который должен использоваться в случае, если эти параметры не указаны при вызове функции. Эти значения по умолчанию могут значительно облегчить другим пользователям начало работы с этой функцией, сократив количество параметров, которые они должны указать для простых сценариев.

    function New-AzureVNet {

    … prior code lines from above …

    [CmdletBinding()]
    param
    (
    [string]$newDnsServerName = 'dns01',
    [string]$newDnsServerIP = '10.0.0.4',
    [string]$newVNetName = 'vnet01',
    [string]$newVNetLocation = 'East US',
    [string]$newVNetAddressRange = '10.1.0.0/16',
    [string]$newSubnetName = 'Subnet-1',
    [string]$newSubnetAddressRange = '10.1.0.0/24',
    [string]$configFile = "$env:Temp\AzureVNetConfig.XML"
    )

    }

В приведенном выше примере мы определяем 8 параметров со значением по умолчанию для каждого параметра. Это позволит вызывать функцию без указания каких-либо параметров для создания простой виртуальной виртуальной сети с общими значениями. В качестве альтернативы, функция может быть вызвана с определенными значениями параметров, чтобы переопределить значения по умолчанию.

Я также включил атрибут [CmdletBinding ()] в блок кода выше. Это позволяет функции работать больше как настоящий командлет. В частности, я обычно включаю этот атрибут в функции, чтобы при вызове функции можно было использовать параметр –VerboseКомандлеты Write-Verbose могут быть включены в тело функции, которая отображает вывод только при использовании этого параметра. Это может быть очень полезно для включения комментариев в функцию, которая также служит функциональным выходом для целей отслеживания и отладки.

Задача 4: НАЧАТЬ?

Во многих случаях, когда функция начинает выполняться, есть некоторые начальные шаги, которые, возможно, должны быть выполнены как начальная установка или очистка от предыдущих запусков функции. Мы можем приспособить эти начальные шаги, которые помогут подготовить среду, в которой будет выполняться наша функция, включив в нашу функцию блок BEGIN.

    function New-AzureVNet {

    … prior code lines from above …

    begin {

      Write-Verbose "Deleting $configFile if it exists"
      Del $configFile -ErrorAction:SilentlyContinue

    }

    }

В приведенном выше примере мы добавили простой блок BEGIN, который очищает все предыдущие временные файлы конфигурации, которые использует эта функция. Это помогает подготовить «чистую» стартовую среду, чтобы наша функция не «случайно» наследовала какие-либо старые значения от предыдущих запусков этой функции.

Задача 5: Теперь приступим к работе — блок ПРОЦЕСС …

Теперь, когда у нас есть «заглушка», определенная для нашей новой функции, мы можем «приступить к работе» по написанию кода, который будет выполнять фактическую работу по подготовке новой Azure VNet в нашей подписке. Как правило, этот код включен в блок PROCESS внутри функции следующим образом:

    function New-AzureVNet {

    … prior code lines from above …

    begin {

      Write-Verbose "Deleting $configFile if it exists"
      Del $configFile -ErrorAction:SilentlyContinue

    }

    process {

    # INSERT CODE HERE

    }

    }

С точки зрения нашей конкретной функции New-AzureVNet, в этом блоке PROCESS мы вставим код, чтобы выполнить следующие шаги при подготовке нашей новой Azure VNet:

  1. Создайте общий шаблон конфигурации XML для новой виртуальной сети Azure.
  2. Добавьте новые значения атрибута DNS в общий шаблон конфигурации XML для новой виртуальной сети Azure.
  3. Добавьте новые значения атрибутов VNet в общий шаблон конфигурации XML для имен VNet, подсетей и диапазонов IP-адресов, которые мы добавляем
  4. Получите существующую конфигурацию виртуальной сети для других виртуальных сетей, которые могут уже существовать в нашей подписке Azure.
  5. Объедините новые значения атрибутов DNS и VNet, которые мы добавляем, с любой конфигурацией DNS, VNet и локальной сети, которая может уже существовать в нашей подписке Azure.
  6. Сохраните новую объединенную конфигурацию VNet во временный файл
  7. Вызовите командлет Set-AzureVNetConfig для предоставления новой конфигурации VNet из временного файла.

Когда я начинаю писать код в блоке PROCESS, я часто начинаю определять каждую из общих задач, которые должны быть выполнены как функциональные комментарии с Write-Verbose, такие как…

    function New-AzureVNet {

    … prior code lines from above …

    begin {

      Write-Verbose "Deleting $configFile if it exists"
      Del $configFile -ErrorAction:SilentlyContinue

    }

    process {

      Write-Verbose "Build generic XML template for new Virtual Network"

      Write-Verbose "Add DNS attribute values to XML template"

      Write-Verbose "Add VNet attribute values to XML template"

      Write-Verbose "Get existing VNet configuration from Azure subscription"

      Write-Verbose "Merge existing DNS servers into new VNet XML configuration"

      Write-Verbose "Merge existing VNets into new VNet XML configuration"

      Write-Verbose "Merge existing Local Networks into new VNet XML configuration"

      Write-Verbose "Saving new VNet XML configuration to $configFile"

      Write-Verbose "Provisioning new VNet configuration from $configFile"

    }

    }

… Этот подход обеспечивает хорошую отправную точку для кодирования и тестирования блока PROCESS по одному разделу за раз. Конечно, по пути я могу решить, что некоторые из этих разделов кода действительно будут лучше обслуживать, определив их как сами функции. Однако, с точки зрения простоты для примера в этой статье, я буду кодировать этот блок PROCESS как отдельный блок кода без вложенных функций.

Теперь довольно просто начать кодирование и тестирование каждого раздела, используя переключатель –Verbose, когда я тестирую функцию, чтобы определить, в каком разделе кода выполняется функция. 

Для нашего примера функции, которая обеспечивает новые виртуальные сети Azure, вот завершенный блок PROCESS, который выполняет всю работу …

    function New-AzureVNet {

      … prior code lines from above …

      process {

      Write-Verbose "Build generic XML template for new Virtual Network"
      $newVNetConfig = [xml] '
      <NetworkConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration">
      <VirtualNetworkConfiguration>
      <Dns>
      <DnsServers>
      <DnsServer name="" IPAddress="" />
      </DnsServers>
      </Dns>
      <VirtualNetworkSites>
      <VirtualNetworkSite name="" Location="">
      <AddressSpace>
      <AddressPrefix></AddressPrefix>
      </AddressSpace>
      <Subnets>
      <Subnet name="">
      <AddressPrefix></AddressPrefix>
      </Subnet>
      </Subnets>
      <DnsServersRef>
      <DnsServerRef name="" />
      </DnsServersRef>
      </VirtualNetworkSite>
      </VirtualNetworkSites>
      </VirtualNetworkConfiguration>
      </NetworkConfiguration>
      '

      Write-Verbose "Add DNS attribute values to XML template"
      $newDnsElements = $newVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.Dns.DnsServers.DnsServer
      $newDnsElements.SetAttribute('name', $newDnsServerName)
      $newDnsElements.SetAttribute('IPAddress', $newDnsServerIP)

      Write-Verbose "Add VNet attribute values to XML template"
      $newVNetElements = $newVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.VirtualNetworkSites.VirtualNetworkSite
      $newVNetElements.SetAttribute('name', $newVNetName)
      $newVNetElements.SetAttribute('Location', $newVNetLocation)
      $newVNetElements.AddressSpace.AddressPrefix = $newVNetAddressRange
      $newVNetElements.Subnets.Subnet.SetAttribute('name', $NewSubNetName)
      $newVNetElements.Subnets.Subnet.AddressPrefix = $newSubnetAddressRange
      $newVNetElements.DnsServersRef.DnsServerRef.SetAttribute('name', $newDnsServerName)

      Write-Verbose "Get existing VNet configuration from Azure subscription"
      $existingVNetConfig = [xml] (Get-AzureVnetConfig).XMLConfiguration

      Write-Verbose "Merge existing DNS servers into new VNet XML configuration"
      $existingDnsServers = $existingVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.Dns.DnsServers
      if ($existingDnsServers.HasChildNodes) {
      ForEach ($existingDnsServer in $existingDnsServers.ChildNodes) {
      if ($existingDnsServer.name -ne $newDnsServerName) {
      $importedDnsServer = $newVNetConfig.ImportNode($existingDnsServer,$True)
      $newVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.Dns.DnsServers.AppendChild($importedDnsServer) | Out-Null
      }
      }
      }

      Write-Verbose "Merge existing VNets into new VNet XML configuration"
      $existingVNets = $existingVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.VirtualNetworkSites
      if ($existingVNets.HasChildNodes) {
      ForEach ($existingVNet in $existingVNets.ChildNodes) {
      if ($existingVNet.name -ne $newVNetName) {
      $importedVNet = $newVNetConfig.ImportNode($existingVNet,$True)
      $newVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.VirtualNetworkSites.AppendChild($importedVNet) | Out-Null
      }
      }
      }

      Write-Verbose "Merge existing Local Networks into new VNet XML configuration"
      $existingLocalNets = $existingVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.LocalNetworkSites
      if ($existingLocalNets.HasChildNodes) {
      $dnsNode = $newVNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.Dns
      $importedLocalNets = $newVNetConfig.ImportNode($existingLocalNets,$True)
      $newVnetConfig.NetworkConfiguration.VirtualNetworkConfiguration.InsertAfter($importedLocalNets,$dnsNode) | Out-Null
      }

      Write-Verbose "Saving new VNet XML configuration to $configFile"
      $newVNetConfig.Save($configFile)

      Write-Verbose "Provisioning new VNet configuration from $configFile"
      Set-AzureVNetConfig -ConfigurationPath $configFile | Out-Null

      }

      }

Наконечник! Вы заметите, что несколько строк в приведенном выше блоке кода заканчиваются командлетом Out-Null . Этот командлет используется для отбрасывания вывода из предыдущего конвейера команд, который в противном случае был бы включен в возвращенный вывод функции. Мы хотим, чтобы наша функция New-AzureVNet возвращала только окончательный вывод XML для конфигурации VNet, который был успешно применен, поэтому мы используем Out-Null, чтобы отбросить любые другие выходные данные, которые могут возникнуть по пути. Если вы знакомы с функциями других языков программирования или языков сценариев, это может показаться немного странным, но это обычное явление при построении функций в PowerShell.

Задача 6: Все хорошо, что хорошо заканчивается!

Когда функция завершила блок PROCESS, у нас, скорее всего, будет какой-то код, который мы хотели бы выполнить, чтобы очистить нашу среду или вернуть определенные значения до завершения функции. Мы можем выполнить это с помощью блока END в нашей функции.

    function New-AzureVNet {

      … prior code lines from above …

      end {

      Write-Verbose "Deleting $configFile if it exists"
      Del $configFile -ErrorAction:SilentlyContinue

      Write-Verbose "Returning the final VNet XML Configuration"
      (Get-AzureVnetConfig).XMLConfiguration

      }

    }

В примере из этой статьи мы используем блок END в нашей функции для очистки временных файлов, а также возвращаем окончательный вывод XML, который отражает конфигурацию VNet, которая была успешно обработана для нашей подписки Azure.

Задача 7: Беги!

After defining your new function, be sure to save your code in NewAzureVNet.ps1, and then you’re ready for a test run …

    # Import Azure PowerShell Module
    Import-Module 'C:\Program Files (x86)\Microsoft SDKs\Windows Azure\PowerShell\ServiceManagement\Azure\Azure.psd1'

    # Dot-source the function script for New-AzureVNet
    . .\myAzureLibrary.ps1

    # Authenticate to Azure subscription
    Add-AzureAccount

    # Select Azure subscription, if more than one
    $subscriptionName = 'Free Trial'
    Select-AzureSubscription `
      –SubscriptionName $subscriptionName

    # Provision a new VNet using our new function[xml]$newAzureVNet = New-AzureVNet -Verbose

    # Show resulting XML$newAzureVNet.InnerXml

    # Show new provisioned VNet
    Get-AzureVNetSite

After this simple test run, execute the New-AzureVNet function with various combinations of parameters to make sure those work successfully for you, too.

Continue your Hybrid Cloud learning!

In this article, we’ve defined a custom PowerShell function to quickly provision a new Virtual Network in an existing Azure subscription.  Along the way, we’ve explored an approach that you can leverage to elevate your PowerShell skills from simple scripts to reusable tools.

To continue your learning on Microsoft Azure and Hybrid Cloud, be sure to join our FREE Hybrid Cloud study track in our online Early Experts study group!

To continue learning more about PowerShell, be sure to check out these great courses on Microsoft Virtual Academy!