Using PowerShell to deploy DevTest Lab Artifacts


Azure DevTest Labs allow improved management of virtual machines, including being able to install and configure software on the machine using to use an automated method to add artifacts is essential when managing multiple virtual machines across multiple labs and subscriptions.  Especially in the situation where an administrator wants to push an update or new tool out to all virtual machines.  To that end here is a sample PowerShell script that will deploy an artifact to a virtual machine.

Background

This document is written with the assumption that the reader has a solid understanding of DevTest labs and the DTL artifacts.  Here are some documents with more background information on DevTest Labs, creating custom artifacts, and connecting a lab to a custom repository.  While the script is currently designed so that it can be used standalone, comments have been added in the code that can be modified when part of a larger system that will handle multiple labs across multiple subscriptions, like the Custom Image Factory.

What is going on?

The key component is the Invoke-AzureRmResourceAction with an action of applyArtifacts, the rest of the script is getting the appropriate information to pass to the command.

Script Inputs

What the different parameters are needed for the script to deploy an artifact to a VM.

  • SubscriptionId
    • The subscription id where the DevTest Lab exists.
  • DevTestLabName
    • The name of the DevTest lab.
  • VirtualMachineName
    • The DevTest lab machine name where the artifact is to be deployed to.
  • RepositoryName
    • The Repository name, the DevTest Lab managed repository is public repo. Any custom repositories will have a name set at creation.
  • ArtifactName
    • The Artifact title as shown in the Portal which is specified in the artifactfile.json.
  • Params
    • Custom name-value parameters that will be passed to the artifact where the name is prefaced with ‘-param_’. These are the parameters that the artifact needs.

Example Usage

As an example, the DTL public repo has an artifact to install 7Zip without parameters and ATOM on a DTL Windows VM that requires the installUserName and InstallPassword with values to run successfully.  Using this script to install the two artifact the inputs would be:

#apply the 7Zip artifact

ApplyArtifact.ps1 -SubscriptionID $sub -DevTestLabName $lab -VirtualMachineName $vmName -RepositoryName “Public Repo” -ArtifactName 'windows-7zip'

#apply the Atom artifact with the specified parameters

ApplyArtifact.ps1 -SubscriptionID $sub -DevTestLabName $lab -VirtualMachineName $vmName -RepositoryName “Public Repo” -ArtifactName 'Atom'

-param_installUserName YourUserName -param_installPassword YourUserPassword

While more complex, this allows the script to handle any artifact and any number of parameters.

Description

Walkthrough the different pieces of code to clarify what is occurring.

Getting support information using the friendly names.

# Get the lab resource group name

$resourceGroupName = (Find-AzureRmResource -ResourceType 'Microsoft.DevTestLab/labs' | Where-Object { $_.Name -eq $DevTestLabName}).ResourceGroupName

if ($resourceGroupName -eq $null) { throw "Unable to find lab $DevTestLabName in subscription $SubscriptionId." }

# Get the internal repo name

$repository = Get-AzureRmResource -ResourceGroupName $resourceGroupName `

-ResourceType 'Microsoft.DevTestLab/labs/artifactsources' `

-ResourceName $DevTestLabName `

-ApiVersion 2016-05-15 `

| Where-Object { $RepositoryName -in ($_.Name, $_.Properties.displayName) } `

| Select-Object -First 1

if ($repository -eq $null) { throw "Unable to find repository $RepositoryName in lab $DevTestLabName." }

# Get the internal artifact name

$template = Get-AzureRmResource -ResourceGroupName $resourceGroupName `

-ResourceType "Microsoft.DevTestLab/labs/artifactSources/artifacts" `

-ResourceName "$DevTestLabName/$($repository.Name)" `

-ApiVersion 2016-05-15 `

| Where-Object { $ArtifactName -in ($_.Name, $_.Properties.title) } `

| Select-Object -First 1

if ($template -eq $null) { throw "Unable to find template $ArtifactName in lab $DevTestLabName." }

The code above uses the friendly names to get the internal names for use when generating the artifact id that will be passed to the invoke command.  When dealing with a single artifact across multiple labs this code could be moved to a central location and the artifact id could passed in which would reduce the cost by reducing the Get-AzureRmResource requests.

Getting the VM to apply the artifact to.

# Find the virtual machine in Azure

$FullVMId = "/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName`

/providers/Microsoft.DevTestLab/labs/$DevTestLabName/virtualmachines/$virtualMachineName"

$virtualMachine = Get-AzureRmResource -ResourceId $FullVMId

Generate the artifact Id

# Generate the artifact id $FullArtifactId = "/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName` /providers/Microsoft.DevTestLab/labs/$DevTestLabName/artifactSources/$($repository.Name)` /artifacts/$($template.Name)"

The full artifact resource id.

Custom parameters

# Fill artifact parameter with the additional -param_ data and strip off the -param_

$Params | ForEach-Object {

if ($_ -match '^-param_(.*)') {

$name = $_.TrimStart('^-param_')

} elseif ( $name ) {

$artifactParameters += @{ "name" = "$name"; "value" = "$_" }

$name = $null #reset name variable

}

}

The above code is to take the inputted custom parameters, trim off the indentifier token and put the data into an array.

Property formatting

# Create structure for the artifact data to be passed to the action

$prop = @{

artifacts = @(

@{

artifactId = $FullArtifactId

parameters = $artifactParameters

}
)
}

This code creates the properties $prop with the proper structure.  This structure supports artifacts that don’t have any parameters.  When dealing with multiple labs this array could be passed in to the script and the Custom Parameters code and $Params could be removed.

Apply the artifact

$status = Invoke-AzureRmResourceAction -Parameters $prop -ResourceId $virtualMachine.ResourceId -Action "applyArtifacts" -ApiVersion 2016-05-15 -Force

This action applies the artifact in the $prop to the virtual machine $virtualMachine.

If you have any questions about DevTest Labs, please check out the MSDN forum.

Hope this helps.

clip_image002[6] Roger Best, Senior Software Engineer

Roger is part of the Visual Studio and .NET engineering team focused on Visual Studio and Azure customers. He has been at Microsoft for 19 years, focusing on developer technologies for the past decade or so. In his spare time, he watches too many movies, and tries to survive triathlons.

Script

<#
The MIT License (MIT)

Copyright (c) Microsoft Corporation

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

.SYNOPSIS

This script creates a new environment in the lab using an existing environment template.

.PARAMETER SubscriptionId

The subscription ID that the lab is created in.

.PARAMETER DevTestLabName

The name of the lab.

.PARAMETER VirtualMachineName

The virtual machine name to deploy to

.PARAMETER RepositoryName

The name of the repository in the lab.

.PARAMETER ArtifactName

The name of the artifact to be deployed

 

.PARAMETER Params

The parameters pairs to be passed into the artifact ie params_TestVMAdminUserName = adminuser params_TestVMAdminPassword = pwd

 

.NOTES

The script assumes that a lab exists, has a repository connected, and the artifact is in the repository.
#>

#Requires -Version 3.0

#Requires -Module AzureRM.Resources

param

(

[Parameter(Mandatory=$true, HelpMessage="The Subscription Id containing the DevTest lab")]

[string] $SubscriptionId,

[Parameter(Mandatory=$true, HelpMessage="The name of the DevTest Lab containing the Virtual Machine")]

[string] $DevTestLabName,

[Parameter(Mandatory=$true, HelpMessage="The name of the Virtual Machine")]

[string] $VirtualMachineName,

[Parameter(Mandatory=$true, HelpMessage="The repository where the artifact is stored")]

[string] $RepositoryName,

[Parameter(Mandatory=$true, HelpMessage="The artifact to apply to the virtual machine")]

[string] $ArtifactName,

[Parameter(ValueFromRemainingArguments=$true)]

$Params
)
# Set the appropriate subscription

Set-AzureRmContext -SubscriptionId $SubscriptionId | Out-Null
 
# Get the lab resource group name

$resourceGroupName = (Find-AzureRmResource -ResourceType 'Microsoft.DevTestLab/labs' | Where-Object { $_.Name -eq $DevTestLabName}).ResourceGroupName

if ($resourceGroupName -eq $null) { throw "Unable to find lab $DevTestLabName in subscription $SubscriptionId." }

# Get the internal repo name

$repository = Get-AzureRmResource -ResourceGroupName $resourceGroupName `

-ResourceType 'Microsoft.DevTestLab/labs/artifactsources' `

-ResourceName $DevTestLabName `

-ApiVersion 2016-05-15 `

| Where-Object { $RepositoryName -in ($_.Name, $_.Properties.displayName) } `

| Select-Object -First 1

if ($repository -eq $null) { "Unable to find repository $RepositoryName in lab $DevTestLabName." }

# Get the internal artifact name

$template = Get-AzureRmResource -ResourceGroupName $resourceGroupName `

-ResourceType "Microsoft.DevTestLab/labs/artifactSources/artifacts" `

-ResourceName "$DevTestLabName/$($repository.Name)" `

-ApiVersion 2016-05-15 `

| Where-Object { $ArtifactName -in ($_.Name, $_.Properties.title) } `

| Select-Object -First 1

if ($template -eq $null) { throw "Unable to find template $ArtifactName in lab $DevTestLabName." }

# Find the virtual machine in Azure

$FullVMId = "/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName`

/providers/Microsoft.DevTestLab/labs/$DevTestLabName/virtualmachines/$virtualMachineName"

$virtualMachine = Get-AzureRmResource -ResourceId $FullVMId

# Generate the artifact id

$FullArtifactId = "/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName`

/providers/Microsoft.DevTestLab/labs/$DevTestLabName/artifactSources/$($repository.Name)`

/artifacts/$($template.Name)"

# Handle the inputted parameters to pass through

$artifactParameters = @()

# Fill artifact parameter with the additional -param_ data and strip off the -param_

$Params | ForEach-Object {

if ($_ -match '^-param_(.*)') {

$name = $_.TrimStart('^-param_')

} elseif ( $name ) {

$artifactParameters += @{ "name" = "$name"; "value" = "$_" }

$name = $null #reset name variable

}

}
# Create structure for the artifact data to be passed to the action

$prop = @{

artifacts = @(

@{

artifactId = $FullArtifactId

parameters = $artifactParameters

}

)

}
# Check the VM

if ($virtualMachine -ne $null) {

# Apply the artifact by name to the virtual machine

$status = Invoke-AzureRmResourceAction -Parameters $prop -ResourceId $virtualMachine.ResourceId -Action "applyArtifacts" -ApiVersion 2016-05-15 -Force

if ($status.Status -eq 'Succeeded') {

Write-Output "##[section] Successfully applied artifact: $ArtifactName to $VirtualMachineName"

}

else {

Write-Error "##[error]Failed to apply artifact: $ArtifactName to $VirtualMachineName"

}

}

else {

Write-Error "##[error]$VirtualMachine was not found in the DevTest Lab, unable to apply the artifact"

}

 

 

 


Comments (0)

Skip to main content