Src/LabVMDiskFile.ps1
function SetLabVMDiskModule { <# .SYNOPSIS Downloads (if required) PowerShell/DSC modules and expands them to the destination path specified. #> [CmdletBinding()] param ( ## Lability PowerShell modules/DSC resource hashtable [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable[]] $Module, ## The target VHDX modules path [Parameter(Mandatory, ValueFromPipeline)] [System.String] $DestinationPath, ## Force a download of the module(s) even if they already exist in the cache. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Removes existing target module directory (if present) [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Clean ) process { ## Invokes the module download if not cached, and returns the source [ref] $null = InvokeModuleCacheDownload -Module $Module -Force:$Force ## Expand the modules into the VHDX file [ref] $null = ExpandModuleCache -Module $Module -DestinationPath $DestinationPath -Clean:$Clean; } #end process } #end function SetLabVMDiskModule function SetLabVMDiskFileResource { <# .SYNOPSIS Copies a node's defined resources to VHD(X) file. #> [CmdletBinding()] param ( ## Lab VM/Node name [Parameter(Mandatory, ValueFromPipeline)] [System.String] $NodeName, ## Lab DSC configuration data [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $ConfigurationData, ## Mounted VHD path [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $VhdDriveLetter, ## Catch all to enable splatting @PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $hostDefaults = GetConfigurationData -Configuration Host; $resourceDestinationPath = '{0}:\{1}' -f $vhdDriveLetter, $hostDefaults.ResourceShareName; $expandLabResourceParams = @{ ConfigurationData = $ConfigurationData; Name = $NodeName; DestinationPath = $resourceDestinationPath; } WriteVerbose -Message ($localized.AddingVMResource -f 'VM'); ExpandLabResource @expandLabResourceParams; } #end process } #end function SetLabVMDiskFileResource function SetLabVMDiskFileModule { <# .SYNOPSIS Copies a node's PowerShell and DSC resource modules to a VHD(X) file. #> [CmdletBinding()] param ( ## Lab VM/Node name [Parameter(Mandatory, ValueFromPipeline)] [System.String] $NodeName, ## Lab DSC configuration data [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $ConfigurationData, ## Mounted VHD path [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $VhdDriveLetter, ## Catch all to enable splatting @PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { ## Resolve the localized %ProgramFiles% directory $programFilesPath = '{0}\WindowsPowershell\Modules' -f (ResolveProgramFilesFolder -Drive $VhdDriveLetter).FullName ## Add the DSC resource modules $resolveLabDscModuleParams =@{ ConfigurationData = $ConfigurationData; NodeName = $NodeName; ModuleType = 'DscResource'; } $setLabVMDiskDscModuleParams = @{ Module = ResolveLabModule @resolveLabDscModuleParams; DestinationPath = $programFilesPath; } if ($null -ne $setLabVMDiskDscModuleParams['Module']) { WriteVerbose -Message ($localized.AddingDSCResourceModules -f $programFilesPath); SetLabVMDiskModule @setLabVMDiskDscModuleParams; } ## Add the PowerShell resource modules $resolveLabPowerShellModuleParams =@{ ConfigurationData = $ConfigurationData; NodeName = $NodeName; ModuleType = 'Module'; } $setLabVMDiskPowerShellModuleParams = @{ Module = ResolveLabModule @resolveLabPowerShellModuleParams; DestinationPath = $programFilesPath; } if ($null -ne $setLabVMDiskPowerShellModuleParams['Module']) { WriteVerbose -Message ($localized.AddingPowerShellModules -f $programFilesPath); SetLabVMDiskModule @setLabVMDiskPowerShellModuleParams; } } #end process } #end function SetLabVMDiskFileModule function SetLabVMDiskFileUnattendXml { <# .SYNOPSIS Copies a node's unattent.xml to a VHD(X) file. #> [CmdletBinding()] param ( ## Lab VM/Node name [Parameter(Mandatory, ValueFromPipeline)] [System.String] $NodeName, ## Lab DSC configuration data [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $ConfigurationData, ## Mounted VHD path [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $VhdDriveLetter, ## Local administrator password of the VM [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Media-defined product key [Parameter(ValueFromPipelineByPropertyName)] [System.String] $ProductKey, ## Catch all to enable splatting @PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $node = ResolveLabVMProperties -NodeName $NodeName -ConfigurationData $ConfigurationData -ErrorAction Stop; ## Create Unattend.xml $newUnattendXmlParams = @{ ComputerName = $node.NodeName; Credential = $Credential; InputLocale = $node.InputLocale; SystemLocale = $node.SystemLocale; UserLocale = $node.UserLocale; UILanguage = 'en-US'; Timezone = $node.Timezone; RegisteredOwner = $node.RegisteredOwner; RegisteredOrganization = $node.RegisteredOrganization; } WriteVerbose -Message $localized.SettingAdministratorPassword; ## Node defined Product Key takes preference over key defined in the media definition if ($node.CustomData.ProductKey) { $newUnattendXmlParams['ProductKey'] = $node.CustomData.ProductKey; } elseif ($PSBoundParameters.ContainsKey('ProductKey')) { $newUnattendXmlParams['ProductKey'] = $ProductKey; } ## TODO: We probably need to be localise the \Windows\ (%ProgramFiles% has been done) directory? $unattendXmlPath = '{0}:\Windows\System32\Sysprep\Unattend.xml' -f $VhdDriveLetter; WriteVerbose -Message ($localized.AddingUnattendXmlFile -f $unattendXmlPath); [ref] $null = SetUnattendXml @newUnattendXmlParams -Path $unattendXmlPath; } #end process } #end function SetLabVMDiskFileUnattendXml function SetLabVMDiskFileBootstrap { <# .SYNOPSIS Copies a the Lability bootstrap file to a VHD(X) file. #> [CmdletBinding()] param ( ## Mounted VHD path [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $VhdDriveLetter, ## Custom bootstrap script [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $CustomBootstrap, ## CoreCLR [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $CoreCLR, ## Catch all to enable splatting @PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $bootStrapPath = '{0}:\BootStrap' -f $VhdDriveLetter; WriteVerbose -Message ($localized.AddingBootStrapFile -f $bootStrapPath); if ($CustomBootStrap) { SetBootStrap -Path $bootStrapPath -CustomBootStrap $CustomBootStrap -CoreCLR:$CoreCLR; } else { SetBootStrap -Path $bootStrapPath -CoreCLR:$CoreCLR; } $setupCompleteCmdPath = '{0}:\Windows\Setup\Scripts' -f $vhdDriveLetter; WriteVerbose -Message ($localized.AddingSetupCompleteCmdFile -f $setupCompleteCmdPath); SetSetupCompleteCmd -Path $setupCompleteCmdPath -CoreCLR:$CoreCLR; } #end process } #end function SetLabVMDiskFileBootstrap function SetLabVMDiskFileMof { <# .SYNOPSIS Copies a node's mof files to a VHD(X) file. #> [CmdletBinding()] param ( ## Lab VM/Node name [Parameter(Mandatory, ValueFromPipeline)] [System.String] $NodeName, ## Lab VM/Node DSC .mof and .meta.mof configuration files [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Path, ## Mounted VHD path [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $VhdDriveLetter, ## Catch all to enable splatting @PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $bootStrapPath = '{0}:\BootStrap' -f $VhdDriveLetter; $mofPath = Join-Path -Path $Path -ChildPath ('{0}.mof' -f $NodeName); if (-not (Test-Path -Path $mofPath)) { WriteWarning -Message ($localized.CannotLocateMofFileError -f $mofPath); } else { $destinationMofPath = Join-Path -Path $bootStrapPath -ChildPath 'localhost.mof'; WriteVerbose -Message ($localized.AddingDscConfiguration -f $destinationMofPath); Copy-Item -Path $mofPath -Destination $destinationMofPath -Force -ErrorAction Stop; } $metaMofPath = Join-Path -Path $Path -ChildPath ('{0}.meta.mof' -f $NodeName); if (Test-Path -Path $metaMofPath -PathType Leaf) { $destinationMetaMofPath = Join-Path -Path $bootStrapPath -ChildPath 'localhost.meta.mof'; WriteVerbose -Message ($localized.AddingDscConfiguration -f $destinationMetaMofPath); Copy-Item -Path $metaMofPath -Destination $destinationMetaMofPath -Force; } } #end process } #end function SetLabVMDiskFileBootstrap function SetLabVMDiskFileCertificate { <# .SYNOPSIS Copies a node's certificate(s) to a VHD(X) file. #> [CmdletBinding()] param ( ## Lab VM/Node name [Parameter(Mandatory, ValueFromPipeline)] [System.String] $NodeName, ## Lab DSC configuration data [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $ConfigurationData, ## Mounted VHD path [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $VhdDriveLetter, ## Catch all to enable splatting @PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) process { $node = ResolveLabVMProperties -NodeName $NodeName -ConfigurationData $ConfigurationData -ErrorAction Stop; $bootStrapPath = '{0}:\BootStrap' -f $VhdDriveLetter; if (-not [System.String]::IsNullOrWhitespace($node.ClientCertificatePath)) { $destinationCertificatePath = Join-Path -Path $bootStrapPath -ChildPath 'LabClient.pfx'; $expandedClientCertificatePath = [System.Environment]::ExpandEnvironmentVariables($node.ClientCertificatePath); WriteVerbose -Message ($localized.AddingCertificate -f 'Client', $destinationCertificatePath); Copy-Item -Path $expandedClientCertificatePath -Destination $destinationCertificatePath -Force; } if (-not [System.String]::IsNullOrWhitespace($node.RootCertificatePath)) { $destinationCertificatePath = Join-Path -Path $bootStrapPath -ChildPath 'LabRoot.cer'; $expandedRootCertificatePath = [System.Environment]::ExpandEnvironmentVariables($node.RootCertificatePath); WriteVerbose -Message ($localized.AddingCertificate -f 'Root', $destinationCertificatePath); Copy-Item -Path $expandedRootCertificatePath -Destination $destinationCertificatePath -Force; } } #end process } #end function SetLabVMDiskFileBootstrap function SetLabVMDiskFile { <# .SYNOPSIS Copies Lability files to a node's VHD(X) file. .DESCRIPTION Copies the Lability bootstrap file, SetupComplete.cmd, unattend.xml, mof files, certificates and PowerShell/DSC resource modules to a VHD(X) file. #> [CmdletBinding()] param ( ## Lab VM/Node name [Parameter(Mandatory, ValueFromPipeline)] [System.String] $NodeName, ## Lab DSC configuration data [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $ConfigurationData, ## Local administrator password of the VM [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Lab VM/Node DSC .mof and .meta.mof configuration files [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Path, ## Custom bootstrap script [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $CustomBootstrap, ## CoreCLR [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $CoreCLR, ## Media-defined product key [Parameter(ValueFromPipelineByPropertyName)] [System.String] $ProductKey ) process { ## Temporarily disable Windows Explorer popup disk initialization and format notifications ## http://blogs.technet.com/b/heyscriptingguy/archive/2013/05/29/use-powershell-to-initialize-raw-disks-and-partition-and-format-volumes.aspx Stop-Service -Name 'ShellHWDetection' -Force -ErrorAction Ignore; $node = ResolveLabVMProperties -NodeName $NodeName -ConfigurationData $ConfigurationData -ErrorAction Stop; $vhdPath = ResolveLabVMDiskPath -Name $node.NodeDisplayName; WriteVerbose -Message ($localized.MountingDiskImage -f $VhdPath); $vhd = Mount-Vhd -Path $vhdPath -Passthru; [ref] $null = Get-PSDrive; $vhdDriveLetter = Get-Partition -DiskNumber $vhd.DiskNumber | Where-Object DriveLetter | Select-Object -Last 1 -ExpandProperty DriveLetter; Start-Service -Name 'ShellHWDetection'; SetLabVMDiskFileResource @PSBoundParameters -VhdDriveLetter $vhdDriveLetter; SetLabVMDiskFileBootstrap @PSBoundParameters -VhdDriveLetter $vhdDriveLetter; SetLabVMDiskFileUnattendXml @PSBoundParameters -VhdDriveLetter $vhdDriveLetter; SetLabVMDiskFileMof @PSBoundParameters -VhdDriveLetter $vhdDriveLetter; SetLabVMDiskFileCertificate @PSBoundParameters -VhdDriveLetter $vhdDriveLetter; SetLabVMDiskFileModule @PSBoundParameters -VhdDriveLetter $vhdDriveLetter; WriteVerbose -Message ($localized.DismountingDiskImage -f $VhdPath); Dismount-Vhd -Path $VhdPath; } #end process } #end function SetLabVMDiskFile |