From 29fb9058ee0fc57fe26117fed4e5ab237f4b259d Mon Sep 17 00:00:00 2001 From: Iain Date: Fri, 14 Jun 2019 17:54:06 +0100 Subject: [PATCH 01/11] Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) --- ChangeLog.md | 4 ++++ Src/Private/Expand-LabImage.ps1 | 2 +- Src/Private/Expand-LabIso.ps1 | 2 +- Src/Private/Set-LabVMDiskFile.ps1 | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 821530fe..25e3ac7c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,10 @@ ## Versions ## +### Unreleased ### + +* Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) + ### v0.18.0 ### * Updates bundled xHyper-V DSC resource to v3.16.0 diff --git a/Src/Private/Expand-LabImage.ps1 b/Src/Private/Expand-LabImage.ps1 index 558290ab..5e117040 100644 --- a/Src/Private/Expand-LabImage.ps1 +++ b/Src/Private/Expand-LabImage.ps1 @@ -172,7 +172,7 @@ function Expand-LabImage { ## Always dismount ISO (#166) Write-Verbose -Message ($localized.DismountingDiskImage -f $MediaPath); - Storage\Dismount-DiskImage -ImagePath $MediaPath; + $null = Storage\Dismount-DiskImage -ImagePath $MediaPath; } ## Enable BitLocker (if required) diff --git a/Src/Private/Expand-LabIso.ps1 b/Src/Private/Expand-LabIso.ps1 index 99181752..0bb6a5d9 100644 --- a/Src/Private/Expand-LabIso.ps1 +++ b/Src/Private/Expand-LabIso.ps1 @@ -26,7 +26,7 @@ function Expand-LabIso { Write-Verbose -Message ($localized.ExpandingIsoResource -f $DestinationPath); CopyDirectory -SourcePath $sourcePath -DestinationPath $DestinationPath -Force -Verbose:$false; Write-Verbose -Message ($localized.DismountingDiskImage -f $Path); - Storage\Dismount-DiskImage -ImagePath $Path; + $null = Storage\Dismount-DiskImage -ImagePath $Path; ## Enable BitLocker (if required) Assert-BitLockerFDV; diff --git a/Src/Private/Set-LabVMDiskFile.ps1 b/Src/Private/Set-LabVMDiskFile.ps1 index b395da66..54acad52 100644 --- a/Src/Private/Set-LabVMDiskFile.ps1 +++ b/Src/Private/Set-LabVMDiskFile.ps1 @@ -96,7 +96,7 @@ function Set-LabVMDiskFile { ## Ensure the VHD is dismounted (#185) Write-Verbose -Message ($localized.DismountingDiskImage -f $VhdPath); - Hyper-V\Dismount-Vhd -Path $VhdPath -Confirm:$false; + $null = Hyper-V\Dismount-Vhd -Path $VhdPath -Confirm:$false; ## Enable BitLocker (if required) Assert-BitLockerFDV; From 609d09b594be923364f9151057d8c46480e08c16 Mon Sep 17 00:00:00 2001 From: Iain Brighton Date: Tue, 25 Jun 2019 16:16:22 +0100 Subject: [PATCH 02/11] Adds Export-LabImage --- ChangeLog.md | 1 + Lability.psd1 | 1 + Lability.psm1 | 42 +++++------ Src/Public/Export-LabImage.ps1 | 119 ++++++++++++++++++++++++++++++ Src/Public/Get-LabHostDefault.ps1 | 1 + en-US/Lability.Resources.psd1 | 2 + en-US/about_Media.help.txt | 10 +-- 7 files changed, 150 insertions(+), 26 deletions(-) create mode 100644 Src/Public/Export-LabImage.ps1 diff --git a/ChangeLog.md b/ChangeLog.md index 25e3ac7c..23d814eb 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,7 @@ ### Unreleased ### * Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) +* Adds Export-LabImage cmdlet to export VHD(X) master/parent images ### v0.18.0 ### diff --git a/Lability.psd1 b/Lability.psd1 index a1639c80..c1154bfb 100644 --- a/Lability.psd1 +++ b/Lability.psd1 @@ -13,6 +13,7 @@ 'Clear-LabModuleCache', 'Clear-ModulePath', 'Export-LabHostConfiguration', + 'Export-LabImage', 'Get-LabHostConfiguration', 'Get-LabHostDefault', 'Get-LabImage', diff --git a/Lability.psm1 b/Lability.psm1 index e6a90e08..e05acf9e 100644 --- a/Lability.psm1 +++ b/Lability.psm1 @@ -2,17 +2,17 @@ ## Set the global defaults $labDefaults = @{ - ModuleRoot = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; - ModuleName = 'Lability'; - ConfigurationData = 'Config'; - HostConfigFilename = 'HostDefaults.json'; - VmConfigFilename = 'VmDefaults.json'; - MediaConfigFilename = 'Media.json'; + ModuleRoot = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; + ModuleName = 'Lability'; + ConfigurationData = 'Config'; + HostConfigFilename = 'HostDefaults.json'; + VmConfigFilename = 'VmDefaults.json'; + MediaConfigFilename = 'Media.json'; CustomMediaConfigFilename = 'CustomMedia.json'; - LegacyMediaPath = 'LegacyMedia'; - DscResourceDirectory = 'DSCResources'; - RepositoryUri = 'https://www.powershellgallery.com/api/v2/package'; - DismVersion = $null; + LegacyMediaPath = 'LegacyMedia'; + DscResourceDirectory = 'DSCResources'; + RepositoryUri = 'https://www.powershellgallery.com/api/v2/package'; + DismVersion = $null; } #region LocalizedData @@ -22,9 +22,9 @@ if (Test-Path -Path (Join-Path -Path $labDefaults.ModuleRoot -ChildPath $PSUICul } $importLocalizedDataParams = @{ BindingVariable = 'localized'; - Filename = "Lability.Resources.psd1"; - BaseDirectory = $moduleRoot; - UICulture = $culture; + Filename = 'Lability.Resources.psd1'; + BaseDirectory = $moduleRoot; + UICulture = $culture; } Import-LocalizedData @importLocalizedDataParams; #endregion LocalizedData @@ -33,19 +33,19 @@ Import-LocalizedData @importLocalizedDataParams; $moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; $moduleSrcPath = Join-Path -Path $moduleRoot -ChildPath 'Src'; Get-ChildItem -Path $moduleSrcPath -Include *.ps1 -Exclude '*.Tests.ps1' -Recurse | - ForEach-Object { - Write-Verbose -Message ('Importing library\source file ''{0}''.' -f $_.FullName); - ## https://becomelotr.wordpress.com/2017/02/13/expensive-dot-sourcing/ - . ([System.Management.Automation.ScriptBlock]::Create( - [System.IO.File]::ReadAllText($_.FullName) - )); - } +ForEach-Object { + Write-Verbose -Message ('Importing library\source file ''{0}''.' -f $_.FullName); + ## https://becomelotr.wordpress.com/2017/02/13/expensive-dot-sourcing/ + . ([System.Management.Automation.ScriptBlock]::Create( + [System.IO.File]::ReadAllText($_.FullName) + )); +} ## Deploy builtin certificates to %ALLUSERSPROFILE%\PSLab $moduleConfigPath = Join-Path -Path $moduleRoot -ChildPath 'Config'; $allUsersConfigPath = Join-Path -Path $env:AllUsersProfile -ChildPath "$($labDefaults.ModuleName)\Certificates\"; [ref] $null = New-Directory -Path $allUsersConfigPath; -Get-ChildItem -Path $moduleConfigPath -Include *.cer,*.pfx -Recurse | ForEach-Object { +Get-ChildItem -Path $moduleConfigPath -Include *.cer, *.pfx -Recurse | ForEach-Object { Write-Verbose -Message ('Updating certificate ''{0}''.' -f $_.FullName); Copy-Item -Path $_ -Destination $allUsersConfigPath; } diff --git a/Src/Public/Export-LabImage.ps1 b/Src/Public/Export-LabImage.ps1 new file mode 100644 index 00000000..2bed17a7 --- /dev/null +++ b/Src/Public/Export-LabImage.ps1 @@ -0,0 +1,119 @@ + +function Export-LabImage { +<# + .SYNOPSIS + Exports a lab image (.vhdx file) and creates Lability custom media registration document (.json file). +#> + [CmdletBinding()] + param ( + ## Lab media Id + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Id, + + # Specifies the export path location. + [Parameter(Mandatory, ParameterSetName = 'Path', ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias("PSPath")] + [System.String] $Path, + + # Specifies a literal export location path. + [Parameter(Mandatory, ParameterSetName = 'LiteralPath', ValueFromPipeline, ValueFromPipelineByPropertyName)] + [System.String] $LiteralPath, + + ## Force the re(creation) of the master/parent image + [Parameter(ValueFromPipelineByPropertyName)] + [System.Management.Automation.SwitchParameter] $Force, + + ## Do not calculate media checksum. + [Parameter()] + [System.Management.Automation.SwitchParameter] $NoChecksum, + + ## Do not create Lability custom media regstration (json) file. + [Parameter()] + [System.Management.Automation.SwitchParameter] $NoRegistrationDocument + ) + begin { + + try { + + if ($PSCmdlet.ParameterSetName -eq 'Path') { + + # Resolve any relative paths + $Path = $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path); + } + else { + + $Path = $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($LiteralPath); + } + + $pathItem = Get-Item -Path $Path + if ($pathItem -isnot [System.IO.DirectoryInfo]) { + throw "not a directory path" + } + } + catch { + + Write-Error -ErrorRecord $_ + } + + } + process { + + foreach ($mediaId in $Id) { + + try { + + $media = Resolve-LabMedia -Id $mediaId -ErrorAction Stop + $image = Get-LabImage -Id $mediaId -ErrorAction Stop + + ## Copy vhd/x file to destination path + $imageItem = Get-Item -Path $image.ImagePath + $destinationVhdxPath = Join-Path -Path $Path -ChildPath $imageItem.Name + if (-not (Test-Path -Path $destinationVhdxPath -PathType Leaf) -or $Force) { + + Write-Verbose -Message ($localized.ExportingImage -f $image.ImagePath, $destinationVhdxPath) + Copy-Item -Path $image.ImagePath -Destination $destinationVhdxPath -Force + + if (-not $NoRegistrationDocument) { + + ## Create media registration json + $registerLabMedia = [ordered] @{ + Id = $media.Id + Filename = $imageItem.Name + Description = $media.Description + OperatingSystem = $media.OperatingSystem + Architecture = $media.Architecture + MediaType = 'VHD' + Uri = $destinationVhdxPath -as [System.Uri] + CustomData = $media.CustomData + } + + if (-not $NoChecksum) { + Write-Verbose ($localized.CalculatingResourceChecksum -f $image.ImagePath) + $checksum = (Get-FileHash -Path $image.ImagePath -Algorithm MD5).Hash + $registerLabMedia['Checksum'] = $checksum + } + + $registerLabMediaFilename = '{0}.json' -f $media.Id + $registerLabMediaPath = Join-Path -Path $Path -ChildPath $registerLabMediaFilename + Write-Verbose -Message ($localized.ExportingImageRegistrationFile -f $registerLabMediaPath) + ConvertTo-Json -InputObject $registerLabMedia | + Out-File -FilePath $registerLabMediaPath -Encoding ASCII -Force + } + } + else { + + $errorMessage = $localized.FileAlreadyExistsError -f $destinationVhdxPath; + $ex = New-Object -TypeName System.InvalidOperationException -ArgumentList $errorMessage; + $errorCategory = [System.Management.Automation.ErrorCategory]::ResourceExists; + $errorRecord = New-Object System.Management.Automation.ErrorRecord $ex, 'FileExists', $errorCategory, $destinationVhdxPath; + $PSCmdlet.WriteError($errorRecord); + } + } + catch { + + Write-Error -ErrorRecord $_ + } + } + } +} diff --git a/Src/Public/Get-LabHostDefault.ps1 b/Src/Public/Get-LabHostDefault.ps1 index 245dff94..fa7a0f2f 100644 --- a/Src/Public/Get-LabHostDefault.ps1 +++ b/Src/Public/Get-LabHostDefault.ps1 @@ -24,6 +24,7 @@ function Get-LabHostDefault { $env:LabilityResourcePath = $hostDefaults.ResourcePath; $env:LabilityDismPath = $hostDefaults.DismPath; $env:LabilityRepositoruUri = $hostDefaults.RepositoryUri; + $env:LabilityParentVhdPath = $hostDefaults.ParentVhdPath; return $hostDefaults; diff --git a/en-US/Lability.Resources.psd1 b/en-US/Lability.Resources.psd1 index ba8010fa..a098c26a 100644 --- a/en-US/Lability.Resources.psd1 +++ b/en-US/Lability.Resources.psd1 @@ -178,6 +178,8 @@ ConvertFrom-StringData -StringData @' AutomaticCheckPointsNotSupported = Automatic checkpoints are only supported in Windows 10 "Fall Creators" update (1709 and later) builds. DisablingBitLockerWriteProtection = Temporarily disabling BitLocker fixed drive write protection to prevent read-only mounted drives. EnablingBitLockerWriteProtection = Enabling BitLocker fixed drive write protection to enforce read-only mounted drives. + ExportingImage = Exporting image '{0}' to '{1}'. + ExportingImageRegistrationFile = Exporting image registration file '{0}'. NoCertificateFoundWarning = No '{0}' certificate was found. CannotLocateLcmFileWarning = Cannot locate LCM configuration file '{0}'. No DSC Local Configuration Manager configuration will be applied. diff --git a/en-US/about_Media.help.txt b/en-US/about_Media.help.txt index 8d9ba12f..b7ae0671 100644 --- a/en-US/about_Media.help.txt +++ b/en-US/about_Media.help.txt @@ -103,11 +103,11 @@ EXISTING DISK IMAGES an existing `2012R2_x64_Standard_EN_VL.vhdx` virtual hard disk file. PS C:\> $customMedia = @{ - Id = 2012R2_x64_Standard_EN_VL; - Filename = 2012R2_x64_Standard_EN_VL.vhdx; + Id = '2012R2_x64_Standard_EN_VL'; + Filename = '2012R2_x64_Standard_EN_VL.vhdx'; Description = 'Windows Server 2012 R2 Standard 64bit English Volume License'; - Architecture = x64; - MediaType = VHD; + Architecture = 'x64'; + MediaType = 'VHD'; Uri = '\\MyFileServer\Images\2012R2_x64_Standard_EN_VL.vhdx'; } PS C:\> Register-LabMedia @customMedia @@ -125,7 +125,7 @@ LINUX DISK IMAGES Filename = "NSVPX-11.0-63.16_nc.vhd" Description = "Citrix NetScaler 11.0 VPX Build 63.16_nc"; Architecture = "x64"; - MediaType = "VHD"; + MediaType = "VHD"; OperatingSystem = "Linux"; Uri = "\\MyFileServer\Images\NSVPX-11.0-63.16_nc.vhd"; } From 5e6b6ced0ea752521df5f4bd83b7eb3833f18ec4 Mon Sep 17 00:00:00 2001 From: Iain Brighton Date: Tue, 25 Jun 2019 16:20:15 +0100 Subject: [PATCH 03/11] Adds CustomId to Register-LabMedia --- ChangeLog.md | 1 + Src/Public/Register-LabMedia.ps1 | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 23d814eb..5f414d72 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ * Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) * Adds Export-LabImage cmdlet to export VHD(X) master/parent images +* Permits overriding media Id with -CustomId parameter when importing legacy media definitions or media from an external file/Uri ### v0.18.0 ### diff --git a/Src/Public/Register-LabMedia.ps1 b/Src/Public/Register-LabMedia.ps1 index f72f634b..77f05aa4 100644 --- a/Src/Public/Register-LabMedia.ps1 +++ b/Src/Public/Register-LabMedia.ps1 @@ -70,10 +70,15 @@ function Register-LabMedia { [ValidateSet('Windows','Linux')] [System.String] $OperatingSystem = 'Windows', - ## Registers media via a JSON file hosted externally + ## Registers media via a JSON file hosted externally. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FromUri')] [System.String] $FromUri, + ## Registers media using a custom Id. + [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'FromUri')] + [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] + [System.String] $CustomId, + ## Specifies that an exiting media entry should be overwritten. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force @@ -103,6 +108,11 @@ function Register-LabMedia { ## Download the json content and convert into a hashtable $legacyMedia = Get-LabMedia -Id $PSBoundParameters.Legacy -Legacy | Convert-PSObjectToHashtable; + if ($PSBoundParameters.ContainsKey('CustomId')) { + + $legacyMedia['Id'] = $CustomId + } + ## Recursively call Register-LabMedia and splat the properties return (Register-LabMedia @legacyMedia -Force:$Force); } @@ -112,6 +122,11 @@ function Register-LabMedia { ## Download the json content and convert into a hashtable $customMedia = Invoke-RestMethod -Uri $FromUri | Convert-PSObjectToHashtable; + if ($PSBoundParameters.ContainsKey('CustomId')) { + + $customMedia['Id'] = $CustomId + } + ## Recursively call Register-LabMedia and splat the properties return (Register-LabMedia @customMedia -Force:$Force); } @@ -134,7 +149,15 @@ function Register-LabMedia { } ## Resolve the media Id to see if it's already been used - $media = Resolve-LabMedia -Id $Id -ErrorAction SilentlyContinue; + try { + + $media = Resolve-LabMedia -Id $Id -ErrorAction SilentlyContinue; + } + catch { + + Write-Debug -Message ($localized.CannotLocateMediaError -f $Id); + } + if ($media -and (-not $Force)) { throw ($localized.MediaAlreadyRegisteredError -f $Id, '-Force'); From 9f86848643791eee56083125e6e6d1a64e7f52c0 Mon Sep 17 00:00:00 2001 From: Iain Date: Wed, 26 Jun 2019 09:10:54 +0100 Subject: [PATCH 04/11] Adds 'Latest' property to module info Fixes #367 --- ChangeLog.md | 1 + Examples/ModuleExample.psd1 | 2 ++ Src/Private/Invoke-LabModuleCacheDownload.ps1 | 2 +- .../Private/Invoke-LabModuleCacheDownload.Tests.ps1 | 12 ++++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 5f414d72..5ad8d159 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,7 @@ * Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) * Adds Export-LabImage cmdlet to export VHD(X) master/parent images * Permits overriding media Id with -CustomId parameter when importing legacy media definitions or media from an external file/Uri +* Adds 'Latest' property to module info to force latest version download on every configuration run (#367) ### v0.18.0 ### diff --git a/Examples/ModuleExample.psd1 b/Examples/ModuleExample.psd1 index 2a1fefcb..90e91afb 100644 --- a/Examples/ModuleExample.psd1 +++ b/Examples/ModuleExample.psd1 @@ -16,6 +16,8 @@ @{ Name = 'PScribo' } ## Downloads the development branch of the Lability module directly from Github @{ Name = 'Lability'; Provider = 'GitHub'; Owner = 'VirtualEngine'; Branch = 'dev'; } + ## Force a download the latest published module version from the PSGallery - every configuration run + @{ Name = 'PSReadLine'; Latest = $true } ) }; }; diff --git a/Src/Private/Invoke-LabModuleCacheDownload.ps1 b/Src/Private/Invoke-LabModuleCacheDownload.ps1 index d981b062..fad296f1 100644 --- a/Src/Private/Invoke-LabModuleCacheDownload.ps1 +++ b/Src/Private/Invoke-LabModuleCacheDownload.ps1 @@ -110,7 +110,7 @@ function Invoke-LabModuleCacheDownload { foreach ($moduleInfo in $Module) { - if ((-not (Test-LabModuleCache @moduleInfo)) -or ($Force)) { + if ((-not (Test-LabModuleCache @moduleInfo)) -or ($Force) -or ($moduleInfo.Latest -eq $true)) { if ((-not $moduleInfo.ContainsKey('Provider')) -or ($moduleInfo['Provider'] -eq 'PSGallery')) { diff --git a/Tests/Unit/Src/Private/Invoke-LabModuleCacheDownload.Tests.ps1 b/Tests/Unit/Src/Private/Invoke-LabModuleCacheDownload.Tests.ps1 index 7be55433..91f4d5a5 100644 --- a/Tests/Unit/Src/Private/Invoke-LabModuleCacheDownload.Tests.ps1 +++ b/Tests/Unit/Src/Private/Invoke-LabModuleCacheDownload.Tests.ps1 @@ -76,5 +76,17 @@ Describe 'Unit\Src\Private\Invoke-LabModuleCacheDownload' { Assert-MockCalled Invoke-LabModuleDownloadFromGitHub -Scope It -Exactly 0; } + It 'Forces module download when module is cached and "Latest" is specified via ModuleInfo (#367)' { + Mock Test-LabModuleCache -MockWith { return $true; } + $moduleInfo = @{ + Name = $testModuleName; + Latest = $true; + } + + Invoke-LabModuleCacheDownload -Module $moduleInfo; + + Assert-MockCalled Invoke-LabModuleDownloadFromPSGallery -Scope It -Exactly 1; + } + } #end InModuleScope } #end Describe From e7425d7b5c0b47cd1ee2f7bcd69dfb78a3fe70cf Mon Sep 17 00:00:00 2001 From: Iain Date: Wed, 3 Jul 2019 08:31:07 +0100 Subject: [PATCH 05/11] Adds environment name support for differencing VHDs --- ChangeLog.md | 9 ++- Config/HostDefaults.json | 4 +- Src/Private/Copy-LabModule.ps1 | 4 +- Src/Private/Get-ConfigurationData.ps1 | 7 ++ Src/Private/Get-LabVirtualMachineProperty.ps1 | 7 +- Src/Private/Remove-LabVMDisk.ps1 | 9 ++- .../Remove-LabVirtualMachineHardDiskDrive.ps1 | 9 ++- Src/Private/Resolve-ConfigurationPath.ps1 | 19 +++-- ...> Resolve-LabVMDiskGenerationDiskPath.ps1} | 13 +++- Src/Private/Resolve-LabVMDiskPath.ps1 | 31 +++++++- Src/Private/Set-LabVMDisk.ps1 | 12 +-- Src/Private/Set-LabVMDiskFile.ps1 | 1 - Src/Private/Test-LabVMDisk.ps1 | 13 ++-- Src/Public/Get-LabVM.ps1 | 8 +- Src/Public/Remove-LabConfiguration.ps1 | 2 + Src/Public/Set-LabHostDefault.ps1 | 11 ++- .../Private/Get-ConfigurationData.Tests.ps1 | 13 ++++ .../Private/Resolve-LabVMDiskPath.Tests.ps1 | 73 +++++++++++++++++++ 18 files changed, 197 insertions(+), 48 deletions(-) rename Src/Private/{Resolve-LabVMDiskGenerationPath.ps1 => Resolve-LabVMDiskGenerationDiskPath.ps1} (71%) diff --git a/ChangeLog.md b/ChangeLog.md index 5ad8d159..40c76ee1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,9 +5,12 @@ ### Unreleased ### * Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) -* Adds Export-LabImage cmdlet to export VHD(X) master/parent images -* Permits overriding media Id with -CustomId parameter when importing legacy media definitions or media from an external file/Uri -* Adds 'Latest' property to module info to force latest version download on every configuration run (#367) +* Adds `Export-LabImage` cmdlet to export VHD(X) master/parent images +* Permits overriding media Id with `-CustomId` parameter when importing legacy media definitions or media from an external file/Uri with `Register-LabMedia` +* Adds `Latest` property to module info to force latest module version download - on every configuration run (#367) +* Adds `-DisableVhdEnvironmentName` parameter to `Set-LabHostDefault` (#78) + * New installations/deployments will default to False - creating VM differencing disks in a subdirectory when EnvironmentName is defined in configuration data + * Existing installations will default to True (at least until `Set-LabHostDefault` or `Reset-LabHostDefault` are called) ### v0.18.0 ### diff --git a/Config/HostDefaults.json b/Config/HostDefaults.json index 2bc0f094..10d092fc 100644 --- a/Config/HostDefaults.json +++ b/Config/HostDefaults.json @@ -9,5 +9,7 @@ "ResourcePath": "%SYSTEMDRIVE%\\Lability\\Resources", "ResourceShareName": "Resources", "DisableLocalFileCaching": false, - "DisableSwitchEnvironmentName": false + "DisableSwitchEnvironmentName": false, + "DisableVhdEnvironmentName": false + } diff --git a/Src/Private/Copy-LabModule.ps1 b/Src/Private/Copy-LabModule.ps1 index fe62974e..ecdfb284 100644 --- a/Src/Private/Copy-LabModule.ps1 +++ b/Src/Private/Copy-LabModule.ps1 @@ -45,7 +45,7 @@ function Copy-LabModule { } else { - $powerShellModules = $ConfigurationData.NonNodeData.Lability.Module; + $powerShellModules = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).Module; } if ($null -ne $powerShellModules) { @@ -73,7 +73,7 @@ function Copy-LabModule { } else { - $dscResourceModules = $ConfigurationData.NonNodeData.Lability.DSCResource; + $dscResourceModules = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).DSCResource; } if ($null -ne $dscResourceModules) { diff --git a/Src/Private/Get-ConfigurationData.ps1 b/Src/Private/Get-ConfigurationData.ps1 index 77c50a32..46c26507 100644 --- a/Src/Private/Get-ConfigurationData.ps1 +++ b/Src/Private/Get-ConfigurationData.ps1 @@ -107,6 +107,13 @@ function Get-ConfigurationData { [ref] $null = Add-Member -InputObject $configurationData -MemberType NoteProperty -Name 'DisableSwitchEnvironmentName' -Value $true; } + ## This property may not be present in the original machine configuration file. Defaults to $true for existing + ## deployments, but is disabled ($false) in the default HostDefaults.json for new installs. + if ($configurationData.PSObject.Properties.Name -notcontains 'DisableVhdEnvironmentName') { + + [ref] $null = Add-Member -InputObject $configurationData -MemberType NoteProperty -Name 'DisableVhdEnvironmentName' -Value $true; + } + ## Remove deprecated UpdatePath, if present (Issue #77) $configurationData.PSObject.Properties.Remove('UpdatePath'); } diff --git a/Src/Private/Get-LabVirtualMachineProperty.ps1 b/Src/Private/Get-LabVirtualMachineProperty.ps1 index cead8d6c..798389a9 100644 --- a/Src/Private/Get-LabVirtualMachineProperty.ps1 +++ b/Src/Private/Get-LabVirtualMachineProperty.ps1 @@ -130,7 +130,12 @@ function Get-LabVirtualMachineProperty { } } - $vhdPath = Resolve-LabVMDiskPath -Name $Name -Generation $labImage.Generation; + $resolveLabVMDiskPathParams = @{ + Name = $Name; + Generation = $labImage.Generation; + EnvironmentName = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).EnvironmentName; + } + $vhdPath = Resolve-LabVMDiskPath @resolveLabVMDiskPathParams; [ref] $null = $PSBoundParameters.Remove('Media'); [ref] $null = $PSBoundParameters.Remove('ConfigurationData'); diff --git a/Src/Private/Remove-LabVMDisk.ps1 b/Src/Private/Remove-LabVMDisk.ps1 index e77e37f8..1c475343 100644 --- a/Src/Private/Remove-LabVMDisk.ps1 +++ b/Src/Private/Remove-LabVMDisk.ps1 @@ -28,7 +28,6 @@ function Remove-LabVMDisk { ) process { - $hostDefaults = Get-ConfigurationData -Configuration Host; if ($PSBoundParameters.ContainsKey('ConfigurationData')) { $image = Get-LabImage -Id $Media -ConfigurationData $ConfigurationData -ErrorAction Stop; @@ -38,18 +37,19 @@ function Remove-LabVMDisk { $image = Get-LabImage -Id $Media -ErrorAction Stop; } + $environmentName = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).EnvironmentName; + ## If the parent image isn't there, the differencing VHD won't be either! if ($image) { ## Ensure we look for the correct file extension (#182) - $vhdFilename = '{0}.{1}' -f $Name, $image.Generation; - $vhdPath = Join-Path -Path $hostDefaults.DifferencingVhdPath -ChildPath $vhdFilename; + $vhdPath = Resolve-LabVMDiskPath -Name $Name -Generation $image.Generation -EnvironmentName $environmentName; if (Test-Path -Path $vhdPath) { ## Only attempt to remove the differencing disk if it's there (and xVHD will throw) $vhd = @{ Name = $Name; - Path = $hostDefaults.DifferencingVhdPath; + Path = Split-Path -Path $vhdPath -Parent; ParentPath = $image.ImagePath; Generation = $image.Generation; Type = 'Differencing'; @@ -75,6 +75,7 @@ function Remove-LabVMDisk { $removeLabVirtualMachineHardDiskDriveParams = @{ NodeName = $node.NodeDisplayName; HardDiskDrive = $node.HardDiskDrive; + EnvironmentName = $environmentName; } $null = Remove-LabVirtualMachineHardDiskDrive @removeLabVirtualMachineHardDiskDriveParams; } diff --git a/Src/Private/Remove-LabVirtualMachineHardDiskDrive.ps1 b/Src/Private/Remove-LabVirtualMachineHardDiskDrive.ps1 index 24c3ea0d..1b40a0c7 100644 --- a/Src/Private/Remove-LabVirtualMachineHardDiskDrive.ps1 +++ b/Src/Private/Remove-LabVirtualMachineHardDiskDrive.ps1 @@ -15,11 +15,16 @@ function Remove-LabVirtualMachineHardDiskDrive { ## Collection of additional hard disk drive configurations [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Collections.Hashtable[]] - $HardDiskDrive + $HardDiskDrive, + + ## Configuration environment name + [Parameter()] + [AllowNull()] + [System.String] $EnvironmentName ) process { - $vmHardDiskPath = (Get-ConfigurationData -Configuration Host).DifferencingVhdPath; + $vmHardDiskPath = Resolve-LabVMDiskPath -Name $NodeName -EnvironmentName $EnvironmentName -Parent; for ($i = 0; $i -lt $HardDiskDrive.Count; $i++) { diff --git a/Src/Private/Resolve-ConfigurationPath.ps1 b/Src/Private/Resolve-ConfigurationPath.ps1 index 9ce71579..e1831b3b 100644 --- a/Src/Private/Resolve-ConfigurationPath.ps1 +++ b/Src/Private/Resolve-ConfigurationPath.ps1 @@ -1,11 +1,10 @@ function Resolve-ConfigurationPath { <# .SYNOPSIS - Resolves a Lability image by its path. + Resolves a node's .mof configuration file path. .DESCRIPTION - When running Remove-LabVM there is not always a configuration document supplied. This - causes issues removing a VMs VHD/X file. The ResolveLabImage function locates the image - by its physical path. + Searches the current working directory and host configuration data path for a node's .mof + files, searching an environment name subdirectory if environment name is defined. #> [CmdletBinding()] param ( @@ -46,7 +45,7 @@ function Resolve-ConfigurationPath { ## Search the Specified path $resolvedPath = Resolve-PathEx -Path $Path; - if (Test-Configurationpath -Name $Name -Path $resolvedPath) { + if (Test-ConfigurationPath -Name $Name -Path $resolvedPath) { return $resolvedPath; } @@ -54,7 +53,7 @@ function Resolve-ConfigurationPath { ## Search the Specified\ConfigurationName path $resolvedPath = Join-Path -Path $resolvedPath -ChildPath $configurationName; - if (Test-Configurationpath -Name $Name -Path $resolvedPath) { + if (Test-ConfigurationPath -Name $Name -Path $resolvedPath) { return $resolvedPath; } @@ -64,7 +63,7 @@ function Resolve-ConfigurationPath { ## Search the ConfigurationPath path $configurationPath = Get-LabHostDscConfigurationPath; $resolvedPath = Resolve-PathEx -Path $configurationPath; - if (Test-Configurationpath -Name $Name -Path $resolvedPath) { + if (Test-ConfigurationPath -Name $Name -Path $resolvedPath) { return $resolvedPath; } @@ -72,7 +71,7 @@ function Resolve-ConfigurationPath { ## Search the ConfigurationPath\ConfigurationName path $resolvedPath = Join-Path -Path $resolvedPath -ChildPath $configurationName; - if (Test-Configurationpath -Name $Name -Path $resolvedPath) { + if (Test-ConfigurationPath -Name $Name -Path $resolvedPath) { return $resolvedPath; } @@ -81,7 +80,7 @@ function Resolve-ConfigurationPath { ## Search the Current path $currentPath = (Get-Location -PSProvider FileSystem).Path; $resolvedPath = Resolve-PathEx -Path $currentPath; - if (Test-Configurationpath -Name $Name -Path $resolvedPath) { + if (Test-ConfigurationPath -Name $Name -Path $resolvedPath) { return $resolvedPath; } @@ -89,7 +88,7 @@ function Resolve-ConfigurationPath { ## Search the Current\ConfigurationName path $resolvedPath = Join-Path -Path $resolvedPath -ChildPath $configurationName; - if (Test-Configurationpath -Name $Name -Path $resolvedPath) { + if (Test-ConfigurationPath -Name $Name -Path $resolvedPath) { return $resolvedPath; } diff --git a/Src/Private/Resolve-LabVMDiskGenerationPath.ps1 b/Src/Private/Resolve-LabVMDiskGenerationDiskPath.ps1 similarity index 71% rename from Src/Private/Resolve-LabVMDiskGenerationPath.ps1 rename to Src/Private/Resolve-LabVMDiskGenerationDiskPath.ps1 index 1aa8ee11..c239c0f1 100644 --- a/Src/Private/Resolve-LabVMDiskGenerationPath.ps1 +++ b/Src/Private/Resolve-LabVMDiskGenerationDiskPath.ps1 @@ -22,11 +22,16 @@ function Resolve-LabVMGenerationDiskPath { ) process { - $hostDefaults = Get-ConfigurationData -Configuration Host; $image = Get-LabImage -Id $Media -ConfigurationData $ConfigurationData; - $vhdName = '{0}.{1}' -f $Name, $image.Generation.ToLower(); - $vhdPath = Join-Path -Path $hostDefaults.DifferencingVhdPath -ChildPath $vhdName; + + $resolveLabVMDiskPathParams = @{ + Name = $Name; + Generation = $image.Generation; + EnvironmentName = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).EnvironmentName; + } + $vhdPath = Resolve-LabVMDiskPath @resolveLabVMDiskPathParams + return $vhdPath; } #end process -} #end function Resolve-LabVMGenerationDiskPath +} #end function diff --git a/Src/Private/Resolve-LabVMDiskPath.ps1 b/Src/Private/Resolve-LabVMDiskPath.ps1 index ccfc7ab0..e48d85a6 100644 --- a/Src/Private/Resolve-LabVMDiskPath.ps1 +++ b/Src/Private/Resolve-LabVMDiskPath.ps1 @@ -11,13 +11,38 @@ function Resolve-LabVMDiskPath { [Parameter()] [ValidateSet('VHD','VHDX')] - [System.String] $Generation = 'VHDX' + [System.String] $Generation = 'VHDX', + + ## Configuration environment name + [Parameter()] + [AllowNull()] + [System.String] $EnvironmentName, + + ## Return the parent/folder path + [Parameter()] + [System.Management.Automation.SwitchParameter] $Parent ) process { $hostDefaults = Get-ConfigurationData -Configuration Host; - $vhdName = '{0}.{1}' -f $Name, $Generation.ToLower(); - $vhdPath = Join-Path -Path $hostDefaults.DifferencingVhdPath -ChildPath $vhdName; + $differencingVhdPath = $hostDefaults.DifferencingVhdPath; + + if ((-not $hostDefaults.DisableVhdEnvironmentName) -and + (-not [System.String]::IsNullOrEmpty($EnvironmentName))) { + + $differencingVhdPath = Join-Path -Path $differencingVhdPath -ChildPath $EnvironmentName; + } + + if ($Parent) { + + $vhdPath = $differencingVhdPath; + } + else { + + $vhdName = '{0}.{1}' -f $Name, $Generation.ToLower(); + $vhdPath = Join-Path -Path $differencingVhdPath -ChildPath $vhdName; + } + return $vhdPath; } #end process diff --git a/Src/Private/Set-LabVMDisk.ps1 b/Src/Private/Set-LabVMDisk.ps1 index 8775b62b..8b15b4ac 100644 --- a/Src/Private/Set-LabVMDisk.ps1 +++ b/Src/Private/Set-LabVMDisk.ps1 @@ -1,12 +1,12 @@ function Set-LabVMDisk { -<# + <# .SYNOPSIS Sets a lab VM disk file (VHDX) configuration. .DESCRIPTION Configures a VM disk configuration using the xVHD DSC resource. #> [CmdletBinding()] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param ( ## VM/Node name [Parameter(Mandatory, ValueFromPipeline)] @@ -24,7 +24,6 @@ function Set-LabVMDisk { ) process { - $hostDefaults = Get-ConfigurationData -Configuration Host; if ($PSBoundParameters.ContainsKey('ConfigurationData')) { $image = Get-LabImage -Id $Media -ConfigurationData $ConfigurationData -ErrorAction Stop; @@ -34,12 +33,13 @@ function Set-LabVMDisk { $image = Get-LabImage -Id $Media -ErrorAction Stop; } + $environmentName = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).EnvironmentName; $vhd = @{ - Name = $Name; - Path = $hostDefaults.DifferencingVhdPath; + Name = $Name; + Path = Resolve-LabVMDiskPath -Name $Name -EnvironmentName $environmentName -Parent; ParentPath = $image.ImagePath; Generation = $image.Generation; - Type = 'Differencing'; + Type = 'Differencing'; } Import-LabDscResource -ModuleName xHyper-V -ResourceName MSFT_xVHD -Prefix VHD; diff --git a/Src/Private/Set-LabVMDiskFile.ps1 b/Src/Private/Set-LabVMDiskFile.ps1 index 54acad52..3b05ba72 100644 --- a/Src/Private/Set-LabVMDiskFile.ps1 +++ b/Src/Private/Set-LabVMDiskFile.ps1 @@ -90,7 +90,6 @@ function Set-LabVMDiskFile { ## Bubble up the error to the caller throw $_; - } finally { diff --git a/Src/Private/Test-LabVMDisk.ps1 b/Src/Private/Test-LabVMDisk.ps1 index 365e6c79..d3e56745 100644 --- a/Src/Private/Test-LabVMDisk.ps1 +++ b/Src/Private/Test-LabVMDisk.ps1 @@ -1,5 +1,5 @@ function Test-LabVMDisk { -<# + <# .SYNOPSIS Checks whether the lab virtual machine disk (VHDX) is present. #> @@ -21,13 +21,11 @@ function Test-LabVMDisk { $ConfigurationData, [Parameter()] - [ValidateSet('Present','Absent')] + [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present' ) process { - $hostDefaults = Get-ConfigurationData -Configuration Host; - if ($PSBoundParameters.ContainsKey('ConfigurationData')) { $image = Get-LabImage -Id $Media -ConfigurationData $ConfigurationData; @@ -37,12 +35,13 @@ function Test-LabVMDisk { $image = Get-LabImage -Id $Media; } + $environmentName = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).EnvironmentName; $vhd = @{ - Name = $Name; - Path = $hostDefaults.DifferencingVhdPath; + Name = $Name; + Path = Resolve-LabVMDiskPath -Name $Name -EnvironmentName $environmentName -Parent; ParentPath = $image.ImagePath; Generation = $image.Generation; - Type = 'Differencing'; + Type = 'Differencing'; } if (-not $image) { diff --git a/Src/Public/Get-LabVM.ps1 b/Src/Public/Get-LabVM.ps1 index 3bb9b62d..6df14d91 100644 --- a/Src/Public/Get-LabVM.ps1 +++ b/Src/Public/Get-LabVM.ps1 @@ -1,5 +1,5 @@ function Get-LabVM { -<# + <# .SYNOPSIS Retrieves the current configuration of a VM. .DESCRIPTION @@ -27,12 +27,14 @@ function Get-LabVM { $Name = $ConfigurationData.AllNodes | Where-Object NodeName -ne '*' | ForEach-Object { $_.NodeName; } } + $environmentName = $ConfigurationData.NonNodeData.$($labDefaults.ModuleName).EnvironmentName; + foreach ($nodeName in $Name) { $node = Resolve-NodePropertyValue -NodeName $nodeName -ConfigurationData $ConfigurationData; $xVMParams = @{ - Name = $node.NodeDisplayName; - VhdPath = Resolve-LabVMDiskPath -Name $node.NodeDisplayName;; + Name = $node.NodeDisplayName; + VhdPath = Resolve-LabVMDiskPath -Name $node.NodeDisplayName -EnvironmentName $environmentName; } try { diff --git a/Src/Public/Remove-LabConfiguration.ps1 b/Src/Public/Remove-LabConfiguration.ps1 index 1fc17f89..ba79936f 100644 --- a/Src/Public/Remove-LabConfiguration.ps1 +++ b/Src/Public/Remove-LabConfiguration.ps1 @@ -56,6 +56,8 @@ function Remove-LabConfiguration { } #end foreach node + ## TODO: Remove empty (environment name) differencing disk folder(s) + Write-Progress -Id 42 -Activity $activity -Completed; Write-Verbose -Message $localized.FinishedLabConfiguration; diff --git a/Src/Public/Set-LabHostDefault.ps1 b/Src/Public/Set-LabHostDefault.ps1 index 0a9af767..7e6d2a4b 100644 --- a/Src/Public/Set-LabHostDefault.ps1 +++ b/Src/Public/Set-LabHostDefault.ps1 @@ -72,7 +72,12 @@ function Set-LabHostDefault { ## Specifies whether environment name prefixes/suffixes are applied to virtual switches. [Parameter(ValueFromPipelineByPropertyName)] - [System.Management.Automation.SwitchParameter] $DisableSwitchEnvironmentName + [System.Management.Automation.SwitchParameter] $DisableSwitchEnvironmentName, + + ## Specifies whether VM differencing disks are placed into a subdirectory when an + ## environment name is defined. + [Parameter(ValueFromPipelineByPropertyName)] + [System.Management.Automation.SwitchParameter] $DisableVhdEnvironmentName ) process { @@ -131,6 +136,10 @@ function Set-LabHostDefault { $hostDefaults.DisableSwitchEnvironmentName = $DisableSwitchEnvironmentName.ToBool(); } + if ($PSBoundParameters.ContainsKey('DisableVhdEnvironmentName')) { + + $hostDefaults.DisableVhdEnvironmentName = $DisableVhdEnvironmentName.ToBool(); + } Set-ConfigurationData -Configuration Host -InputObject $hostDefaults; Import-DismModule; diff --git a/Tests/Unit/Src/Private/Get-ConfigurationData.Tests.ps1 b/Tests/Unit/Src/Private/Get-ConfigurationData.Tests.ps1 index ae495579..b8ef6ccd 100644 --- a/Tests/Unit/Src/Private/Get-ConfigurationData.Tests.ps1 +++ b/Tests/Unit/Src/Private/Get-ConfigurationData.Tests.ps1 @@ -114,6 +114,19 @@ Describe 'Unit\Src\Private\Get-ConfigurationData' { $customMediaConfiguration.DisableSwitchEnvironmentName | Should Be $true; } + It 'Adds missing "DisableVhdEnvironmentName" property to Host configuration' { + $testConfigurationFilename = 'TestMediaConfiguration.json'; + $testConfigurationPath = "$env:SystemRoot\Temp\$testConfigurationFilename"; + $fakeConfiguration = '{ "ConfigurationPath": "%SYSTEMDRIVE%\\TestLab\\Configurations" }'; + [ref] $null = New-Item -Path $testConfigurationPath -ItemType File -Force; + Mock Resolve-ConfigurationDataPath -MockWith { return $testConfigurationPath } + Mock Get-Content -MockWith { return $fakeConfiguration; } + + $customMediaConfiguration = Get-ConfigurationData -Configuration Host; + + $customMediaConfiguration.DisableVhdEnvironmentName | Should Be $true; + } + It 'Adds missing "MaxEnvelopeSizeKb" property to VM configuration' { $testConfigurationFilename = 'TestVMConfiguration.json'; $testConfigurationPath = "$env:SystemRoot\Temp\$testConfigurationFilename"; diff --git a/Tests/Unit/Src/Private/Resolve-LabVMDiskPath.Tests.ps1 b/Tests/Unit/Src/Private/Resolve-LabVMDiskPath.Tests.ps1 index 7430942b..d1548676 100644 --- a/Tests/Unit/Src/Private/Resolve-LabVMDiskPath.Tests.ps1 +++ b/Tests/Unit/Src/Private/Resolve-LabVMDiskPath.Tests.ps1 @@ -19,6 +19,79 @@ Describe 'Unit\Src\Private\Resolve-LabVMDiskPath' { $vhdPath | Should Be "$testDifferencingVhdPath\$testVMName.vhdx"; } + It 'Defaults to "VHDX" when no "Generation" is specified' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName; + + $vhdPath | Should Be "$testDifferencingVhdPath\$testVMName.vhdx"; + } + + It 'Returns "VHD" when VHD "Generation" is specified' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName -Generation 'VHD'; + + $vhdPath | Should Be "$testDifferencingVhdPath\$testVMName.vhd"; + } + + It 'Adds environment name to host "DifferencingVhdPath" when "EnvironmentName" is defined' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + $testEnvironmentName = 'TestEnvironment'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName -EnvironmentName $testEnvironmentName; + + $vhdPath | Should Be "$testDifferencingVhdPath\$testEnvironmentName\$testVMName.vhdx"; + } + + It 'Does not add environment name to host "DifferencingVhdPath" when "EnvironmentName" is null' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; DisableVhdEnvironmentName = $true; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName -EnvironmentName $null; + + $vhdPath | Should Be "$testDifferencingVhdPath\$testVMName.vhdx"; + } + + It 'Does not add environment name to host "DifferencingVhdPath" when "DisableVhdEnvironmentName" is defined' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + $testEnvironmentName = 'TestEnvironment'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; DisableVhdEnvironmentName = $true; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName -EnvironmentName $testEnvironmentName; + + $vhdPath | Should Be "$testDifferencingVhdPath\$testVMName.vhdx"; + } + + It 'Returns host "DifferencingVhdPath" when "Parent" is specified' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName -Parent; + + $vhdPath | Should Be "$testDifferencingVhdPath"; + } + + It 'Returns host "DifferencingVhdPath" when "EnvironmentName" is defined and "Parent" is specified' { + $testVMName = 'TestVM'; + $testDifferencingVhdPath = 'TestDrive:'; + $testEnvironmentName = 'TestEnvironment'; + Mock Get-ConfigurationData -MockWith { return @{ DifferencingVhdPath = $testDifferencingVhdPath; DisableVhdEnvironmentName = $false; } } + + $vhdPath = Resolve-LabVMDiskPath -Name $testVMName -EnvironmentName $testEnvironmentName -Parent; + + $vhdPath | Should Be "$testDifferencingVhdPath\$testEnvironmentName"; + } + } #end InModuleScope } #end describe From 163f96a4e38950539d4e46520fd6cefeab54b06b Mon Sep 17 00:00:00 2001 From: Iain Date: Wed, 3 Jul 2019 08:41:30 +0100 Subject: [PATCH 06/11] Updates broken 'The Ultimate Hyper-V Lab Tool' link --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 123e191e..84bd4dbd 100644 --- a/Readme.md +++ b/Readme.md @@ -94,7 +94,7 @@ __[here](https://www.youtube.com/watch?v=jefhLaJsG3E "Man vs TestLab")__. Other * [Lability tutorial](https://me.micahrl.com/lability-tutorial) by [mrled](https://github.com/mrled) * [Building A Lab using Hyper-V and Lability](https://blog.kilasuit.org/2016/04/13/building-a-lab-using-hyper-v-and-lability-the-end-to-end-example/) via @kilasuit -* [The Ultimate Hyper-V Lab Tool](http://www.absolutejam.co.uk/blog/lability-ultimate-hyperv-lab-tool/) via @absolutejam +* [The Ultimate Hyper-V Lab Tool](https://web.archive.org/web/20180830192145/http://www.absolutejam.co.uk/blog/lability-ultimate-hyperv-lab-tool/) via @absolutejam * [Create Your Virtual Lab Environment with Lability How-To](http://blog.mscloud.guru/2016/09/17/create-your-virtual-lab-environment-with-lability-howto/) via @Naboo2604 * [Microsoft Channel 9 PSDEVOPS](https://channel9.msdn.com/Blogs/PSDEVOPSSIG/PSDEVOPSSIGEventLability-Demo-w-Iain-Brigton) presentation recording * [Using Lability, DSC and ARM to define and deploy multi-VM environments] (https://blogs.blackmarble.co.uk/rhepworth/2017/03/02/define-once-deploy-everywhere-sort-of/) via @rikhepworth From 143f53e934f0182ca3a184841f5605b8fcfc1b74 Mon Sep 17 00:00:00 2001 From: Iain Date: Fri, 19 Jul 2019 21:37:46 +0100 Subject: [PATCH 07/11] Updates code signing certificate --- Build.PSake.ps1 | 8 ++++---- Readme.md | 2 +- VE_Certificate_2019.pfx.enc | Bin 5568 -> 0 bytes VE_Certificate_2021.pfx.enc | Bin 0 -> 5968 bytes 4 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 VE_Certificate_2019.pfx.enc create mode 100644 VE_Certificate_2021.pfx.enc diff --git a/Build.PSake.ps1 b/Build.PSake.ps1 index 37ac6755..9cfbaa57 100644 --- a/Build.PSake.ps1 +++ b/Build.PSake.ps1 @@ -9,8 +9,8 @@ Properties { $buildDir = 'Release'; $buildPath = (Join-Path -Path $basePath -ChildPath $buildDir); $releasePath = (Join-Path -Path $buildPath -ChildPath $moduleName); - $thumbprint = '3DACD0F2D1E60EB33EC774B9CFC89A4BEE9037AF'; - $timeStampServer = 'http://timestamp.verisign.com/scripts/timestamp.dll'; + $thumbprint = '177FC8E667D4C022C7CD9CFDFEB66991890F4090'; + $timeStampServer = 'http://timestamp.digicert.com'; $exclude = @( '.git*', '.vscode', @@ -79,9 +79,9 @@ Task Sign -Depends Deploy { if (-not (Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object Thumbprint -eq $thumbprint)) { ## Decrypt and import code signing cert - .\appveyor-tools\secure-file.exe -decrypt .\VE_Certificate_2019.pfx.enc -secret $env:certificate_secret + .\appveyor-tools\secure-file.exe -decrypt .\VE_Certificate_2021.pfx.enc -secret $env:certificate_secret $certificatePassword = ConvertTo-SecureString -String $env:certificate_secret -AsPlainText -Force - Import-PfxCertificate -FilePath .\VE_Certificate_2019.pfx -CertStoreLocation 'Cert:\CurrentUser\My' -Password $certificatePassword + Import-PfxCertificate -FilePath .\VE_Certificate_2021.pfx -CertStoreLocation 'Cert:\CurrentUser\My' -Password $certificatePassword } Get-ChildItem -Path $releasePath -Exclude $signExclude | ForEach-Object { diff --git a/Readme.md b/Readme.md index 84bd4dbd..09e3f529 100644 --- a/Readme.md +++ b/Readme.md @@ -97,7 +97,7 @@ __[here](https://www.youtube.com/watch?v=jefhLaJsG3E "Man vs TestLab")__. Other * [The Ultimate Hyper-V Lab Tool](https://web.archive.org/web/20180830192145/http://www.absolutejam.co.uk/blog/lability-ultimate-hyperv-lab-tool/) via @absolutejam * [Create Your Virtual Lab Environment with Lability How-To](http://blog.mscloud.guru/2016/09/17/create-your-virtual-lab-environment-with-lability-howto/) via @Naboo2604 * [Microsoft Channel 9 PSDEVOPS](https://channel9.msdn.com/Blogs/PSDEVOPSSIG/PSDEVOPSSIGEventLability-Demo-w-Iain-Brigton) presentation recording -* [Using Lability, DSC and ARM to define and deploy multi-VM environments] (https://blogs.blackmarble.co.uk/rhepworth/2017/03/02/define-once-deploy-everywhere-sort-of/) via @rikhepworth +* [Using Lability, DSC and ARM to define and deploy multi-VM environments](https://blogs.blackmarble.co.uk/rhepworth/2017/03/02/define-once-deploy-everywhere-sort-of/) via @rikhepworth * [Building Hyper-V lab environments based on PowerShell DSC](http://www.powershell.no/hyper-v,/powershell/dsc/2017/07/19/lability.html) via @JanEgilRing [__Lability__ image/logo attribution credit](https://openclipart.org/image/300px/svg_to_png/22734/papapishu-Lab-icon-1.png) diff --git a/VE_Certificate_2019.pfx.enc b/VE_Certificate_2019.pfx.enc deleted file mode 100644 index f6816bd39b8e3e3022b6330ef0aa48daa676aac1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5568 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 O2g%S%_@mpCf9XT-xBxo< diff --git a/VE_Certificate_2021.pfx.enc b/VE_Certificate_2021.pfx.enc new file mode 100644 index 0000000000000000000000000000000000000000..81eb8e4ecdbf3c0cbd2120790dc58c320af51b67 GIT binary patch literal 5968 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 literal 0 HcmV?d00001 From 5e82382b4c772ea1ceb268fb6b691c6c797ed0b9 Mon Sep 17 00:00:00 2001 From: Iain Date: Tue, 17 Sep 2019 09:01:18 +0100 Subject: [PATCH 08/11] Adds version checking of downloaded modules Fixes #375 --- ChangeLog.md | 1 + Src/Private/Invoke-LabModuleCacheDownload.ps1 | 23 +++++------ .../Invoke-LabModuleDownloadFromGitHub.ps1 | 38 +++++++++++++++---- ...Invoke-LabModuleDownloadFromPSGallery.ps1} | 23 ++++++++--- Src/Private/Rename-LabModuleCacheVersion.ps1 | 29 ++++++++++++-- ...voke-LabModuleDownloadFromGitHub.Tests.ps1 | 36 ++++++++++++++++++ ...e-LabModuleDownloadFromPSGallery.Tests.ps1 | 35 +++++++++++++++-- en-US/Lability.Resources.psd1 | 1 + 8 files changed, 154 insertions(+), 32 deletions(-) rename Src/Private/{Invoke-ModuleDownloadFromPSGallery.ps1 => Invoke-LabModuleDownloadFromPSGallery.ps1} (69%) diff --git a/ChangeLog.md b/ChangeLog.md index 40c76ee1..0e9689f3 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,7 @@ * Adds `-DisableVhdEnvironmentName` parameter to `Set-LabHostDefault` (#78) * New installations/deployments will default to False - creating VM differencing disks in a subdirectory when EnvironmentName is defined in configuration data * Existing installations will default to True (at least until `Set-LabHostDefault` or `Reset-LabHostDefault` are called) +* Adds version checking of downloaded modules (#375) ### v0.18.0 ### diff --git a/Src/Private/Invoke-LabModuleCacheDownload.ps1 b/Src/Private/Invoke-LabModuleCacheDownload.ps1 index fad296f1..65748b2d 100644 --- a/Src/Private/Invoke-LabModuleCacheDownload.ps1 +++ b/Src/Private/Invoke-LabModuleCacheDownload.ps1 @@ -112,23 +112,20 @@ function Invoke-LabModuleCacheDownload { if ((-not (Test-LabModuleCache @moduleInfo)) -or ($Force) -or ($moduleInfo.Latest -eq $true)) { - if ((-not $moduleInfo.ContainsKey('Provider')) -or ($moduleInfo['Provider'] -eq 'PSGallery')) { - - if ($moduleInfo.ContainsKey('RequiredVersion')) { - Write-Verbose -Message ($localized.ModuleVersionNotCached -f $moduleInfo.Name, $moduleInfo.RequiredVersion); - } - elseif ($moduleInfo.ContainsKey('MinimumVersion')) { - Write-Verbose -Message ($localized.ModuleMinmumVersionNotCached -f $moduleInfo.Name, $moduleInfo.MinimumVersion); - } - else { - Write-Verbose -Message ($localized.ModuleNotCached -f $moduleInfo.Name); - } + if ($moduleInfo.ContainsKey('RequiredVersion')) { + Write-Verbose -Message ($localized.ModuleVersionNotCached -f $moduleInfo.Name, $moduleInfo.RequiredVersion); + } + elseif ($moduleInfo.ContainsKey('MinimumVersion')) { + Write-Verbose -Message ($localized.ModuleMinmumVersionNotCached -f $moduleInfo.Name, $moduleInfo.MinimumVersion); + } + else { + Write-Verbose -Message ($localized.ModuleNotCached -f $moduleInfo.Name); + } + if ((-not $moduleInfo.ContainsKey('Provider')) -or ($moduleInfo['Provider'] -eq 'PSGallery')) { Invoke-LabModuleDownloadFromPSGallery @moduleInfo; } elseif ($moduleInfo['Provider'] -eq 'GitHub') { - - Write-Verbose -Message ($localized.ModuleNotCached -f $moduleInfo.Name); Invoke-LabModuleDownloadFromGitHub @moduleInfo; } elseif ($moduleInfo['Provider'] -eq 'FileSystem') { diff --git a/Src/Private/Invoke-LabModuleDownloadFromGitHub.ps1 b/Src/Private/Invoke-LabModuleDownloadFromGitHub.ps1 index 3930a122..f3eb8908 100644 --- a/Src/Private/Invoke-LabModuleDownloadFromGitHub.ps1 +++ b/Src/Private/Invoke-LabModuleDownloadFromGitHub.ps1 @@ -1,11 +1,11 @@ function Invoke-LabModuleDownloadFromGitHub { -<# + <# .SYNOPSIS Downloads a DSC resource if it has not already been downloaded from Github. .NOTES Uses the GitHubRepository module! #> - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'Latest')] [OutputType([System.IO.DirectoryInfo])] param ( ## PowerShell DSC resource module name @@ -17,7 +17,6 @@ function Invoke-LabModuleDownloadFromGitHub { [ValidateNotNullOrEmpty()] [System.String] $DestinationPath = (Get-ConfigurationData -Configuration Host).ModuleCachePath, - ## The GitHub repository owner, typically 'PowerShell' [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] @@ -43,6 +42,14 @@ function Invoke-LabModuleDownloadFromGitHub { [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, + ## The minimum version of the module required + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'MinimumVersion')] + [System.Version] $MinimumVersion, + + ## The exact version of the module required + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'RequiredVersion')] + [System.Version] $RequiredVersion, + ## Catch all, for splatting parameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments @@ -62,20 +69,35 @@ function Invoke-LabModuleDownloadFromGitHub { $PSBoundParameters['Repository'] = $Repository; $PSBoundParameters['Branch'] = $Branch; - $Branch = $Branch.Replace('/','_') # Fix branch names with slashes (#361) + $Branch = $Branch.Replace('/', '_') # Fix branch names with slashes (#361) } process { - ## GitHub modules are suffixed with .Owner_Branch.zip + ## GitHub modules are suffixed with '_Owner_Branch.zip' $destinationModuleName = '{0}_{1}_{2}.zip' -f $Name, $Owner, $Branch; $moduleCacheDestinationPath = Join-Path -Path $DestinationPath -ChildPath $destinationModuleName; $setResourceDownloadParams = @{ DestinationPath = $moduleCacheDestinationPath; - Uri = Resolve-GitHubModuleUri @PSBoundParameters; - NoCheckSum = $true; + Uri = Resolve-GitHubModuleUri @PSBoundParameters; + NoCheckSum = $true; } $moduleDestinationPath = Set-ResourceDownload @setResourceDownloadParams; - return (Rename-LabModuleCacheVersion -Name $Name -Path $moduleDestinationPath -Owner $Owner -Branch $Branch); + + $renameLabModuleCacheVersionParams = @{ + Name = $Name; + Path = $moduleDestinationPath; + Owner = $Owner; + Branch = $Branch + } + if ($PSBoundParameters.ContainsKey('RequiredVersion')) { + + $renameLabModuleCacheVersionParams['RequiredVersion'] = $RequiredVersion + } + elseif ($PSBoundParameters.ContainsKey('MinimumVersion')) { + + $renameLabModuleCacheVersionParams['MinimumVersion'] = $MinimumVersion + } + return (Rename-LabModuleCacheVersion @renameLabModuleCacheVersionParams); } #end process } #end function diff --git a/Src/Private/Invoke-ModuleDownloadFromPSGallery.ps1 b/Src/Private/Invoke-LabModuleDownloadFromPSGallery.ps1 similarity index 69% rename from Src/Private/Invoke-ModuleDownloadFromPSGallery.ps1 rename to Src/Private/Invoke-LabModuleDownloadFromPSGallery.ps1 index d80b70eb..7e80127f 100644 --- a/Src/Private/Invoke-ModuleDownloadFromPSGallery.ps1 +++ b/Src/Private/Invoke-LabModuleDownloadFromPSGallery.ps1 @@ -1,9 +1,9 @@ function Invoke-LabModuleDownloadFromPSGallery { -<# + <# .SYNOPSIS Downloads a PowerShell module/DSC resource from the PowerShell gallery to the host's module cache. #> - [CmdletBinding(DefaultParameterSetName = 'LatestAvailable')] + [CmdletBinding(DefaultParameterSetName = 'Latest')] [OutputType([System.IO.FileInfo])] param ( ## PowerShell module/DSC resource module name @@ -33,11 +33,24 @@ function Invoke-LabModuleDownloadFromPSGallery { $moduleCacheDestinationPath = Join-Path -Path $DestinationPath -ChildPath $destinationModuleName; $setResourceDownloadParams = @{ DestinationPath = $moduleCacheDestinationPath; - Uri = Resolve-PSGalleryModuleUri @PSBoundParameters; - NoCheckSum = $true; + Uri = Resolve-PSGalleryModuleUri @PSBoundParameters; + NoCheckSum = $true; } $moduleDestinationPath = Set-ResourceDownload @setResourceDownloadParams; - return (Rename-LabModuleCacheVersion -Name $Name -Path $moduleDestinationPath); + + $renameLabModuleCacheVersionParams = @{ + Name = $Name; + Path = $moduleDestinationPath; + } + if ($PSBoundParameters.ContainsKey('RequiredVersion')) { + + $renameLabModuleCacheVersionParams['RequiredVersion'] = $RequiredVersion + } + elseif ($PSBoundParameters.ContainsKey('MinimumVersion')) { + + $renameLabModuleCacheVersionParams['MinimumVersion'] = $MinimumVersion + } + return (Rename-LabModuleCacheVersion @renameLabModuleCacheVersionParams); } #end process } #end function diff --git a/Src/Private/Rename-LabModuleCacheVersion.ps1 b/Src/Private/Rename-LabModuleCacheVersion.ps1 index 35e49b25..8d6d15af 100644 --- a/Src/Private/Rename-LabModuleCacheVersion.ps1 +++ b/Src/Private/Rename-LabModuleCacheVersion.ps1 @@ -20,17 +20,40 @@ function Rename-LabModuleCacheVersion { ## GitHub module branch [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'GitHub')] - [System.String] $Branch + [System.String] $Branch, + + ## The minimum version of the module required + [Parameter(ValueFromPipelineByPropertyName)] + [System.Version] $MinimumVersion, + + ## The exact version of the module required + [Parameter(ValueFromPipelineByPropertyName)] + [System.Version] $RequiredVersion ) process { if ($PSCmdlet.ParameterSetName -eq 'GitHub') { $moduleManifest = Get-LabModuleCacheManifest -Path $Path -Provider 'GitHub'; - $versionedModuleFilename = '{0}-v{1}_{2}_{3}.zip' -f $Name, $moduleManifest.ModuleVersion, $Owner, $Branch; + $moduleVersion = $moduleManifest.ModuleVersion; + $versionedModuleFilename = '{0}-v{1}_{2}_{3}.zip' -f $Name, $moduleVersion, $Owner, $Branch; } else { $moduleManifest = Get-LabModuleCacheManifest -Path $Path; - $versionedModuleFilename = '{0}-v{1}.zip' -f $Name, $moduleManifest.ModuleVersion; + $moduleVersion = $moduleManifest.ModuleVersion; + $versionedModuleFilename = '{0}-v{1}.zip' -f $Name, $moduleVersion; + } + + if ($PSBoundParameters.ContainsKey('RequiredVersion')) { + + if ($moduleVersion -ne $RequiredVersion) { + throw ($localized.ModuleVersionMismatchError -f $Name, $moduleVersion, $RequiredVersion); + } + } + elseif ($PSBoundParameters.ContainsKey('MinimumVersion')) { + + if ($moduleVersion -lt $MinimumVersion) { + throw ($localized.ModuleVersionMismatchError -f $Name, $moduleVersion, $MinimumVersion); + } } $versionedModulePath = Join-Path -Path (Split-Path -Path $Path -Parent) -ChildPath $versionedModuleFilename; diff --git a/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromGitHub.Tests.ps1 b/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromGitHub.Tests.ps1 index d186c668..e8e6e3e3 100644 --- a/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromGitHub.Tests.ps1 +++ b/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromGitHub.Tests.ps1 @@ -10,6 +10,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromGitHub' { InModuleScope $moduleName { $testModuleName = 'TestModule'; + $testModuleVersion = '1.2.3.4' $testOwner = 'TestOwnder'; $testBranch = 'TestBranch'; $testDestinationPath = '{0}\Modules' -f (Get-PSDrive -Name TestDrive).Root; @@ -31,6 +32,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromGitHub' { DestinationPath = $testDestinationPath; Owner = $testOwner; Branch = $testBranch; + RequiredVersion = $testModuleVersion; } $result = Invoke-LabModuleDownloadFromGitHub @testParams; @@ -45,6 +47,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromGitHub' { DestinationPath = $testDestinationPath; Owner = $testOwner; Branch = $testBranch; + RequiredVersion = $testModuleVersion; } Invoke-LabModuleDownloadFromGitHub @testParams; @@ -61,6 +64,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromGitHub' { DestinationPath = $testDestinationPath; Owner = $testOwner; Branch = $testBranch; + RequiredVersion = $testModuleVersion; OverrideRepositoryName = $testRepositoryOverrideName } Invoke-LabModuleDownloadFromGitHub @testParams; @@ -91,5 +95,37 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromGitHub' { { Invoke-LabModuleDownloadFromGitHub @testParams -WarningAction Stop 3>&1 } | Should Throw; } + It 'Throws when downloaded module version does not match expected minimum version (#375)' { + ## BeforeEach does not (currently) work inside InModuleScope scriptblocks https://github.com/pester/Pester/issues/236 + New-Item -Path $testDestinationPath -ItemType Directory -Force -ErrorAction SilentlyContinue; + New-Item -Path $testModulePath -ItemType File -Force -ErrorAction SilentlyContinue; + + $testParams = @{ + Name = $testModuleName; + DestinationPath = $testDestinationPath; + Owner = $testOwner; + Branch = $testBranch; + MinimumVersion = '1.2.3.5'; + } + { Invoke-LabModuleDownloadFromGitHub @testParams } | Should Throw; + } + + It 'Throws when downloaded module version does not match required version (#375)' { + Mock Get-LabModuleCacheManifest -MockWith { return @{ ModuleVersion = '1.2.3.5' } } + + ## BeforeEach does not (currently) work inside InModuleScope scriptblocks https://github.com/pester/Pester/issues/236 + New-Item -Path $testDestinationPath -ItemType Directory -Force -ErrorAction SilentlyContinue; + New-Item -Path $testModulePath -ItemType File -Force -ErrorAction SilentlyContinue; + + $testParams = @{ + Name = $testModuleName; + DestinationPath = $testDestinationPath; + Owner = $testOwner; + Branch = $testBranch; + RequiredVersion = $testModuleVersion; + } + { Invoke-LabModuleDownloadFromPSGallery @testParams } | Should Throw + } + } #end InModuleScope } #end Describe diff --git a/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromPSGallery.Tests.ps1 b/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromPSGallery.Tests.ps1 index b4c83076..85c2a346 100644 --- a/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromPSGallery.Tests.ps1 +++ b/Tests/Unit/Src/Private/Invoke-LabModuleDownloadFromPSGallery.Tests.ps1 @@ -10,6 +10,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromPSGallery' { InModuleScope $moduleName { $testModuleName = 'TestModule'; + $testModuleVersion = '1.2.3.4' $testDestinationPath = '{0}\Modules' -f (Get-PSDrive -Name TestDrive).Root; $testModulePath = '{0}\{1}.zip' -f $testDestinationPath, $testModuleName; @@ -27,7 +28,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromPSGallery' { $testParams = @{ Name = $testModuleName; DestinationPath = $testDestinationPath; - RequiredVersion = '1.2.3.4'; + RequiredVersion = $testModuleVersion; } $result = Invoke-LabModuleDownloadFromPSGallery @testParams; @@ -40,7 +41,7 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromPSGallery' { $testParams = @{ Name = $testModuleName; DestinationPath = $testDestinationPath; - RequiredVersion = '1.2.3.4'; + RequiredVersion = $testModuleVersion; } Invoke-LabModuleDownloadFromPSGallery @testParams; @@ -53,12 +54,40 @@ Describe 'Src\Private\Invoke-LabModuleDownloadFromPSGallery' { $testParams = @{ Name = $testModuleName; DestinationPath = $testDestinationPath; - MinimumVersion = '1.2.3.4'; + MinimumVersion = $testModuleVersion; } Invoke-LabModuleDownloadFromPSGallery @testParams; Assert-MockCalled Resolve-PSGalleryModuleUri -ParameterFilter { $null -ne $MinimumVersion } -Scope It; } + It 'Throws when downloaded module version does not match expected minimum version (#375)' { + ## BeforeEach does not (currently) work inside InModuleScope scriptblocks https://github.com/pester/Pester/issues/236 + New-Item -Path $testDestinationPath -ItemType Directory -Force -ErrorAction SilentlyContinue; + New-Item -Path $testModulePath -ItemType File -Force -ErrorAction SilentlyContinue; + + $testParams = @{ + Name = $testModuleName; + DestinationPath = $testDestinationPath; + MinimumVersion = '1.2.3.5'; + } + { Invoke-LabModuleDownloadFromPSGallery @testParams } | Should Throw + } + + It 'Throws when downloaded module version does not match required version (#375)' { + Mock Get-LabModuleCacheManifest -MockWith { return @{ ModuleVersion = '1.2.3.5' } } + + ## BeforeEach does not (currently) work inside InModuleScope scriptblocks https://github.com/pester/Pester/issues/236 + New-Item -Path $testDestinationPath -ItemType Directory -Force -ErrorAction SilentlyContinue; + New-Item -Path $testModulePath -ItemType File -Force -ErrorAction SilentlyContinue; + + $testParams = @{ + Name = $testModuleName; + DestinationPath = $testDestinationPath; + RequiredVersion = $testModuleVersion; + } + { Invoke-LabModuleDownloadFromPSGallery @testParams } | Should Throw + } + } #end InModuleScope } #end Describe diff --git a/en-US/Lability.Resources.psd1 b/en-US/Lability.Resources.psd1 index a098c26a..0ae15295 100644 --- a/en-US/Lability.Resources.psd1 +++ b/en-US/Lability.Resources.psd1 @@ -227,6 +227,7 @@ ConvertFrom-StringData -StringData @' CannotResolveModuleNameError = Cannot resolve {0} name '{1}'. DscResourceNotFoundError = DSC module\\resource '{0}' not found. ResourceVersionMismatchError = DSC module\\resource '{0}' version '{1}' is less than the required '{2}'. + ModuleVersionMismatchError = Module\\resource '{0}' download version '{1}' does not match specified version '{2}'. CannotFindCertificateError = Cannot locate {0} certificate '{1}'. CannotLocateMofFileError = Cannot locate node '{0}' file and no DSC configuration can be applied. Ensure the DSC configuration has been run successfully. CannotLocateNodeError = Cannot locate node '{0}'. From 052565b8661a2b6a44f7de42b5971efa84b16a24 Mon Sep 17 00:00:00 2001 From: Iain Brighton Date: Mon, 3 Feb 2020 20:15:09 +0000 Subject: [PATCH 09/11] Updates default Win 10 evaluation media to build 1909 --- ChangeLog.md | 4 ++- .../WIN10_x64_Enterprise_1903_EN_Eval.json | 25 +++++++++++++++++++ .../WIN10_x86_Enterprise_1903_EN_Eval.json | 25 +++++++++++++++++++ Config/Media.json | 16 ++++++------ Config/VMDefaults.json | 2 +- Lability.psm1 | 20 ++++++++------- Src/Public/Get-LabMedia.ps1 | 20 ++++++++++++++- Src/Public/Register-LabMedia.ps1 | 4 +++ 8 files changed, 96 insertions(+), 20 deletions(-) create mode 100644 Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json create mode 100644 Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json diff --git a/ChangeLog.md b/ChangeLog.md index 0e9689f3..0ea1b1e5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,12 +6,14 @@ * Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) * Adds `Export-LabImage` cmdlet to export VHD(X) master/parent images -* Permits overriding media Id with `-CustomId` parameter when importing legacy media definitions or media from an external file/Uri with `Register-LabMedia` +* Permits overriding media Id with -CustomId` parameter when importing legacy media definitions or media from an external file/Uri with `Register-LabMedia` * Adds `Latest` property to module info to force latest module version download - on every configuration run (#367) * Adds `-DisableVhdEnvironmentName` parameter to `Set-LabHostDefault` (#78) * New installations/deployments will default to False - creating VM differencing disks in a subdirectory when EnvironmentName is defined in configuration data * Existing installations will default to True (at least until `Set-LabHostDefault` or `Reset-LabHostDefault` are called) * Adds version checking of downloaded modules (#375) +* Updates default Windows 10 evaluation media to 19H2 build (1909) +* Updates default VM media from `2012R2_x64_Standard_EN_Eval` to `2019_x64_Standard_EN_Eval` ### v0.18.0 ### diff --git a/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json b/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json new file mode 100644 index 00000000..3fc62b47 --- /dev/null +++ b/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json @@ -0,0 +1,25 @@ +{ + "Id": "WIN10_x64_Enterprise_1903_EN_Eval", + "Filename": "WIN10_x64_ENT_19H1_EN_Eval.iso", + "Description": "Windows 10 64bit Enterprise 1903 English Evaluation", + "Architecture": "x64", + "ImageName": "Windows 10 Enterprise Evaluation", + "MediaType": "ISO", + "OperatingSystem": "Windows", + "Uri": "https://software-download.microsoft.com/download/pr/18362.30.190401-1528.19h1_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x64FRE_en-us.iso", + "Checksum": "70C7E8F0599BBB4526F08E6814925E50", + "CustomData": { + "WindowsOptionalFeature": [ + "NetFx3" + ], + "CustomBootstrap": [ + "## Unattend.xml will set the Administrator password, but it won't enable the account on client OSes", + "NET USER Administrator /active:yes;", + "Set-ItemProperty -Path HKLM:\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell -Name ExecutionPolicy -Value RemoteSigned -Force; #306", + "## Kick-start PowerShell remoting on clients to permit applying DSC configurations", + "Enable-PSRemoting -SkipNetworkProfileCheck -Force;" + ], + "MinimumDismVersion": "10.0.0.0" + }, + "Hotfixes": [] +} \ No newline at end of file diff --git a/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json b/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json new file mode 100644 index 00000000..4739b693 --- /dev/null +++ b/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json @@ -0,0 +1,25 @@ +{ + "Id": "WIN10_x86_Enterprise_1903_EN_Eval", + "Filename": "WIN10_x86_ENT_19H1_EN_Eval.iso", + "Description": "Windows 10 32bit Enterprise 1903 English Evaluation", + "Architecture": "x86", + "ImageName": "Windows 10 Enterprise Evaluation", + "MediaType": "ISO", + "OperatingSystem": "Windows", + "Uri": "https://software-download.microsoft.com/download/pr/18362.30.190401-1528.19h1_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x86FRE_en-us.iso", + "Checksum": "1945092B75613D7822B82D3AFB378F1D", + "CustomData": { + "WindowsOptionalFeature": [ + "NetFx3" + ], + "CustomBootstrap": [ + "## Unattend.xml will set the Administrator password, but it won't enable the account on client OSes", + "NET USER Administrator /active:yes;", + "Set-ItemProperty -Path HKLM:\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell -Name ExecutionPolicy -Value RemoteSigned -Force; #306", + "## Kick-start PowerShell remoting on clients to permit applying DSC configurations", + "Enable-PSRemoting -SkipNetworkProfileCheck -Force;" + ], + "MinimumDismVersion": "10.0.0.0" + }, + "Hotfixes": [] +} \ No newline at end of file diff --git a/Config/Media.json b/Config/Media.json index 009d196e..1514ea22 100644 --- a/Config/Media.json +++ b/Config/Media.json @@ -713,14 +713,14 @@ }, { "Id": "WIN10_x64_Enterprise_EN_Eval", - "Filename": "WIN10_x64_ENT_19H1_EN_Eval.iso", - "Description": "Windows 10 64bit Enterprise 1903 English Evaluation", + "Filename": "WIN10_x64_ENT_19H2_EN_Eval.iso", + "Description": "Windows 10 64bit Enterprise 1909 English Evaluation", "Architecture": "x64", "ImageName": "Windows 10 Enterprise Evaluation", "MediaType": "ISO", "OperatingSystem": "Windows", - "Uri": "https://software-download.microsoft.com/download/pr/18362.30.190401-1528.19h1_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x64FRE_en-us.iso", - "Checksum": "70C7E8F0599BBB4526F08E6814925E50", + "Uri": "https://software-download.microsoft.com/download/pr/18363.418.191007-0143.19h2_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x64FRE_en-us.iso", + "Checksum": "B3CD4BAE54E74F1CA497216A3F347CA7", "CustomData": { "WindowsOptionalFeature": ["NetFx3"], "CustomBootstrap": [ @@ -736,14 +736,14 @@ }, { "Id": "WIN10_x86_Enterprise_EN_Eval", - "Filename": "WIN10_x86_ENT_19H1_EN_Eval.iso", - "Description": "Windows 10 32bit Enterprise 1903 English Evaluation", + "Filename": "WIN10_x86_ENT_19H2_EN_Eval.iso", + "Description": "Windows 10 32bit Enterprise 1909 English Evaluation", "Architecture": "x86", "ImageName": "Windows 10 Enterprise Evaluation", "MediaType": "ISO", "OperatingSystem": "Windows", - "Uri": "https://software-download.microsoft.com/download/pr/18362.30.190401-1528.19h1_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x86FRE_en-us.iso", - "Checksum": "1945092B75613D7822B82D3AFB378F1D", + "Uri": "https://software-download.microsoft.com/download/pr/18363.418.191007-0143.19h2_release_svc_refresh_CLIENTENTERPRISEEVAL_OEMRET_x86FRE_en-us.iso", + "Checksum": "B700FF7684BE392F266B22289CA75979", "CustomData": { "WindowsOptionalFeature": ["NetFx3"], "CustomBootstrap": [ diff --git a/Config/VMDefaults.json b/Config/VMDefaults.json index 21761467..5198a672 100644 --- a/Config/VMDefaults.json +++ b/Config/VMDefaults.json @@ -4,7 +4,7 @@ "MaximumMemory": 1099511627776, "ProcessorCount": 1, "SwitchName": "Default Switch", - "Media": "2012R2_x64_Standard_EN_Eval", + "Media": "2019_x64_Standard_EN_Eval", "TimeZone": "UTC", "UILanguage": "en-US", "SystemLocale": "en-US", diff --git a/Lability.psm1 b/Lability.psm1 index e05acf9e..b1ac0aa7 100644 --- a/Lability.psm1 +++ b/Lability.psm1 @@ -33,21 +33,23 @@ Import-LocalizedData @importLocalizedDataParams; $moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; $moduleSrcPath = Join-Path -Path $moduleRoot -ChildPath 'Src'; Get-ChildItem -Path $moduleSrcPath -Include *.ps1 -Exclude '*.Tests.ps1' -Recurse | -ForEach-Object { - Write-Verbose -Message ('Importing library\source file ''{0}''.' -f $_.FullName); - ## https://becomelotr.wordpress.com/2017/02/13/expensive-dot-sourcing/ - . ([System.Management.Automation.ScriptBlock]::Create( - [System.IO.File]::ReadAllText($_.FullName) - )); -} + ForEach-Object { + Write-Verbose -Message ('Importing library\source file ''{0}''.' -f $_.FullName); + # https://becomelotr.wordpress.com/2017/02/13/expensive-dot-sourcing/ + . ([System.Management.Automation.ScriptBlock]::Create( + [System.IO.File]::ReadAllText($_.FullName) + )); + } ## Deploy builtin certificates to %ALLUSERSPROFILE%\PSLab $moduleConfigPath = Join-Path -Path $moduleRoot -ChildPath 'Config'; $allUsersConfigPath = Join-Path -Path $env:AllUsersProfile -ChildPath "$($labDefaults.ModuleName)\Certificates\"; [ref] $null = New-Directory -Path $allUsersConfigPath; Get-ChildItem -Path $moduleConfigPath -Include *.cer, *.pfx -Recurse | ForEach-Object { - Write-Verbose -Message ('Updating certificate ''{0}''.' -f $_.FullName); - Copy-Item -Path $_ -Destination $allUsersConfigPath; + if (-not (Test-Path -Path (Join-Path -Path $allUsersConfigPath -ChildPath $_.Name))) { + Write-Verbose -Message ('Updating certificate ''{0}''.' -f $_.FullName); + Copy-Item -Path $_ -Destination $allUsersConfigPath; + } } ## Create the credential check scriptblock diff --git a/Src/Public/Get-LabMedia.ps1 b/Src/Public/Get-LabMedia.ps1 index 20597f12..20a150bb 100644 --- a/Src/Public/Get-LabMedia.ps1 +++ b/Src/Public/Get-LabMedia.ps1 @@ -9,7 +9,25 @@ function Get-LabMedia { .PARAMETER CustomOnly Specifies that only registered custom media are returned. .PARAMETER Legacy - Specifies that legacy Windows 10 evaluation media definition(s) are returned. + Specifies that legacy evaluation media definition(s) are returned. These deprecated media definitions can be + reregistered using the Register-LabMedia cmdlet with the -Legacy switch. + .EXAMPLE + Get-LabMedia -Legacy + + Id Arch Media Description + -- ---- ----- ----------- + WIN10_x64_Enterprise_1709_EN_Eval x64 ISO Windows 10 64bit Enterprise 1709 English Evaluation + WIN10_x64_Enterprise_1803_EN_Eval x64 ISO Windows 10 64bit Enterprise 1804 English Evaluation + WIN10_x64_Enterprise_1809_EN_Eval x64 ISO Windows 10 64bit Enterprise 1809 English Evaluation + WIN10_x64_Enterprise_1903_EN_Eval x64 ISO Windows 10 64bit Enterprise 1903 English Evaluation + WIN10_x64_Enterprise_LTSB_2016_EN_Eval x64 ISO Windows 10 64bit Enterprise LTSB 2016 English Evaluation + WIN10_x86_Enterprise_1709_EN_Eval x86 ISO Windows 10 32bit Enterprise 1709 English Evaluation + WIN10_x86_Enterprise_1803_EN_Eval x86 ISO Windows 10 32bit Enterprise 1804 English Evaluation + WIN10_x86_Enterprise_1809_EN_Eval x86 ISO Windows 10 32bit Enterprise 1809 English Evaluation + WIN10_x86_Enterprise_1903_EN_Eval x86 ISO Windows 10 32bit Enterprise 1903 English Evaluation + WIN10_x86_Enterprise_LTSB_2016_EN_Eval x86 ISO Windows 10 32bit Enterprise LTSB 2016 English Evaluation + + Returns deprecated/previous media definitions. #> [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([System.Management.Automation.PSCustomObject])] diff --git a/Src/Public/Register-LabMedia.ps1 b/Src/Public/Register-LabMedia.ps1 index 77f05aa4..9025992a 100644 --- a/Src/Public/Register-LabMedia.ps1 +++ b/Src/Public/Register-LabMedia.ps1 @@ -10,6 +10,10 @@ function Register-LabMedia { To override a built-in media entry, specify the same media Id with the -Force switch. .PARAMETER Legacy Specifies registering a legacy Windows 10 media as custom media. + .EXAMPLE + Register-LabMedia -Legacy WIN10_x64_Enterprise_1809_EN_Eval + + Reregisters the deprecated Windows 10 Enterprise x64 English evaluation media. .LINK Get-LabMedia Unregister-LabMedia From 71497014b4200691e09cc943d343b60c50044056 Mon Sep 17 00:00:00 2001 From: Iain Brighton Date: Mon, 3 Feb 2020 20:23:00 +0000 Subject: [PATCH 10/11] Fixes json document formatting trailing line error --- Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json | 2 +- Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json b/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json index 3fc62b47..2a05bb46 100644 --- a/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json +++ b/Config/LegacyMedia/WIN10_x64_Enterprise_1903_EN_Eval.json @@ -22,4 +22,4 @@ "MinimumDismVersion": "10.0.0.0" }, "Hotfixes": [] -} \ No newline at end of file +} diff --git a/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json b/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json index 4739b693..4e24ef4b 100644 --- a/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json +++ b/Config/LegacyMedia/WIN10_x86_Enterprise_1903_EN_Eval.json @@ -22,4 +22,4 @@ "MinimumDismVersion": "10.0.0.0" }, "Hotfixes": [] -} \ No newline at end of file +} From a3600715afd608ea1fa14f44b2f77c52ff66a725 Mon Sep 17 00:00:00 2001 From: Iain Brighton Date: Mon, 3 Feb 2020 20:31:00 +0000 Subject: [PATCH 11/11] Releasing v0.19.0 --- ChangeLog.md | 2 +- Lability.psd1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 0ea1b1e5..123ae54f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,7 +2,7 @@ ## Versions ## -### Unreleased ### +### v0.19.0 ### * Removes extraneous output when unmounting ISOs (Windows 10 1903 only?) * Adds `Export-LabImage` cmdlet to export VHD(X) master/parent images diff --git a/Lability.psd1 b/Lability.psd1 index c1154bfb..6b72c8b3 100644 --- a/Lability.psd1 +++ b/Lability.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'Lability.psm1'; - ModuleVersion = '0.18.0'; + ModuleVersion = '0.19.0'; GUID = '374126b4-f3d4-471d-b25e-767f69ee03d0'; Author = 'Iain Brighton'; CompanyName = 'Virtual Engine';