Deploying Virtual Machines in Azure using Service Manager and SMA Part III

This is the 3rd post in a 3-part series on deploying VMs in Azure using System Center Service Manager and Service Management Automation (SMA). In this post we’ll cover using the Service Manager Self-Service Portal to trigger an SMA Runbook which deploys a new VM in Azure.

Part I: Preparing your VM for upload, uploading it to Azure, and configuring Azure

Part II: Deploying your VM in PowerShell and SMA

Part III: Deploying your VM using the Service Manager Self-Service Portal

Summary

In Part I we uploaded a custom image to Azure and in Part II we created an SMA Runbook to deploy a new virtual machine using that image. The steps below will take you through configuring System Center Service Manager to launch an SMA Runbook which will deploy a Virtual Machine in Azure.

Prerequisites

  1. Install the Service Manager Authoring Tool on a workstation
  2. Install Windows Azure PowerShell on the machine where your SMA Runbook will be launched (configured in Part II)
  3. Install the Service Manager Console on the machine where your SMA Runbook will be launched.
  4. Install SMA PowerShell on the Service Manager Management Server where the workflow will be run. You can install this from the Orchestrator installation CD, just launch setup and it’s on the bottom.
  5. Add the workflow account in Service Manager to the smaAdminGroup (local group) on the SMA Server. You can get this account by opening the Service Manager console and going to Administration\Security\Run As Accounts\Workflow Account.
  6. Download the Azure Settings File and place it somewhere on the SMA Server. In order for scripts to connect to Azure we need to download an Azure settings file for authentication. You can download this file from the following URL: https://windows.azure.com/download/publishprofile.aspx.
  7. Get all of the parameters needed to execute the SMA Runbook manually through SMA. Part II has steps to get these.

Modify the SMA Runbook

The SMA Runbook we created in Part II will mostly work as-is but we’ll want to make several modifications so it works better with Service Manager. The main modification adds to the end of the script so that it will set the Service Manager activity to Completed. If we don’t do this the Virtual Machine will get deployed but the Service Request will remain In Progress. If you plan on using the SMA Runbook without Service Manager then instead of modifying the existing one you can create a new one.

  • Logon to the Service Management Portal and click on Automation\Runbooks
  • Scroll down to the DeployVMInAzure Runbook and select it
  • Click Author, Draft, and Edit Runbook
  • Replace the script with the one below
 workflow DeployVMInAzure
{
  param(
  $azureSettingsFile,
  $azureAccountName,
  $imageName,
  $vmName,
  $instanceSize,
  $adminUserName,
  $adminPassword,
  $domain,
  $domainUserName,
  $domainPassword,
  $affinityGroup,
  $networkName,
  $azureStorageAccountName,
  $subnetName,
  $activityID,
  $smManagementServer
  )
  
  #Set Instance Size
  if ($instanceSize -eq "A0") { $instanceSize = 'ExtraSmall' }
  elseif ($instanceSize -eq "A1") { $instanceSize = 'Small' }
  elseif ($instanceSize -eq "A2") { $instanceSize = 'Medium' }
  elseif ($instanceSize -eq "A3") { $instanceSize = 'Large' }
  elseif ($instanceSize -eq "A4") { $instanceSize = 'ExtraLarge' }
   
  inlinescript
  {
    #Import Azure PowerShell Module
    Import-Module Azure

    #Remove Existing Azure Sessions
    Remove-AzureAccount -Name $Using:azureAccountName -Force
    
    #Import Azure Settings File
    Import-AzurePublishSettingsFile -PublishSettingsFile ($Using:azureSettingsFile).Replace("\\","\")
    
    #Set Azure Subscription
    Get-AzureSubscription | Set-AzureSubscription -currentstorageaccountname $Using:azureStorageAccountName
    
    #Increment VM Name
    $i = 0
    foreach ($vm in Get-AzureVM)
    {
      if ($vm.InstanceName.Contains($Using:vmName))
      {
        [int]$increment = $vm.InstanceName.SubString($Using:vmName.Length)
        if ($increment -gt $i) { $i = $increment }
      }
    }
    $i++
    $vmName = $Using:vmName + $i

    #Create New Azure Cloud Service
    New-AzureService -ServiceName $vmName -AffinityGroup $Using:affinityGroup

    #Create Azure VM
    if ($Using:domain)
    {
      New-AzureVMConfig -Name $vmName -InstanceSize $Using:instanceSize -ImageName $Using:imageName | 
      Add-AzureProvisioningConfig -WindowsDomain -Password $Using:adminPassword -JoinDomain $Using:domain -Domain $Using:domain -DomainUserName $Using:domainUserName -DomainPassword $Using:domainPassword -AdminUsername $Using:adminUserName | 
      Set-AzureSubnet $Using:subnetName |
      New-AzureVM -ServiceName $vmName -VNetName $Using:networkName
    }
    else
    {
      New-AzureVMConfig -Name $vmName -InstanceSize $Using:instanceSize -ImageName $Using:imageName | 
      Add-AzureProvisioningConfig -Windows -AdminUsername $Using:adminUserName -Password $Using:adminPassword | 
      Set-AzureSubnet $Using:subnetName |
      New-AzureVM -ServiceName $vmName -VNetName $Using:networkName
    }

    #Set SM Activity to Completed
    if ($Using:activityID)
    {
      #Connect to SM
      $smDir = (Get-ItemProperty 'hklm:/software/microsoft/System Center/2010/Service Manager/Setup').InstallDirectory
      Import-Module ($smDir + "\Powershell\System.Center.Service.Manager.psd1")
      $sm = New-SCManagementGroupConnection -computerName $Using:smManagementServer

      #Get SM Class
      $class = Get-SCClass -name 'Custom.Example.Azure.DeployVM'

      #Get SM Class Instance
      $instance = Get-SCClassInstance -class $class -filter ('Id -eq {0}' -f $Using:activityID)

      #Update Activity to Completed
      $instance.Status = "ActivityStatusEnum.Completed"
      Update-SCSMClassInstance -Instance $instance
    }
  }
}
  • Publish the Runbook

Create a Management Pack in the Authoring Tool

  • Launch the Service Manager Authoring Tool

  • Select File\New, and name your MP Custom.Example.Azure.xml

  • Under Custom.Example.Azure, right-click on Classes and choose Create other class…

    • For Base class, choose Activity and click OK
    • For Internal name type Custom.Example.Azure.DeployVM and click Create
  • Under your new class, delete the Property_xx property (mine is Property_33)

  • Create the following String properties for you Custom.Example.Azure.DeployVMclass

    • WebServiceEndPoint
    • SMARunbookName
    • azureSettingsFile
    • azureAccountName
    • imageName
    • vmName
    • instanceSize
    • adminUserName
    • adminPassword
    • domain
    • domainUserName
    • domainPassword
    • affinityGroup
    • networkName
    • azureStorageAccountName
    • subnetName
    • smManagementServer
  • Under Custom.Example.Azure, right-click on Workflows and choose Create

    • Under Name type DeployVM and click Next

    • Under Trigger Condition choose Run only when a database object meets specified conditions and click Next

    • Under Trigger Criteria choose the Custom.Example.Azure.DeployVM class, choose When a object of the selected class is updated, and click Additional Criteria

      • Select Changed To and set a Criteria of Status equals In Progress

image

  • Click OK, Create, and Close
  • The Workflow Designer should open along with an Activities Toolbox. Drag the Windows PowerShell Script Activity into the Workflow

image

  • Select the windowsPowerShellScript1 activity and then click on the ellipsis next to Script Body under Details
  • Paste the following script into the Script Body window
 Start-SmaRunbook -WebServiceEndpoint $WebServiceEndpoint -Name $SMARunbookName -Parameters @{"azureSettingsFile" = "$azureSettingsFile"; "azureAccountName" = "$azureAccountName"; "imageName" = "$imageName"; "vmName" = "$vmName"; "instanceSize" = "$instanceSize"; "adminUserName" = "$adminUserName"; "adminPassword" = "$adminPassword"; "domain" = "$domain"; "domainUserName" = "$domainUserName"; "domainPassword" = "$domainPassword"; "affinityGroup" = "$affinityGroup"; "networkName" = "$networkName"; "azureStorageAccountName" = "$azureStorageAccountName"; "subnetName" = "$subnetName"; "smManagementServer" = "$smManagementServer"; "activityID" = "$activityID"}
  • Click on Script Properties and Add the following properties to the Activity (Click Use a class property to find the matching class property) and click OK

    • WebServiceEndPoint
    • SMARunbookName
    • azureSettingsFile
    • azureAccountName
    • imageName
    • vmName
    • instanceSize
    • adminUserName
    • adminPassword
    • domain
    • domainUserName
    • domainPassword
    • affinityGroup
    • networkName
    • azureStorageAccountName
    • subnetName
    • smManagementServer
    • activityID (maps to ID)

image

  • Save the Management Pack in the Authoring Tool
  • Copy the DeployVM.dll file from your management pack folder to your Service Manager Management Server folder (\Program Files\Microsoft System Center 2012 R2\Service Manager) and restart the Microsoft Monitoring Agent service on the Service Manager Management Server.
  • Restart your Service Manager Console if you had it open

Import the Management Pack in Service Manager

  • Open the Service Manager Console and go to Administration\Management Packs
  • Right-click on Management Packs and choose Import
  • Select the Custom.Example.Azure management pack, click Open, Import, and OK

Create a new Template for the Custom.Example.Azure.DeployVM Class

  • Open the Service Manager Console and go to Library\Templates

  • Right-click on Templates and select Create Template

    • For Name, type Deploy Azure VM Template
    • For Class, choose Custom.Example.Azure.DeployVM
    • Click OK
  • Fill in the following properties of the form based on the parameters used running the SMA Runbook manually

    • Display Name: Deploy Azure VM

    • Title: Deploy Azure VM

    • WebServiceEndPoint (your SMA Server): https://mysmaserver.contoso.com

    • SMARunbookName: DeployVMInAzure

    • azureSettingsFile: c:\temp\myAzureSettings.publishsettings

    • azureAccountName: myAzureAccount@myemailaccount.com

    • imageName: w2012r2base3

    • vmName: rslatenTest

    • instanceSize: A0

    • adminUserName: rslaten

    • adminPassword: <P@$$W0Rd1>

    • domain: contoso.com

    • domainUserName: rslaten

    • domainPassword: <P@$$W0Rd1>

    • affinityGroup: Lab

    • networkName: Lab

    • azureStorageAccountName: rslaten

    • subnetName: Subnet-1

    • smManagementServer: sm1.contoso.com

  • Click OK

Create a Service Request Template

  • Open the Service Manager Console and go to Library\Templates

  • Right-click on Templates and select Create Template

    • For Name, type Deploy Azure VM Service Request Template
    • For Class, choose Service Request
    • Change the Management Pack to Custom.Example.Azure
    • Click OK
  • Enter a Title: Deploy Azure VM Service Request

  • Click the Activities tab and click the Plus sign

  • Select the Deploy Azure VM Template, click OK, and click OK

Create a Request Offering

In this request offering I’m only going to let the user select the instance size. With additional customization you can, however, allow other selections such as VM Name, Image Name, etc…

  • Open the Service Manager Console and go to Library\Request Offerings and click Create Request Offering
  • Click Next and enter Deploy Azure Virtual Machine for the Title
  • Select the Deploy Azure VM Service Request Template and click OK
  • Under User Prompts type Instance Size with a type of Simple List and click Next
  • Under Configure Prompts select Instance Size and click Configure
  • Enter the following list properties for instance size (A0 – A9)

image

  • Click OK
  • Click Next
  • Under Map Prompts select Custom.Example.Azure.DeployVM
  • Map InstanceSize to InstanceSize:ListValue

image

  • Click Next, Click Next
  • Under Publish change Offering Status to Published and click Next
  • Click Create and Close

Create a Service Offering

  • Open the Service Manager Console and go to Library\Service Offerings and click Create Service Offering
  • Click Next, enter Cloud Offerings for Title, Click Next 3 Times
  • Under Request Offering Add the Deploy Azure Virtual Machine request offering, click OK, and click Next
  • Under Publish change the Offering Status to Published and click Next
  • Click Create and Close

The output from the exported Management Pack is below:

 <ManagementPack ContentReadable="true" SchemaVersion="2.0" OriginalSchemaVersion="1.1" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsl="https://www.w3.org/1999/XSL/Transform">
  <Manifest>
    <Identity>
      <ID>Custom.Example.Azure</ID>
      <Version>1.0.0.0</Version>
    </Identity>
    <Name>Custom.Example.Azure</Name>
    <References>
      <Reference Alias="System">
        <ID>System.Library</ID>
        <Version>7.5.8501.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Console">
        <ID>Microsoft.EnterpriseManagement.ServiceManager.UI.Console</ID>
        <Version>7.5.3079.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca">
        <ID>System.WorkItem.Activity.Library</ID>
        <Version>7.5.3079.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="EnterpriseManagement">
        <ID>Microsoft.EnterpriseManagement.ServiceManager.UI.Administration</ID>
        <Version>7.5.3079.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="SystemCenter">
        <ID>Microsoft.SystemCenter.Library</ID>
        <Version>7.0.8433.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="SystemCenter1">
        <ID>Microsoft.SystemCenter.Subscriptions</ID>
        <Version>7.5.3079.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Core">
        <ID>ServiceManager.Core.Library</ID>
        <Version>7.5.3079.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Windows">
        <ID>Microsoft.Windows.Library</ID>
        <Version>7.5.8501.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="System_WorkItem_Library">
        <ID>System.WorkItem.Library</ID>
        <Version>7.5.3079.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Example">
        <ID>Custom.Example.ServiceRequestExtended</ID>
        <Version>1.0.0.1</Version>
        <PublicKeyToken>e24f5efd5fd6e660</PublicKeyToken>
      </Reference>
    </References>
  </Manifest>
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="Custom.Example.Azure.DeployVM" Accessibility="Public" Abstract="false" Base="Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItem.Activity" Hosted="false" Singleton="false" Extension="false">
          <Property ID="WebServiceEndpoint" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="azureSettingsFile" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="imageName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="vmName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="instanceSize" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="adminUserName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="adminPassword" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="domain" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="domainUserName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="domainPassword" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="affinityGroup" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="networkName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="azureStorageAccountName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="subnetName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="SMARunbookName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="azureAccountName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
          <Property ID="smManagementServer" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
        </ClassType>
      </ClassTypes>
    </EntityTypes>
    <ModuleTypes>
      <WriteActionModuleType ID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00.MT" Accessibility="Public" RunAs="Core!Microsoft.SystemCenter.ServiceManager.WorkflowAccount" Batching="false">
        <Configuration>
          <IncludeSchemaTypes>
            <SchemaType>Windows!Microsoft.Windows.PowerShellSchema</SchemaType>
          </IncludeSchemaTypes>
          <xsd:element name="WebServiceEndPoint" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="SMARunbookName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="azureSettingsFile" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="imageName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="vmName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="instanceSize" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="adminUserName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="domain" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="domainUserName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="domainPassword" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="affinityGroup" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="networkName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="azureStorageAccountName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="subnetName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="adminPassword" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="azureAccountName" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="smManagementServer" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
          <xsd:element name="activityID" type="xsd:string" xmlns:xsd="https://www.w3.org/2001/XMLSchema" />
        </Configuration>
        <OverrideableParameters>
          <OverrideableParameter ID="WebServiceEndPoint" Selector="$Config/WebServiceEndPoint$" ParameterType="string" />
          <OverrideableParameter ID="SMARunbookName" Selector="$Config/SMARunbookName$" ParameterType="string" />
          <OverrideableParameter ID="azureSettingsFile" Selector="$Config/azureSettingsFile$" ParameterType="string" />
          <OverrideableParameter ID="imageName" Selector="$Config/imageName$" ParameterType="string" />
          <OverrideableParameter ID="vmName" Selector="$Config/vmName$" ParameterType="string" />
          <OverrideableParameter ID="instanceSize" Selector="$Config/instanceSize$" ParameterType="string" />
          <OverrideableParameter ID="adminUserName" Selector="$Config/adminUserName$" ParameterType="string" />
          <OverrideableParameter ID="domain" Selector="$Config/domain$" ParameterType="string" />
          <OverrideableParameter ID="domainUserName" Selector="$Config/domainUserName$" ParameterType="string" />
          <OverrideableParameter ID="domainPassword" Selector="$Config/domainPassword$" ParameterType="string" />
          <OverrideableParameter ID="affinityGroup" Selector="$Config/affinityGroup$" ParameterType="string" />
          <OverrideableParameter ID="networkName" Selector="$Config/networkName$" ParameterType="string" />
          <OverrideableParameter ID="azureStorageAccountName" Selector="$Config/azureStorageAccountName$" ParameterType="string" />
          <OverrideableParameter ID="subnetName" Selector="$Config/subnetName$" ParameterType="string" />
          <OverrideableParameter ID="adminPassword" Selector="$Config/adminPassword$" ParameterType="string" />
          <OverrideableParameter ID="azureAccountName" Selector="$Config/azureAccountName$" ParameterType="string" />
          <OverrideableParameter ID="smManagementServer" Selector="$Config/smManagementServer$" ParameterType="string" />
          <OverrideableParameter ID="activityID" Selector="$Config/activityID$" ParameterType="string" />
        </OverrideableParameters>
        <ModuleImplementation Isolation="Any">
          <Composite>
            <MemberModules>
              <WriteAction ID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00.PSWA" TypeID="Windows!Microsoft.Windows.PowerShellWriteAction">
                <ScriptName>DoWork.ps1</ScriptName>
                <ScriptBody>param ( [string]$WebServiceEndPoint,[string]$SMARunbookName,[string]$azureSettingsFile,[string]$imageName,[string]$vmName,[string]$instanceSize,[string]$adminUserName,[string]$domain,[string]$domainUserName,[string]$domainPassword,[string]$affinityGroup,[string]$networkName,[string]$azureStorageAccountName,[string]$subnetName,[string]$adminPassword,[string]$azureAccountName,[string]$smManagementServer,[string]$activityID )
Start-SmaRunbook -WebServiceEndpoint $WebServiceEndpoint -Name $SMARunbookName -Parameters @{"azureSettingsFile" = "$azureSettingsFile"; "azureAccountName" = "$azureAccountName"; "imageName" = "$imageName"; "vmName" = "$vmName"; "instanceSize" = "$instanceSize"; "adminUserName" = "$adminUserName"; "adminPassword" = "$adminPassword"; "domain" = "$domain"; "domainUserName" = "$domainUserName"; "domainPassword" = "$domainPassword"; "affinityGroup" = "$affinityGroup"; "networkName" = "$networkName"; "azureStorageAccountName" = "$azureStorageAccountName"; "subnetName" = "$subnetName"; "smManagementServer" = "$smManagementServer"; "activityID" = "$activityID"}</ScriptBody>
                <SnapIns />
                <Parameters>
                  <Parameter>
                    <Name>WebServiceEndPoint</Name>
                    <Value>$Config/WebServiceEndPoint$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>SMARunbookName</Name>
                    <Value>$Config/SMARunbookName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>azureSettingsFile</Name>
                    <Value>$Config/azureSettingsFile$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>imageName</Name>
                    <Value>$Config/imageName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>vmName</Name>
                    <Value>$Config/vmName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>instanceSize</Name>
                    <Value>$Config/instanceSize$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>adminUserName</Name>
                    <Value>$Config/adminUserName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>domain</Name>
                    <Value>$Config/domain$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>domainUserName</Name>
                    <Value>$Config/domainUserName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>domainPassword</Name>
                    <Value>$Config/domainPassword$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>affinityGroup</Name>
                    <Value>$Config/affinityGroup$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>networkName</Name>
                    <Value>$Config/networkName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>azureStorageAccountName</Name>
                    <Value>$Config/azureStorageAccountName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>subnetName</Name>
                    <Value>$Config/subnetName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>adminPassword</Name>
                    <Value>$Config/adminPassword$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>azureAccountName</Name>
                    <Value>$Config/azureAccountName$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>smManagementServer</Name>
                    <Value>$Config/smManagementServer$</Value>
                  </Parameter>
                  <Parameter>
                    <Name>activityID</Name>
                    <Value>$Config/activityID$</Value>
                  </Parameter>
                </Parameters>
                <TimeoutSeconds>300</TimeoutSeconds>
                <StrictErrorHandling>true</StrictErrorHandling>
                <SerializationDepth>3</SerializationDepth>
              </WriteAction>
            </MemberModules>
            <Composition>
              <Node ID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00.PSWA" />
            </Composition>
          </Composite>
        </ModuleImplementation>
        <InputType>System!System.BaseData</InputType>
      </WriteActionModuleType>
    </ModuleTypes>
  </TypeDefinitions>
  <Categories>
    <Category ID="Custom.Example.Azure.Category" Value="Console!Microsoft.EnterpriseManagement.ServiceManager.ManagementPack">
      <ManagementPackName>Custom.Example.Azure</ManagementPackName>
      <ManagementPackVersion>1.0.0.0</ManagementPackVersion>
    </Category>
    <Category ID="DeployVMCategory" Target="DeployVM" Value="EnterpriseManagement!Microsoft.EnterpriseManagement.ServiceManager.Rules.WorkflowSubscriptions" />
  </Categories>
  <Monitoring>
    <Rules>
      <Rule ID="DeployVM" Enabled="true" Target="SystemCenter!Microsoft.SystemCenter.SubscriptionWorkflowTarget" ConfirmDelivery="true" Remotable="true" Priority="Normal" DiscardLevel="100">
        <Category>Notification</Category>
        <DataSources>
          <DataSource ID="DS" TypeID="SystemCenter1!Microsoft.SystemCenter.CmdbInstanceSubscription.DataSourceModule">
            <Subscription>
              <InstanceSubscription Type="$MPElement[Name='Custom.Example.Azure.DeployVM']$">
                <UpdateInstance>
                  <Criteria>
                    <Expression>
                      <SimpleExpression>
                        <ValueExpression>
                          <Property State="Post">$Context/Property[Type='Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItem.Activity']/Status$</Property>
                        </ValueExpression>
                        <Operator>Equal</Operator>
                        <ValueExpression>
                          <Value>{11fc3cef-15e5-bca4-dee0-9c1155ec8d83}</Value>
                        </ValueExpression>
                      </SimpleExpression>
                    </Expression>
                  </Criteria>
                </UpdateInstance>
              </InstanceSubscription>
              <PollingIntervalInSeconds>60</PollingIntervalInSeconds>
              <BatchSize>100</BatchSize>
            </Subscription>
          </DataSource>
        </DataSources>
        <WriteActions>
          <WriteAction ID="WA" TypeID="SystemCenter1!Microsoft.EnterpriseManagement.SystemCenter.Subscription.WindowsWorkflowTaskWriteAction">
            <Subscription>
              <WindowsWorkflowConfiguration>
                <AssemblyName>DeployVM</AssemblyName>
                <WorkflowTypeName>WorkflowAuthoring.DeployVM</WorkflowTypeName>
                <WorkflowParameters>
                  <WorkflowParameter Name="windowsPowerShellScript1_WebServiceEndPoint" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/WebServiceEndpoint$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_azureSettingsFile" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/azureSettingsFile$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_imageName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/imageName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_vmName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/vmName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_instanceSize" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/instanceSize$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_adminUserName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/adminUserName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_domain" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/domain$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_domainUserName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/domainUserName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_domainPassword" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/domainPassword$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_affinityGroup" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/affinityGroup$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_networkName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/networkName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_azureStorageAccountName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/azureStorageAccountName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_subnetName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/subnetName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_SMARunbookName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/SMARunbookName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_adminPassword" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/adminPassword$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_azureAccountName" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/azureAccountName$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_smManagementServer" Type="string">$Data/Property[@ID='$MPElement[Name="Custom.Example.Azure.DeployVM"]/smManagementServer$' and @State='Post']$</WorkflowParameter>
                  <WorkflowParameter Name="windowsPowerShellScript1_activityID" Type="string">$Data/Property[@ID='$MPElement[Name="System_WorkItem_Library!System.WorkItem"]/Id$' and @State='Post']$</WorkflowParameter>
                </WorkflowParameters>
                <RetryExceptions />
                <RetryDelaySeconds>60</RetryDelaySeconds>
                <MaximumRunningTimeSeconds>300</MaximumRunningTimeSeconds>
              </WindowsWorkflowConfiguration>
            </Subscription>
          </WriteAction>
        </WriteActions>
      </Rule>
    </Rules>
    <Tasks>
      <Task ID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00" Accessibility="Public" Enabled="true" Target="Windows!Microsoft.Windows.Computer" Timeout="300" Remotable="true">
        <Category>Notification</Category>
        <WriteAction ID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00.WA" TypeID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00.MT">
          <WebServiceEndPoint />
          <SMARunbookName />
          <azureSettingsFile />
          <imageName />
          <vmName />
          <instanceSize />
          <adminUserName />
          <domain />
          <domainUserName />
          <domainPassword />
          <affinityGroup />
          <networkName />
          <azureStorageAccountName />
          <subnetName />
          <adminPassword />
          <azureAccountName />
          <smManagementServer />
          <activityID />
        </WriteAction>
      </Task>
    </Tasks>
  </Monitoring>
  <Templates>
    <ObjectTemplate ID="Template.87826befabdb478da4e836bdb4b93c8e" TypeID="Custom.Example.Azure.DeployVM">
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/WebServiceEndpoint$">https://mysmaserver.contoso.com</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/azureSettingsFile$">c:\temp\myAzureSettings.publishsettings</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/imageName$">w2012r2base3</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/vmName$">rslatenTest</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/instanceSize$">A0</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/adminUserName$">rslaten</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/adminPassword$">P@$$W0Rd1</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/domain$">contoso.com</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/domainUserName$">rslaten</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/domainPassword$">P@$$W0Rd1</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/affinityGroup$">Lab</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/networkName$">Lab</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/azureStorageAccountName$">rslaten</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/subnetName$">Subnet-1</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/SMARunbookName$">DeployVMInAzure</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/azureAccountName$">myAzureAccount@myemailaccount.com</Property>
      <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/smManagementServer$">sm1.contoso.com</Property>
      <Property Path="$Context/Property[Type='Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItem.Activity']/Skip$">False</Property>
      <Property Path="$Context/Property[Type='System_WorkItem_Library!System.WorkItem']/Title$">Deploy Azure VM</Property>
      <Property Path="$Context/Property[Type='System!System.Entity']/DisplayName$">Deploy Azure VM</Property>
    </ObjectTemplate>
    <ObjectTemplate ID="Template.e0b9b5533a9d4a82b37658e95c03f0f7" TypeID="Example!CustomForm_0c972943_1f64_4cd2_84bc_6a9c7da0c6eb_TypeProjection">
      <Object Path="$Context/Path[Relationship='Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItemContainsActivity' TypeConstraint='Custom.Example.Azure.DeployVM']$">
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/WebServiceEndpoint$">https://mysmaserver.contoso.com</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/azureSettingsFile$">c:\temp\myAzureSettings.publishsettings</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/imageName$">w2012r2base3</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/vmName$">rslatenTest</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/instanceSize$">A0</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/adminUserName$">rslaten</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/adminPassword$">P@$$W0Rd1</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/domain$">contoso.com</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/domainUserName$">rslaten</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/domainPassword$">P@$$W0Rd1</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/affinityGroup$">Lab</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/networkName$">Lab</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/azureStorageAccountName$">rslaten</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/subnetName$">Subnet-1</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/SMARunbookName$">DeployVMInAzure</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/azureAccountName$">myAzureAccount@myemailaccount.com</Property>
        <Property Path="$Context/Property[Type='Custom.Example.Azure.DeployVM']/smManagementServer$">sm1.contoso.com</Property>
        <Property Path="$Context/Property[Type='Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItem.Activity']/SequenceId$">0</Property>
        <Property Path="$Context/Property[Type='Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItem.Activity']/ChildId$">828</Property>
        <Property Path="$Context/Property[Type='Alias_952b56e8_dd9f_480c_8f57_1d53f5894aca!System.WorkItem.Activity']/Skip$">False</Property>
        <Property Path="$Context/Property[Type='System_WorkItem_Library!System.WorkItem']/Title$">Deploy Azure VM</Property>
        <Property Path="$Context/Property[Type='System!System.Entity']/DisplayName$">Deploy Azure VM</Property>
      </Object>
    </ObjectTemplate>
  </Templates>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="Custom.Example.Azure">
          <Name>Custom.Example.Azure</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM">
          <Name>Custom.Example.Azure.DeployVM</Name>
          <Description>Specify your class description.</Description>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="WebServiceEndpoint">
          <Name>WebServiceEndpoint</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="SMARunbookName">
          <Name>SMARunbookName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="azureSettingsFile">
          <Name>azureSettingsFile</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="imageName">
          <Name>imageName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="vmName">
          <Name>vmName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="instanceSize">
          <Name>instanceSize</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="adminUserName">
          <Name>adminUserName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="adminPassword">
          <Name>adminPassword</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="domain">
          <Name>domain</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="domainUserName">
          <Name>domainUserName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="domainPassword">
          <Name>domainPassword</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="affinityGroup">
          <Name>affinityGroup</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="networkName">
          <Name>networkName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="azureStorageAccountName">
          <Name>azureStorageAccountName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="subnetName">
          <Name>subnetName</Name>
        </DisplayString>
        <DisplayString ElementID="DeployVM">
          <Name>DeployVM</Name>
        </DisplayString>
        <DisplayString ElementID="DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00">
          <Name>DeployVM.WindowsPowerShellScript.6992e768_20fa_4bc1_80fd_1b116ad8bc00</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="azureAccountName">
          <Name>azureAccountName</Name>
        </DisplayString>
        <DisplayString ElementID="Custom.Example.Azure.DeployVM" SubElementID="smManagementServer">
          <Name>smManagementServer</Name>
        </DisplayString>
        <DisplayString ElementID="Template.87826befabdb478da4e836bdb4b93c8e">
          <Name>Deploy Azure VM Template</Name>
        </DisplayString>
        <DisplayString ElementID="Template.e0b9b5533a9d4a82b37658e95c03f0f7">
          <Name>Deploy Azure VM Service Request Template</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
  <Extensions>
    <RequestOffering ID="Offering59cfa70b0f134665821820b78a3f744a" InstanceName="Deploy Azure Virtual Machine" Title="Deploy Azure Virtual Machine" PublishDate="2014-08-23T16:45:46-05:00" Status="System.Offering.StatusEnum.Published" TargetTemplate="Template.e0b9b5533a9d4a82b37658e95c03f0f7" HideGotoRequestButton="false">
      <PresentationMappingTemplate>
        <Sources>
          <Source Id="00000000-0000-0000-0000-000000000000" Ordinal="0" ReadOnly="false" Optional="false" ControlType="System.SupportingItem.PortalControl">
            <ControlConfiguration>
              <Dependencies />
              <AddressableOutputs>
                <AddressableOutput OutputName="Token: Portal User Name" OutputType="string" />
              </AddressableOutputs>
            </ControlConfiguration>
            <Targets />
          </Source>
          <Source Id="979c2819-ad73-4319-b5c7-e8b55edc956b" Ordinal="1" Prompt="Instance Size" ReadOnly="false" Optional="false" ControlType="System.SupportingItem.PortalControl.InlineList">
            <ControlConfiguration>
              <Dependencies />
              <AddressableOutputs>
                <AddressableOutput OutputName="ListValue" OutputType="string" />
              </AddressableOutputs>
              <Configuration>
                <Details>
                  <ListValue DisplayName="A0" IsDefault="true" />
                  <ListValue DisplayName="A1" IsDefault="false" />
                  <ListValue DisplayName="A2" IsDefault="false" />
                  <ListValue DisplayName="A3" IsDefault="false" />
                  <ListValue DisplayName="A4" IsDefault="false" />
                  <ListValue DisplayName="A5" IsDefault="false" />
                  <ListValue DisplayName="A6" IsDefault="false" />
                  <ListValue DisplayName="A7" IsDefault="false" />
                  <ListValue DisplayName="A8" IsDefault="false" />
                  <ListValue DisplayName="A9" IsDefault="false" />
                </Details>
              </Configuration>
            </ControlConfiguration>
            <Targets>
              <Target Path="$Target/Path[Relationship='Activity!System.WorkItemContainsActivity']$?ChildId='828'/instanceSize" OutputName="ListValue" />
            </Targets>
          </Source>
        </Sources>
      </PresentationMappingTemplate>
    </RequestOffering>
    <ServiceOffering ID="Offeringa5d733744b234dfcb6d41383780ca6a4" InstanceName="Cloud Offerings" Title="Cloud Offerings" PublishDate="2014-08-23T16:46:55-05:00" Status="System.Offering.StatusEnum.Published" Category="System.ServiceOffering.CategoryEnum.General" CultureName="" />
  </Extensions>
</ManagementPack>

Test the Solution

  • Open the Service Manager Self-Service Portal

image

  • Click on Cloud Offerings, click on Deploy Azure Virtual Machine, and click on Go to request form

image

  • Select the appropriate instance size, click Next, and Submit. Once completed the request should also change to Completed.

image

  • View the completed Runbook in the WAP Portal

image

  • View the new VM in the Azure Portal

image

DeployVMInAzurePartIII.zip