Azure RemoteApp allows you to host windows apps on any device. I recently had to setup a proof of concept using Azure RemoteApp and an internal client server application. The actual process is pretty straight forward if you are familiar with the process of capturing an image of an Azure VM or you already have an image you would like to use.
If you are starting this process from scratch make sure you use the Windows Server 2012 Remote Desktop Session Host image, this image is a base image valid for use with Azure RemoteApp. This image also contains the RemoteApp PowerShell Validation Script that is a required step before Sysprepping the VM and capturing the image. Also note that this image seems to only be available if you are creating the Azure VM from the Old Azure Portal.
If you already have an image or an Azure VM you would like to import into the RemoteApp service, but have not run the Validation Script, I have included it with this post for future reference. As stated above you can also spin up a VM based on the Remote Desktop Session Host image and pull the script from there.
Azure RemoteApp PowerShell Validation Script:
Location of script on the RDSH image:
c:\program files\WindowsPowershell\Upload-GoldImage.ps1
You will want to execute this with the “-validateCurrentOS” parameter.
param( [Parameter(Mandatory=$true, ParameterSetName="UploadVhd")] [string] $uri, [Parameter(Mandatory=$true, ParameterSetName="UploadVhd")] [string] $sas, [Parameter(Mandatory=$false, ParameterSetName="UploadVhd")] [string] $vhdPath = $null, [Parameter(Mandatory=$true, ParameterSetName="ValidateCurrentOS")] [switch] $validateCurrentOS ) ############################# # Localization string names # ############################# $SuccessfullyMountedVhdScriptText = "SuccessfullyMountedVhdScriptText" $FailedToMountVhdScriptError = "FailedToMountVhdScriptError" $SuccessfullyUnmountedVhdScriptText = "SuccessfullyUnmountedVhdScriptText" $UnsupportedOsScriptError = "UnsupportedOsScriptError" $UnsupportedOsSkuScriptError = "UnsupportedOsSkuScriptError" $UnsupportedOsEditionScriptError = "UnsupportedOsEditionScriptError" $ImageIsntGeneralizedScriptError = "ImageIsntGeneralizedScriptError" $FailedToReadAppServerRegistryKeyScriptError = "FailedToReadAppServerRegistryKeyScriptError" $RdshRoleNotInstalledScriptError = "RdshRoleNotInstalledScriptError" $NotRemoteAppReadyImageScriptError = "NotRemoteAppReadyImageScriptError" $NotInAzurePowershellEnvironmentScriptError = "NotInAzurePowershellEnvironmentScriptError" $StartingImageUploadScriptText = "StartingImageUploadScriptText" $ImageUploadCompleteScriptText = "ImageUploadCompleteScriptText" $MountingVhdScriptText = "MountingVhdScriptText" $NotRunningAsAdminScriptError = "NotRunningAsAdminScriptError" $NoOsFoundOnTemplateImage = "NoOsFoundOnTemplateImage" $MultipleOsFoundOnTemplateImage = "MultipleOsFoundOnTemplateImage" $SysprepVmModeNotSupported = "SysprepVmModeNotSupported" $UnattendFileError = "UnattendFileError" $NumberOfWindowsVolumes = "NumberOfWindowsVolumes" $HiveUnloadSuccess = "HiveUnloadSuccess" $HiveUnloadFailure = "HiveUnloadFailure" $CurrentVmModeValue = "CurrentVmModeValue" $NtfsDisableEncryptionError = "NtfsDisableEncryptionError" $ImportingAzureModuleScriptText = "ImportingAzureModuleScriptText" $ImportingStorageModuleScriptText = "ImportingStorageModuleScriptText" $FailedToLoadAzureModuleError = "FailedToLoadAzureModuleError" $FailedToLoadStorageModuleError = "FailedToLoadStorageModuleError" $IncompatibleAzurePowerShellModuleError = "IncompatibleAzurePowerShellModuleError" $AzureDotNetSdkNotInstalledError = "AzureDotNetSdkNotInstalledError" $RdpInitVersionInfo = "RdpInitVersionInfo" $FailedRdpInitVersion = "FailedRdpInitVersion" $RdpInitVersionCheckSuccess = "RdpInitVersionCheckSuccess" $ImageSizeNotMultipleOfMBs = "ImageSizeNotMultipleOfMBs" $ImageSizeGreaterThanMaxSizeLimit = "ImageSizeGreaterThanMaxSizeLimit" $StorageModuleImportFailed = "StorageModuleImportFailed" $ImagePartitionStyleNotSupported = "ImagePartitionStyleNotSupported" $DiskPartitionStyle = "DiskPartitionStyle" $RdcbRoleInstalledScriptError = "RdcbRoleInstalledScriptError" $FailedToReadTssdisRegistryKeyScriptError = "FailedToReadTssdisRegistryKeyScriptError" $FailedToFindMountedDriveVhd = "FailedToFindMountedDriveVhd" $ClientMachineRunningOnDownlevelOs = "ClientMachineRunningOnDownlevelOs" $FailedToGetVolumeListError = "FailedToGetVolumeListError" $CurrentOsCheckSuccess = "CurrentOsCheckSuccess" ####################### # Localization tables # ####################### $LocalizationTable_en = @{ $SuccessfullyMountedVhdScriptText = "Successfully mounted the VHD."; $FailedToMountVhdScriptError = "Could not mount the specified VHD."; $SuccessfullyUnmountedVhdScriptText = "Successfully detached the VHD."; $UnsupportedOsScriptError = "The template image must be created using Windows Server 2012 R2 as the operating system."; $UnsupportedOsSkuScriptError = "The template image must be created using Windows Server as the operating system."; $UnsupportedOsEditionScriptError = "The template image must be created using Windows Server 2012 R2 DataCenter or Standard editions. Your template image edition is {0}"; $ImageIsntGeneralizedScriptError = "The template image is not in a generalized state. You can use Sysprep on the image to change the template image to a generalized state."; $FailedToReadAppServerRegistryKeyScriptError = "Failed to read the App Server reg key:"; $RdshRoleNotInstalledScriptError = "The RD Session Host server role is not installed on the template image."; $NotRemoteAppReadyImageScriptError = "The image does not satisfy Windows Azure RemoteApp requirements."; $NotInAzurePowershellEnvironmentScriptError = "Run this script using Windows Azure PowerShell."; $StartingImageUploadScriptText = "Starting image upload."; $ImageUploadCompleteScriptText = "Successfully uploaded template image."; $MountingVhdScriptText = "Mounting the VHD."; $NotRunningAsAdminScriptError = "This script requires elevation. Run Windows Azure PowerShell as Administrator and try again."; $NoOsFoundOnTemplateImage = "Windows Operating System could not be located on the Image Template. Please verifiy that the image has a valid Windows Operating System."; $MultipleOsFoundOnTemplateImage = "The specified Template Image has multiple Windows Operating Systems installed. Please use an image which has only one Windows Operating System."; $SysprepVmModeNotSupported = "Images prepared with sysprep /mode:VM flag are not supported. Please rerun sysprep.exe without /mode:vm flag and try to upload again." $UnattendFileError = "Please make sure there are no custom unattend files on the disk. Found: " $NumberOfWindowsVolumes = "Number of volumes with Windows OS on it: " $HiveUnloadSuccess = "Successfully unloaded hive: HKLM:\{0} from the VHD." $HiveUnloadFailure = "Failed to unload registry hive HKLM:\{0} from the VHD. Please unmount the registry hive manually." $CurrentVmModeValue = "Current sysprep VM Mode value of the image is: " $NtfsDisableEncryptionError = "Please make sure Encrypting File System (EFS) is disabled on the template image you are trying to upload. In order to disable EFS, boot into a VM running with this template image, run 'Fsutil behavior set disableencryption 1' on an elevated command window and then sysprep and try uploading again" $ImportingAzureModuleScriptText = "Importing Azure module..." $ImportingStorageModuleScriptText = "Importing Storage module..." $FailedToLoadAzureModuleError = "Failed to load Azure module. Make sure you have the latest Azure PowerShell module installed." $FailedToLoadStorageModuleError = "Failed to load Storage powershell module." $IncompatibleAzurePowerShellModuleError = "This version of the Azure PowerShell module is not compatible with Azure RemoteApp services. Please install Azure PowerShell module version 0.8.3 or lower and then try uploading again." $AzureDotNetSdkNotInstalledError = "This script requires Azure .Net SDK installed on the system. Please install Azure SDK and then run the script from a Windows Azure PowerShell" $RdpInitVersionInfo = "RdpInit.exe version in the VHD:" $FailedRdpInitVersion = "!!!!!!!!!RdpInit.exe in the '\{0}' is not up to date. Please install KB2977219 from 'http://support.microsoft.com/kb/2977219'." $RdpInitVersionCheckSuccess = "RdpInit.exe in the '\{0}' is up to date." $ImageSizeNotMultipleOfMBs = "Size of the specified template image is not a multiple of MBs. Please upload an image whose size is a multiple of 1MB." $ImageSizeGreaterThanMaxSizeLimit = "Size of the specified template image is greater than 128 GB. Please upload an image whose size is 128 GB or less." $StorageModuleImportFailed = "Please close all powershell windows and try executing the script again on a new powershell window. If you are running the script from Azure powershell window, please switch to a Windows powershell window and retry." $ImagePartitionStyleNotSupported = "The template image you are trying to upload does not have 'MBR' partition style. Please re-create the image with 'MBR' partition style and upload again." $DiskPartitionStyle = "Template image disk partitioning style is: " $RdcbRoleInstalledScriptError = "Remote Desktop Connection Manager role seems to be installed on the template image. Please uninstall the role, sysprep the image and try uploading again." $FailedToReadTssdisRegistryKeyScriptError = "Failed to read the Tssdis reg key on the template image" $FailedToFindMountedDriveVhd = "Failed to find mounted '\{0}' VHD drive" $ClientMachineRunningOnDownlevelOs = "Client machine is running on OS lower than Windows 8.1 or Windows Server 2012 R2" $FailedToGetVolumeListError = "Failed to retrieve the list of volumes on current operating system" $CurrentOsCheckSuccess = "The current image satisfies all the requirements for Azure RemoteApp Template image." } ################### # Table of tables # ################### $LocalizationTables = @{ "en" = $LocalizationTable_en; } ########################### # Pick localization table # ########################### $CurrentCulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture $CurrentCultureTable = $null while($CurrentCultureTable -eq $null) { if([string]::IsNullOrEmpty($CurrentCulture.Name)) { $CurrentCultureTable = $LocalizationTable_en; } else { if($LocalizationTables.ContainsKey($CurrentCulture.Name)) { $CurrentCultureTable = $LocalizationTables[$CurrentCulture.Name] } else { $CurrentCulture = $CurrentCulture.Parent } } } function Get-FileVersion { param( [Parameter(Mandatory=$true)] [string]$FileName) $ver = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($FileName) if ([string]::IsNullOrEmpty($ver.FileVersion)) { return $null } return New-Object Version($ver.FileMajorPart, $ver.FileMinorPart, $ver.FileBuildPart, $ver.FilePrivatePart) } function Get-File { [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.initialDirectory = "C:\" $OpenFileDialog.filter = "VHD files (*.vhd;*.vhdx)| *vhd; *.vhdx" $dialogResult = $OpenFileDialog.ShowDialog() if ($dialogResult -eq [System.Windows.Forms.DialogResult]::Cancel) { exit } $OpenFileDialog.filename Split-Path $OpenFileDialog.filename -Leaf } function Is-RunningAsAdmin { $windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $windowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($windowsIdentity) $administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator $isRunningAsAdmin = $windowsPrincipal.IsInRole($administratorRole) return $isRunningAsAdmin } function Get-AzureVersion { $path = "HKLM:\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting" $versions = Get-ChildItem -Path "$path" | Sort-Object -Descending $version = Split-Path $versions[0].Name -Leaf return $version } function Get-VolumeList() { try { $volumeInstances = Get-WmiObject -Class Win32_Volume foreach($volume in $volumeInstances) { if(!($global:volumeList.Contains($volume.DeviceID))) { $global:volumeList.Add($volume.DeviceID) } } } catch { } if ($global:volumeList.Count -gt 0) { return $true } Write-Error($CurrentCultureTable[$FailedToGetVolumeListError]) return $false } function Attach-VHD([string] $vhdfile) { $success = $true $diskpartOutput = [string]::Empty $errorOutput = [string]::Empty Write-Verbose "`r`n" Write-Verbose ($CurrentCultureTable[$MountingVhdScriptText]) Write-Verbose "`r`n" # register for new drive event $guid = ([guid]::NewGuid()).ToString() Register-WmiEvent -SourceIdentifier $guid ` -Query "Select * From __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Volume'" ` -Action { if(!($global:volumeList.Contains($event.SourceEventArgs.NewEvent.TargetInstance.DeviceId))) {$global:volumeList.Add( $event.SourceEventArgs.NewEvent.TargetInstance.DeviceId )} } | Out-Null try { $diskpartOutput = $(@" SELECT VDISK FILE="$vhdfile" ATTACH VDISK READONLY EXIT "@ | diskpart) } catch { $success = $false } if ($success -eq $true) { $retry = 0 # Sleep for a bit to make sure the event is fired! do { $retry++ if($global:volumeList.Count -le 0) { Start-Sleep -Seconds 2 } else { break } } while($retry -lt 15) } # Sleep another 5 seconds to make sure that all the drive letters are received Start-Sleep -Seconds 5 Unregister-Event -SourceIdentifier $guid -Force # if drive letter detected, return the value if ($global:volumeList.Count -gt 0) { Write-Verbose ($CurrentCultureTable[$SuccessfullyMountedVhdScriptText]); $result = $true } else { $result = $false $errorOutput = $diskpartOutput -join "`r`n" Write-Error([string]::Concat($CurrentCultureTable[$FailedToMountVhdScriptError], "`r`n", $errorOutput)) } return $result } function Detach-VHD([string] $vhdfile) { try { $result = $(@" SELECT VDISK FILE="$vhdfile" DETACH VDISK EXIT "@ | diskpart) } catch { Write-Verbose $_ Write-Verbose ($result -join "`r`n") return $false } Write-Verbose ($CurrentCultureTable[$SuccessfullyUnmountedVhdScriptText]); } function Test-RdpInitInVhd([string] $winVolume) { $isRdpInitUpToDate = $true $rdpInitPathInVHD=$winVolume + "Windows\system32\RdpInit.exe" $tempfilename = $env:Temp + "\UploadGoldImageCheckRdpInit.exe" try { # Most of the powershell doesn't work with the volume path. Use the dos command copy the file and check the version. (cmd /c copy $rdpInitPathInVHD $tempfilename) | Out-Null $rdpInitVerInVHD=Get-FileVersion $tempfilename Write-Verbose ([string]::Format($CurrentCultureTable[$RdpInitVersionInfo]) + $rdpInitVerInVHD) $supportedVersion = new-Object Version(6, 3, 9600, 17211) if ( $rdpInitVerInVHD -lt $supportedVersion) { Write-Error([string]::Format($CurrentCultureTable[$FailedRdpInitVersion], $vhdPath)) $isRdpInitUpToDate = $false } else { Write-Verbose ([string]::Format($CurrentCultureTable[$RdpInitVersionCheckSuccess], $vhdPath)) } } catch { Write-Error ([string]::Format($CurrentCultureTable[$FailedToFindMountedDriveVhd], $vhdPath)) $isRdpInitUpToDate = $false } if (Test-Path -Path $tempfilename -PathType Leaf) { Remove-Item $tempfilename } return $isRdpInitUpToDate } function Check-VHDSizeAndPartitionStyleRequirements([string] $vhdPath) { $sizeAndPartitionStyleRequirementsSatisfied = $true try { $disk = Get-DiskImage $vhdPath # Disk size should be a multiple of 1MB if (($disk.Size)%(1MB) -ne 0) { Write-Error ($CurrentCultureTable[$ImageSizeNotMultipleOfMBs]) return $false } #disk size should be less than 128 GB, this is Azure limit for an OS disk if ($disk.Size -gt 128GB) { Write-Error ($CurrentCultureTable[$ImageSizeGreaterThanMaxSizeLimit]) $sizeAndPartitionStyleRequirementsSatisfied = $false } # Get the mounted disk number $diskNumber = $disk.Number #Get the PartitionStyle for the mounted disk and ensure it's MBR $partitionStyle = (Get-Disk -Number $diskNumber).PartitionStyle Write-Verbose ($CurrentCultureTable[$DiskPartitionStyle] + $partitionStyle) if ($partitionStyle -ne "MBR") { Write-Error ($CurrentCultureTable[$ImagePartitionStyleNotSupported]) $sizeAndPartitionStyleRequirementsSatisfied = $false } } catch { Write-Verbose $_ $sizeAndPartitionStyleRequirementsSatisfied = $false } return $sizeAndPartitionStyleRequirementsSatisfied } function Test-WindowsVolume() { $winVolumeCount = 0 foreach( $volume in $global:volumeList) { if(Test-Path -LiteralPath ($volume + "windows\system32\config") -PathType Container) { $winVolume = $volume $winVolumeCount++ } } Write-Verbose ($CurrentCultureTable[$NumberOfWindowsVolumes] + $winVolumeCount) if($winVolumeCount -eq 0) { Write-Error ($CurrentCultureTable[$NoOsFoundOnTemplateImage]) return $null } if($winVolumeCount -gt 1) { Write-Error ($CurrentCultureTable[$MultipleOsFoundOnTemplateImage]) return $null } return $winVolume } function Unload-Reghive([string] $vhd_hive) { $regUnloadRetries = 0 do { (reg unload ('hklm\'+$vhd_hive) 2>&1) | Out-Null if( (Test-Path('HKLM:\'+ $vhd_hive)) ) { Start-Sleep -Seconds 1 } else { Write-Verbose ([string]::Format($CurrentCultureTable[$HiveUnloadSuccess], $vhd_hive)) return } } while( $regUnloadRetries++ -lt 30 ) if( (Test-Path('HKLM:\'+ $vhd_hive)) ) { Write-Error ([string]::Format($CurrentCultureTable[$HiveUnloadFailure], $vhd_hive)) } } function Test-MohoroImageRequirements() { $validImage = $true $global:volumeList = New-Object System.Collections.Generic.List[string] #get list of current volumes if validating current OS if ($validateCurrentOS) { if($false -eq (Get-VolumeList)) { return $false } } #else get the mounted volume list from the VHD elseif($false -eq (Attach-VHD($vhdPath))) { return $false } $winVolume = Test-WindowsVolume if( $null -eq ($winVolume) ) { if(!($validateCurrentOS)) { Detach-VHD($vhdPath) } return $false } #check VHD size and partitioning style if uploading a VHD if (!($validateCurrentOS) -and ($IsStorageModuleAvailable)) { if ($false -eq (Check-VHDSizeAndPartitionStyleRequirements($vhdPath))) { Detach-VHD($vhdPath) return $false } } $validImage = Test-RdpInitInVhd($winVolume) # use current OS registry hive if validating current OS # else load software reg hive from the mounted VHD's volume if($validateCurrentOS) { $vhd_hive = 'SOFTWARE' } else { $vhd_hive = 'vhd_sft' (reg load ('hklm\'+$vhd_hive) ($winVolume + "windows\system32\config\SOFTWARE") 2>&1) | Out-Null } # verify Windows Server 2012 R2 (Blue) image $osVer = (Get-ItemProperty ('HKLM:\'+ $vhd_hive + '\Microsoft\Windows NT\CurrentVersion') CurrentVersion).CurrentVersion if ($osVer -ne '6.3') { Write-Error ($CurrentCultureTable[$UnsupportedOsScriptError]) $validImage = $false; } Remove-Variable osVer # verify it is server $osInstallationType = (Get-ItemProperty ('HKLM:\'+ $vhd_hive + '\Microsoft\Windows NT\CurrentVersion') InstallationType).InstallationType if ($osInstallationType -ne 'Server') { Write-Error ($CurrentCultureTable[$UnsupportedOsSkuScriptError]) $validImage = $false; } Remove-Variable osInstallationType # verify Standard or DataCenter edition $edition = (Get-ItemProperty ('HKLM:\'+ $vhd_hive + '\Microsoft\Windows NT\CurrentVersion') EditionID).EditionID if (!($edition.Contains( 'Datacenter') -or $edition.Contains( 'Standard'))) { Write-Error ([string]::Format($CurrentCultureTable[$UnsupportedOsEditionScriptError], $edition)) $validImage = $false; } Remove-Variable edition #skip sysprep checks if validating current OS if(!($validateCurrentOS)) { # verify sysprep state $sysprepState = (Get-ItemProperty ('HKLM:\'+ $vhd_hive + '\Microsoft\Windows\CurrentVersion\Setup\State') ImageState).ImageState if($sysprepState -ne "IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE") { Write-Error ($CurrentCultureTable[$ImageIsntGeneralizedScriptError]) $validImage = $false; } Remove-Variable sysprepState # verify sysprep vm mode $oobeRegKeyPath = ('HKLM:\'+ $vhd_hive + '\Microsoft\Windows\CurrentVersion\Setup\OOBE') $sysprepVmMode = (Get-ItemProperty $oobeRegKeyPath SysprepSetVMMode -ErrorAction SilentlyContinue).SysprepSetVMMode if ($sysprepVmMode) { Write-Verbose ($CurrentCultureTable[$CurrentVmModeValue] + $sysprepVmMode) } if($sysprepVmMode -eq 1) { Write-Error ($CurrentCultureTable[$SysprepVmModeNotSupported]) $validImage = $false } Remove-Variable sysprepVmMode # unload software reg hive [GC]::Collect() Unload-Reghive($vhd_hive) } # use current OS registry hive if validating current OS # else load software reg hive from the mounted VHD's volume if($validateCurrentOS) { $vhd_hive = 'SYSTEM' } else { $vhd_hive = 'vhd_sys' (reg load ('hklm\'+$vhd_hive) ($winVolume + "windows\system32\config\SYSTEM") 2>&1) | Out-Null } # verify if RDSH role and Desktop experince is installed $appCompat=$null try { $appCompat = (Get-ItemProperty ('HKLM:\'+ $vhd_hive + '\ControlSet001\Control\Terminal Server') TSAppCompat ).TSAppCompat } catch { Write-Verbose($CurrentCultureTable[$FailedToReadAppServerRegistryKeyScriptError] + $_.Exception.Message) } if(-not($appCompat)) { Write-Error ($CurrentCultureTable[$RdshRoleNotInstalledScriptError]) $validImage = $false; } Remove-Variable appCompat # verify if sysprep unattend registry key is set $unattendRegPath = ('HKLM:\'+ $vhd_hive + '\Setup') $unattendReg = (Get-ItemProperty $unattendRegPath UnattendFile -ErrorAction SilentlyContinue).UnattendFile if ($unattendReg) { Write-Error ($CurrentCultureTable[$UnattendFileError]) $validImage = $false; } Remove-Variable unattendReg # verify if efs is disabled $fileSystemRegPath = ('HKLM:\'+ $vhd_hive + '\ControlSet001\Control\FileSystem') $ntfsDisableEncryptionReg = (Get-ItemProperty $fileSystemRegPath NtfsDisableEncryption -ErrorAction SilentlyContinue).NtfsDisableEncryption if ($ntfsDisableEncryptionReg -eq 0) { Write-Error ($CurrentCultureTable[$NtfsDisableEncryptionError]) $validImage = $false; } Remove-Variable ntfsDisableEncryptionReg #verify that RDCB role is not installed $rdcbIsInstalled = $false $tssdisRegKeyValue = "" try { $tssdisRegKeyValue = Get-Item ('HKLM:\' + $vhd_hive + '\ControlSet001\Services\Tssdis') -ErrorAction Stop $rdcbIsInstalled = $true } catch { if ($_.Exception.GetType().ToString() -ne "System.Management.Automation.ItemNotFoundException") { Write-Verbose($CurrentCultureTable[$FailedToReadTssdisRegistryKeyScriptError] + $_.Exception.Message) } } if ($rdcbIsInstalled) { Write-Error ($CurrentCultureTable[$RdcbRoleInstalledScriptError]) $validImage = $false; } Remove-Variable tssdisRegKeyValue # unload system reg hive [GC]::Collect() if(!($validateCurrentOS)) { Unload-Reghive($vhd_hive) } # verify unattend file locations if(Test-Path -LiteralPath ($winVolume + "windows\Panther\Unattend\Unattend.xml") -PathType Leaf) { if($validateCurrentOS) { Remove-Item -Path ($winVolume + "windows\Panther\Unattend\Unattend.xml") } else { Write-Error ($CurrentCultureTable[$UnattendFileError] + " $winDrive\windows\Panther\Unattend\Unattend.xml.") $validImage = $false } } if(Test-Path -LiteralPath ($winVolume + "windows\Panther\Unattend\Autounattend.xml") -PathType Leaf) { Write-Error ($CurrentCultureTable[$UnattendFileError] + " $winDrive\windows\Panther\Unattend\Autounattend.xml.") $validImage = $false } if(Test-Path -LiteralPath ($winVolume + "windows\Panther\Unattend.xml") -PathType Leaf) { if($validateCurrentOS) { Remove-Item -Path ($winVolume + "windows\Panther\Unattend.xml") } else { Write-Error ($CurrentCultureTable[$UnattendFileError] + " $winDrive\windows\Panther\Unattend.xml.") $validImage = $false } } if(Test-Path -LiteralPath ($winVolume + "windows\Panther\Autounattend.xml") -PathType Leaf) { Write-Error ($CurrentCultureTable[$UnattendFileError] + " $winDrive\windows\Panther\Autounattend.xml.") $validImage = $false } if(Test-Path -LiteralPath ($winVolume + "Unattend.xml") -PathType Leaf) { if($validateCurrentOS) { Remove-Item -Path ($winVolume + "Unattend.xml") } else { Write-Error ($CurrentCultureTable[$UnattendFileError] + " Found: $winDrive\Unattend.xml.") $validImage = $false } } if(Test-Path -LiteralPath ($winVolume + "Autounattend.xml") -PathType Leaf) { Write-Error ($CurrentCultureTable[$UnattendFileError] + " $winDrive\Autounattend.xml.") $validImage = $false } # detach VHD if(!($validateCurrentOS)) { Detach-VHD($vhdPath) } return $validImage } ############### # Main script # ############### $verbosepreference='continue'; $isRunningAsAdmin = Is-RunningAsAdmin if (!($isRunningAsAdmin)) { Write-Error ($CurrentCultureTable[$NotRunningAsAdminScriptError]) return } if(!($validateCurrentOS)) { #Get OS Version of the machine where this script is running $IsStorageModuleAvailable = $false $CurrentVersionKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $OsVersion = (Get-ItemProperty -Path $CurrentVersionKey -Name CurrentVersion).CurrentVersion if ($OsVersion -ge 6.3) { $IsStorageModuleAvailable = $true } else { Write-Verbose ($CurrentCultureTable[$ClientMachineRunningOnDownlevelOs]) } if ($IsStorageModuleAvailable) { #Import storage module and make sure it loaded correctly Write-Verbose ($CurrentCultureTable[$ImportingStorageModuleScriptText]) try { Import-Module "Storage" } catch { Write-Error($CurrentCultureTable[$StorageModuleImportFailed]) return } $storageModule = get-module "Storage" if($null -eq ($storageModule)) { Write-Error ($CurrentCultureTable[$FailedToLoadStorageModuleError]) return } } # Import Azure module and make sure azure module is loaded Write-Verbose ($CurrentCultureTable[$ImportingAzureModuleScriptText]) Import-Module "azure" $azmodule = get-module "azure" if($null -eq ($azmodule)) { Write-Error ($CurrentCultureTable[$FailedToLoadAzureModuleError]) return } if ([string]::IsNullOrEmpty($vhdPath)) { $vhdPaths = Get-File $vhdPath = $vhdPaths[0] } } function Confirm-Sysprep() { $title = "Launch Sysprep?" $message = 'Please select "Yes" if you have completed all the customizations on this machine. Caution: Selecting "Yes" will start sysprep generalize command and automatically shut down this machine.' $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` "Yes" $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", ` "No" $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no) $result = $host.ui.PromptForChoice($title, $message, $options, 0) $result } $validImage = Test-MohoroImageRequirements if(!($validImage)) { Write-Error ($CurrentCultureTable[$NotRemoteAppReadyImageScriptError]) return } if($validateCurrentOS) { Write-Host($CurrentCultureTable[$CurrentOsCheckSuccess]) if((Confirm-Sysprep) -eq 0) { Start-Process -FilePath ($env:windir + "\system32\sysprep\sysprep.exe") -ArgumentList "/generalize /oobe /shutdown" } return } Write-Output $CurrentCultureTable[$StartingImageUploadScriptText] $vhdContext = Add-AzureVhd -Destination ($uri+$sas) -LocalFilePath $vhdPath if($null -ne $vhdContext) { # no need to load the storage lib, as it is already loaded by azure PS $sasCred = New-Object Microsoft.WindowsAzure.Storage.Auth.StorageCredentials($sas) $imageBlob = New-Object Microsoft.WindowsAzure.Storage.Blob.CloudPageBlob($uri, $sasCred) $imageBlob.Metadata["Status"] = "UploadComplete" $imageBlob.SetMetadata() $vhdContext Write-Output $CurrentCultureTable[$ImageUploadCompleteScriptText] }