Skip to content

Instantly share code, notes, and snippets.

@pvandervelde
Created November 10, 2014 08:47
Show Gist options
  • Save pvandervelde/349767f804a32195ee67 to your computer and use it in GitHub Desktop.
Save pvandervelde/349767f804a32195ee67 to your computer and use it in GitHub Desktop.
Syspreps an Azure VM and then creates an image from it.
<#
.SYNOPSIS
Syspreps an Azure VM and then creates an image from it.
.DESCRIPTION
The New-AzureSyspreppedVMImage function executes sysprep on a given Azure VM and then once the VM is shut down creates an image from it.
.PARAMETER session
The PSSession that provides the connection between the local machine and the remote machine.
.PARAMETER resourceGroupName
The name of the resource group in which the VM exists.
.PARAMETER vmName
The azure name of the VM.
.PARAMETER imageName
The name of the image.
.PARAMETER imageLabel
The label of the image.
.EXAMPLE
New-AzureSyspreppedVMImage
-session $session
-resourceGroupName 'jenkinsresource'
-vmName 'ajm-305-220615'
-imageName "jenkins-master-win2012R2_0.2.0"
-imageLabel "Jenkins master on Windows Server 2012 R2"
#>
function New-AzureSyspreppedVMImage
{
[CmdletBinding()]
param(
[System.Management.Automation.Runspaces.PSSession] $session,
[string] $resourceGroupName,
[string] $vmName,
[string] $imageName,
[string] $imageLabel
)
# Stop everything if there are errors
$ErrorActionPreference = 'Stop'
$commonParameterSwitches =
@{
Verbose = $PSBoundParameters.ContainsKey('Verbose');
Debug = $PSBoundParameters.ContainsKey('Debug');
ErrorAction = "Stop"
}
$cmd = 'Write-Verbose "Executing $sysPrepScript on VM"; & c:\Windows\system32\sysprep\sysprep.exe /oobe /generalize /shutdown'
$tempDir = Join-Path $env:TEMP ([System.Guid]::NewGuid().ToString())
$sysprepCmd = Join-Path $tempDir 'sysprep.ps1'
$remoteDirectory = "c:\sysprep"
try
{
if (-not (Test-Path $tempDir))
{
New-Item -Path $tempDir -ItemType Directory | Out-Null
}
Set-Content -Value $cmd -Path $sysprepCmd
Add-AzureFilesToVM -session $session -remoteDirectory $remoteDirectory -filesToCopy @( $sysprepCmd )
}
finally
{
Remove-Item -Path $tempDir -Force -Recurse
}
# Sysprep
# Note that apparently this can't be done just remotely because sysprep starts but doesn't actually
# run (i.e. it exits without doing any work). So this needs to be done from the local machine
# that is about to be sysprepped.
Write-Verbose "Starting sysprep ..."
Invoke-Command `
-Session $session `
-ArgumentList @( (Join-Path $remoteDirectory (Split-Path -Leaf $sysprepCmd)) ) `
-ScriptBlock {
param(
[string] $sysPrepScript
)
& "$sysPrepScript"
} `
-Verbose `
-ErrorAction Continue
# Wait for machine to turn off. Wait for a maximum of 5 minutes before we fail it.
$isRunning = $true
$timeout = [System.TimeSpan]::FromMinutes(20)
$killTime = [System.DateTimeOffset]::Now + $timeout
$hasFailed = $false
Write-Verbose "SysPrep is shutting down machine. Waiting ..."
try
{
while ($isRunning)
{
$vm = Get-AzureVM -ServiceName $resourceGroupName -Name $vmName
Write-Verbose ("$vmName is status: " + $vm.Status)
if (($vm.Status -eq "StoppedDeallocated") -or ($vm.Status -eq "StoppedVM"))
{
Write-Verbose "VM stopped"
$isRunning = $false
}
if ([System.DateTimeOffset]::Now -gt $killTime)
{
Write-Verbose "VM failed to stop within time-out"
$isRunning = false;
$hasFailed = $true
}
}
}
catch
{
Write-Verbose "Failed during time-out loop"
# failed. Just ignore it
}
if ($hasFailed)
{
throw "Virtual machine Sysprep failed to complete within $timeout"
}
Write-Verbose "Sysprep complete. Starting image creation"
Write-Verbose "ServiceName: $resourceGroupName"
Write-Verbose "Name: $vmName"
Write-Verbose "ImageName: $imageName"
Write-Verbose "ImageLabel: $imageLabel"
Save-AzureVMImage -ServiceName $resourceGroupName -Name $vmName -ImageName $imageName -OSState Generalized -ImageLabel $imageLabel @commonParameterSwitches
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment