From b7bfe9ab118140f34579cd1df04178104c1476dd Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sun, 17 Nov 2019 15:01:38 +1300 Subject: [PATCH 01/14] Fix DHCP DSC Library examples and correct DSC Library styles --- CHANGELOG.md | 37 +++ src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 | 77 +++--- src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 | 80 +++--- src/dsclibrary/DC_SECONDARY.DSC.ps1 | 73 +++--- src/dsclibrary/MEMBER_ADFS.DSC.ps1 | 35 +-- src/dsclibrary/MEMBER_ADRMS.DSC.ps1 | 35 +-- .../MEMBER_BRANCHCACHE_HOST.DSC.ps1 | 27 +- src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 | 38 +-- src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 | 25 +- src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 | 140 +++++----- src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 | 40 ++- src/dsclibrary/MEMBER_DHCP.DSC.ps1 | 91 ++++--- src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 | 108 ++++---- src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 | 96 +++---- src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 | 95 +++---- src/dsclibrary/MEMBER_DNS.DSC.ps1 | 38 ++- src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 | 32 ++- .../MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 | 52 ++-- .../MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 | 144 +++++----- .../MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 | 81 +++--- src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 | 130 +++++----- .../MEMBER_FILESERVER_FSRMTEST.DSC.ps1 | 127 ++++----- .../MEMBER_FILESERVER_ISCSI.DSC.ps1 | 97 +++---- src/dsclibrary/MEMBER_IPAM.DSC.ps1 | 30 +-- src/dsclibrary/MEMBER_JENKINS.DSC.ps1 | 40 ++- src/dsclibrary/MEMBER_NANO.DSC.ps1 | 18 +- src/dsclibrary/MEMBER_NLB.DSC.ps1 | 32 ++- src/dsclibrary/MEMBER_NPS.DSC.ps1 | 36 ++- src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 | 74 +++--- src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 | 30 +-- .../MEMBER_REMOTEACCESS_WAP.DSC.ps1 | 36 ++- src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 | 19 +- src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 | 29 ++- src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 | 31 +-- src/dsclibrary/MEMBER_SUBCA.DSC.ps1 | 245 ++++++++++-------- src/dsclibrary/MEMBER_WDS.DSC.ps1 | 13 +- src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 | 85 +++--- src/dsclibrary/MEMBER_WSUS.DSC.ps1 | 33 ++- src/dsclibrary/RODC_SECONDARY.DSC.ps1 | 49 ++-- src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 | 6 +- src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 | 32 +-- src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 | 30 +-- src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 | 20 +- src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 | 9 +- .../STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 | 9 +- .../xCertAuthorityServer.DSC.Schema.psm1 | 8 +- .../MyDSCResources/xDC/xDC.DSC.Schema.psm1 | 43 +-- .../xDHCPServer/xDHCPServer.DSC.Schema.psm1 | 2 +- .../xFileServer/xFileServer.DSC.Schema.psm1 | 18 +- .../xJoinDomain/xJoinDomain.DSC.Schema.psm1 | 4 +- .../xNPSServer/xNPSServer.DSC.Schema.psm1 | 38 +-- .../xRemoteAccessServer.DSC.Schema.psm1 | 28 +- .../dsclibrary/PesterTest.DSC.ps1 | 4 +- .../expectedcontent/ExpectedDSCConfig.txt | 4 +- 54 files changed, 1453 insertions(+), 1300 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7085ad1..256c2348 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,42 @@ # Change Log +## Unreleased + +- Convert all DSC configurations to use ComputerManagementDsc version + 7.1.0.0. +- Clean up code style on all DSC Library files. +- `dsclibrary\DC_FORESTCHILDDOMAIN.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. +- `dsclibrary\DC_FORESTPRIMARY.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. +- `dsclibrary\DC_FORESTSECONDARY.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. +- `dsclibrary\MEMBER_DHCP.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. + - Correct DHCP scope example. +- `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. + - Correct DHCP scope example. +- `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. + - Correct DHCP scope example. +- `dsclibrary\MEMBER_DNS.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. +- `dsclibrary\STNADALONE_DHCPDNS.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. + - Correct DHCP scope example. +- `dsclibrary\STNADALONE_INTERNET.DSC.ps1`: + - Convert to use xDnsServer version 1.16.0.0. + - Clean up code style. + ## 1.0.5.104 - Samples\Sample_WS2019_AzureADConnect.xml: Added sample for installing Azure AD diff --git a/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 b/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 index 41fa46e1..cb4a305a 100644 --- a/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 +++ b/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 @@ -8,9 +8,9 @@ DSC Template Configuration File For use by LabBuilder Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional configuration of the DNS Server. .Parameters: - ParentDomainName = "LABBUILDER.COM" - DomainName = "DEV" - DomainAdminPassword = "P@ssword!1" + ParentDomainName = 'LABBUILDER.COM' + DomainName = 'DEV' + DomainAdminPassword = 'P@ssword!1' PSDscAllowDomainUser = $true InstallRSATTools = $true Forwarders = @('8.8.8.8','8.8.4.4') @@ -30,64 +30,69 @@ DSC Template Configuration File For use by LabBuilder Configuration DC_FORESTCHILDDOMAIN { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.ParentDomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.ParentDomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature BackupInstall { - Ensure = "Present" - Name = "Windows-Server-Backup" + Ensure = 'Present' + Name = 'Windows-Server-Backup' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } WindowsFeature ADDSInstall { - Ensure = "Present" - Name = "AD-Domain-Services" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' } WindowsFeature RSAT-AD-PowerShellInstall { - Ensure = "Present" - Name = "RSAT-AD-PowerShell" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-AD-Tools", "RSAT-DNS-Server" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' } } WaitForADDomain DscDomainWait { - DomainName = $Node.ParentDomainName - Credential = $DomainAdminCredential - WaitTimeout = 300 - RestartCount = 5 - DependsOn = "[WindowsFeature]ADDSInstall" + DomainName = $Node.ParentDomainName + Credential = $DomainAdminCredential + WaitTimeout = 300 + RestartCount = 5 + DependsOn = '[WindowsFeature]ADDSInstall' } ADDomain PrimaryDC @@ -96,7 +101,7 @@ Configuration DC_FORESTCHILDDOMAIN ParentDomainName = $Node.ParentDomainName Credential = $DomainAdminCredential SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } # DNS Server Settings @@ -106,33 +111,35 @@ Configuration DC_FORESTCHILDDOMAIN { IsSingleInstance = 'Yes' IPAddresses = $Node.Forwarders - DependsOn = "[ADDomain]PrimaryDC" + DependsOn = '[ADDomain]PrimaryDC' } } - $Count=0 + + $count = 0 foreach ($ADZone in $Node.ADZones) { - $Count++ - xDnsServerADZone "ADZone$Count" + $count++ + xDnsServerADZone "ADZone$count" { Ensure = 'Present' Name = $ADZone.Name DynamicUpdate = $ADZone.DynamicUpdate ReplicationScope = $ADZone.ReplicationScope - DependsOn = "[ADDomain]PrimaryDC" + DependsOn = '[ADDomain]PrimaryDC' } } - $Count=0 + + $count = 0 foreach ($PrimaryZone in $Node.PrimaryZones) { - $Count++ - xDnsServerPrimaryZone "PrimaryZone$Count" + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" { Ensure = 'Present' Name = $PrimaryZone.Name ZoneFile = $PrimaryZone.ZoneFile DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = "[ADDomain]PrimaryDC" + DependsOn = '[ADDomain]PrimaryDC' } } } diff --git a/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 b/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 index 75313a7d..06156eef 100644 --- a/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 +++ b/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 @@ -10,9 +10,9 @@ DSC Template Configuration File For use by LabBuilder Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional configuration of the DNS Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainNetBiosName = "LABBUILDER" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainNetBiosName = 'LABBUILDER' + DomainAdminPassword = 'P@ssword!1' InstallRSATTools = $true Forwarders = @('8.8.8.8','8.8.4.4') ADZones = @( @@ -31,54 +31,59 @@ DSC Template Configuration File For use by LabBuilder Configuration DC_FORESTPRIMARY { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature BackupInstall { - Ensure = "Present" - Name = "Windows-Server-Backup" + Ensure = 'Present' + Name = 'Windows-Server-Backup' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } WindowsFeature ADDSInstall { - Ensure = "Present" - Name = "AD-Domain-Services" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' } WindowsFeature RSAT-AD-PowerShellInstall { - Ensure = "Present" - Name = "RSAT-AD-PowerShell" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-AD-Tools", "RSAT-DNS-Server" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' } } @@ -90,7 +95,7 @@ Configuration DC_FORESTPRIMARY DomainNetBiosName = $Node.DomainNetBiosName Credential = $DomainAdminCredential SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = "[WindowsFeature]ADDSInstall" + DependsOn = '[WindowsFeature]ADDSInstall' } } else @@ -100,7 +105,7 @@ Configuration DC_FORESTPRIMARY DomainName = $Node.DomainName Credential = $DomainAdminCredential SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = "[WindowsFeature]ADDSInstall" + DependsOn = '[WindowsFeature]ADDSInstall' } } @@ -110,16 +115,16 @@ Configuration DC_FORESTPRIMARY Credential = $DomainAdminCredential WaitTimeout = 300 RestartCount = 5 - DependsOn = "[ADDomain]PrimaryDC" + DependsOn = '[ADDomain]PrimaryDC' } # Enable AD Recycle bin ADOptionalFeature RecycleBin { - FeatureName = "Recycle Bin Feature" + FeatureName = 'Recycle Bin Feature' EnterpriseAdministratorCredential = $DomainAdminCredential ForestFQDN = $Node.DomainName - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } # Install a KDS Root Key so we can create MSA/gMSA accounts @@ -135,7 +140,7 @@ Configuration DC_FORESTPRIMARY TestScript = { if (-not (Get-KDSRootKey)) { - Write-Verbose -Message "KDS Root Key Needs to be installed..." + Write-Verbose -Message 'KDS Root Key Needs to be installed...' Return $false } Return $true @@ -150,37 +155,38 @@ Configuration DC_FORESTPRIMARY { IsSingleInstance = 'Yes' IPAddresses = $Node.Forwarders - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } } - $Count = 0 + $count = 0 foreach ($ADZone in $Node.ADZones) { - $Count++ - xDnsServerADZone "ADZone$Count" + $count++ + xDnsServerADZone "ADZone$count" { Ensure = 'Present' Name = $ADZone.Name DynamicUpdate = $ADZone.DynamicUpdate ReplicationScope = $ADZone.ReplicationScope - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } } - $Count = 0 + $count = 0 foreach ($PrimaryZone in $Node.PrimaryZones) { - $Count++ - xDnsServerPrimaryZone "PrimaryZone$Count" + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" { Ensure = 'Present' Name = $PrimaryZone.Name ZoneFile = $PrimaryZone.ZoneFile DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } } + <# # Create a Reverse Lookup Zone xDnsServerPrimaryZone GlobalNamesZone @@ -205,7 +211,7 @@ Configuration DC_FORESTPRIMARY { PSDSCRunAsCredential = $DomainAdminCredential SetScript = { - Write-Verbose -Message "Enabling Global Name Zone..." + Write-Verbose -Message 'Enabling Global Name Zone...' Set-DNSServerGlobalNameZone -Enable } GetScript = { @@ -215,7 +221,7 @@ Configuration DC_FORESTPRIMARY } TestScript = { if (-not (Get-DNSServerGlobalNameZone).Enable) { - Write-Verbose -Message "Global Name Zone needs to be enabled..." + Write-Verbose -Message 'Global Name Zone needs to be enabled...' Return $false } Return $true diff --git a/src/dsclibrary/DC_SECONDARY.DSC.ps1 b/src/dsclibrary/DC_SECONDARY.DSC.ps1 index 7b923b6b..87459107 100644 --- a/src/dsclibrary/DC_SECONDARY.DSC.ps1 +++ b/src/dsclibrary/DC_SECONDARY.DSC.ps1 @@ -29,52 +29,59 @@ DSC Template Configuration File For use by LabBuilder Configuration DC_SECONDARY { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentName ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentName ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature BackupInstall { - Ensure = "Present" - Name = "Windows-Server-Backup" + Ensure = 'Present' + Name = 'Windows-Server-Backup' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } WindowsFeature ADDSInstall { - Ensure = "Present" - Name = "AD-Domain-Services" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' } WindowsFeature RSAT-AD-PowerShellInstall { - Ensure = "Present" - Name = "RSAT-AD-PowerShell" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-AD-Tools","RSAT-DNS-Server" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' } } @@ -84,7 +91,7 @@ Configuration DC_SECONDARY Credential = $DomainAdminCredential WaitTimeout = 300 RestartCount = 5 - DependsOn = "[WindowsFeature]ADDSInstall" + DependsOn = '[WindowsFeature]ADDSInstall' } ADDomainController SecondaryDC @@ -92,7 +99,7 @@ Configuration DC_SECONDARY DomainName = $Node.DomainName Credential = $DomainAdminCredential SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } # DNS Server Settings @@ -102,31 +109,33 @@ Configuration DC_SECONDARY { IsSingleInstance = 'Yes' IPAddresses = $Node.Forwarders - DependsOn = "[ADDomainController]SecondaryDC" + DependsOn = '[ADDomainController]SecondaryDC' } } - [System.Int32]$Count=0 - foreach ($ADZone in $Node.ADZones) { - $Count++ - xDnsServerADZone "ADZone$Count" + [System.Int32]$count = 0 + foreach ($ADZone in $Node.ADZones) + { + $count++ + xDnsServerADZone "ADZone$count" { Ensure = 'Present' Name = $ADZone.Name DynamicUpdate = $ADZone.DynamicUpdate ReplicationScope = $ADZone.ReplicationScope - DependsOn = "[ADDomainController]SecondaryDC" + DependsOn = '[ADDomainController]SecondaryDC' } } - [System.Int32]$Count=0 - foreach ($PrimaryZone in $Node.PrimaryZones) { - $Count++ - xDnsServerPrimaryZone "PrimaryZone$Count" + [System.Int32]$count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" { Ensure = 'Present' Name = $PrimaryZone.Name ZoneFile = $PrimaryZone.ZoneFile DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = "[ADDomainController]SecondaryDC" + DependsOn = '[ADDomainController]SecondaryDC' } } } diff --git a/src/dsclibrary/MEMBER_ADFS.DSC.ps1 b/src/dsclibrary/MEMBER_ADFS.DSC.ps1 index 3459e0e0..1e43ca36 100644 --- a/src/dsclibrary/MEMBER_ADFS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_ADFS.DSC.ps1 @@ -5,39 +5,44 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into an ADFS Server using WID. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_ADFS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature WIDInstall { - Ensure = "Present" - Name = "Windows-Internal-Database" + Ensure = 'Present' + Name = 'Windows-Internal-Database' } WindowsFeature ADFSInstall { - Ensure = "Present" - Name = "ADFS-Federation" - DependsOn = "[WindowsFeature]WIDInstall" + Ensure = 'Present' + Name = 'ADFS-Federation' + DependsOn = '[WindowsFeature]WIDInstall' } WaitForAll DC @@ -53,27 +58,27 @@ Configuration MEMBER_ADFS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # Enable ADFS FireWall rules Firewall ADFSFirewall1 { - Name = "ADFSSrv-HTTP-In-TCP" + Name = 'ADFSSrv-HTTP-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall ADFSFirewall2 { - Name = "ADFSSrv-HTTPS-In-TCP" + Name = 'ADFSSrv-HTTPS-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall ADFSFirewall3 { - Name = "ADFSSrv-SmartcardAuthN-HTTPS-In-TCP" + Name = 'ADFSSrv-SmartcardAuthN-HTTPS-In-TCP' Ensure = 'Present' Enabled = 'True' } diff --git a/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 b/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 index eda24d5b..018b7ad9 100644 --- a/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into an ADRMS Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ADFSSupport = $true @@ -14,40 +14,45 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_ADRMS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature WIDInstall { - Ensure = "Present" - Name = "Windows-Internal-Database" + Ensure = 'Present' + Name = 'Windows-Internal-Database' } WindowsFeature ADRMSServerInstall { - Ensure = "Present" - Name = "ADRMS-Server" - DependsOn = "[WindowsFeature]WIDInstall" + Ensure = 'Present' + Name = 'ADRMS-Server' + DependsOn = '[WindowsFeature]WIDInstall' } if ($Node.ADFSSupport) { WindowsFeature ADRMSIdentityInstall { - Ensure = "Present" - Name = "ADRMS-Identity" - DependsOn = "[WindowsFeature]ADRMSServerInstall" + Ensure = 'Present' + Name = 'ADRMS-Identity' + DependsOn = '[WindowsFeature]ADRMSServerInstall' } } @@ -64,7 +69,7 @@ Configuration MEMBER_ADRMS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 b/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 index 5710ced2..40cc46ee 100644 --- a/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 +++ b/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 @@ -5,16 +5,16 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into a BranchCache Hosted Mode Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_BRANCHCACHE_HOST { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc @@ -22,17 +22,22 @@ Configuration MEMBER_BRANCHCACHE_HOST # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature BranchCache { - Ensure = "Present" - Name = "BranchCache" + Ensure = 'Present' + Name = 'BranchCache' } # Wait for the Domain to be available so we can join it. @@ -50,20 +55,20 @@ Configuration MEMBER_BRANCHCACHE_HOST Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # Enable BranchCache Hosted Mode Firewall Fules Firewall FSRMFirewall1 { - Name = "Microsoft-Windows-PeerDist-HostedServer-In" + Name = 'Microsoft-Windows-PeerDist-HostedServer-In' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall2 { - Name = "Microsoft-Windows-PeerDist-HostedServer-Out" + Name = 'Microsoft-Windows-PeerDist-HostedServer-Out' Ensure = 'Present' Enabled = 'True' } diff --git a/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 b/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 index 9edb30f4..67f293fc 100644 --- a/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 +++ b/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 @@ -7,8 +7,8 @@ DSC Template Configuration File For use by LabBuilder This should only be used on a Windows Server 2016 RTM host. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> @@ -23,18 +23,22 @@ Configuration MEMBER_CONTAINER_HOST Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc - Import-DscResource -ModuleName xPendingReboot + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArguementList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArguementList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WaitForAll DC @@ -56,8 +60,8 @@ Configuration MEMBER_CONTAINER_HOST # Install containers feature WindowsFeature ContainerInstall { - Ensure = "Present" - Name = "Containers" + Ensure = 'Present' + Name = 'Containers' } # Download Docker Engine @@ -89,12 +93,14 @@ Configuration MEMBER_CONTAINER_HOST DependsOn = '[xArchive]DockerEngineExtract' } - # Reboot the system to complete Containers feature setup - # Perform this after setting the Environment variable - # so that PowerShell and other consoles can access it. - xPendingReboot Reboot + <# + Reboot the system to complete Containers feature setup + Perform this after setting the Environment variable + so that PowerShell and other consoles can access it. + #> + PendingReboot Reboot { - Name = "Reboot After Containers" + Name = 'Reboot After Containers' } # Install the Docker Daemon as a service @@ -119,8 +125,10 @@ Configuration MEMBER_CONTAINER_HOST DependsOn = '[xArchive]DockerEngineExtract' } - # Start up the Docker Service and ensure it is set - # to start up automatically. + <# + Start up the Docker Service and ensure it is set + to start up automatically. + #> xServiceSet DockerService { Ensure = 'Present' diff --git a/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 b/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 index 00ee790e..c15fb938 100644 --- a/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 @@ -5,24 +5,31 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_DEFAULT { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WaitForAll DC @@ -38,7 +45,7 @@ Configuration MEMBER_DEFAULT Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 b/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 index 2a7b1d08..bf2c1d8a 100644 --- a/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into a File Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true SpokeComputerName = @('Spoke1','Spoke2') @@ -18,146 +18,154 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_DFSHUB { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName DFSDsc Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FileServerInstall { - Ensure = "Present" - Name = "FS-FileServer" + Ensure = 'Present' + Name = 'FS-FileServer' } WindowsFeature DFSNameSpaceInstall { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]FileServerInstall" + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]FileServerInstall' } WindowsFeature DFSReplicationInstall { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' } WindowsFeature RSATDFSMgmtConInstall { Ensure = 'Present' - Name = 'RSAT-DFS-Mgmt-Con' + Name = 'RSAT-DFS-Mgmt-Con' } # Wait for the Domain to be available so we can join it. WaitForAll DC { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 } # Join this Server to the Domain Computer JoinDomain { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' } WaitforDisk Disk2 { - DiskId = 1 + DiskId = 1 RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = "[Computer]JoinDomain" + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' } Disk DVolume { - DiskId = 1 + DiskId = 1 DriveLetter = 'D' - DependsOn = "[WaitforDisk]Disk2" + DependsOn = '[WaitforDisk]Disk2' } WaitForAll WaitForAllSpokes { - ResourceName = "[Disk]DVolume" - NodeName = $Node.SpokeComputerName + ResourceName = '[Disk]DVolume' + NodeName = $Node.SpokeComputerName RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = "[Computer]JoinDomain" + RetryCount = 30 + DependsOn = '[Computer]JoinDomain' } # Configure the Replication Group DFSReplicationGroup RGWebSite { - GroupName = $Node.ResourceGroupName - Description = $Node.ResourceGroupDescription - Ensure = 'Present' - DomainName = $Node.DomainName - Members = @() + $Node.NodeName + $Node.SpokeComputerName - Folders = $Node.ResourceGroupFolderName + GroupName = $Node.ResourceGroupName + Description = $Node.ResourceGroupDescription + Ensure = 'Present' + DomainName = $Node.DomainName + Members = @() + $Node.NodeName + $Node.SpokeComputerName + Folders = $Node.ResourceGroupFolderName PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[Disk]DVolume' + DependsOn = '[Disk]DVolume' } # End of RGWebSite Resource DFSReplicationGroupFolder RGWebSiteFolder { - GroupName = $Node.ResourceGroupName - FolderName = $Node.ResourceGroupFolderName - DomainName = $Node.DomainName - Description = $Node.ResourceGroupDescription + GroupName = $Node.ResourceGroupName + FolderName = $Node.ResourceGroupFolderName + DomainName = $Node.DomainName + Description = $Node.ResourceGroupDescription PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[DFSReplicationGroup]RGWebSite' + DependsOn = '[DFSReplicationGroup]RGWebSite' } # End of RGWebSiteFolder Resource DFSReplicationGroupMembership RGWebSiteMembershipHub { - GroupName = $Node.ResourceGroupName - FolderName = $Node.ResourceGroupFolderName - DomainName = $Node.DomainName - ComputerName = $Node.NodeName - ContentPath = $Node.ResourceGroupContentPath - PrimaryMember = $true + GroupName = $Node.ResourceGroupName + FolderName = $Node.ResourceGroupFolderName + DomainName = $Node.DomainName + ComputerName = $Node.NodeName + ContentPath = $Node.ResourceGroupContentPath + PrimaryMember = $true PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' + DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' } # End of RGWebSiteMembershipHub Resource # Configure the connection and membership for each Spoke - foreach ($spoke in $Node.SpokeComputerName) { + foreach ($spoke in $Node.SpokeComputerName) + { DFSReplicationGroupConnection "RGWebSiteConnection$spoke" { - GroupName = $Node.ResourceGroupName - DomainName = $Node.DomainName - Ensure = 'Present' - SourceComputerName = $Node.NodeName + GroupName = $Node.ResourceGroupName + DomainName = $Node.DomainName + Ensure = 'Present' + SourceComputerName = $Node.NodeName DestinationComputerName = $spoke - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' } # End of RGWebSiteConnection$spoke Resource DFSReplicationGroupMembership "RGWebSiteMembership$spoke" { - GroupName = $Node.ResourceGroupName - FolderName = $Node.ResourceGroupFolderName - DomainName = $Node.DomainName - ComputerName = $spoke - ContentPath = $Node.ResourceGroupContentPath + GroupName = $Node.ResourceGroupName + FolderName = $Node.ResourceGroupFolderName + DomainName = $Node.DomainName + ComputerName = $spoke + ContentPath = $Node.ResourceGroupContentPath PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = "[DFSReplicationGroupConnection]RGWebSiteConnection$spoke" + DependsOn = "[DFSReplicationGroupConnection]RGWebSiteConnection$spoke" } # End of RGWebSiteMembership$spoke Resource } } diff --git a/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 b/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 index fa29598f..f2fc8cc8 100644 --- a/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 @@ -6,49 +6,47 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then made into a Spoke for a DFS Hub and Spoke replication group. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_DFSSPOKE { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName DFSDsc Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FileServerInstall { - Ensure = "Present" - Name = "FS-FileServer" + Ensure = 'Present' + Name = 'FS-FileServer' } WindowsFeature DFSNameSpaceInstall { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]FileServerInstall" + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]FileServerInstall' } WindowsFeature DFSReplicationInstall { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' } # Wait for the Domain to be available so we can join it. @@ -66,7 +64,7 @@ Configuration MEMBER_DFSSPOKE Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } WaitforDisk Disk2 @@ -74,14 +72,14 @@ Configuration MEMBER_DFSSPOKE DiskId = 1 RetryIntervalSec = 60 RetryCount = 60 - DependsOn = "[Computer]JoinDomain" + DependsOn = '[Computer]JoinDomain' } Disk DVolume { DiskId = 1 DriveLetter = 'D' - DependsOn = "[WaitforDisk]Disk2" + DependsOn = '[WaitforDisk]Disk2' } } } diff --git a/src/dsclibrary/MEMBER_DHCP.DSC.ps1 b/src/dsclibrary/MEMBER_DHCP.DSC.ps1 index 9d5fa0e9..b5038e38 100644 --- a/src/dsclibrary/MEMBER_DHCP.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DHCP.DSC.ps1 @@ -5,49 +5,56 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into a DHCP Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallRSATTools = $true Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' AddressFamily = 'IPv4' } ) Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; + @{ + Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' AddressFamily = 'IPv4' }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' AddressFamily = 'IPv4' } ) ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' AddressFamily = 'IPv4' } ) @@ -55,40 +62,38 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_DHCP { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" + Ensure = 'Present' + Name = 'DHCP' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-DHCP", "RSAT-DNS-Server" - DependsOn = "[WindowsFeature]DHCPInstall" + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]DHCPInstall' } } WaitForAll DC { - ResourceName = '[ADDomainPrimaryDC' + ResourceName = '[ADDomain]PrimaryDC' NodeName = $Node.DCname RetryIntervalSec = 15 RetryCount = 60 @@ -99,7 +104,7 @@ Configuration MEMBER_DHCP Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # DHCP Server Settings @@ -120,7 +125,7 @@ Configuration MEMBER_DHCP DependsOn = '[Computer]JoinDomain' } - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -138,7 +143,7 @@ Configuration MEMBER_DHCP } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -153,7 +158,7 @@ Configuration MEMBER_DHCP } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ diff --git a/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 b/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 index 11d5dbaa..130e2ea8 100644 --- a/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 @@ -5,99 +5,105 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into a DHCP Server and DNS Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallRSATTools = $true Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' AddressFamily = 'IPv4' } ) Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; + @{ + Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' AddressFamily = 'IPv4' }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' AddressFamily = 'IPv4' } ) ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' AddressFamily = 'IPv4' } ) Forwarders = @('8.8.8.8','8.8.4.4') PrimaryZones = @( - @{ Name = 'BRAVO.LOCAL'; - ZoneFile = 'bravo.local.dns'; - DynamicUpdate = 'None'; + @{ + Name = 'BRAVO.LOCAL' + ZoneFile = 'bravo.local.dns' + DynamicUpdate = 'None' } ) ###################################################################################################> Configuration MEMBER_DHCPDNS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" + Ensure = 'Present' + Name = 'DHCP' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" - DependsOn = "[WindowsFeature]DHCPInstall" + Ensure = 'Present' + Name = 'DNS' + DependsOn = '[WindowsFeature]DHCPInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-DHCP", "RSAT-DNS-Server" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]DNSInstall' } } @@ -114,7 +120,7 @@ Configuration MEMBER_DHCPDNS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # DHCP Server Settings @@ -135,7 +141,7 @@ Configuration MEMBER_DHCPDNS DependsOn = '[Computer]JoinDomain' } - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -153,7 +159,7 @@ Configuration MEMBER_DHCPDNS } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -168,7 +174,7 @@ Configuration MEMBER_DHCPDNS } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ @@ -190,12 +196,11 @@ Configuration MEMBER_DHCPDNS { IsSingleInstance = 'Yes' IPAddresses = $Node.Forwarders - Credential = $DomainAdminCredential DependsOn = '[Computer]JoinDomain' } } - $count=0 + $count = 0 foreach ($PrimaryZone in $Node.PrimaryZones) { $count++ @@ -205,7 +210,6 @@ Configuration MEMBER_DHCPDNS Name = $PrimaryZone.Name ZoneFile = $PrimaryZone.ZoneFile DynamicUpdate = $PrimaryZone.DynamicUpdate - Credential = $DomainAdminCredential DependsOn = '[Computer]JoinDomain' } } diff --git a/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 b/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 index 8649f7d1..d10f6240 100644 --- a/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 @@ -9,49 +9,55 @@ DSC Template Configuration File For use by LabBuilder .Notes NPAS requires a full server install, so ensure that this OS is not a Core version. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallRSATTools = $true Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' AddressFamily = 'IPv4' } ) Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; + @{ Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' AddressFamily = 'IPv4' }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' AddressFamily = 'IPv4' } ) ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' AddressFamily = 'IPv4' } ) @@ -59,47 +65,45 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_DHCPNPAS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature NPASPolicyServerInstall { - Ensure = "Present" - Name = "NPAS-Policy-Server" + Ensure = 'Present' + Name = 'NPAS-Policy-Server' } WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + Ensure = 'Present' + Name = 'DHCP' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-DHCP", "RSAT-NPAS" - DependsOn = "[WindowsFeature]DHCPInstall" + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-NPAS' + DependsOn = '[WindowsFeature]DHCPInstall' } } WaitForAll DC { - ResourceName = '[ADDomainPrimaryDC' + ResourceName = '[ADDomain]PrimaryDC' NodeName = $Node.DCname RetryIntervalSec = 15 RetryCount = 60 @@ -110,7 +114,7 @@ Configuration MEMBER_DHCPNPAS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # DHCP Server Settings @@ -131,7 +135,7 @@ Configuration MEMBER_DHCPNPAS DependsOn = '[Computer]JoinDomain' } - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -149,7 +153,7 @@ Configuration MEMBER_DHCPNPAS } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -164,7 +168,7 @@ Configuration MEMBER_DHCPNPAS } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ diff --git a/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 b/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 index 46d00bd8..ab6d5ce9 100644 --- a/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 @@ -9,49 +9,56 @@ DSC Template Configuration File For use by LabBuilder .Notes NPAS requires a full server install, so ensure that this OS is not a Core version. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallRSATTools = $true Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' AddressFamily = 'IPv4' } ) Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' AddressFamily = 'IPv4' }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; + @{ + Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' AddressFamily = 'IPv4' }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' AddressFamily = 'IPv4' } ) ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' AddressFamily = 'IPv4' } ) @@ -59,41 +66,39 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_DHCPNPAS2016 { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature NPASPolicyServerInstall { - Ensure = "Present" - Name = "NPAS" + Ensure = 'Present' + Name = 'NPAS' } WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + Ensure = 'Present' + Name = 'DHCP' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-DHCP", "RSAT-NPAS" - DependsOn = "[WindowsFeature]DHCPInstall" + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-NPAS' + DependsOn = '[WindowsFeature]DHCPInstall' } } @@ -110,7 +115,7 @@ Configuration MEMBER_DHCPNPAS2016 Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # DHCP Server Settings @@ -131,7 +136,7 @@ Configuration MEMBER_DHCPNPAS2016 DependsOn = '[Computer]JoinDomain' } - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -149,7 +154,7 @@ Configuration MEMBER_DHCPNPAS2016 } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -164,7 +169,7 @@ Configuration MEMBER_DHCPNPAS2016 } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ diff --git a/src/dsclibrary/MEMBER_DNS.DSC.ps1 b/src/dsclibrary/MEMBER_DNS.DSC.ps1 index da1400c7..c3a95178 100644 --- a/src/dsclibrary/MEMBER_DNS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DNS.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into a DNS Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallRSATTools = $true @@ -21,34 +21,32 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_DNS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-DNS-Server" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]DNSInstall' } } @@ -75,21 +73,19 @@ Configuration MEMBER_DNS { IsSingleInstance = 'Yes' IPAddresses = $Node.Forwarders - Credential = $DomainAdminCredential DependsOn = '[Computer]JoinDomain' } } - $Count=0 + $count = 0 foreach ($PrimaryZone in $Node.PrimaryZones) { - $Count++ - xDnsServerPrimaryZone "PrimaryZone$Count" + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" { Ensure = 'Present' Name = $PrimaryZone.Name ZoneFile = $PrimaryZone.ZoneFile DynamicUpdate = $PrimaryZone.DynamicUpdate - Credential = $DomainAdminCredential DependsOn = '[Computer]JoinDomain' } } diff --git a/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 b/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 index 1dc0b15c..db1c252f 100644 --- a/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 +++ b/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 @@ -5,12 +5,12 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into an DSC Pull Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true Port = 8080 - PhysicalPath = "D:\inetpub\PSDSCPullServer" + PhysicalPath = 'D:\inetpub\PSDSCPullServer' # Set to a valid certificate thumbprint to allow HTTP traffic CertificateThumbprint = 'AllowUnencryptedTraffic' RegistrationKey = '140a952b-b9d6-406b-b416-e0f759c9c0e4' @@ -19,36 +19,34 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_DSCPULLSERVER { Import-DSCResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xWebAdministration Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature IISInstall { - Ensure = "Present" - Name = "Web-Server" + Ensure = 'Present' + Name = 'Web-Server' } WindowsFeature AspNet45Install { - Ensure = "Present" - Name = "Web-Asp-Net45" + Ensure = 'Present' + Name = 'Web-Asp-Net45' } WindowsFeature WebMgmtServiceInstall { - Ensure = "Present" - Name = "Web-Mgmt-Service" + Ensure = 'Present' + Name = 'Web-Mgmt-Service' } WindowsFeature DSCServiceFeature @@ -70,7 +68,7 @@ Configuration MEMBER_DSCPULLSERVER Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } xDscWebService PSDSCPullServer diff --git a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 index 5384e133..b6cdc93b 100644 --- a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 +++ b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 @@ -6,8 +6,8 @@ DSC Template Configuration File For use by LabBuilder Builds a Network failover clustering node for use as a DHCP Server. It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ISCSIServerName = 'SA-FS1' @@ -59,40 +59,38 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_FAILOVERCLUSTER_FS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xPSDesiredStateConfiguration Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FailoverClusteringInstall { - Ensure = "Present" - Name = "Failover-Clustering" + Ensure = 'Present' + Name = 'Failover-Clustering' } WindowsFeature FailoverClusteringPSInstall { - Ensure = "Present" - Name = "RSAT-Clustering-PowerShell" - DependsOn = "[WindowsFeature]FailoverClusteringInstall" + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + DependsOn = '[WindowsFeature]FailoverClusteringInstall' } WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" - DependsOn = "[WindowsFeature]FailoverClusteringPSInstall" + Ensure = 'Present' + Name = 'DHCP' + DependsOn = '[WindowsFeature]FailoverClusteringPSInstall' } # Wait for the Domain to be available so we can join it. @@ -110,7 +108,7 @@ Configuration MEMBER_FAILOVERCLUSTER_FS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } if ($Node.ServerTargetName) @@ -126,11 +124,11 @@ Configuration MEMBER_FAILOVERCLUSTER_FS # Wait for the iSCSI Server Target to become available WaitForAny WaitForiSCSIServerTarget { - ResourceName = "[ISCSIServerTarget]ClusterServerTarget" + ResourceName = '[ISCSIServerTarget]ClusterServerTarget' NodeName = $Node.ServerName RetryIntervalSec = 30 RetryCount = 30 - DependsOn = "[Service]iSCSIService" + DependsOn = '[Service]iSCSIService' } # Connect the Initiator @@ -141,19 +139,19 @@ Configuration MEMBER_FAILOVERCLUSTER_FS TargetPortalAddress = $Node.TargetPortalAddress InitiatorPortalAddress = $Node.InitiatorPortalAddress IsPersistent = $true - DependsOn = "[WaitForAny]WaitForiSCSIServerTarget" + DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' } # End of ISCSITarget Resource # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS Firewall iSCSIFirewallIn { - Name = "MsiScsi-In-TCP" + Name = 'MsiScsi-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall iSCSIFirewallOut { - Name = "MsiScsi-Out-TCP" + Name = 'MsiScsi-Out-TCP' Ensure = 'Present' Enabled = 'True' } @@ -177,7 +175,7 @@ Configuration MEMBER_FAILOVERCLUSTER_FS DependsOn = '[Computer]JoinDomain' } - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -195,7 +193,7 @@ Configuration MEMBER_FAILOVERCLUSTER_FS } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -210,7 +208,7 @@ Configuration MEMBER_FAILOVERCLUSTER_FS } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ diff --git a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 index 850b599d..5c055007 100644 --- a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 @@ -6,8 +6,8 @@ DSC Template Configuration File For use by LabBuilder Builds a Network failover clustering node for use as a File Server. It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ServerName = 'SA-FS1' @@ -18,91 +18,91 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_FAILOVERCLUSTER_FS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xPSDesiredStateConfiguration Import-DscResource -ModuleName ISCSIDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FailoverClusteringInstall { - Ensure = "Present" - Name = "Failover-Clustering" + Ensure = 'Present' + Name = 'Failover-Clustering' } WindowsFeature FailoverClusteringPSInstall { - Ensure = "Present" - Name = "RSAT-Clustering-PowerShell" - DependsOn = "[WindowsFeature]FailoverClusteringInstall" + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + DependsOn = '[WindowsFeature]FailoverClusteringInstall' } WindowsFeature FileServerInstall { - Ensure = "Present" - Name = "FS-FileServer" - DependsOn = "[WindowsFeature]FailoverClusteringPSInstall" + Ensure = 'Present' + Name = 'FS-FileServer' + DependsOn = '[WindowsFeature]FailoverClusteringPSInstall' } WindowsFeature DataDedupInstall { - Ensure = "Present" - Name = "FS-Data-Deduplication" - DependsOn = "[WindowsFeature]FileServerInstall" + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' } WindowsFeature BranchCacheInstall { - Ensure = "Present" - Name = "FS-BranchCache" - DependsOn = "[WindowsFeature]DataDedupInstall" + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' } WindowsFeature DFSNameSpaceInstall { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]BranchCacheInstall" + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' } WindowsFeature DFSReplicationInstall { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' } WindowsFeature FSResourceManagerInstall { - Ensure = "Present" - Name = "FS-Resource-Manager" - DependsOn = "[WindowsFeature]DFSReplicationInstall" + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' } # Wait for the Domain to be available so we can join it. WaitForAll DC { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 } # Join this Server to the Domain so that it can be an Enterprise CA. Computer JoinDomain { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' } if ($Node.ServerTargetName) @@ -110,43 +110,43 @@ Configuration MEMBER_FAILOVERCLUSTER_FS # Ensure the iSCSI Initiator service is running Service iSCSIService { - Name = 'MSiSCSI' + Name = 'MSiSCSI' StartupType = 'Automatic' - State = 'Running' + State = 'Running' } # Wait for the iSCSI Server Target to become available WaitForAny WaitForiSCSIServerTarget { - ResourceName = "[ISCSIServerTarget]ClusterServerTarget" - NodeName = $Node.ServerName + ResourceName = '[ISCSIServerTarget]ClusterServerTarget' + NodeName = $Node.ServerName RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = "[Service]iSCSIService" + RetryCount = 30 + DependsOn = '[Service]iSCSIService' } # Connect the Initiator ISCSIInitiator iSCSIInitiator { - Ensure = 'Present' - NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" - TargetPortalAddress = $Node.TargetPortalAddress + Ensure = 'Present' + NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" + TargetPortalAddress = $Node.TargetPortalAddress InitiatorPortalAddress = $Node.InitiatorPortalAddress - IsPersistent = $true - DependsOn = "[WaitForAny]WaitForiSCSIServerTarget" + IsPersistent = $true + DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' } # End of ISCSITarget Resource # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS Firewall iSCSIFirewallIn { - Name = "MsiScsi-In-TCP" - Ensure = 'Present' + Name = 'MsiScsi-In-TCP' + Ensure = 'Present' Enabled = 'True' } Firewall iSCSIFirewallOut { - Name = "MsiScsi-Out-TCP" - Ensure = 'Present' + Name = 'MsiScsi-Out-TCP' + Ensure = 'Present' Enabled = 'True' } } @@ -154,57 +154,57 @@ Configuration MEMBER_FAILOVERCLUSTER_FS # Enable FSRM FireWall rules so we can remote manage FSRM Firewall FSRMFirewall1 { - Name = "FSRM-WMI-ASYNC-In-TCP" - Ensure = 'Present' + Name = 'FSRM-WMI-ASYNC-In-TCP' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall2 { - Name = "FSRM-WMI-WINMGMT-In-TCP" - Ensure = 'Present' + Name = 'FSRM-WMI-WINMGMT-In-TCP' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall3 { - Name = "FSRM-RemoteRegistry-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-RemoteRegistry-In (RPC)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall4 { - Name = "FSRM-Task-Scheduler-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-Task-Scheduler-In (RPC)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall5 { - Name = "FSRM-SrmReports-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-SrmReports-In (RPC)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall6 { - Name = "FSRM-RpcSs-In (RPC-EPMAP)" - Ensure = 'Present' + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall7 { - Name = "FSRM-System-In (TCP-445)" - Ensure = 'Present' + Name = 'FSRM-System-In (TCP-445)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall8 { - Name = "FSRM-SrmSvc-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-SrmSvc-In (RPC)' + Ensure = 'Present' Enabled = 'True' } } diff --git a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 index 7ebccc33..3463cd87 100644 --- a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 +++ b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 @@ -6,8 +6,8 @@ DSC Template Configuration File For use by LabBuilder Builds a Network failover clustering node Hyper-V. It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ISCSIServerName = 'SA-FS1' @@ -18,53 +18,60 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_FAILOVERCLUSTER_HV { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xPSDesiredStateConfiguration Node $AllNodes.NodeName { # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FailoverClusteringInstall { - Ensure = "Present" - Name = "Failover-Clustering" + Ensure = 'Present' + Name = 'Failover-Clustering' } WindowsFeature FailoverClusteringPSInstall { - Ensure = "Present" - Name = "RSAT-Clustering-PowerShell" + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' } WindowsFeature InstallHyperV { - Ensure = "Present" - Name = "Hyper-V" + Ensure = 'Present' + Name = 'Hyper-V' } # Wait for the Domain to be available so we can join it. WaitForAll DC { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 } # Join this Server to the Domain so that it can be an Enterprise CA. Computer JoinDomain { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' } if ($Node.ServerTargetName) @@ -72,43 +79,43 @@ Configuration MEMBER_FAILOVERCLUSTER_HV # Ensure the iSCSI Initiator service is running Service iSCSIService { - Name = 'MSiSCSI' + Name = 'MSiSCSI' StartupType = 'Automatic' - State = 'Running' + State = 'Running' } # Wait for the iSCSI Server Target to become available WaitForAny WaitForiSCSIServerTarget { - ResourceName = "[ISCSIServerTarget]ClusterServerTarget" - NodeName = $Node.ServerName + ResourceName = '[ISCSIServerTarget]ClusterServerTarget' + NodeName = $Node.ServerName RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = "[Service]iSCSIService" + RetryCount = 30 + DependsOn = '[Service]iSCSIService' } # Connect the Initiator ISCSIInitiator iSCSIInitiator { - Ensure = 'Present' - NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" - TargetPortalAddress = $Node.TargetPortalAddress + Ensure = 'Present' + NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" + TargetPortalAddress = $Node.TargetPortalAddress InitiatorPortalAddress = $Node.InitiatorPortalAddress - IsPersistent = $true - DependsOn = "[WaitForAny]WaitForiSCSIServerTarget" + IsPersistent = $true + DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' } # End of ISCSITarget Resource # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS Firewall iSCSIFirewallIn { - Name = "MsiScsi-In-TCP" - Ensure = 'Present' + Name = 'MsiScsi-In-TCP' + Ensure = 'Present' Enabled = 'True' } Firewall iSCSIFirewallOut { - Name = "MsiScsi-Out-TCP" - Ensure = 'Present' + Name = 'MsiScsi-Out-TCP' + Ensure = 'Present' Enabled = 'True' } } diff --git a/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 b/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 index deeba5f4..a2222ea9 100644 --- a/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 +++ b/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 @@ -5,178 +5,178 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into a File Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_FILESERVER { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FileServerInstall { - Ensure = "Present" - Name = "FS-FileServer" + Ensure = 'Present' + Name = 'FS-FileServer' } WindowsFeature DataDedupInstall { - Ensure = "Present" - Name = "FS-Data-Deduplication" - DependsOn = "[WindowsFeature]FileServerInstall" + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' } WindowsFeature BranchCacheInstall { - Ensure = "Present" - Name = "FS-BranchCache" - DependsOn = "[WindowsFeature]DataDedupInstall" + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' } WindowsFeature DFSNameSpaceInstall { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]BranchCacheInstall" + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' } WindowsFeature DFSReplicationInstall { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' } WindowsFeature FSResourceManagerInstall { - Ensure = "Present" - Name = "FS-Resource-Manager" - DependsOn = "[WindowsFeature]DFSReplicationInstall" + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' } WindowsFeature FSSyncShareInstall { - Ensure = "Present" - Name = "FS-SyncShareService" - DependsOn = "[WindowsFeature]FSResourceManagerInstall" + Ensure = 'Present' + Name = 'FS-SyncShareService' + DependsOn = '[WindowsFeature]FSResourceManagerInstall' } WindowsFeature StorageServicesInstall { - Ensure = "Present" - Name = "Storage-Services" - DependsOn = "[WindowsFeature]FSSyncShareInstall" + Ensure = 'Present' + Name = 'Storage-Services' + DependsOn = '[WindowsFeature]FSSyncShareInstall' } WindowsFeature ISCSITargetServerInstall { - Ensure = "Present" - Name = "FS-iSCSITarget-Server" - DependsOn = "[WindowsFeature]StorageServicesInstall" + Ensure = 'Present' + Name = 'FS-iSCSITarget-Server' + DependsOn = '[WindowsFeature]StorageServicesInstall' } # Wait for the Domain to be available so we can join it. WaitForAll DC { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 } # Join this Server to the Domain Computer JoinDomain { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' } # Enable FSRM FireWall rules so we can remote manage FSRM Firewall FSRMFirewall1 { - Name = "FSRM-WMI-ASYNC-In-TCP" - Ensure = 'Present' + Name = 'FSRM-WMI-ASYNC-In-TCP' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall2 { - Name = "FSRM-WMI-WINMGMT-In-TCP" - Ensure = 'Present' + Name = 'FSRM-WMI-WINMGMT-In-TCP' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall3 { - Name = "FSRM-RemoteRegistry-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-RemoteRegistry-In (RPC)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall4 { - Name = "FSRM-Task-Scheduler-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-Task-Scheduler-In (RPC)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall5 { - Name = "FSRM-SrmReports-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-SrmReports-In (RPC)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall6 { - Name = "FSRM-RpcSs-In (RPC-EPMAP)" - Ensure = 'Present' + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall7 { - Name = "FSRM-System-In (TCP-445)" - Ensure = 'Present' + Name = 'FSRM-System-In (TCP-445)' + Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall8 { - Name = "FSRM-SrmSvc-In (RPC)" - Ensure = 'Present' + Name = 'FSRM-SrmSvc-In (RPC)' + Ensure = 'Present' Enabled = 'True' } WaitforDisk Disk2 { - DiskId = 1 + DiskId = 1 RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = "[Computer]JoinDomain" + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' } Disk DVolume { - DiskId = 1 + DiskId = 1 DriveLetter = 'D' - DependsOn = "[WaitforDisk]Disk2" + DependsOn = '[WaitforDisk]Disk2' } } } diff --git a/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 b/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 index 00a1c886..30db7a88 100644 --- a/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 +++ b/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 @@ -6,16 +6,16 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then made into a File Server. Includes tests for FSRM Resources. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_FILESERVER_FSRMTEST { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc Import-DscResource -ModuleName FSRMDsc @@ -23,72 +23,77 @@ Configuration MEMBER_FILESERVER_FSRMTEST Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FileServerInstall { - Ensure = "Present" - Name = "FS-FileServer" + Ensure = 'Present' + Name = 'FS-FileServer' } WindowsFeature DataDedupInstall { - Ensure = "Present" - Name = "FS-Data-Deduplication" - DependsOn = "[WindowsFeature]FileServerInstall" + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' } WindowsFeature BranchCacheInstall { - Ensure = "Present" - Name = "FS-BranchCache" - DependsOn = "[WindowsFeature]DataDedupInstall" + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' } WindowsFeature DFSNameSpaceInstall { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]BranchCacheInstall" + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' } WindowsFeature DFSReplicationInstall { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' } WindowsFeature FSResourceManagerInstall { - Ensure = "Present" - Name = "FS-Resource-Manager" - DependsOn = "[WindowsFeature]DFSReplicationInstall" + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' } WindowsFeature FSSyncShareInstall { - Ensure = "Present" - Name = "FS-SyncShareService" - DependsOn = "[WindowsFeature]FSResourceManagerInstall" + Ensure = 'Present' + Name = 'FS-SyncShareService' + DependsOn = '[WindowsFeature]FSResourceManagerInstall' } WindowsFeature StorageServicesInstall { - Ensure = "Present" - Name = "Storage-Services" - DependsOn = "[WindowsFeature]FSSyncShareInstall" + Ensure = 'Present' + Name = 'Storage-Services' + DependsOn = '[WindowsFeature]FSSyncShareInstall' } WindowsFeature ISCSITargetServerInstall { - Ensure = "Present" - Name = "FS-iSCSITarget-Server" - DependsOn = "[WindowsFeature]StorageServicesInstall" + Ensure = 'Present' + Name = 'FS-iSCSITarget-Server' + DependsOn = '[WindowsFeature]StorageServicesInstall' } @@ -107,62 +112,62 @@ Configuration MEMBER_FILESERVER_FSRMTEST Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # Enable FSRM FireWall rules so we can remote manage FSRM Firewall FSRMFirewall1 { - Name = "FSRM-WMI-ASYNC-In-TCP" + Name = 'FSRM-WMI-ASYNC-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall2 { - Name = "FSRM-WMI-WINMGMT-In-TCP" + Name = 'FSRM-WMI-WINMGMT-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall3 { - Name = "FSRM-RemoteRegistry-In (RPC)" + Name = 'FSRM-RemoteRegistry-In (RPC)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall4 { - Name = "FSRM-Task-Scheduler-In (RPC)" + Name = 'FSRM-Task-Scheduler-In (RPC)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall5 { - Name = "FSRM-SrmReports-In (RPC)" + Name = 'FSRM-SrmReports-In (RPC)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall6 { - Name = "FSRM-RpcSs-In (RPC-EPMAP)" + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall7 { - Name = "FSRM-System-In (TCP-445)" + Name = 'FSRM-System-In (TCP-445)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall8 { - Name = "FSRM-SrmSvc-In (RPC)" + Name = 'FSRM-SrmSvc-In (RPC)' Ensure = 'Present' Enabled = 'True' } @@ -172,14 +177,14 @@ Configuration MEMBER_FILESERVER_FSRMTEST DiskId = 1 RetryIntervalSec = 60 RetryCount = 60 - DependsOn = "[Computer]JoinDomain" + DependsOn = '[Computer]JoinDomain' } Disk DVolume { DiskId = 1 DriveLetter = 'D' - DependsOn = "[WaitforDisk]Disk2" + DependsOn = '[WaitforDisk]Disk2' } File UsersFolder @@ -187,7 +192,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST DestinationPath = 'd:\Users' Ensure = 'Present' Type = 'Directory' - DependsOn = "[Disk]DVolume" + DependsOn = '[Disk]DVolume' } FSRMQuotaTemplate HardLimit5GB @@ -198,7 +203,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Size = 5GB SoftLimit = $false ThresholdPercentages = @( 85, 100 ) - DependsOn = "[File]UsersFolder" + DependsOn = '[File]UsersFolder' } FSRMQuotaTemplateAction HardLimit5GBEmail85 @@ -212,7 +217,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST MailBCC = '' MailCC = 'fileserveradmins@contoso.com' MailTo = '[Source Io Owner Email]' - DependsOn = "[FSRMQuotaTemplate]HardLimit5GB" + DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' } # End of FSRMQuotaTemplateAction Resource FSRMQuotaTemplateAction HardLimit5GBEvent85 @@ -223,7 +228,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Type = 'Event' Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' EventType = 'Warning' - DependsOn = "[FSRMQuotaTemplate]HardLimit5GB" + DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' } # End of FSRMQuotaTemplateAction Resource FSRMQuotaTemplateAction HardLimit5GBEmail100 @@ -237,7 +242,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST MailBCC = '' MailCC = 'fileserveradmins@contoso.com' MailTo = '[Source Io Owner Email]' - DependsOn = "[FSRMQuotaTemplate]HardLimit5GB" + DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' } # End of FSRMQuotaTemplateAction Resource FSRMQuota DUsersQuota @@ -247,7 +252,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Ensure = 'Present' Template = '5 GB Limit' MatchesTemplate = $true - DependsOn = "[FSRMQuotaTemplateAction]HardLimit5GBEmail100" + DependsOn = '[FSRMQuotaTemplateAction]HardLimit5GBEmail100' } # End of FSRMQuota Resource File SharedFolder @@ -255,7 +260,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST DestinationPath = 'd:\shared' Ensure = 'Present' Type = 'Directory' - DependsOn = "[Disk]DVolume" + DependsOn = '[Disk]DVolume' } FSRMQuota DSharedQuota @@ -266,7 +271,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Size = 5GB SoftLimit = $false ThresholdPercentages = @( 75, 100 ) - DependsOn = "[File]SharedFolder" + DependsOn = '[File]SharedFolder' } # End of FSRMQuota Resource FSRMQuotaAction DSharedEmail75 @@ -280,7 +285,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST MailBCC = '' MailCC = 'fileserveradmins@contoso.com' MailTo = '[Source Io Owner Email]' - DependsOn = "[FSRMQuota]DSharedQuota" + DependsOn = '[FSRMQuota]DSharedQuota' } # End of FSRMQuotaAction Resource FSRMQuotaAction DSharedEmail100 @@ -294,7 +299,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST MailBCC = '' MailCC = 'fileserveradmins@contoso.com' MailTo = '[Source Io Owner Email]' - DependsOn = "[FSRMQuota]DSharedQuota" + DependsOn = '[FSRMQuota]DSharedQuota' } # End of FSRMQuotaAction Resource File AutoFolder @@ -302,7 +307,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST DestinationPath = 'd:\auto' Ensure = 'Present' Type = 'Directory' - DependsOn = "[Disk]DVolume" + DependsOn = '[Disk]DVolume' } FSRMAutoQuota DAutoQuota @@ -310,7 +315,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Path = 'd:\auto' Ensure = 'Present' Template = '100 MB Limit' - DependsOn = "[File]SharedFolder" + DependsOn = '[File]SharedFolder' } # End of FSRMQuota Resource FSRMFileGroup FSRMFileGroupPortableFiles @@ -340,7 +345,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST MailBCC = '' MailCC = 'fileserveradmins@contoso.com' MailTo = '[Source Io Owner Email]' - DependsOn = "[FSRMFileScreenTemplate]FileScreenSomeFiles" + DependsOn = '[FSRMFileScreenTemplate]FileScreenSomeFiles' } # End of FSRMFileScreenTemplateAction Resource FSRMFileScreenTemplateAction FileScreenSomeFilesEvent @@ -350,7 +355,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Type = 'Event' Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' EventType = 'Warning' - DependsOn = "[FSRMFileScreenTemplate]FileScreenSomeFiles" + DependsOn = '[FSRMFileScreenTemplate]FileScreenSomeFiles' } # End of FSRMFileScreenTemplateAction Resource FSRMFileScreen DUsersFileScreen @@ -372,7 +377,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST MailBCC = '' MailCC = 'fileserveradmins@contoso.com' MailTo = '[Source Io Owner Email]' - DependsOn = "[FSRMFileScreen]DUsersFileScreen" + DependsOn = '[FSRMFileScreen]DUsersFileScreen' } # End of FSRMFileScreenAction Resource FSRMFileScreenAction DUsersFileScreenSomeFilesEvent @@ -382,7 +387,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Type = 'Event' Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' EventType = 'Warning' - DependsOn = "[FSRMFileScreen]DUsersFileScreen" + DependsOn = '[FSRMFileScreen]DUsersFileScreen' } # End of FSRMFileScreenAction Resource FSRMFileScreenException DUsersFileScreenException @@ -410,7 +415,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST PropertyName = 'Privacy' Description = 'Publically accessible files.' Ensure = 'Present' - DependsOn = "[FSRMClassificationProperty]PrivacyClasificationProperty" + DependsOn = '[FSRMClassificationProperty]PrivacyClasificationProperty' } # End of FSRMClassificationPropertyValue Resource FSRMClassificationPropertyValue SecretClasificationPropertyValue @@ -418,7 +423,7 @@ Configuration MEMBER_FILESERVER_FSRMTEST Name = 'Secret' PropertyName = 'Privacy' Ensure = 'Present' - DependsOn = "[FSRMClassificationProperty]PrivacyClasificationProperty" + DependsOn = '[FSRMClassificationProperty]PrivacyClasificationProperty' } # End of FSRMClassificationPropertyValue Resource FSRMClassification FSRMClassificationSettings { diff --git a/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 b/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 index 01b64386..8e834ec8 100644 --- a/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 +++ b/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 @@ -6,8 +6,8 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then made into a File Server. Configures a iSCSI targets and virtual disks. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true TargetName = 'sa-foc-target' @@ -38,8 +38,8 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_FILESERVER_ISCSI { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc Import-DscResource -ModuleName ISCSI @@ -48,73 +48,78 @@ Configuration MEMBER_FILESERVER_ISCSI # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FileServerInstall { - Ensure = "Present" - Name = "FS-FileServer" + Ensure = 'Present' + Name = 'FS-FileServer' } WindowsFeature DataDedupInstall { - Ensure = "Present" - Name = "FS-Data-Deduplication" - DependsOn = "[WindowsFeature]FileServerInstall" + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' } WindowsFeature BranchCacheInstall { - Ensure = "Present" - Name = "FS-BranchCache" - DependsOn = "[WindowsFeature]DataDedupInstall" + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' } WindowsFeature DFSNameSpaceInstall { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]BranchCacheInstall" + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' } WindowsFeature DFSReplicationInstall { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' } WindowsFeature FSResourceManagerInstall { - Ensure = "Present" - Name = "FS-Resource-Manager" - DependsOn = "[WindowsFeature]DFSReplicationInstall" + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' } WindowsFeature FSSyncShareInstall { - Ensure = "Present" - Name = "FS-SyncShareService" - DependsOn = "[WindowsFeature]FSResourceManagerInstall" + Ensure = 'Present' + Name = 'FS-SyncShareService' + DependsOn = '[WindowsFeature]FSResourceManagerInstall' } WindowsFeature StorageServicesInstall { - Ensure = "Present" - Name = "Storage-Services" - DependsOn = "[WindowsFeature]FSSyncShareInstall" + Ensure = 'Present' + Name = 'Storage-Services' + DependsOn = '[WindowsFeature]FSSyncShareInstall' } WindowsFeature ISCSITargetServerInstall { - Ensure = "Present" - Name = "FS-iSCSITarget-Server" - DependsOn = "[WindowsFeature]StorageServicesInstall" + Ensure = 'Present' + Name = 'FS-iSCSITarget-Server' + DependsOn = '[WindowsFeature]StorageServicesInstall' } # Wait for the Domain to be available so we can join it. @@ -132,62 +137,62 @@ Configuration MEMBER_FILESERVER_ISCSI Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # Enable FSRM FireWall rules so we can remote manage FSRM Firewall FSRMFirewall1 { - Name = "FSRM-WMI-ASYNC-In-TCP" + Name = 'FSRM-WMI-ASYNC-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall2 { - Name = "FSRM-WMI-WINMGMT-In-TCP" + Name = 'FSRM-WMI-WINMGMT-In-TCP' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall3 { - Name = "FSRM-RemoteRegistry-In (RPC)" + Name = 'FSRM-RemoteRegistry-In (RPC)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall4 { - Name = "FSRM-Task-Scheduler-In (RPC)" + Name = 'FSRM-Task-Scheduler-In (RPC)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall5 { - Name = "FSRM-SrmReports-In (RPC)" + Name = 'FSRM-SrmReports-In (RPC)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall6 { - Name = "FSRM-RpcSs-In (RPC-EPMAP)" + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall7 { - Name = "FSRM-System-In (TCP-445)" + Name = 'FSRM-System-In (TCP-445)' Ensure = 'Present' Enabled = 'True' } Firewall FSRMFirewall8 { - Name = "FSRM-SrmSvc-In (RPC)" + Name = 'FSRM-SrmSvc-In (RPC)' Ensure = 'Present' Enabled = 'True' } @@ -197,14 +202,14 @@ Configuration MEMBER_FILESERVER_ISCSI DiskId = 1 RetryIntervalSec = 60 RetryCount = 60 - DependsOn = "[Computer]JoinDomain" + DependsOn = '[Computer]JoinDomain' } Disk DVolume { DiskId = 1 DriveLetter = 'D' - DependsOn = "[WaitforDisk]Disk2" + DependsOn = '[WaitforDisk]Disk2' } File VirtualDisksFolder @@ -216,11 +221,11 @@ Configuration MEMBER_FILESERVER_ISCSI } $DependsOn = '[File]VirtualDisksFolder' - [System.Int32] $Count = 0 + [System.Int32] $count = 0 foreach ($VirtualDisk in $Node.VirtualDisks) { - $Count++ - $Name = "$($Node.TargetName)_Disk_$Count" + $count++ + $Name = "$($Node.TargetName)_Disk_$count" ISCSIVirtualDisk $Name { Ensure = 'Present' diff --git a/src/dsclibrary/MEMBER_IPAM.DSC.ps1 b/src/dsclibrary/MEMBER_IPAM.DSC.ps1 index 8d108e60..70e066d0 100644 --- a/src/dsclibrary/MEMBER_IPAM.DSC.ps1 +++ b/src/dsclibrary/MEMBER_IPAM.DSC.ps1 @@ -5,39 +5,37 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into an IPAM Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_IPAM { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature WIDInstall { - Ensure = "Present" - Name = "Windows-Internal-Database" + Ensure = 'Present' + Name = 'Windows-Internal-Database' } WindowsFeature IPAMInstall { - Ensure = "Present" - Name = "IPAM" - DependsOn = "[WindowsFeature]WIDInstall" + Ensure = 'Present' + Name = 'IPAM' + DependsOn = '[WindowsFeature]WIDInstall' } WaitForAll DC @@ -53,7 +51,7 @@ Configuration MEMBER_IPAM Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 b/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 index 1360069f..e45ebc05 100644 --- a/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Windows Server, joins it to a Domain and installs Jenkins CI on it. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true JenkinsPort = 80 @@ -14,26 +14,24 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_JENKINS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName cChoco Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature NetFrameworkCore { - Ensure = "Present" - Name = "NET-Framework-Core" + Ensure = 'Present' + Name = 'NET-Framework-Core' } # Wait for the Domain to be available so we can join it. @@ -51,28 +49,28 @@ Configuration MEMBER_JENKINS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # Install Chocolatey cChocoInstaller installChoco { - InstallDir = "c:\choco" - DependsOn = "[WindowsFeature]NetFrameworkCore" + InstallDir = 'c:\choco' + DependsOn = '[WindowsFeature]NetFrameworkCore' } # Install JDK8 cChocoPackageInstaller installJdk8 { - Name = "jdk8" - DependsOn = "[cChocoInstaller]installChoco" + Name = 'jdk8' + DependsOn = '[cChocoInstaller]installChoco' } # Install Jenkins cChocoPackageInstaller installJenkins { - Name = "Jenkins" - DependsOn = "[cChocoInstaller]installChoco" + Name = 'Jenkins' + DependsOn = '[cChocoInstaller]installChoco' } # Set the Jenkins Port @@ -93,7 +91,7 @@ Configuration MEMBER_JENKINS -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" ` -Value $NewConfig ` -Force - Write-Verbose -Message "Restarting Jenkins" + Write-Verbose -Message 'Restarting Jenkins' Restart-Service ` -Name Jenkins } @@ -120,7 +118,7 @@ Configuration MEMBER_JENKINS # Jenkins is already on correct port Return $true } - DependsOn = "[cChocoPackageInstaller]installJenkins" + DependsOn = '[cChocoPackageInstaller]installJenkins' } } } diff --git a/src/dsclibrary/MEMBER_NANO.DSC.ps1 b/src/dsclibrary/MEMBER_NANO.DSC.ps1 index 8330ea4a..4be2c839 100644 --- a/src/dsclibrary/MEMBER_NANO.DSC.ps1 +++ b/src/dsclibrary/MEMBER_NANO.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Nano Server and joins it to a Domain using an ODJ Request File. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ODJRequestFile = 'C:\ODJRequest.txt' @@ -14,20 +14,10 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_NANO { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) - { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - WaitForAll DC { ResourceName = '[ADDomain]PrimaryDC' diff --git a/src/dsclibrary/MEMBER_NLB.DSC.ps1 b/src/dsclibrary/MEMBER_NLB.DSC.ps1 index 2f1c48aa..5bc98ae8 100644 --- a/src/dsclibrary/MEMBER_NLB.DSC.ps1 +++ b/src/dsclibrary/MEMBER_NLB.DSC.ps1 @@ -5,46 +5,44 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Network Load Balancing cluster node. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_NLB { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xPSDesiredStateConfiguration Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature InstallWebServer { - Ensure = "Present" - Name = "Web-Server" + Ensure = 'Present' + Name = 'Web-Server' } WindowsFeature InstallWebMgmtService { - Ensure = "Present" - Name = "Web-Mgmt-Service" + Ensure = 'Present' + Name = 'Web-Mgmt-Service' } WindowsFeature InstallNLB { - Ensure = "Present" - Name = "NLB" + Ensure = 'Present' + Name = 'NLB' } # Wait for the Domain to be available so we can join it. @@ -62,7 +60,7 @@ Configuration MEMBER_NLB Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_NPS.DSC.ps1 b/src/dsclibrary/MEMBER_NPS.DSC.ps1 index b56bc6e5..f2912c60 100644 --- a/src/dsclibrary/MEMBER_NPS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_NPS.DSC.ps1 @@ -7,46 +7,44 @@ DSC Template Configuration File For use by LabBuilder .Requires Windows Server 2012 R2 Full (Server core not supported). .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_NPS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature NPASPolicyServerInstall { - Ensure = "Present" - Name = "NPAS-Policy-Server" + Ensure = 'Present' + Name = 'NPAS-Policy-Server' } WindowsFeature NPASHealthInstall { - Ensure = "Present" - Name = "NPAS-Health" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + Ensure = 'Present' + Name = 'NPAS-Health' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' } WindowsFeature RSATNPAS { - Ensure = "Present" - Name = "RSAT-NPAS" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + Ensure = 'Present' + Name = 'RSAT-NPAS' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' } # Wait for the Domain to be available so we can join it. @@ -64,7 +62,7 @@ Configuration MEMBER_NPS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 b/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 index cebedd8d..86024ee1 100644 --- a/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 +++ b/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 @@ -10,83 +10,83 @@ DSC Template Configuration File For use by LabBuilder .Requires Windows Server 2012 R2 Full (Server core not supported). .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_NPS_DFSTEST { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName DFSDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature NPASPolicyServerInstall { - Ensure = "Present" - Name = "NPAS-Policy-Server" + Ensure = 'Present' + Name = 'NPAS-Policy-Server' } WindowsFeature NPASHealthInstall { - Ensure = "Present" - Name = "NPAS-Health" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + Ensure = 'Present' + Name = 'NPAS-Health' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' } WindowsFeature RSATNPAS { - Ensure = "Present" - Name = "RSAT-NPAS" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + Ensure = 'Present' + Name = 'RSAT-NPAS' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' } WindowsFeature RSATDFSMgmtConInstall { - Ensure = "Present" - Name = "RSAT-DFS-Mgmt-Con" - DependsOn = "[WindowsFeature]RSATNPAS" + Ensure = 'Present' + Name = 'RSAT-DFS-Mgmt-Con' + DependsOn = '[WindowsFeature]RSATNPAS' } # Wait for the Domain to be available so we can join it. WaitForAll DC { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 } # Join this Server to the Domain Computer JoinDomain { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' } DFSReplicationGroup RGPublic { - GroupName = 'Public' - Description = 'Public files for use by all departments' - Ensure = 'Present' - Members = 'SA_FS1','SA_FS2' - Folders = 'Software','Misc' - Topology = 'Fullmesh' - ContentPaths = 'd:\public\Software','d:\public\Misc' + GroupName = 'Public' + Description = 'Public files for use by all departments' + Ensure = 'Present' + Members = 'SA_FS1', 'SA_FS2' + Folders = 'Software', 'Misc' + Topology = 'Fullmesh' + ContentPaths = 'd:\public\Software', 'd:\public\Misc' PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[Computer]JoinDomain' + DependsOn = '[Computer]JoinDomain' } # End of RGPublic Resource } } diff --git a/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 b/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 index 8bf99811..467a95aa 100644 --- a/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 @@ -5,39 +5,37 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then contains Remote Access components. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_REMOTEACCESS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature DirectAccessVPNInstall { - Ensure = "Present" - Name = "DirectAccess-VPN" + Ensure = 'Present' + Name = 'DirectAccess-VPN' } WindowsFeature RoutingInstall { - Ensure = "Present" - Name = "Routing" - DependsOn = "[WindowsFeature]DirectAccessVPNInstall" + Ensure = 'Present' + Name = 'Routing' + DependsOn = '[WindowsFeature]DirectAccessVPNInstall' } # Wait for the Domain to be available so we can join it. @@ -55,7 +53,7 @@ Configuration MEMBER_REMOTEACCESS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 b/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 index 16691030..e71aa746 100644 --- a/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 +++ b/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 @@ -6,46 +6,44 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then contains Remote Access and Web Application Proxy components. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_REMOTEACCESS_WAP { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature DirectAccessVPNInstall { - Ensure = "Present" - Name = "DirectAccess-VPN" + Ensure = 'Present' + Name = 'DirectAccess-VPN' } WindowsFeature RoutingInstall { - Ensure = "Present" - Name = "Routing" - DependsOn = "[WindowsFeature]DirectAccessVPNInstall" + Ensure = 'Present' + Name = 'Routing' + DependsOn = '[WindowsFeature]DirectAccessVPNInstall' } WindowsFeature WebApplicationProxyInstall { - Ensure = "Present" - Name = "Web-Application-Proxy" - DependsOn = "[WindowsFeature]RoutingInstall" + Ensure = 'Present' + Name = 'Web-Application-Proxy' + DependsOn = '[WindowsFeature]RoutingInstall' } # Wait for the Domain to be available so we can join it. @@ -63,7 +61,7 @@ Configuration MEMBER_REMOTEACCESS_WAP Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } } } diff --git a/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 b/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 index 30740323..985bf08b 100644 --- a/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 +++ b/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 @@ -25,9 +25,9 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_ROOTCA { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName ActiveDirectoryCSDsc Import-DscResource -ModuleName xPSDesiredStateConfiguration Import-DscResource -ModuleName NetworkingDsc @@ -36,11 +36,16 @@ Configuration MEMBER_ROOTCA # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } # Install the CA Service @@ -164,7 +169,8 @@ Configuration MEMBER_ROOTCA } # Configure the Web Enrollment Feature - ADCSWebEnrollment ConfigWebEnrollment { + ADCSWebEnrollment ConfigWebEnrollment + { Ensure = 'Present' IsSingleInstance = 'Yes' Credential = $LocalAdminCredential @@ -303,7 +309,8 @@ Configuration MEMBER_ROOTCA if ($Node.InstallOnlineResponder) { # Configure the Online Responder Feature - ADCSOnlineResponder ConfigOnlineResponder { + ADCSOnlineResponder ConfigOnlineResponder + { Ensure = 'Present' IsSingleInstance = 'Yes' Credential = $LocalAdminCredential diff --git a/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 b/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 index 34159b46..2cdf1b7f 100644 --- a/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 +++ b/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 @@ -26,24 +26,25 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_SQLSERVER2014 { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName SQLServerDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } + if ($Node.InstallerPassword) { - [PSCredential]$InstallerCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) + $InstallerCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) } # Install the SQL Server Dependencies @@ -87,10 +88,12 @@ Configuration MEMBER_SQLSERVER2014 foreach ($Instance in $Node.Instances) { $Features = $Instance.Features + if ([System.String]::IsNullOrEmpty($Features)) { $Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' } # if + SqlServerSetup ($Instance.Name) { SourcePath = $Node.SourcePath @@ -112,7 +115,7 @@ Configuration MEMBER_SQLSERVER2014 ASTempDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Temp" ASConfigDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Config" PsDscRunAsCredential = $InstallerCredential - DependsOn = "[Computer]JoinDomain", "[WindowsFeature]NET35Install" + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' } SqlServerFirewall ($Instance.Name) @@ -129,10 +132,10 @@ Configuration MEMBER_SQLSERVER2014 SqlServerSetup SQLMT { SourcePath = $Node.SourcePath - InstanceName = "NULL" - Features = "SSMS,ADV_SSMS" + InstanceName = 'NULL' + Features = 'SSMS,ADV_SSMS' PsDscRunAsCredential = $InstallerCredential - DependsOn = "[Computer]JoinDomain", "[WindowsFeature]NET35Install" + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' } } } diff --git a/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 b/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 index 46fd7c5c..8c4b752e 100644 --- a/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 +++ b/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 @@ -6,8 +6,8 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then installs SQL Server 2016. It will install SQLServer from a locally mounted ISO file. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallerUsername = 'Administrator' @@ -26,24 +26,25 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_SQLSERVER2016 { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName SQLServerDsc Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } + if ($Node.InstallerPassword) { - [PSCredential]$InstallerCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) + $InstallerCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) } # Install the SQL Server Dependencies @@ -112,7 +113,7 @@ Configuration MEMBER_SQLSERVER2016 ASTempDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Temp" ASConfigDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Config" PsDscRunAsCredential = $InstallerCredential - DependsOn = "[Computer]JoinDomain", "[WindowsFeature]NET35Install" + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' } SqlServerFirewall ($Instance.Name) @@ -129,10 +130,10 @@ Configuration MEMBER_SQLSERVER2016 SqlServerSetup SQLMT { SourcePath = $Node.SourcePath - InstanceName = "NULL" - Features = "SSMS,ADV_SSMS" + InstanceName = 'NULL' + Features = 'SSMS,ADV_SSMS' PsDscRunAsCredential = $InstallerCredential - DependsOn = "[Computer]JoinDomain", "[WindowsFeature]NET35Install" + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' } } } diff --git a/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 b/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 index 05bb0367..a3ddd952 100644 --- a/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 +++ b/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 @@ -20,38 +20,47 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_SUBCA { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName ActiveDirectoryCSDsc Import-DscResource -ModuleName xPSDesiredStateConfiguration Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } # Install the CA Service - WindowsFeature ADCSCA { - Name = 'ADCS-Cert-Authority' + WindowsFeature ADCSCA + { + Name = 'ADCS-Cert-Authority' Ensure = 'Present' } # Install the Web Enrollment Service - WindowsFeature WebEnrollmentCA { - Name = 'ADCS-Web-Enrollment' - Ensure = 'Present' + WindowsFeature WebEnrollmentCA + { + Name = 'ADCS-Web-Enrollment' + Ensure = 'Present' DependsOn = '[WindowsFeature]ADCSCA' } WindowsFeature InstallWebMgmtService { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' + Ensure = 'Present' + Name = 'Web-Mgmt-Service' DependsOn = '[WindowsFeature]ADCSWebEnrollment' } @@ -65,26 +74,31 @@ Configuration MEMBER_SUBCA } } - if ($Node.InstallOnlineResponder) { + if ($Node.InstallOnlineResponder) + { # Install the Online Responder Service - WindowsFeature OnlineResponderCA { - Name = 'ADCS-Online-Cert' - Ensure = 'Present' + WindowsFeature OnlineResponderCA + { + Name = 'ADCS-Online-Cert' + Ensure = 'Present' DependsOn = '[WindowsFeature]ADCSCA' } } - if ($Node.InstallEnrollmentWebService) { + if ($Node.InstallEnrollmentWebService) + { # Install the Enrollment Web Service/Enrollment Policy Web Service - WindowsFeature EnrollmentWebSvc { - Name = 'ADCS-Enroll-Web-Svc' - Ensure = 'Present' + WindowsFeature EnrollmentWebSvc + { + Name = 'ADCS-Enroll-Web-Svc' + Ensure = 'Present' DependsOn = '[WindowsFeature]ADCSCA' } - WindowsFeature EnrollmentWebPol { - Name = 'ADCS-Enroll-Web-Pol' - Ensure = 'Present' + WindowsFeature EnrollmentWebPol + { + Name = 'ADCS-Enroll-Web-Pol' + Ensure = 'Present' DependsOn = '[WindowsFeature]WebEnrollmentCA' } } @@ -92,29 +106,29 @@ Configuration MEMBER_SUBCA # Wait for the Domain to be available so we can join it. WaitForAll DC { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 } # Join this Server to the Domain Computer JoinDomain { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' } # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. File CAPolicy { - Ensure = 'Present' + Ensure = 'Present' DestinationPath = 'C:\Windows\CAPolicy.inf' - Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=10`r`n AlternateSignatureAlgorithm=1`r`n CNGHashAlgorithm=SHA256`r`n LoadDefaultTemplates=0`r`n" - Type = 'File' - DependsOn = '[Computer]JoinDomain' + Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=10`r`n AlternateSignatureAlgorithm=1`r`n CNGHashAlgorithm=SHA256`r`n LoadDefaultTemplates=0`r`n" + Type = 'File' + DependsOn = '[Computer]JoinDomain' } <# @@ -123,10 +137,10 @@ Configuration MEMBER_SUBCA #> File CertEnrollFolder { - Ensure = 'Present' + Ensure = 'Present' DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' - Type = 'Directory' - DependsOn = '[File]CAPolicy' + Type = 'Directory' + DependsOn = '[File]CAPolicy' } <# @@ -135,34 +149,34 @@ Configuration MEMBER_SUBCA #> WaitForAny RootCA { - ResourceName = '[ADCSWebEnrollment]ConfigWebEnrollment' - NodeName = $Node.RootCAName + ResourceName = '[ADCSWebEnrollment]ConfigWebEnrollment' + NodeName = $Node.RootCAName RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[File]CertEnrollFolder' + RetryCount = 30 + DependsOn = '[File]CertEnrollFolder' } # Download the Root CA certificate file. xRemoteFile DownloadRootCACRTFile { DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCAName)_$($Node.RootCACommonName).crt" - Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCAName)_$($Node.RootCACommonName).crt" - DependsOn = '[WaitForAny]RootCA' + Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCAName)_$($Node.RootCACommonName).crt" + DependsOn = '[WaitForAny]RootCA' } # Download the Root CA certificate revocation list. xRemoteFile DownloadRootCACRLFile { DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" - Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCACommonName).crl" - DependsOn = '[xRemoteFile]DownloadRootCACRTFile' + Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCACommonName).crl" + DependsOn = '[xRemoteFile]DownloadRootCACRTFile' } # Install the Root CA Certificate to the LocalMachine Root Store and DS Script InstallRootCACert { PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { + SetScript = { Write-Verbose -Message "Registering the Root CA Certificate C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt in DS..." & "$($ENV:SystemRoot)\system32\certutil.exe" -f -dspublish "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt" RootCA Write-Verbose -Message "Registering the Root CA CRL C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl in DS..." @@ -172,19 +186,20 @@ Configuration MEMBER_SUBCA Write-Verbose -Message "Installing the Root CA CRL C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl..." & "$($ENV:SystemRoot)\system32\certutil.exe" -addstore -f root "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" } - GetScript = { + GetScript = { return @{ Installed = ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) } } - TestScript = { - if ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) { - Write-Verbose -Message "Root CA Certificate Needs to be installed..." + TestScript = { + if ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) + { + Write-Verbose -Message 'Root CA Certificate Needs to be installed...' return $false } return $true } - DependsOn = '[xRemoteFile]DownloadRootCACRTFile' + DependsOn = '[xRemoteFile]DownloadRootCACRTFile' } <# @@ -193,88 +208,91 @@ Configuration MEMBER_SUBCA #> ADCSCertificationAuthority ConfigCA { - Ensure = 'Present' + Ensure = 'Present' IsSingleInstance = 'Yes' - Credential = $DomainAdminCredential - CAType = 'EnterpriseSubordinateCA' - CACommonName = $Node.CACommonName + Credential = $DomainAdminCredential + CAType = 'EnterpriseSubordinateCA' + CACommonName = $Node.CACommonName CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix - OverwriteExistingCAinDS = $true - OutputCertRequestFile = "c:\windows\system32\certsrv\certenroll\$($Node.NodeName).req" - CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' - HashAlgorithmName = 'SHA256' - KeyLength = 2048 - DependsOn = '[Script]InstallRootCACert' + OverwriteExistingCAinDS = $true + OutputCertRequestFile = "c:\windows\system32\certsrv\certenroll\$($Node.NodeName).req" + CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' + HashAlgorithmName = 'SHA256' + KeyLength = 2048 + DependsOn = '[Script]InstallRootCACert' } # Configure the Web Enrollment Feature - ADCSWebEnrollment ConfigWebEnrollment { - Ensure = 'Present' + ADCSWebEnrollment ConfigWebEnrollment + { + Ensure = 'Present' IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[ADCSCertificationAuthority]ConfigCA' + Credential = $LocalAdminCredential + DependsOn = '[ADCSCertificationAuthority]ConfigCA' } # Set the IIS Mime Type to allow the REQ request to be downloaded by the Root CA Script SetREQMimeType { - SetScript = { - Add-WebConfigurationProperty -PSPath IIS:\ -Filter //staticContent -Name "." -Value @{fileExtension='.req';mimeType='application/pkcs10'} + SetScript = { + Add-WebConfigurationProperty -PSPath IIS:\ -Filter //staticContent -Name "." -Value @{fileExtension = '.req'; mimeType = 'application/pkcs10' } } - GetScript = { + GetScript = { return @{ 'MimeType' = ((Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *).mimeType); } } TestScript = { - if (-not (Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *)) { + if (-not (Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *)) + { # Mime type is not set return $false } # Mime Type is already set return $true } - DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' + DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' } # Wait for the Root CA to have completed issuance of the certificate for this SubCA. WaitForAny SubCACer { - ResourceName = "[Script]IssueCert_$($Node.NodeName)" - NodeName = $Node.RootCAName + ResourceName = "[Script]IssueCert_$($Node.NodeName)" + NodeName = $Node.RootCAName RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Script]SetREQMimeType' + RetryCount = 30 + DependsOn = '[Script]SetREQMimeType' } # Download the Certificate for this SubCA but rename it so that it'll match the name expected by the CA xRemoteFile DownloadSubCACERFile { DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.NodeName)_$($Node.CACommonName).crt" - Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.NodeName).crt" - DependsOn = '[WaitForAny]SubCACer' + Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.NodeName).crt" + DependsOn = '[WaitForAny]SubCACer' } # Register the Sub CA Certificate with the Certification Authority Script RegisterSubCA { PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { + SetScript = { Write-Verbose -Message "Registering the Sub CA Certificate with the Certification Authority C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.NodeName)_$($Using:Node.CACommonName).crt..." & "$($ENV:SystemRoot)\system32\certutil.exe" -installCert "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.NodeName)_$($Using:Node.CACommonName).crt" } - GetScript = { + GetScript = { return @{ } } - TestScript = { - if (-not (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertHash')) { + TestScript = { + if (-not (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertHash')) + { Write-Verbose -Message 'Sub CA Certificate needs to be registered with the Certification Authority...' return $false } return $true } - DependsOn = '[xRemoteFile]DownloadSubCACERFile' + DependsOn = '[xRemoteFile]DownloadSubCACERFile' } <# @@ -283,17 +301,20 @@ Configuration MEMBER_SUBCA #> Script ADCSAdvConfig { - SetScript = { - if ($Using:Node.CADistinguishedNameSuffix) { + SetScript = { + if ($Using:Node.CADistinguishedNameSuffix) + { & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" } - if ($Using:Node.CRLPublicationURLs) { + if ($Using:Node.CRLPublicationURLs) + { & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) } - if ($Using:Node.CACertPublicationURLs) { + if ($Using:Node.CACertPublicationURLs) + { & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) } @@ -302,66 +323,72 @@ Configuration MEMBER_SUBCA Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' } - GetScript = { + GetScript = { return @{ - 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); - 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); - 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); - 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') + 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); + 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); + 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); + 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') } } TestScript = { - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) + { return $false } - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) + { return $false } - if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) { + if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) + { return $false } - if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) { + if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) + { return $false } return $true } - DependsOn = '[Script]RegisterSubCA' + DependsOn = '[Script]RegisterSubCA' } - if ($Node.InstallOnlineResponder) { + if ($Node.InstallOnlineResponder) + { # Configure the Online Responder Feature - ADCSOnlineResponder ConfigOnlineResponder { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[Script]ADCSAdvConfig' + ADCSOnlineResponder ConfigOnlineResponder + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + DependsOn = '[Script]ADCSAdvConfig' } # Enable Online Responder FireWall rules so we can remote manage Online Responder Firewall OnlineResponderFirewall1 { - Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In' - Enabled = 'True' + Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In' + Enabled = 'True' DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' } Firewall OnlineResponderirewall2 { - Name = 'Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In' - Enabled = 'True' + Name = 'Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In' + Enabled = 'True' DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' } Firewall OnlineResponderFirewall3 { - Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out' - Enabled = 'True' + Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out' + Enabled = 'True' DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' } } diff --git a/src/dsclibrary/MEMBER_WDS.DSC.ps1 b/src/dsclibrary/MEMBER_WDS.DSC.ps1 index e7535855..069cec06 100644 --- a/src/dsclibrary/MEMBER_WDS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_WDS.DSC.ps1 @@ -13,19 +13,24 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_WDS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential 'Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature WDSDeploymentInstall diff --git a/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 b/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 index 23f3ef62..e862b1a4 100644 --- a/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 +++ b/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Server that is joined to a domain and then made into an IIS Web Application Server. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true WebAppPools = @( @@ -24,17 +24,17 @@ DSC Template Configuration File For use by LabBuilder BindingInfo = @( MSFT_xWebBindingInformation { - Protocol = "HTTPS" + Protocol = 'HTTPS' Port = 8443 - CertificateThumbprint = "71AD93562316F21F74606F1096B85D66289ED60F" - CertificateStoreName = "WebHosting" + CertificateThumbprint = '71AD93562316F21F74606F1096B85D66289ED60F' + CertificateStoreName = 'WebHosting' }, MSFT_xWebBindingInformation { - Protocol = "HTTPS" + Protocol = 'HTTPS' Port = 8444 - CertificateThumbprint = "DEDDD963B28095837F558FE14DA1FDEFB7FA9DA7" - CertificateStoreName = "MY" + CertificateThumbprint = 'DEDDD963B28095837F558FE14DA1FDEFB7FA9DA7' + CertificateStoreName = 'MY' } ) ApplicationPool = 'MyAppPool'; @@ -62,35 +62,34 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_WEBSERVER { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xWebAdministration Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } + # Assemble the Admin Credentials if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature IISInstall { - Ensure = "Present" - Name = "Web-Server" + Ensure = 'Present' + Name = 'Web-Server' } WindowsFeature AspNet45Install { - Ensure = "Present" - Name = "Web-Asp-Net45" + Ensure = 'Present' + Name = 'Web-Asp-Net45' } WindowsFeature WebMgmtServiceInstall { - Ensure = "Present" - Name = "Web-Mgmt-Service" + Ensure = 'Present' + Name = 'Web-Mgmt-Service' } WaitForAll DC @@ -106,14 +105,14 @@ Configuration MEMBER_WEBSERVER Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } # Create the Web App Pools - [System.Int32]$Count=0 + $count=0 foreach ($WebAppPool in $Node.WebAppPools) { - $Count++ - xWebAppPool "WebAppPool$Count" + $count++ + xWebAppPool "WebAppPool$count" { Ensure = $WebAppPool.Ensure Name = $WebAppPool.Name @@ -122,33 +121,33 @@ Configuration MEMBER_WEBSERVER } # Create the Web Sites - [System.Int32]$Count=0 + $count=0 foreach ($WebSite in $Node.WebSites) { - $Count++ + $count++ # Create an empty folder or copy content from Source Path if ($WebSite.SourcePath) { - File "WebSiteContent$Count" + File "WebSiteContent$count" { - Ensure = "Present" + Ensure = 'Present' SourcePath = $WebSite.SourcePath DestinationPath = $WebSite.PhysicalPath Recurse = $true - Type = "Directory" + Type = 'Directory' } } else { - File "WebSiteContent$Count" + File "WebSiteContent$count" { - Ensure = "Present" - Type = "Directory" + Ensure = 'Present' + Type = 'Directory' DestinationPath = $WebSite.PhysicalPath } } # if - xWebsite "WebSite$Count" + xWebsite "WebSite$count" { Ensure = $WebSite.Ensure Name = $WebSite.Name @@ -156,7 +155,7 @@ Configuration MEMBER_WEBSERVER PhysicalPath = $WebSite.PhysicalPath BindingInfo = $WebSite.BindingInfo ApplicationPool = $WebSite.ApplicationPool - DependsOn = "[File]WebSiteContent$Count" + DependsOn = "[File]WebSiteContent$count" } } @@ -170,19 +169,19 @@ Configuration MEMBER_WEBSERVER { File "WebApplicationContent$count" { - Ensure = "Present" + Ensure = 'Present' SourcePath = $WebApplication.SourcePath DestinationPath = $WebApplication.PhysicalPath Recurse = $true - Type = "Directory" + Type = 'Directory' } } else { File "WebApplicationContent$count" { - Ensure = "Present" - Type = "Directory" + Ensure = 'Present' + Type = 'Directory' DestinationPath = $WebApplication.PhysicalPath } } # if @@ -208,19 +207,19 @@ Configuration MEMBER_WEBSERVER { File "WebVirtualDirectoryContent$count" { - Ensure = "Present" + Ensure = 'Present' SourcePath = $WebVirtualDirectory.SourcePath DestinationPath = $WebVirtualDirectory.PhysicalPath Recurse = $true - Type = "Directory" + Type = 'Directory' } } else { File "WebVirtualDirectoryContent$count" { - Ensure = "Present" - Type = "Directory" + Ensure = 'Present' + Type = 'Directory' DestinationPath = $WebVirtualDirectory.PhysicalPath } } # if diff --git a/src/dsclibrary/MEMBER_WSUS.DSC.ps1 b/src/dsclibrary/MEMBER_WSUS.DSC.ps1 index a3418c84..83bafb9e 100644 --- a/src/dsclibrary/MEMBER_WSUS.DSC.ps1 +++ b/src/dsclibrary/MEMBER_WSUS.DSC.ps1 @@ -6,16 +6,16 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then installs WSUS components. Requires cMicrosoftUpdate resource from https://github.com/fabiendibot/cMicrosoftUpdate .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true ###################################################################################################> Configuration MEMBER_WSUS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName xWindowsUpdate Import-DscResource -ModuleName StorageDsc @@ -23,24 +23,29 @@ Configuration MEMBER_WSUS # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } + if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature UpdateServicesWIDDBInstall { - Ensure = "Present" - Name = "UpdateServices-WidDB" + Ensure = 'Present' + Name = 'UpdateServices-WidDB' } WindowsFeature UpdateServicesServicesInstall { - Ensure = "Present" - Name = "UpdateServices-Services" - DependsOn = "[WindowsFeature]UpdateServicesWIDDBInstall" + Ensure = 'Present' + Name = 'UpdateServices-Services' + DependsOn = '[WindowsFeature]UpdateServicesWIDDBInstall' } # Wait for the Domain to be available so we can join it. @@ -58,7 +63,7 @@ Configuration MEMBER_WSUS Name = $Node.NodeName DomainName = $Node.DomainName Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" + DependsOn = '[WaitForAll]DC' } WaitforDisk Disk2 @@ -66,14 +71,14 @@ Configuration MEMBER_WSUS DiskId = 1 RetryIntervalSec = 60 RetryCount = 60 - DependsOn = "[Computer]JoinDomain" + DependsOn = '[Computer]JoinDomain' } Disk DVolume { DiskId = 1 DriveLetter = 'D' - DependsOn = "[WaitforDisk]Disk2" + DependsOn = '[WaitforDisk]Disk2' } } } diff --git a/src/dsclibrary/RODC_SECONDARY.DSC.ps1 b/src/dsclibrary/RODC_SECONDARY.DSC.ps1 index 7dea1e37..e77976cd 100644 --- a/src/dsclibrary/RODC_SECONDARY.DSC.ps1 +++ b/src/dsclibrary/RODC_SECONDARY.DSC.ps1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Read Only Domain Controller and adds it to the existing domain provided in the Parameter DomainName. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' DCName = 'SA-DC1' PSDscAllowDomainUser = $true InstallRSATTools = $true @@ -14,51 +14,58 @@ DSC Template Configuration File For use by LabBuilder Configuration RODC_SECONDARY { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature BackupInstall { - Ensure = "Present" - Name = "Windows-Server-Backup" + Ensure = 'Present' + Name = 'Windows-Server-Backup' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } WindowsFeature ADDSInstall { - Ensure = "Present" - Name = "AD-Domain-Services" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' } WindowsFeature RSAT-AD-PowerShellInstall { - Ensure = "Present" - Name = "RSAT-AD-PowerShell" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' } if ($InstallRSATTools) { WindowsFeature RSAT-ManagementTools { - Ensure = "Present" - Name = "RSAT-AD-Tools","RSAT-DNS-Server" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' } } @@ -78,7 +85,7 @@ Configuration RODC_SECONDARY Credential = $DomainAdminCredential SafemodeAdministratorPassword = $LocalAdminCredential ReadOnlyReplica = $true - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } } } diff --git a/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 b/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 index 88c954ca..46954ce4 100644 --- a/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 +++ b/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 @@ -9,13 +9,15 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_DEFAULT { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } } } diff --git a/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 b/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 index 0a72edbd..0bddeb61 100644 --- a/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 +++ b/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 @@ -63,34 +63,28 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_DHCPDNS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" + Ensure = 'Present' + Name = 'DHCP' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } <# Add the DHCP Scope, Reservation and Options from the node configuration #> - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -109,7 +103,7 @@ Configuration STANDALONE_DHCPDNS } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -125,7 +119,7 @@ Configuration STANDALONE_DHCPDNS } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ @@ -148,12 +142,11 @@ Configuration STANDALONE_DHCPDNS { IsSingleInstance = 'Yes' IPAddresses = $Node.Forwarders - Credential = $DomainAdminCredential DependsOn = '[Computer]JoinDomain' } } - $count=0 + $count = 0 foreach ($ADZone in $Node.ADZones) { $count++ @@ -168,17 +161,16 @@ Configuration STANDALONE_DHCPDNS } } - $count=0 + $count = 0 foreach ($PrimaryZone in $Node.PrimaryZones) { $count++ - xDnsServerSecondaryZone "PrimaryZone$count" + xDnsServerPrimaryZone "PrimaryZone$count" { Ensure = 'Present' Name = $PrimaryZone.Name ZoneFile = $PrimaryZone.ZoneFile DynamicUpdate = $PrimaryZone.DynamicUpdate - Credential = $DomainAdminCredential DependsOn = '[Computer]JoinDomain' } } diff --git a/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 b/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 index e258e626..ede854e3 100644 --- a/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 +++ b/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 @@ -11,34 +11,28 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_INTERNET { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 Import-DscResource -ModuleName xWebAdministration Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - WindowsFeature WebServerInstall { - Ensure = "Present" - Name = "Web-WebServer" + Ensure = 'Present' + Name = 'Web-WebServer' } WindowsFeature DHCPInstall { - Ensure = "Present" - Name = "DHCP" + Ensure = 'Present' + Name = 'DHCP' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } # Create the default ncsi.txt. @@ -46,7 +40,7 @@ Configuration STANDALONE_INTERNET { Ensure = 'Present' DestinationPath = 'c:\inetpub\wwwroot\ncsi.txt' - Contents = "Microsoft NCSI" + Contents = 'Microsoft NCSI' Type = 'File' DependsOn = '[WindowsFeature]WebServerInstall' } @@ -55,7 +49,7 @@ Configuration STANDALONE_INTERNET Add the DHCP Scope, Reservation and Options from the node configuration #> - $count=0 + $count = 0 foreach ($Scope in $Node.Scopes) { $count++ @@ -74,7 +68,7 @@ Configuration STANDALONE_INTERNET } } - $count=0 + $count = 0 foreach ($Reservation in $Node.Reservations) { $count++ @@ -90,7 +84,7 @@ Configuration STANDALONE_INTERNET } } - $count=0 + $count = 0 foreach ($ScopeOption in $Node.ScopeOptions) { $count++ diff --git a/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 b/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 index 1409dbed..4173d3b8 100644 --- a/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 +++ b/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 @@ -10,36 +10,36 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_JENKINS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName cChoco Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { WindowsFeature NetFrameworkCore { - Ensure = "Present" - Name = "NET-Framework-Core" + Ensure = 'Present' + Name = 'NET-Framework-Core' } # Install Chocolatey cChocoInstaller installChoco { - InstallDir = "c:\choco" - DependsOn = "[WindowsFeature]NetFrameworkCore" + InstallDir = 'c:\choco' + DependsOn = '[WindowsFeature]NetFrameworkCore' } # Install JDK8 cChocoPackageInstaller installJdk8 { - Name = "jdk8" - DependsOn = "[cChocoInstaller]installChoco" + Name = 'jdk8' + DependsOn = '[cChocoInstaller]installChoco' } # Install Jenkins cChocoPackageInstaller installJenkins { - Name = "Jenkins" - DependsOn = "[cChocoInstaller]installChoco" + Name = 'Jenkins' + DependsOn = '[cChocoInstaller]installChoco' } # Set the Jenkins Port @@ -87,7 +87,7 @@ Configuration STANDALONE_JENKINS # Jenkins is already on correct port Return $true } - DependsOn = "[cChocoPackageInstaller]installJenkins" + DependsOn = '[cChocoPackageInstaller]installJenkins' } } } diff --git a/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 b/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 index 73d331c4..62ae7f3f 100644 --- a/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 +++ b/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 @@ -21,7 +21,7 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_ROOTCA { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryCSDsc Import-DscResource -ModuleName xPSDesiredStateConfiguration @@ -29,7 +29,9 @@ Configuration STANDALONE_ROOTCA # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } # Install the ADCS Certificate Authority @@ -85,7 +87,8 @@ Configuration STANDALONE_ROOTCA } # Configure the ADCS Web Enrollment - ADCSWebEnrollment ConfigWebEnrollment { + ADCSWebEnrollment ConfigWebEnrollment + { Ensure = 'Present' IsSingleInstance = 'Yes' CAConfig = 'CertSrv' diff --git a/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 b/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 index 2575b133..9d1b8ff5 100644 --- a/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 +++ b/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 @@ -13,14 +13,16 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_ROOTCA_NOSUBCA { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryCSDsc Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } # Install the ADCS Certificate Authority @@ -76,7 +78,8 @@ Configuration STANDALONE_ROOTCA_NOSUBCA } # Configure the ADCS Web Enrollment - ADCSWebEnrollment ConfigWebEnrollment { + ADCSWebEnrollment ConfigWebEnrollment + { Ensure = 'Present' IsSingleInstance = 'Yes' CAConfig = 'CertSrv' diff --git a/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 index 913296df..719073f2 100644 --- a/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 @@ -23,19 +23,19 @@ DSC Template Configuration File For use by LabBuilder Configuration MEMBER_ROOTCA { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName ActiveDirectoryCSDsc Import-DscResource -ModuleName xPSDesiredStateConfiguration Import-DscResource -ModuleName NetworkingDsc Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } # Install the CA Service diff --git a/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 index e7157146..8a116abd 100644 --- a/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 @@ -5,8 +5,8 @@ DSC Template Configuration File For use by LabBuilder .Desription Builds a Domain Controller as the first DC in a forest with the name of the Domain Name parameter passed. .Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' ###################################################################################################> Configuration DC @@ -39,42 +39,47 @@ Configuration DC $OUName ) - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 # Assemble the Local Admin Credentials if ($LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) } + if ($DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) } WindowsFeature BackupInstall { - Ensure = "Present" - Name = "Windows-Server-Backup" + Ensure = 'Present' + Name = 'Windows-Server-Backup' } WindowsFeature DNSInstall { - Ensure = "Present" - Name = "DNS" + Ensure = 'Present' + Name = 'DNS' } WindowsFeature ADDSInstall { - Ensure = "Present" - Name = "AD-Domain-Services" - DependsOn = "[WindowsFeature]DNSInstall" + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' } WindowsFeature RSAT-AD-PowerShellInstall { - Ensure = "Present" - Name = "RSAT-AD-PowerShell" - DependsOn = "[WindowsFeature]ADDSInstall" + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' } ADDomain ADDomainCreateDC @@ -82,7 +87,7 @@ Configuration DC DomainName = $DomainName Credential = $DomainAdminCredential SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = "[WindowsFeature]ADDSInstall" + DependsOn = '[WindowsFeature]ADDSInstall' } WaitForADDomain DscDomainWait @@ -91,7 +96,7 @@ Configuration DC Credential = $DomainAdminCredential WaitTimeout = 300 RestartCount = 5 - DependsOn = "[WindowsFeature]ADDSInstall" + DependsOn = '[WindowsFeature]ADDSInstall' } ADOrganizationalUnit NewOU @@ -101,6 +106,6 @@ Configuration DC ProtectedFromAccidentalDeletion = $true Description = $OUDescription Ensure = 'Present' - DependsOn = "[WaitForADDomain]DscDomainWait" + DependsOn = '[WaitForADDomain]DscDomainWait' } } diff --git a/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 index 2b10745c..4a1bc236 100644 --- a/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 @@ -33,7 +33,7 @@ Configuration DHCP $Reservations ) - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 # Assemble the Local Admin Credentials diff --git a/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 index a09cca50..a8749b30 100644 --- a/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 @@ -37,17 +37,17 @@ Configuration FILESERVER ) - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName StorageDsc Import-DscResource -ModuleName NetworkingDsc # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + $DomainAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) } WindowsFeature FileServerInstall @@ -163,22 +163,22 @@ Configuration FILESERVER Enabled = 'True' } - [System.Int32]$Count=0 + [System.Int32]$count=0 ForEach ($Disk in $Disks) { - $Count++ + $count++ - WaitforDisk Disk$Count + WaitforDisk Disk$count { DiskNumber = $Disk.Number RetryIntervalSec = 60 RetryCount = 60 } - Disk Volume$Count + Disk Volume$count { DiskNumber = $Disk.Number DriveLetter = $Disk.Letter - DependsOn = "[WaitforDisk]Disk$Count" + DependsOn = "[WaitforDisk]Disk$count" } } } diff --git a/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 index f263f40d..82c7c49a 100644 --- a/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 @@ -40,8 +40,8 @@ Configuration JOINDOMAIN ) - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' - Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 Import-DscResource -ModuleName NetworkingDsc # Assemble the Local Admin Credentials diff --git a/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 index 3c50f8b5..79d1a1e2 100644 --- a/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 @@ -6,35 +6,35 @@ DSC Template Configuration File For use by LabBuilder Builds a Server that is joined to a domain and then contains NPS/Radius components. .Requires Windows Server 2012 R2 Full (Server core not supported). -.Parameters: +.Parameters: DomainName = "LABBUILDER.COM" DomainAdminPassword = "P@ssword!1" ###################################################################################################> Configuration NPS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration - WindowsFeature NPASPolicyServerInstall - { - Ensure = "Present" - Name = "NPAS-Policy-Server" - } + WindowsFeature NPASPolicyServerInstall + { + Ensure = "Present" + Name = "NPAS-Policy-Server" + } - WindowsFeature NPASHealthInstall - { - Ensure = "Present" - Name = "NPAS-Health" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" - } + WindowsFeature NPASHealthInstall + { + Ensure = "Present" + Name = "NPAS-Health" + DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + } WindowsFeature RSATNPAS - { - Ensure = "Present" - Name = "RSAT-NPAS" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" - } + { + Ensure = "Present" + Name = "RSAT-NPAS" + DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + } + - } diff --git a/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 index e1b96a2d..73da8730 100644 --- a/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 +++ b/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 @@ -4,28 +4,28 @@ DSC Template Configuration File For use by LabBuilder MEMBER_EDGE .Desription Builds a Server that is joined to a domain and then contains Remote Access components. -.Parameters: +.Parameters: DomainName = "LABBUILDER.COM" DomainAdminPassword = "P@ssword!1" ###################################################################################################> Configuration REMOTEACCESS { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName PSDesiredStateConfiguration - WindowsFeature DirectAccessVPNInstall - { - Ensure = "Present" - Name = "DirectAccess-VPN" - } + WindowsFeature DirectAccessVPNInstall + { + Ensure = "Present" + Name = "DirectAccess-VPN" + } + + WindowsFeature RoutingInstall + { + Ensure = "Present" + Name = "Routing" + DependsOn = "[WindowsFeature]DirectAccessVPNInstall" + } - WindowsFeature RoutingInstall - { - Ensure = "Present" - Name = "Routing" - DependsOn = "[WindowsFeature]DirectAccessVPNInstall" - } - } diff --git a/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 b/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 index e31744cc..49b600d5 100644 --- a/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 +++ b/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 @@ -9,14 +9,14 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_DEFAULT { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' -ModuleVersion 1.1 + Import-DscResource -ModuleName PSDesiredStateConfiguration -ModuleVersion 1.1 Import-DscResource -ModuleName xActiveDirectory 2.9.0.0 Import-DscResource ComputerManagementDsc -ModuleVersion 1.4.0.0 Import-DscResource xDHCPServer 1.3.0.0 Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } } } diff --git a/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt b/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt index 2390a7b2..d6bf2e62 100644 --- a/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt +++ b/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt @@ -9,7 +9,7 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_DEFAULT { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' -ModuleVersion '1.0' + Import-DscResource -ModuleName PSDesiredStateConfiguration -ModuleVersion '1.0' Import-DscResource -ModuleName 'xActiveDirectory' Import-DscResource -ModuleName 'ComputerManagementDsc' -ModuleVersion '1.4.0.0' Import-DscResource xDHCPServer 1.3.0.0 @@ -17,7 +17,7 @@ Configuration STANDALONE_DEFAULT Node $AllNodes.NodeName { # Assemble the Local Admin Credentials if ($Node.LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) } } } From 816a7fa64176bd668a002cf11eaf119f97d00ef2 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sun, 17 Nov 2019 15:14:23 +1300 Subject: [PATCH 02/14] Correct CHANGELOG.MD --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 256c2348..b64ece68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,22 +17,22 @@ - `dsclibrary\MEMBER_DHCP.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example. + - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example. + - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example. + - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\MEMBER_DNS.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - `dsclibrary\STNADALONE_DHCPDNS.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example. + - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\STNADALONE_INTERNET.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. From b0a0714d91a58736b192864cff675ca2925ec123 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sun, 17 Nov 2019 15:38:48 +1300 Subject: [PATCH 03/14] Fix tests --- test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 | 2 +- test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 b/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 index 49b600d5..71e80e15 100644 --- a/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 +++ b/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 @@ -9,7 +9,7 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_DEFAULT { - Import-DscResource -ModuleName PSDesiredStateConfiguration -ModuleVersion 1.1 + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' -ModuleVersion 1.1 Import-DscResource -ModuleName xActiveDirectory 2.9.0.0 Import-DscResource ComputerManagementDsc -ModuleVersion 1.4.0.0 Import-DscResource xDHCPServer 1.3.0.0 diff --git a/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt b/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt index d6bf2e62..1abe4dfc 100644 --- a/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt +++ b/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt @@ -9,7 +9,7 @@ DSC Template Configuration File For use by LabBuilder Configuration STANDALONE_DEFAULT { - Import-DscResource -ModuleName PSDesiredStateConfiguration -ModuleVersion '1.0' + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' -ModuleVersion '1.0' Import-DscResource -ModuleName 'xActiveDirectory' Import-DscResource -ModuleName 'ComputerManagementDsc' -ModuleVersion '1.4.0.0' Import-DscResource xDHCPServer 1.3.0.0 From 7cff19cc9aef84172ed53f31da8867578ae9bb56 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sun, 19 Apr 2020 18:13:16 +1200 Subject: [PATCH 04/14] Convert to new CD Pipeline --- source/LabBuilder.psd1 | 123 + source/LabBuilder.psm1 | 1 + source/build.psd1 | 6 + .../dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 | 146 + source/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 | 233 + source/dsclibrary/DC_SECONDARY.DSC.ps1 | 142 + source/dsclibrary/MEMBER_ADFS.DSC.ps1 | 86 + source/dsclibrary/MEMBER_ADRMS.DSC.ps1 | 75 + .../MEMBER_BRANCHCACHE_HOST.DSC.ps1 | 76 + .../dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 | 141 + source/dsclibrary/MEMBER_DEFAULT.DSC.ps1 | 51 + source/dsclibrary/MEMBER_DFSHUB.DSC.ps1 | 172 + source/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 | 85 + source/dsclibrary/MEMBER_DHCP.DSC.ps1 | 176 + source/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 | 217 + source/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 | 186 + source/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 | 187 + source/dsclibrary/MEMBER_DNS.DSC.ps1 | 93 + .../dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 | 95 + .../MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 | 226 + .../MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 | 211 + .../MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 | 123 + source/dsclibrary/MEMBER_FILESERVER.DSC.ps1 | 182 + .../MEMBER_FILESERVER_FSRMTEST.DSC.ps1 | 451 ++ .../MEMBER_FILESERVER_ISCSI.DSC.ps1 | 250 + source/dsclibrary/MEMBER_IPAM.DSC.ps1 | 57 + source/dsclibrary/MEMBER_JENKINS.DSC.ps1 | 124 + source/dsclibrary/MEMBER_NANO.DSC.ps1 | 36 + source/dsclibrary/MEMBER_NLB.DSC.ps1 | 66 + source/dsclibrary/MEMBER_NPS.DSC.ps1 | 68 + source/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 | 92 + source/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 | 59 + .../MEMBER_REMOTEACCESS_WAP.DSC.ps1 | 67 + source/dsclibrary/MEMBER_ROOTCA.DSC.ps1 | 343 ++ .../dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 | 142 + .../dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 | 140 + source/dsclibrary/MEMBER_SUBCA.DSC.ps1 | 396 ++ source/dsclibrary/MEMBER_WDS.DSC.ps1 | 89 + source/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 | 238 + source/dsclibrary/MEMBER_WSUS.DSC.ps1 | 84 + source/dsclibrary/RODC_SECONDARY.DSC.ps1 | 91 + source/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 | 23 + source/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 | 178 + source/dsclibrary/STANDALONE_INTERNET.DSC.ps1 | 103 + source/dsclibrary/STANDALONE_JENKINS.DSC.ps1 | 93 + source/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 | 326 ++ .../STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 | 216 + .../dsclibrary/modules/LabDSCResources.psd1 | 7 + .../xCertAuthorityServer.DSC.Schema.psd1 | 1 + .../xCertAuthorityServer.DSC.Schema.psm1 | 250 + .../MyDSCResources/xDC/xDC.DSC.Schema.psd1 | 1 + .../MyDSCResources/xDC/xDC.DSC.Schema.psm1 | 111 + .../xDHCPServer/xDHCPServer.DSC.Schema.psd1 | 1 + .../xDHCPServer/xDHCPServer.DSC.Schema.psm1 | 119 + .../xFileServer/xFileServer.DSC.Schema.psd1 | 1 + .../xFileServer/xFileServer.DSC.Schema.psm1 | 184 + .../xJoinDomain/xJoinDomain.DSC.Schema.psd1 | 1 + .../xJoinDomain/xJoinDomain.DSC.Schema.psm1 | 72 + .../xNPSServer/xNPSServer.DSC.Schema.psd1 | 1 + .../xNPSServer/xNPSServer.DSC.Schema.psm1 | 40 + .../xRemoteAccessServer.DSC.Schema.psd1 | 1 + .../xRemoteAccessServer.DSC.Schema.psm1 | 31 + source/en-US/LabBuilder_LocalizedData.psd1 | 241 + source/en-US/about_LabBuilder.help.txt | 24 + source/prefix.ps1 | 539 +++ .../Assert-LabValidConfigurationXMLSchema.ps1 | 88 + source/private/Assert-LabValidIpAddress.ps1 | 41 + source/private/ConvertTo-LabAbsolutePath.ps1 | 37 + source/private/Copy-LabOdjFile.ps1 | 149 + source/private/Enable-LabWSMan.ps1 | 71 + source/private/Get-LabBuilderModulePath.ps1 | 15 + .../Get-LabCertificatePsFileContent.ps1 | 119 + source/private/Get-LabDSCNetworkingConfig.ps1 | 201 + .../private/Get-LabIntegrationServiceName.ps1 | 46 + .../private/Get-LabManagementSwitchName.ps1 | 42 + source/private/Get-LabModulesInDSCConfig.ps1 | 82 + source/private/Get-LabNextIpAddress.ps1 | 61 + source/private/Get-LabNextMacAddress.ps1 | 33 + source/private/Get-LabUnattendFileContent.ps1 | 169 + .../private/Get-LabVMManagementIPAddress.ps1 | 58 + source/private/Initialize-LabBootVHD.ps1 | 312 ++ source/private/Initialize-LabDSC.ps1 | 50 + .../Initialize-LabManagementSwitch.ps1 | 91 + source/private/Initialize-LabVHD.ps1 | 331 ++ source/private/Initialize-LabVMPath.ps1 | 65 + source/private/Install-LabHyperV.ps1 | 51 + source/private/Install-LabPackageProvider.ps1 | 67 + .../Invoke-LabDownloadAndUnzipFile.ps1 | 101 + .../Invoke-LabDownloadResourceModule.ps1 | 189 + source/private/New-LabCredential.ps1 | 27 + source/private/New-LabException.ps1 | 69 + .../New-LabHostSelfSignedCertificate.ps1 | 99 + .../private/New-LabVMInitializationFile.ps1 | 200 + .../Recieve-LabSelfSignedCertificate.ps1 | 135 + source/private/Register-LabPackageSource.ps1 | 112 + .../Request-LabSelfSignedCertificate.ps1 | 203 + source/private/Set-LabDSC.ps1 | 158 + source/private/Set-LabModulesInDSCConfig.ps1 | 129 + source/private/Set-LabSwitchAdapter.ps1 | 134 + source/private/Start-LabDSC.ps1 | 239 + source/private/Update-LabDSC.ps1 | 413 ++ source/private/Update-LabVMDataDisk.ps1 | 397 ++ source/private/Update-LabVMDvdDrive.ps1 | 108 + .../Update-LabVMIntegrationService.ps1 | 95 + .../Wait-LabVMInitializationComplete.ps1 | 151 + source/private/Wait-LabVMOff.ps1 | 27 + source/private/Wait-LabVMStarted.ps1 | 45 + source/private/Write-LabMessage.ps1 | 93 + source/public/Connect-LabVm.ps1 | 118 + source/public/Disconnect-LabVm.ps1 | 73 + source/public/Get-Lab.ps1 | 173 + source/public/Get-LabResourceIso.ps1 | 92 + source/public/Get-LabResourceModule.ps1 | 49 + source/public/Get-LabResourceMsu.ps1 | 64 + source/public/Get-LabSwitch.ps1 | 116 + source/public/Get-LabVMTemplate.ps1 | 296 ++ source/public/Get-LabVm.ps1 | 989 ++++ source/public/Get-LabVmTemplateVhd.ps1 | 290 ++ source/public/Initialize-LabResourceIso.ps1 | 78 + .../public/Initialize-LabResourceModule.ps1 | 57 + source/public/Initialize-LabResourceMsu.ps1 | 44 + source/public/Initialize-LabSwitch.ps1 | 320 ++ source/public/Initialize-LabVm.ps1 | 318 ++ source/public/Initialize-LabVmTemplate.ps1 | 228 + source/public/Initialize-LabVmTemplateVhd.ps1 | 364 ++ source/public/Install-Lab.ps1 | 196 + source/public/Install-LabVm.ps1 | 90 + source/public/New-Lab.ps1 | 157 + source/public/Remove-LabSwitch.ps1 | 138 + source/public/Remove-LabVMTemplate.ps1 | 50 + source/public/Remove-LabVm.ps1 | 92 + source/public/Remove-LabVmTemplateVhd.ps1 | 56 + source/public/Start-Lab.ps1 | 174 + source/public/Stop-Lab.ps1 | 146 + source/public/Uninstall-Lab.ps1 | 156 + source/public/Update-Lab.ps1 | 50 + .../samples/Sample_WS2012R2_DCandDHCPOnly.xml | 166 + .../Sample_WS2012R2_DCandDHCPOnly_NAT.xml | 149 + .../Sample_WS2012R2_DCandDHCPandEdge.xml | 203 + .../Sample_WS2012R2_DomainClustering.xml | 719 +++ .../Sample_WS2012R2_DomainComplete.xml | 691 +++ .../samples/Sample_WS2012R2_DomainSQL2014.xml | 263 ++ .../samples/Sample_WS2012R2_MultiForest.xml | 416 ++ .../Sample_WS2012R2_MultiForest_ADFS.xml | 586 +++ source/samples/Sample_WS2012R2_Simple.xml | 85 + .../samples/Sample_WS2016_DCandDHCPOnly.xml | 132 + .../samples/Sample_WS2016_DCandDHCPandCA.xml | 201 + .../Sample_WS2016_DCandDHCPandEdge.xml | 194 + .../samples/Sample_WS2016_DFSHubAndSpoke.xml | 311 ++ .../Sample_WS2016_DomainClustering.xml | 710 +++ .../samples/Sample_WS2016_DomainComplete.xml | 687 +++ .../samples/Sample_WS2016_DomainFunctions.xml | 330 ++ .../samples/Sample_WS2016_DomainSQL2016.xml | 257 ++ source/samples/Sample_WS2016_NanoDomain.xml | 328 ++ source/samples/Sample_WS2016_Simple.xml | 78 + .../samples/Sample_WS2019_AzureADConnect.xml | 225 + .../samples/Sample_WS2019_DCandDHCPandCA.xml | 201 + .../Sample_WS2019_DCandDHCPandEdge.xml | 194 + source/samples/Sample_WS2019_NanoDomain.xml | 328 ++ source/samples/Sample_WS2019_Simple.xml | 78 + ...ut Windows installation ISO files here.txt | 1 + source/schema/labbuilderconfig-schema.xsd | 1569 +++++++ source/suffix.ps1 | 0 .../Convert-LabBuilderConfigSchemaToMD.ps1 | 14 + source/support/Convert-WindowsImage.ps1 | 4053 +++++++++++++++++ source/support/Convert-XSDToMD.ps1 | 24 + .../support/New-SelfSignedCertificateEx.ps1 | 501 ++ source/support/tools/msxsl.exe | Bin 0 -> 24896 bytes .../labbuilderconfig-schema-transformtomd.xsl | 95 + source/template/labbuilderconfig-template.xml | 145 + 170 files changed, 32601 insertions(+) create mode 100644 source/LabBuilder.psd1 create mode 100644 source/LabBuilder.psm1 create mode 100644 source/build.psd1 create mode 100644 source/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 create mode 100644 source/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 create mode 100644 source/dsclibrary/DC_SECONDARY.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_ADFS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_ADRMS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DEFAULT.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DFSHUB.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DHCP.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DNS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_FILESERVER.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_IPAM.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_JENKINS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_NANO.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_NLB.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_NPS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_ROOTCA.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_SUBCA.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_WDS.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 create mode 100644 source/dsclibrary/MEMBER_WSUS.DSC.ps1 create mode 100644 source/dsclibrary/RODC_SECONDARY.DSC.ps1 create mode 100644 source/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 create mode 100644 source/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 create mode 100644 source/dsclibrary/STANDALONE_INTERNET.DSC.ps1 create mode 100644 source/dsclibrary/STANDALONE_JENKINS.DSC.ps1 create mode 100644 source/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 create mode 100644 source/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 create mode 100644 source/dsclibrary/modules/LabDSCResources.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 create mode 100644 source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 create mode 100644 source/en-US/LabBuilder_LocalizedData.psd1 create mode 100644 source/en-US/about_LabBuilder.help.txt create mode 100644 source/prefix.ps1 create mode 100644 source/private/Assert-LabValidConfigurationXMLSchema.ps1 create mode 100644 source/private/Assert-LabValidIpAddress.ps1 create mode 100644 source/private/ConvertTo-LabAbsolutePath.ps1 create mode 100644 source/private/Copy-LabOdjFile.ps1 create mode 100644 source/private/Enable-LabWSMan.ps1 create mode 100644 source/private/Get-LabBuilderModulePath.ps1 create mode 100644 source/private/Get-LabCertificatePsFileContent.ps1 create mode 100644 source/private/Get-LabDSCNetworkingConfig.ps1 create mode 100644 source/private/Get-LabIntegrationServiceName.ps1 create mode 100644 source/private/Get-LabManagementSwitchName.ps1 create mode 100644 source/private/Get-LabModulesInDSCConfig.ps1 create mode 100644 source/private/Get-LabNextIpAddress.ps1 create mode 100644 source/private/Get-LabNextMacAddress.ps1 create mode 100644 source/private/Get-LabUnattendFileContent.ps1 create mode 100644 source/private/Get-LabVMManagementIPAddress.ps1 create mode 100644 source/private/Initialize-LabBootVHD.ps1 create mode 100644 source/private/Initialize-LabDSC.ps1 create mode 100644 source/private/Initialize-LabManagementSwitch.ps1 create mode 100644 source/private/Initialize-LabVHD.ps1 create mode 100644 source/private/Initialize-LabVMPath.ps1 create mode 100644 source/private/Install-LabHyperV.ps1 create mode 100644 source/private/Install-LabPackageProvider.ps1 create mode 100644 source/private/Invoke-LabDownloadAndUnzipFile.ps1 create mode 100644 source/private/Invoke-LabDownloadResourceModule.ps1 create mode 100644 source/private/New-LabCredential.ps1 create mode 100644 source/private/New-LabException.ps1 create mode 100644 source/private/New-LabHostSelfSignedCertificate.ps1 create mode 100644 source/private/New-LabVMInitializationFile.ps1 create mode 100644 source/private/Recieve-LabSelfSignedCertificate.ps1 create mode 100644 source/private/Register-LabPackageSource.ps1 create mode 100644 source/private/Request-LabSelfSignedCertificate.ps1 create mode 100644 source/private/Set-LabDSC.ps1 create mode 100644 source/private/Set-LabModulesInDSCConfig.ps1 create mode 100644 source/private/Set-LabSwitchAdapter.ps1 create mode 100644 source/private/Start-LabDSC.ps1 create mode 100644 source/private/Update-LabDSC.ps1 create mode 100644 source/private/Update-LabVMDataDisk.ps1 create mode 100644 source/private/Update-LabVMDvdDrive.ps1 create mode 100644 source/private/Update-LabVMIntegrationService.ps1 create mode 100644 source/private/Wait-LabVMInitializationComplete.ps1 create mode 100644 source/private/Wait-LabVMOff.ps1 create mode 100644 source/private/Wait-LabVMStarted.ps1 create mode 100644 source/private/Write-LabMessage.ps1 create mode 100644 source/public/Connect-LabVm.ps1 create mode 100644 source/public/Disconnect-LabVm.ps1 create mode 100644 source/public/Get-Lab.ps1 create mode 100644 source/public/Get-LabResourceIso.ps1 create mode 100644 source/public/Get-LabResourceModule.ps1 create mode 100644 source/public/Get-LabResourceMsu.ps1 create mode 100644 source/public/Get-LabSwitch.ps1 create mode 100644 source/public/Get-LabVMTemplate.ps1 create mode 100644 source/public/Get-LabVm.ps1 create mode 100644 source/public/Get-LabVmTemplateVhd.ps1 create mode 100644 source/public/Initialize-LabResourceIso.ps1 create mode 100644 source/public/Initialize-LabResourceModule.ps1 create mode 100644 source/public/Initialize-LabResourceMsu.ps1 create mode 100644 source/public/Initialize-LabSwitch.ps1 create mode 100644 source/public/Initialize-LabVm.ps1 create mode 100644 source/public/Initialize-LabVmTemplate.ps1 create mode 100644 source/public/Initialize-LabVmTemplateVhd.ps1 create mode 100644 source/public/Install-Lab.ps1 create mode 100644 source/public/Install-LabVm.ps1 create mode 100644 source/public/New-Lab.ps1 create mode 100644 source/public/Remove-LabSwitch.ps1 create mode 100644 source/public/Remove-LabVMTemplate.ps1 create mode 100644 source/public/Remove-LabVm.ps1 create mode 100644 source/public/Remove-LabVmTemplateVhd.ps1 create mode 100644 source/public/Start-Lab.ps1 create mode 100644 source/public/Stop-Lab.ps1 create mode 100644 source/public/Uninstall-Lab.ps1 create mode 100644 source/public/Update-Lab.ps1 create mode 100644 source/samples/Sample_WS2012R2_DCandDHCPOnly.xml create mode 100644 source/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml create mode 100644 source/samples/Sample_WS2012R2_DCandDHCPandEdge.xml create mode 100644 source/samples/Sample_WS2012R2_DomainClustering.xml create mode 100644 source/samples/Sample_WS2012R2_DomainComplete.xml create mode 100644 source/samples/Sample_WS2012R2_DomainSQL2014.xml create mode 100644 source/samples/Sample_WS2012R2_MultiForest.xml create mode 100644 source/samples/Sample_WS2012R2_MultiForest_ADFS.xml create mode 100644 source/samples/Sample_WS2012R2_Simple.xml create mode 100644 source/samples/Sample_WS2016_DCandDHCPOnly.xml create mode 100644 source/samples/Sample_WS2016_DCandDHCPandCA.xml create mode 100644 source/samples/Sample_WS2016_DCandDHCPandEdge.xml create mode 100644 source/samples/Sample_WS2016_DFSHubAndSpoke.xml create mode 100644 source/samples/Sample_WS2016_DomainClustering.xml create mode 100644 source/samples/Sample_WS2016_DomainComplete.xml create mode 100644 source/samples/Sample_WS2016_DomainFunctions.xml create mode 100644 source/samples/Sample_WS2016_DomainSQL2016.xml create mode 100644 source/samples/Sample_WS2016_NanoDomain.xml create mode 100644 source/samples/Sample_WS2016_Simple.xml create mode 100644 source/samples/Sample_WS2019_AzureADConnect.xml create mode 100644 source/samples/Sample_WS2019_DCandDHCPandCA.xml create mode 100644 source/samples/Sample_WS2019_DCandDHCPandEdge.xml create mode 100644 source/samples/Sample_WS2019_NanoDomain.xml create mode 100644 source/samples/Sample_WS2019_Simple.xml create mode 100644 source/samples/isofiles/Put Windows installation ISO files here.txt create mode 100644 source/schema/labbuilderconfig-schema.xsd create mode 100644 source/suffix.ps1 create mode 100644 source/support/Convert-LabBuilderConfigSchemaToMD.ps1 create mode 100644 source/support/Convert-WindowsImage.ps1 create mode 100644 source/support/Convert-XSDToMD.ps1 create mode 100644 source/support/New-SelfSignedCertificateEx.ps1 create mode 100644 source/support/tools/msxsl.exe create mode 100644 source/support/transform/labbuilderconfig-schema-transformtomd.xsl create mode 100644 source/template/labbuilderconfig-template.xml diff --git a/source/LabBuilder.psd1 b/source/LabBuilder.psd1 new file mode 100644 index 00000000..04bc2f07 --- /dev/null +++ b/source/LabBuilder.psd1 @@ -0,0 +1,123 @@ +@{ + + # Script module or binary module file associated with this manifest. + RootModule = 'LabBuilder.psm1' + + # Version number of this module. + ModuleVersion = '0.0.1' + + # Supported PSEditions + CompatiblePSEditions = 'Desktop' + + # ID used to uniquely identify this module + GUID = 'e229850e-7a90-4123-9a30-37814119d3a3' + + # Author of this module + Author = 'Daniel Scott-Raynsford' + + # Company or vendor of this module + CompanyName = 'None' + + # Copyright statement for this module + Copyright = '(c) Daniel Scott-Raynsford. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'Builds Hyper-V Windows multi-machine/Active Directory labs using XML configuration files and DSC Resources.' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '5.1' + + # Name of the Windows PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the Windows PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # CLRVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + ProcessorArchitecture = 'None' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = @('Get-LabResourceModule', 'Initialize-LabResourceModule', + 'Get-LabResourceMSU', 'Initialize-LabResourceMSU', + 'Get-LabResourceISO', 'Initialize-LabResourceISO', 'Get-LabSwitch', + 'Initialize-LabSwitch', 'Remove-LabSwitch', 'Get-LabVMTemplateVHD', + 'Initialize-LabVMTemplateVHD', 'Remove-LabVMTemplateVHD', + 'Get-LabVMTemplate', 'Initialize-LabVMTemplate', + 'Remove-LabVMTemplate', 'Get-LabVM', 'Initialize-LabVM', + 'Install-LabVM', 'Remove-LabVM', 'Get-Lab', 'New-Lab', 'Install-Lab', + 'Update-Lab', 'Uninstall-Lab', 'Start-Lab', 'Stop-Lab') + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = '*' + + # Variables to export from this module + VariablesToExport = '*' + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('Hyper-V', 'Lab', 'DesiredStateConfiguration', 'DSC', 'PSEdition_Desktop') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/PlagueHO/LabBuilder/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/PlagueHO/LabBuilder' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = '' + + Prerelease = '' + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} diff --git a/source/LabBuilder.psm1 b/source/LabBuilder.psm1 new file mode 100644 index 00000000..9f6aee56 --- /dev/null +++ b/source/LabBuilder.psm1 @@ -0,0 +1 @@ +# This file will be generated diff --git a/source/build.psd1 b/source/build.psd1 new file mode 100644 index 00000000..afbc9a75 --- /dev/null +++ b/source/build.psd1 @@ -0,0 +1,6 @@ +@{ + Path = 'CosmosDB.psd1' #or build breaks on Linux +} +# Waiting for ModuleBuilder to do away with this file +# when all parameters are provided to the function + diff --git a/source/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 b/source/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 new file mode 100644 index 00000000..cb4a305a --- /dev/null +++ b/source/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 @@ -0,0 +1,146 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + DC_FORESTCHILDDOMAIN +.Desription + Builds a Domain Controller and creates it as the first DC in a new child domain within the + existing forest specified in the DomainName parameter. + Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional + configuration of the DNS Server. +.Parameters: + ParentDomainName = 'LABBUILDER.COM' + DomainName = 'DEV' + DomainAdminPassword = 'P@ssword!1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + ADZones = @( + @{ Name = 'ALPHA.LOCAL'; + DynamicUpdate = 'Secure'; + ReplicationScope = 'Forest'; + } + ) + PrimaryZones = @( + @{ Name = 'BRAVO.LOCAL'; + ZoneFile = 'bravo.local.dns'; + DynamicUpdate = 'None'; + } + ) +###################################################################################################> + +Configuration DC_FORESTCHILDDOMAIN +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.ParentDomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature BackupInstall + { + Ensure = 'Present' + Name = 'Windows-Server-Backup' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + WindowsFeature ADDSInstall + { + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' + } + + WindowsFeature RSAT-AD-PowerShellInstall + { + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' + } + } + + WaitForADDomain DscDomainWait + { + DomainName = $Node.ParentDomainName + Credential = $DomainAdminCredential + WaitTimeout = 300 + RestartCount = 5 + DependsOn = '[WindowsFeature]ADDSInstall' + } + + ADDomain PrimaryDC + { + DomainName = $Node.DomainName + ParentDomainName = $Node.ParentDomainName + Credential = $DomainAdminCredential + SafemodeAdministratorPassword = $LocalAdminCredential + DependsOn = '[WaitForADDomain]DscDomainWait' + } + + # DNS Server Settings + if ($Node.Forwarders) + { + xDnsServerForwarder DNSForwarders + { + IsSingleInstance = 'Yes' + IPAddresses = $Node.Forwarders + DependsOn = '[ADDomain]PrimaryDC' + } + } + + $count = 0 + foreach ($ADZone in $Node.ADZones) + { + $count++ + xDnsServerADZone "ADZone$count" + { + Ensure = 'Present' + Name = $ADZone.Name + DynamicUpdate = $ADZone.DynamicUpdate + ReplicationScope = $ADZone.ReplicationScope + DependsOn = '[ADDomain]PrimaryDC' + } + } + + $count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" + { + Ensure = 'Present' + Name = $PrimaryZone.Name + ZoneFile = $PrimaryZone.ZoneFile + DynamicUpdate = $PrimaryZone.DynamicUpdate + DependsOn = '[ADDomain]PrimaryDC' + } + } + } +} diff --git a/source/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 b/source/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 new file mode 100644 index 00000000..06156eef --- /dev/null +++ b/source/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 @@ -0,0 +1,233 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + DC_FORESTPRIMARY +.Desription + Builds a Domain Controller as the first DC in a forest with the name of the Domain Name + parameter passed. + The optional parameter DomainNetBiosName can be used to set the NetBios name of the domain + if it needs to be different from the DomainName. + Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional + configuration of the DNS Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainNetBiosName = 'LABBUILDER' + DomainAdminPassword = 'P@ssword!1' + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + ADZones = @( + @{ Name = 'ALPHA.LOCAL'; + DynamicUpdate = 'Secure'; + ReplicationScope = 'Forest'; + } + ) + PrimaryZones = @( + @{ Name = 'BRAVO.LOCAL'; + ZoneFile = 'bravo.local.dns'; + DynamicUpdate = 'None'; + } + ) +###################################################################################################> + +Configuration DC_FORESTPRIMARY +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature BackupInstall + { + Ensure = 'Present' + Name = 'Windows-Server-Backup' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + WindowsFeature ADDSInstall + { + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' + } + + WindowsFeature RSAT-AD-PowerShellInstall + { + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' + } + } + + if ($Node.DomainNetBiosName) + { + ADDomain PrimaryDC + { + DomainName = $Node.DomainName + DomainNetBiosName = $Node.DomainNetBiosName + Credential = $DomainAdminCredential + SafemodeAdministratorPassword = $LocalAdminCredential + DependsOn = '[WindowsFeature]ADDSInstall' + } + } + else + { + ADDomain PrimaryDC + { + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + SafemodeAdministratorPassword = $LocalAdminCredential + DependsOn = '[WindowsFeature]ADDSInstall' + } + } + + WaitForADDomain DscDomainWait + { + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + WaitTimeout = 300 + RestartCount = 5 + DependsOn = '[ADDomain]PrimaryDC' + } + + # Enable AD Recycle bin + ADOptionalFeature RecycleBin + { + FeatureName = 'Recycle Bin Feature' + EnterpriseAdministratorCredential = $DomainAdminCredential + ForestFQDN = $Node.DomainName + DependsOn = '[WaitForADDomain]DscDomainWait' + } + + # Install a KDS Root Key so we can create MSA/gMSA accounts + Script CreateKDSRootKey + { + SetScript = { + Add-KDSRootKey -EffectiveTime ((Get-Date).AddHours(-10)) } + GetScript = { + Return @{ + KDSRootKey = (Get-KDSRootKey) + } + } + TestScript = { + if (-not (Get-KDSRootKey)) + { + Write-Verbose -Message 'KDS Root Key Needs to be installed...' + Return $false + } + Return $true + } + DependsOn = '[WaitForADDomain]DscDomainWait' + } + + # DNS Server Settings + if ($Node.Forwarders) + { + xDnsServerForwarder DNSForwarders + { + IsSingleInstance = 'Yes' + IPAddresses = $Node.Forwarders + DependsOn = '[WaitForADDomain]DscDomainWait' + } + } + + $count = 0 + foreach ($ADZone in $Node.ADZones) + { + $count++ + xDnsServerADZone "ADZone$count" + { + Ensure = 'Present' + Name = $ADZone.Name + DynamicUpdate = $ADZone.DynamicUpdate + ReplicationScope = $ADZone.ReplicationScope + DependsOn = '[WaitForADDomain]DscDomainWait' + } + } + + $count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" + { + Ensure = 'Present' + Name = $PrimaryZone.Name + ZoneFile = $PrimaryZone.ZoneFile + DynamicUpdate = $PrimaryZone.DynamicUpdate + DependsOn = '[WaitForADDomain]DscDomainWait' + } + } + + <# + # Create a Reverse Lookup Zone + xDnsServerPrimaryZone GlobalNamesZone + { + Name = $Node.ReverseZone + DynamicUpdate = + Ensure = 'Present' + DependsOn = '[WaitForADDomain]DscDomainWait' + } + + # Create a Global Names zone - can't do this until the resource supports it + xDnsServerPrimaryZone GlobalNamesZone + { + Name = 'GlobalNames' + DynamicUpdate = + Ensure = 'Present' + DependsOn = '[WaitForADDomain]DscDomainWait' + } + + # Enable GlobalNames in DNS Server + Script InstallRootCACert + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Write-Verbose -Message 'Enabling Global Name Zone...' + Set-DNSServerGlobalNameZone -Enable + } + GetScript = { + Return @{ + Enable = (Get-DNSServerGlobalNameZone).Enable + } + } + TestScript = { + if (-not (Get-DNSServerGlobalNameZone).Enable) { + Write-Verbose -Message 'Global Name Zone needs to be enabled...' + Return $false + } + Return $true + } + DependsOn = '[xDnsServerPrimaryZone]GlobalNamesZone' + } +#> + } +} diff --git a/source/dsclibrary/DC_SECONDARY.DSC.ps1 b/source/dsclibrary/DC_SECONDARY.DSC.ps1 new file mode 100644 index 00000000..87459107 --- /dev/null +++ b/source/dsclibrary/DC_SECONDARY.DSC.ps1 @@ -0,0 +1,142 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + DC_SECONDARY +.Desription + Builds a Domain Controller and adds it to the existing domain provided in the Parameter + DomainName. + Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional + configuration of the DNS Server. +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + ADZones = @( + @{ Name = 'ALPHA.LOCAL'; + DynamicUpdate = 'Secure'; + ReplicationScope = 'Forest'; + } + ) + PrimaryZones = @( + @{ Name = 'BRAVO.LOCAL'; + ZoneFile = 'bravo.local.dns'; + DynamicUpdate = 'None'; + } + ) +###################################################################################################> + +Configuration DC_SECONDARY +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentName ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentName ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature BackupInstall + { + Ensure = 'Present' + Name = 'Windows-Server-Backup' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + WindowsFeature ADDSInstall + { + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' + } + + WindowsFeature RSAT-AD-PowerShellInstall + { + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' + } + } + + WaitForADDomain DscDomainWait + { + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + WaitTimeout = 300 + RestartCount = 5 + DependsOn = '[WindowsFeature]ADDSInstall' + } + + ADDomainController SecondaryDC + { + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + SafemodeAdministratorPassword = $LocalAdminCredential + DependsOn = '[WaitForADDomain]DscDomainWait' + } + + # DNS Server Settings + if ($Node.Forwarders) + { + xDnsServerForwarder DNSForwarders + { + IsSingleInstance = 'Yes' + IPAddresses = $Node.Forwarders + DependsOn = '[ADDomainController]SecondaryDC' + } + } + [System.Int32]$count = 0 + foreach ($ADZone in $Node.ADZones) + { + $count++ + xDnsServerADZone "ADZone$count" + { + Ensure = 'Present' + Name = $ADZone.Name + DynamicUpdate = $ADZone.DynamicUpdate + ReplicationScope = $ADZone.ReplicationScope + DependsOn = '[ADDomainController]SecondaryDC' + } + } + [System.Int32]$count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" + { + Ensure = 'Present' + Name = $PrimaryZone.Name + ZoneFile = $PrimaryZone.ZoneFile + DynamicUpdate = $PrimaryZone.DynamicUpdate + DependsOn = '[ADDomainController]SecondaryDC' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_ADFS.DSC.ps1 b/source/dsclibrary/MEMBER_ADFS.DSC.ps1 new file mode 100644 index 00000000..1e43ca36 --- /dev/null +++ b/source/dsclibrary/MEMBER_ADFS.DSC.ps1 @@ -0,0 +1,86 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_ADFS +.Desription + Builds a Server that is joined to a domain and then made into an ADFS Server using WID. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_ADFS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature WIDInstall + { + Ensure = 'Present' + Name = 'Windows-Internal-Database' + } + + WindowsFeature ADFSInstall + { + Ensure = 'Present' + Name = 'ADFS-Federation' + DependsOn = '[WindowsFeature]WIDInstall' + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Enable ADFS FireWall rules + Firewall ADFSFirewall1 + { + Name = 'ADFSSrv-HTTP-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall ADFSFirewall2 + { + Name = 'ADFSSrv-HTTPS-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall ADFSFirewall3 + { + Name = 'ADFSSrv-SmartcardAuthN-HTTPS-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + } +} diff --git a/source/dsclibrary/MEMBER_ADRMS.DSC.ps1 b/source/dsclibrary/MEMBER_ADRMS.DSC.ps1 new file mode 100644 index 00000000..018b7ad9 --- /dev/null +++ b/source/dsclibrary/MEMBER_ADRMS.DSC.ps1 @@ -0,0 +1,75 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_ADRMS +.Desription + Builds a Server that is joined to a domain and then made into an ADRMS Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + ADFSSupport = $true +###################################################################################################> + +Configuration MEMBER_ADRMS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature WIDInstall + { + Ensure = 'Present' + Name = 'Windows-Internal-Database' + } + + WindowsFeature ADRMSServerInstall + { + Ensure = 'Present' + Name = 'ADRMS-Server' + DependsOn = '[WindowsFeature]WIDInstall' + } + + if ($Node.ADFSSupport) + { + WindowsFeature ADRMSIdentityInstall + { + Ensure = 'Present' + Name = 'ADRMS-Identity' + DependsOn = '[WindowsFeature]ADRMSServerInstall' + } + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 b/source/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 new file mode 100644 index 00000000..40cc46ee --- /dev/null +++ b/source/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 @@ -0,0 +1,76 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_BRANCHCACHE_HOST +.Desription + Builds a Server that is joined to a domain and then made into a BranchCache Hosted Mode Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_BRANCHCACHE_HOST +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature BranchCache + { + Ensure = 'Present' + Name = 'BranchCache' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Enable BranchCache Hosted Mode Firewall Fules + Firewall FSRMFirewall1 + { + Name = 'Microsoft-Windows-PeerDist-HostedServer-In' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall2 + { + Name = 'Microsoft-Windows-PeerDist-HostedServer-Out' + Ensure = 'Present' + Enabled = 'True' + } + } +} diff --git a/source/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 b/source/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 new file mode 100644 index 00000000..67f293fc --- /dev/null +++ b/source/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 @@ -0,0 +1,141 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_CONTAINER_HOST +.Desription + Builds a Server that is joined to a domain and then made into a Container Host with Docker. + + This should only be used on a Windows Server 2016 RTM host. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_CONTAINER_HOST +{ + $ProgramFiles = $ENV:ProgramFiles + $DockerPath = Join-Path -Path $ProgramFiles -ChildPath 'Docker' + $DockerZipFileName = 'docker.zip' + $DockerZipPath = Join-Path -Path $ProgramFiles -ChildPath $DockerZipFilename + $DockerUri = 'https://download.docker.com/components/engine/windows-server/cs-1.12/docker.zip' + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArguementList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArguementList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Install containers feature + WindowsFeature ContainerInstall + { + Ensure = 'Present' + Name = 'Containers' + } + + # Download Docker Engine + xRemoteFile DockerEngineDownload + { + DestinationPath = $ProgramFiles + Uri = $DockerUri + MatchSource = $false + } + + # Extract Docker Engine zip file + xArchive DockerEngineExtract + { + Destination = $ProgramFiles + Path = $DockerZipPath + Ensure = 'Present' + Validate = $false + Force = $true + DependsOn = '[xRemoteFile]DockerEngineDownload' + } + + # Add Docker to the Path + xEnvironment DockerPath + { + Ensure = 'Present' + Name = 'Path' + Value = $DockerPath + Path = $true + DependsOn = '[xArchive]DockerEngineExtract' + } + + <# + Reboot the system to complete Containers feature setup + Perform this after setting the Environment variable + so that PowerShell and other consoles can access it. + #> + PendingReboot Reboot + { + Name = 'Reboot After Containers' + } + + # Install the Docker Daemon as a service + Script DockerService + { + SetScript = { + $DockerDPath = (Join-Path -Path $Using:DockerPath -ChildPath 'dockerd.exe') + & $DockerDPath @('--register-service') + } + GetScript = { + return @{ + 'Service' = (Get-Service -Name Docker).Name + } + } + TestScript = { + if (Get-Service -Name Docker -ErrorAction SilentlyContinue) + { + return $true + } + return $false + } + DependsOn = '[xArchive]DockerEngineExtract' + } + + <# + Start up the Docker Service and ensure it is set + to start up automatically. + #> + xServiceSet DockerService + { + Ensure = 'Present' + Name = 'Docker' + StartupType = 'Automatic' + State = 'Running' + DependsOn = '[Script]DockerService' + } + } +} diff --git a/source/dsclibrary/MEMBER_DEFAULT.DSC.ps1 b/source/dsclibrary/MEMBER_DEFAULT.DSC.ps1 new file mode 100644 index 00000000..c15fb938 --- /dev/null +++ b/source/dsclibrary/MEMBER_DEFAULT.DSC.ps1 @@ -0,0 +1,51 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DEFAULT +.Desription + Builds a Server that is joined to a domain. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_DEFAULT +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_DFSHUB.DSC.ps1 b/source/dsclibrary/MEMBER_DFSHUB.DSC.ps1 new file mode 100644 index 00000000..bf2c1d8a --- /dev/null +++ b/source/dsclibrary/MEMBER_DFSHUB.DSC.ps1 @@ -0,0 +1,172 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FILESERVER +.Desription + Builds a Server that is joined to a domain and then made into a File Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + SpokeComputerName = @('Spoke1','Spoke2') + ResourceGroupName = 'WebSite' + ResourceGroupDescription = 'Files for web server' + ResourceGroupFolderName = 'WebSiteFiles' + ResourceGroupContentPath = 'd:\inetpub\wwwroot\WebSiteFiles' +###################################################################################################> + +Configuration MEMBER_DFSHUB +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName DFSDsc + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FileServerInstall + { + Ensure = 'Present' + Name = 'FS-FileServer' + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]FileServerInstall' + } + + WindowsFeature DFSReplicationInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' + } + + WindowsFeature RSATDFSMgmtConInstall + { + Ensure = 'Present' + Name = 'RSAT-DFS-Mgmt-Con' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + + WaitForAll WaitForAllSpokes + { + ResourceName = '[Disk]DVolume' + NodeName = $Node.SpokeComputerName + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[Computer]JoinDomain' + } + + # Configure the Replication Group + DFSReplicationGroup RGWebSite + { + GroupName = $Node.ResourceGroupName + Description = $Node.ResourceGroupDescription + Ensure = 'Present' + DomainName = $Node.DomainName + Members = @() + $Node.NodeName + $Node.SpokeComputerName + Folders = $Node.ResourceGroupFolderName + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = '[Disk]DVolume' + } # End of RGWebSite Resource + + DFSReplicationGroupFolder RGWebSiteFolder + { + GroupName = $Node.ResourceGroupName + FolderName = $Node.ResourceGroupFolderName + DomainName = $Node.DomainName + Description = $Node.ResourceGroupDescription + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = '[DFSReplicationGroup]RGWebSite' + } # End of RGWebSiteFolder Resource + + DFSReplicationGroupMembership RGWebSiteMembershipHub + { + GroupName = $Node.ResourceGroupName + FolderName = $Node.ResourceGroupFolderName + DomainName = $Node.DomainName + ComputerName = $Node.NodeName + ContentPath = $Node.ResourceGroupContentPath + PrimaryMember = $true + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' + } # End of RGWebSiteMembershipHub Resource + + # Configure the connection and membership for each Spoke + foreach ($spoke in $Node.SpokeComputerName) + { + DFSReplicationGroupConnection "RGWebSiteConnection$spoke" + { + GroupName = $Node.ResourceGroupName + DomainName = $Node.DomainName + Ensure = 'Present' + SourceComputerName = $Node.NodeName + DestinationComputerName = $spoke + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' + } # End of RGWebSiteConnection$spoke Resource + + DFSReplicationGroupMembership "RGWebSiteMembership$spoke" + { + GroupName = $Node.ResourceGroupName + FolderName = $Node.ResourceGroupFolderName + DomainName = $Node.DomainName + ComputerName = $spoke + ContentPath = $Node.ResourceGroupContentPath + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = "[DFSReplicationGroupConnection]RGWebSiteConnection$spoke" + } # End of RGWebSiteMembership$spoke Resource + } + } +} diff --git a/source/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 b/source/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 new file mode 100644 index 00000000..f2fc8cc8 --- /dev/null +++ b/source/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 @@ -0,0 +1,85 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DFSSPOKE +.Desription + Builds a Server that is joined to a domain and then made into a Spoke for a DFS Hub and Spoke + replication group. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_DFSSPOKE +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName DFSDsc + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FileServerInstall + { + Ensure = 'Present' + Name = 'FS-FileServer' + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]FileServerInstall' + } + + WindowsFeature DFSReplicationInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + } +} diff --git a/source/dsclibrary/MEMBER_DHCP.DSC.ps1 b/source/dsclibrary/MEMBER_DHCP.DSC.ps1 new file mode 100644 index 00000000..b5038e38 --- /dev/null +++ b/source/dsclibrary/MEMBER_DHCP.DSC.ps1 @@ -0,0 +1,176 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DHCP +.Desription + Builds a Server that is joined to a domain and then made into a DHCP Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Scopes = @( + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) +###################################################################################################> + +Configuration MEMBER_DHCP +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # DHCP Server Settings + Script DHCPAuthorize + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Add-DHCPServerInDC + } + GetScript = { + Return @{ + 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); + } + } + TestScript = { + Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) + } + DependsOn = '[Computer]JoinDomain' + } + + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + } + } + } +} diff --git a/source/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 b/source/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 new file mode 100644 index 00000000..130e2ea8 --- /dev/null +++ b/source/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 @@ -0,0 +1,217 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DHCPDNS +.Desription + Builds a Server that is joined to a domain and then made into a DHCP Server and DNS Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Scopes = @( + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) + Forwarders = @('8.8.8.8','8.8.4.4') + PrimaryZones = @( + @{ + Name = 'BRAVO.LOCAL' + ZoneFile = 'bravo.local.dns' + DynamicUpdate = 'None' + } + ) +###################################################################################################> + +Configuration MEMBER_DHCPDNS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + DependsOn = '[WindowsFeature]DHCPInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]DNSInstall' + } + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # DHCP Server Settings + Script DHCPAuthorize + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Add-DHCPServerInDC + } + GetScript = { + Return @{ + 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); + } + } + TestScript = { + Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) + } + DependsOn = '[Computer]JoinDomain' + } + + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + } + } + + # DNS Server Settings + if ($Node.Forwarders) + { + xDnsServerForwarder DNSForwarders + { + IsSingleInstance = 'Yes' + IPAddresses = $Node.Forwarders + DependsOn = '[Computer]JoinDomain' + } + } + + $count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" + { + Ensure = 'Present' + Name = $PrimaryZone.Name + ZoneFile = $PrimaryZone.ZoneFile + DynamicUpdate = $PrimaryZone.DynamicUpdate + DependsOn = '[Computer]JoinDomain' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 b/source/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 new file mode 100644 index 00000000..d10f6240 --- /dev/null +++ b/source/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 @@ -0,0 +1,186 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DHCPNPAS +.Desription + Builds a Server that is joined to a domain and then made into a DHCP Server. NPAS is also installed. + + This is for use on Windows Server 2012 R2 only. +.Notes + NPAS requires a full server install, so ensure that this OS is not a Core version. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Scopes = @( + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) +###################################################################################################> + +Configuration MEMBER_DHCPNPAS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature NPASPolicyServerInstall + { + Ensure = 'Present' + Name = 'NPAS-Policy-Server' + } + + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-NPAS' + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # DHCP Server Settings + Script DHCPAuthorize + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Add-DHCPServerInDC + } + GetScript = { + Return @{ + 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); + } + } + TestScript = { + Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) + } + DependsOn = '[Computer]JoinDomain' + } + + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + } + } + } +} diff --git a/source/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 b/source/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 new file mode 100644 index 00000000..ab6d5ce9 --- /dev/null +++ b/source/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 @@ -0,0 +1,187 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DHCPNPAS2016 +.Desription + Builds a Server that is joined to a domain and then made into a DHCP Server. NPAS is also installed. + + This is for use on Windows Server 2016 only. +.Notes + NPAS requires a full server install, so ensure that this OS is not a Core version. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Scopes = @( + @{ + Name = 'Site A Primary' + ScopeID = '192.168.128.0' + Start = '192.168.128.50' + End = '192.168.128.254' + SubnetMask = '255.255.255.0' + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ + Name = 'SA-DC1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000000' + IPAddress = '192.168.128.10' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DC2' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000001' + IPAddress = '192.168.128.11' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-DHCP1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000002' + IPAddress = '192.168.128.16' + AddressFamily = 'IPv4' + }, + @{ + Name = 'SA-EDGE1' + ScopeID = '192.168.128.0' + ClientMACAddress = '000000000005' + IPAddress = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ + ScopeID = '192.168.128.0' + DNServerIPAddress = @('192.168.128.10','192.168.128.11') + Router = '192.168.128.19' + AddressFamily = 'IPv4' + } + ) +###################################################################################################> + +Configuration MEMBER_DHCPNPAS2016 +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature NPASPolicyServerInstall + { + Ensure = 'Present' + Name = 'NPAS' + } + + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-DHCP', 'RSAT-NPAS' + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # DHCP Server Settings + Script DHCPAuthorize + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Add-DHCPServerInDC + } + GetScript = { + Return @{ + 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); + } + } + TestScript = { + Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) + } + DependsOn = '[Computer]JoinDomain' + } + + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + } + } + } +} diff --git a/source/dsclibrary/MEMBER_DNS.DSC.ps1 b/source/dsclibrary/MEMBER_DNS.DSC.ps1 new file mode 100644 index 00000000..c3a95178 --- /dev/null +++ b/source/dsclibrary/MEMBER_DNS.DSC.ps1 @@ -0,0 +1,93 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DNS +.Desription + Builds a Server that is joined to a domain and then made into a DNS Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + PrimaryZones = @( + @{ Name = 'BRAVO.LOCAL'; + ZoneFile = 'bravo.local.dns'; + DynamicUpdate = 'None'; + } + ) +###################################################################################################> + +Configuration MEMBER_DNS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]DNSInstall' + } + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # DNS Server Settings + if ($Node.Forwarders) + { + xDnsServerForwarder DNSForwarders + { + IsSingleInstance = 'Yes' + IPAddresses = $Node.Forwarders + DependsOn = '[Computer]JoinDomain' + } + } + $count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" + { + Ensure = 'Present' + Name = $PrimaryZone.Name + ZoneFile = $PrimaryZone.ZoneFile + DynamicUpdate = $PrimaryZone.DynamicUpdate + DependsOn = '[Computer]JoinDomain' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 b/source/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 new file mode 100644 index 00000000..db1c252f --- /dev/null +++ b/source/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 @@ -0,0 +1,95 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_DSCPULLSERVER +.Desription + Builds a Server that is joined to a domain and then made into an DSC Pull Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + Port = 8080 + PhysicalPath = 'D:\inetpub\PSDSCPullServer' + # Set to a valid certificate thumbprint to allow HTTP traffic + CertificateThumbprint = 'AllowUnencryptedTraffic' + RegistrationKey = '140a952b-b9d6-406b-b416-e0f759c9c0e4' +###################################################################################################> + +Configuration MEMBER_DSCPULLSERVER +{ + Import-DSCResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xWebAdministration + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature IISInstall + { + Ensure = 'Present' + Name = 'Web-Server' + } + + WindowsFeature AspNet45Install + { + Ensure = 'Present' + Name = 'Web-Asp-Net45' + } + + WindowsFeature WebMgmtServiceInstall + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + } + + WindowsFeature DSCServiceFeature + { + Ensure = 'Present' + Name = 'DSC-Service' + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + xDscWebService PSDSCPullServer + { + Ensure = 'Present' + EndpointName = 'PSDSCPullServer' + Port = $Node.Port + PhysicalPath = $Node.PhysicalPath + CertificateThumbPrint = $Node.CertificateThumbprint + ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" + ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" + State = 'Started' + DependsOn = '[WindowsFeature]DSCServiceFeature' + } + + File RegistrationKeyFile + { + Ensure = 'Present' + Type = 'File' + DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt" + Contents = $Node.RegistrationKey + } + } +} diff --git a/source/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 b/source/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 new file mode 100644 index 00000000..b6cdc93b --- /dev/null +++ b/source/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 @@ -0,0 +1,226 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FAILOVERCLUSTER_DHCP +.Desription + Builds a Network failover clustering node for use as a DHCP Server. + It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + ISCSIServerName = 'SA-FS1' + ServerTargetName = 'sa-foc-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.28' + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) +###################################################################################################> + +Configuration MEMBER_FAILOVERCLUSTER_FS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FailoverClusteringInstall + { + Ensure = 'Present' + Name = 'Failover-Clustering' + } + + WindowsFeature FailoverClusteringPSInstall + { + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + DependsOn = '[WindowsFeature]FailoverClusteringInstall' + } + + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + DependsOn = '[WindowsFeature]FailoverClusteringPSInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain so that it can be an Enterprise CA. + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + if ($Node.ServerTargetName) + { + # Ensure the iSCSI Initiator service is running + Service iSCSIService + { + Name = 'MSiSCSI' + StartupType = 'Automatic' + State = 'Running' + } + + # Wait for the iSCSI Server Target to become available + WaitForAny WaitForiSCSIServerTarget + { + ResourceName = '[ISCSIServerTarget]ClusterServerTarget' + NodeName = $Node.ServerName + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[Service]iSCSIService' + } + + # Connect the Initiator + ISCSIInitiator iSCSIInitiator + { + Ensure = 'Present' + NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" + TargetPortalAddress = $Node.TargetPortalAddress + InitiatorPortalAddress = $Node.InitiatorPortalAddress + IsPersistent = $true + DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' + } # End of ISCSITarget Resource + + # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS + Firewall iSCSIFirewallIn + { + Name = 'MsiScsi-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + Firewall iSCSIFirewallOut + { + Name = 'MsiScsi-Out-TCP' + Ensure = 'Present' + Enabled = 'True' + } + } + + # DHCP Server Settings + Script DHCPAuthorize + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Add-DHCPServerInDC + } + GetScript = { + Return @{ + 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); + } + } + TestScript = { + Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) + } + DependsOn = '[Computer]JoinDomain' + } + + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + } + } + } +} diff --git a/source/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 b/source/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 new file mode 100644 index 00000000..5c055007 --- /dev/null +++ b/source/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 @@ -0,0 +1,211 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FAILOVERCLUSTER_FS +.Desription + Builds a Network failover clustering node for use as a File Server. + It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + ServerName = 'SA-FS1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.28' +###################################################################################################> + +Configuration MEMBER_FAILOVERCLUSTER_FS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName ISCSIDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FailoverClusteringInstall + { + Ensure = 'Present' + Name = 'Failover-Clustering' + } + + WindowsFeature FailoverClusteringPSInstall + { + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + DependsOn = '[WindowsFeature]FailoverClusteringInstall' + } + + WindowsFeature FileServerInstall + { + Ensure = 'Present' + Name = 'FS-FileServer' + DependsOn = '[WindowsFeature]FailoverClusteringPSInstall' + } + + WindowsFeature DataDedupInstall + { + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' + } + + WindowsFeature BranchCacheInstall + { + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' + } + + WindowsFeature DFSReplicationInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' + } + + WindowsFeature FSResourceManagerInstall + { + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain so that it can be an Enterprise CA. + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + if ($Node.ServerTargetName) + { + # Ensure the iSCSI Initiator service is running + Service iSCSIService + { + Name = 'MSiSCSI' + StartupType = 'Automatic' + State = 'Running' + } + + # Wait for the iSCSI Server Target to become available + WaitForAny WaitForiSCSIServerTarget + { + ResourceName = '[ISCSIServerTarget]ClusterServerTarget' + NodeName = $Node.ServerName + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[Service]iSCSIService' + } + + # Connect the Initiator + ISCSIInitiator iSCSIInitiator + { + Ensure = 'Present' + NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" + TargetPortalAddress = $Node.TargetPortalAddress + InitiatorPortalAddress = $Node.InitiatorPortalAddress + IsPersistent = $true + DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' + } # End of ISCSITarget Resource + + # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS + Firewall iSCSIFirewallIn + { + Name = 'MsiScsi-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + Firewall iSCSIFirewallOut + { + Name = 'MsiScsi-Out-TCP' + Ensure = 'Present' + Enabled = 'True' + } + } + + # Enable FSRM FireWall rules so we can remote manage FSRM + Firewall FSRMFirewall1 + { + Name = 'FSRM-WMI-ASYNC-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall2 + { + Name = 'FSRM-WMI-WINMGMT-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall3 + { + Name = 'FSRM-RemoteRegistry-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall4 + { + Name = 'FSRM-Task-Scheduler-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall5 + { + Name = 'FSRM-SrmReports-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall6 + { + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall7 + { + Name = 'FSRM-System-In (TCP-445)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall8 + { + Name = 'FSRM-SrmSvc-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + } +} diff --git a/source/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 b/source/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 new file mode 100644 index 00000000..3463cd87 --- /dev/null +++ b/source/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 @@ -0,0 +1,123 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FAILOVERCLUSTER_HV +.Desription + Builds a Network failover clustering node Hyper-V. + It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + ISCSIServerName = 'SA-FS1' + ServerTargetName = 'sa-foc-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.28' +###################################################################################################> + +Configuration MEMBER_FAILOVERCLUSTER_HV +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FailoverClusteringInstall + { + Ensure = 'Present' + Name = 'Failover-Clustering' + } + + WindowsFeature FailoverClusteringPSInstall + { + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + } + + WindowsFeature InstallHyperV + { + Ensure = 'Present' + Name = 'Hyper-V' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain so that it can be an Enterprise CA. + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + if ($Node.ServerTargetName) + { + # Ensure the iSCSI Initiator service is running + Service iSCSIService + { + Name = 'MSiSCSI' + StartupType = 'Automatic' + State = 'Running' + } + + # Wait for the iSCSI Server Target to become available + WaitForAny WaitForiSCSIServerTarget + { + ResourceName = '[ISCSIServerTarget]ClusterServerTarget' + NodeName = $Node.ServerName + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[Service]iSCSIService' + } + + # Connect the Initiator + ISCSIInitiator iSCSIInitiator + { + Ensure = 'Present' + NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" + TargetPortalAddress = $Node.TargetPortalAddress + InitiatorPortalAddress = $Node.InitiatorPortalAddress + IsPersistent = $true + DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' + } # End of ISCSITarget Resource + + # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS + Firewall iSCSIFirewallIn + { + Name = 'MsiScsi-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + Firewall iSCSIFirewallOut + { + Name = 'MsiScsi-Out-TCP' + Ensure = 'Present' + Enabled = 'True' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_FILESERVER.DSC.ps1 b/source/dsclibrary/MEMBER_FILESERVER.DSC.ps1 new file mode 100644 index 00000000..a2222ea9 --- /dev/null +++ b/source/dsclibrary/MEMBER_FILESERVER.DSC.ps1 @@ -0,0 +1,182 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FILESERVER +.Desription + Builds a Server that is joined to a domain and then made into a File Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_FILESERVER +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FileServerInstall + { + Ensure = 'Present' + Name = 'FS-FileServer' + } + + WindowsFeature DataDedupInstall + { + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' + } + + WindowsFeature BranchCacheInstall + { + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' + } + + WindowsFeature DFSReplicationInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' + } + + WindowsFeature FSResourceManagerInstall + { + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' + } + + WindowsFeature FSSyncShareInstall + { + Ensure = 'Present' + Name = 'FS-SyncShareService' + DependsOn = '[WindowsFeature]FSResourceManagerInstall' + } + + WindowsFeature StorageServicesInstall + { + Ensure = 'Present' + Name = 'Storage-Services' + DependsOn = '[WindowsFeature]FSSyncShareInstall' + } + + WindowsFeature ISCSITargetServerInstall + { + Ensure = 'Present' + Name = 'FS-iSCSITarget-Server' + DependsOn = '[WindowsFeature]StorageServicesInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Enable FSRM FireWall rules so we can remote manage FSRM + Firewall FSRMFirewall1 + { + Name = 'FSRM-WMI-ASYNC-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall2 + { + Name = 'FSRM-WMI-WINMGMT-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall3 + { + Name = 'FSRM-RemoteRegistry-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall4 + { + Name = 'FSRM-Task-Scheduler-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall5 + { + Name = 'FSRM-SrmReports-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall6 + { + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall7 + { + Name = 'FSRM-System-In (TCP-445)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall8 + { + Name = 'FSRM-SrmSvc-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + } +} diff --git a/source/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 b/source/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 new file mode 100644 index 00000000..30db7a88 --- /dev/null +++ b/source/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 @@ -0,0 +1,451 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FILESERVER_FSRMTEST +.Desription + Builds a Server that is joined to a domain and then made into a File Server. + Includes tests for FSRM Resources. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_FILESERVER_FSRMTEST +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + Import-DscResource -ModuleName FSRMDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FileServerInstall + { + Ensure = 'Present' + Name = 'FS-FileServer' + } + + WindowsFeature DataDedupInstall + { + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' + } + + WindowsFeature BranchCacheInstall + { + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' + } + + WindowsFeature DFSReplicationInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' + } + + WindowsFeature FSResourceManagerInstall + { + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' + } + + WindowsFeature FSSyncShareInstall + { + Ensure = 'Present' + Name = 'FS-SyncShareService' + DependsOn = '[WindowsFeature]FSResourceManagerInstall' + } + + WindowsFeature StorageServicesInstall + { + Ensure = 'Present' + Name = 'Storage-Services' + DependsOn = '[WindowsFeature]FSSyncShareInstall' + } + + WindowsFeature ISCSITargetServerInstall + { + Ensure = 'Present' + Name = 'FS-iSCSITarget-Server' + DependsOn = '[WindowsFeature]StorageServicesInstall' + } + + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Enable FSRM FireWall rules so we can remote manage FSRM + Firewall FSRMFirewall1 + { + Name = 'FSRM-WMI-ASYNC-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall2 + { + Name = 'FSRM-WMI-WINMGMT-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall3 + { + Name = 'FSRM-RemoteRegistry-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall4 + { + Name = 'FSRM-Task-Scheduler-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall5 + { + Name = 'FSRM-SrmReports-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall6 + { + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall7 + { + Name = 'FSRM-System-In (TCP-445)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall8 + { + Name = 'FSRM-SrmSvc-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + + File UsersFolder + { + DestinationPath = 'd:\Users' + Ensure = 'Present' + Type = 'Directory' + DependsOn = '[Disk]DVolume' + } + + FSRMQuotaTemplate HardLimit5GB + { + Name = '5 GB Limit' + Description = '5 GB Hard Limit' + Ensure = 'Present' + Size = 5GB + SoftLimit = $false + ThresholdPercentages = @( 85, 100 ) + DependsOn = '[File]UsersFolder' + } + + FSRMQuotaTemplateAction HardLimit5GBEmail85 + { + Name = '5 GB Limit' + Percentage = 85 + Ensure = 'Present' + Type = 'Email' + Subject = '[Quota Threshold]% quota threshold exceeded' + Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' + MailBCC = '' + MailCC = 'fileserveradmins@contoso.com' + MailTo = '[Source Io Owner Email]' + DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' + } # End of FSRMQuotaTemplateAction Resource + + FSRMQuotaTemplateAction HardLimit5GBEvent85 + { + Name = '5 GB Limit' + Percentage = 85 + Ensure = 'Present' + Type = 'Event' + Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' + EventType = 'Warning' + DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' + } # End of FSRMQuotaTemplateAction Resource + + FSRMQuotaTemplateAction HardLimit5GBEmail100 + { + Name = '5 GB Limit' + Percentage = 100 + Ensure = 'Present' + Type = 'Email' + Subject = '[Quota Threshold]% quota threshold exceeded' + Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' + MailBCC = '' + MailCC = 'fileserveradmins@contoso.com' + MailTo = '[Source Io Owner Email]' + DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' + } # End of FSRMQuotaTemplateAction Resource + + FSRMQuota DUsersQuota + { + Path = 'd:\users' + Description = '5 GB Hard Limit, YEAH!' + Ensure = 'Present' + Template = '5 GB Limit' + MatchesTemplate = $true + DependsOn = '[FSRMQuotaTemplateAction]HardLimit5GBEmail100' + } # End of FSRMQuota Resource + + File SharedFolder + { + DestinationPath = 'd:\shared' + Ensure = 'Present' + Type = 'Directory' + DependsOn = '[Disk]DVolume' + } + + FSRMQuota DSharedQuota + { + Path = 'd:\shared' + Description = '5 GB Hard Limit' + Ensure = 'Present' + Size = 5GB + SoftLimit = $false + ThresholdPercentages = @( 75, 100 ) + DependsOn = '[File]SharedFolder' + } # End of FSRMQuota Resource + + FSRMQuotaAction DSharedEmail75 + { + Path = 'd:\shared' + Percentage = 75 + Ensure = 'Present' + Type = 'Email' + Subject = '[Quota Threshold]% quota threshold exceeded' + Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' + MailBCC = '' + MailCC = 'fileserveradmins@contoso.com' + MailTo = '[Source Io Owner Email]' + DependsOn = '[FSRMQuota]DSharedQuota' + } # End of FSRMQuotaAction Resource + + FSRMQuotaAction DSharedEmail100 + { + Path = 'd:\shared' + Percentage = 100 + Ensure = 'Present' + Type = 'Email' + Subject = '[Quota Threshold]% quota threshold exceeded' + Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' + MailBCC = '' + MailCC = 'fileserveradmins@contoso.com' + MailTo = '[Source Io Owner Email]' + DependsOn = '[FSRMQuota]DSharedQuota' + } # End of FSRMQuotaAction Resource + + File AutoFolder + { + DestinationPath = 'd:\auto' + Ensure = 'Present' + Type = 'Directory' + DependsOn = '[Disk]DVolume' + } + + FSRMAutoQuota DAutoQuota + { + Path = 'd:\auto' + Ensure = 'Present' + Template = '100 MB Limit' + DependsOn = '[File]SharedFolder' + } # End of FSRMQuota Resource + + FSRMFileGroup FSRMFileGroupPortableFiles + { + Name = 'Portable Document Files' + Description = 'Files containing portable document formats' + Ensure = 'Present' + IncludePattern = '*.eps','*.pdf','*.xps' + } + + FSRMFileScreenTemplate FileScreenSomeFiles + { + Name = 'Block Some Files' + Description = 'File Screen for Blocking Some Files' + Ensure = 'Present' + Active = $true + IncludeGroup = 'Audio and Video Files','Executable Files','Backup Files' + } # End of FSRMFileScreenTemplate Resource + + FSRMFileScreenTemplateAction FileScreenSomeFilesEmail + { + Name = 'Block Some Files' + Ensure = 'Present' + Type = 'Email' + Subject = 'Unauthorized file matching [Violated File Group] file group detected' + Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' + MailBCC = '' + MailCC = 'fileserveradmins@contoso.com' + MailTo = '[Source Io Owner Email]' + DependsOn = '[FSRMFileScreenTemplate]FileScreenSomeFiles' + } # End of FSRMFileScreenTemplateAction Resource + + FSRMFileScreenTemplateAction FileScreenSomeFilesEvent + { + Name = 'Block Some Files' + Ensure = 'Present' + Type = 'Event' + Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' + EventType = 'Warning' + DependsOn = '[FSRMFileScreenTemplate]FileScreenSomeFiles' + } # End of FSRMFileScreenTemplateAction Resource + + FSRMFileScreen DUsersFileScreen + { + Path = 'd:\users' + Description = 'File Screen for Blocking Some Files' + Ensure = 'Present' + Active = $true + IncludeGroup = 'Audio and Video Files','Executable Files','Backup Files' + } # End of FSRMFileScreen Resource + + FSRMFileScreenAction DUsersFileScreenSomeFilesEmail + { + Path = 'd:\users' + Ensure = 'Present' + Type = 'Email' + Subject = 'Unauthorized file matching [Violated File Group] file group detected' + Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' + MailBCC = '' + MailCC = 'fileserveradmins@contoso.com' + MailTo = '[Source Io Owner Email]' + DependsOn = '[FSRMFileScreen]DUsersFileScreen' + } # End of FSRMFileScreenAction Resource + + FSRMFileScreenAction DUsersFileScreenSomeFilesEvent + { + Path = 'd:\users' + Ensure = 'Present' + Type = 'Event' + Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' + EventType = 'Warning' + DependsOn = '[FSRMFileScreen]DUsersFileScreen' + } # End of FSRMFileScreenAction Resource + + FSRMFileScreenException DUsersFileScreenException + { + Path = 'd:\users' + Description = 'File Screen Exclusion' + Ensure = 'Present' + IncludeGroup = 'E-mail Files' + } # End of FSRMFileScreenException Resource + + FSRMClassificationProperty PrivacyClasificationProperty + { + Name = 'Privacy' + DisplayName = 'File Privacy' + Description = 'File Privacy Property' + Ensure = 'Present' + Type = 'SingleChoice' + PossibleValue = 'Top Secret','Secret','Confidential','Public' + Parameters = 'Parameter1=Value1','Parameter2=Value2' + } # End of FSRMClassificationProperty Resource + + FSRMClassificationPropertyValue PublicClasificationPropertyValue + { + Name = 'Public' + PropertyName = 'Privacy' + Description = 'Publically accessible files.' + Ensure = 'Present' + DependsOn = '[FSRMClassificationProperty]PrivacyClasificationProperty' + } # End of FSRMClassificationPropertyValue Resource + + FSRMClassificationPropertyValue SecretClasificationPropertyValue + { + Name = 'Secret' + PropertyName = 'Privacy' + Ensure = 'Present' + DependsOn = '[FSRMClassificationProperty]PrivacyClasificationProperty' + } # End of FSRMClassificationPropertyValue Resource + FSRMClassification FSRMClassificationSettings + { + Id = 'Default' + Continuous = $true + ContinuousLog = $true + ContinuousLogSize = 2048 + ScheduleWeekly = 'Monday','Tuesday','Wednesday' + ScheduleRunDuration = 4 + ScheduleTime = '23:30' + } # End of FSRMClassification Resource + FSRMClassificationRule ConfidentialPrivacyClasificationRule + { + Name = 'Confidential' + Description = 'Set Confidential' + Ensure = 'Present' + Property = 'Privacy' + PropertyValue = 'Confidential' + ClassificationMechanism = 'Content Classifier' + ContentString = 'Confidential' + Namespace = '[FolderUsage_MS=User Files]','d:\Users' + ReevaluateProperty = 'Overwrite' + } # End of FSRMClassificationRule Resource + } +} diff --git a/source/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 b/source/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 new file mode 100644 index 00000000..8e834ec8 --- /dev/null +++ b/source/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 @@ -0,0 +1,250 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FILESERVER_ISCSI +.Desription + Builds a Server that is joined to a domain and then made into a File Server. + Configures a iSCSI targets and virtual disks. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + TargetName = 'sa-foc-target' + VirtualDisks = @( + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-witness.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 500MB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk1.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk2.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk3.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + } + ) + ClusterInitiatorIds = @( + 'Iqn:iqn.1991-05.com.microsoft:sa-foc1.labbuilder.com' + 'Iqn:iqn.1991-05.com.microsoft:sa-foc2.labbuilder.com' + 'Iqn:iqn.1991-05.com.microsoft:sa-foc3.labbuilder.com' + ) +###################################################################################################> + +Configuration MEMBER_FILESERVER_ISCSI +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + Import-DscResource -ModuleName ISCSI + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FileServerInstall + { + Ensure = 'Present' + Name = 'FS-FileServer' + } + + WindowsFeature DataDedupInstall + { + Ensure = 'Present' + Name = 'FS-Data-Deduplication' + DependsOn = '[WindowsFeature]FileServerInstall' + } + + WindowsFeature BranchCacheInstall + { + Ensure = 'Present' + Name = 'FS-BranchCache' + DependsOn = '[WindowsFeature]DataDedupInstall' + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Namespace' + DependsOn = '[WindowsFeature]BranchCacheInstall' + } + + WindowsFeature DFSReplicationInstall + { + Ensure = 'Present' + Name = 'FS-DFS-Replication' + DependsOn = '[WindowsFeature]DFSNameSpaceInstall' + } + + WindowsFeature FSResourceManagerInstall + { + Ensure = 'Present' + Name = 'FS-Resource-Manager' + DependsOn = '[WindowsFeature]DFSReplicationInstall' + } + + WindowsFeature FSSyncShareInstall + { + Ensure = 'Present' + Name = 'FS-SyncShareService' + DependsOn = '[WindowsFeature]FSResourceManagerInstall' + } + + WindowsFeature StorageServicesInstall + { + Ensure = 'Present' + Name = 'Storage-Services' + DependsOn = '[WindowsFeature]FSSyncShareInstall' + } + + WindowsFeature ISCSITargetServerInstall + { + Ensure = 'Present' + Name = 'FS-iSCSITarget-Server' + DependsOn = '[WindowsFeature]StorageServicesInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Enable FSRM FireWall rules so we can remote manage FSRM + Firewall FSRMFirewall1 + { + Name = 'FSRM-WMI-ASYNC-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall2 + { + Name = 'FSRM-WMI-WINMGMT-In-TCP' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall3 + { + Name = 'FSRM-RemoteRegistry-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall4 + { + Name = 'FSRM-Task-Scheduler-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall5 + { + Name = 'FSRM-SrmReports-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall6 + { + Name = 'FSRM-RpcSs-In (RPC-EPMAP)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall7 + { + Name = 'FSRM-System-In (TCP-445)' + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall8 + { + Name = 'FSRM-SrmSvc-In (RPC)' + Ensure = 'Present' + Enabled = 'True' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + + File VirtualDisksFolder + { + Ensure = 'Present' + DestinationPath = 'D:\iSCSIVirtualDisks' + Type = 'Directory' + DependsOn = '[Disk]DVolume' + } + + $DependsOn = '[File]VirtualDisksFolder' + [System.Int32] $count = 0 + foreach ($VirtualDisk in $Node.VirtualDisks) + { + $count++ + $Name = "$($Node.TargetName)_Disk_$count" + ISCSIVirtualDisk $Name + { + Ensure = 'Present' + Path = $VirtualDisk.Path + DiskType = $VirtualDisk.DiskType + SizeBytes = $VirtualDisk.SizeBytes + Description = $VirtualDisk.Description + DependsOn = $DependsOn + } + $DependsOn = "[ISCSIVirtualDisk]$Name" + } + + ISCSIServerTarget ClusterServerTarget + { + Ensure = 'Present' + TargetName = $Node.TargetName + InitiatorIds = $Node.ClusterInitiatorIds + Paths = $Node.VirtualDisks.Path + DependsOn = $DependsOn + } + } +} diff --git a/source/dsclibrary/MEMBER_IPAM.DSC.ps1 b/source/dsclibrary/MEMBER_IPAM.DSC.ps1 new file mode 100644 index 00000000..70e066d0 --- /dev/null +++ b/source/dsclibrary/MEMBER_IPAM.DSC.ps1 @@ -0,0 +1,57 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_IPAM +.Desription + Builds a Server that is joined to a domain and then made into an IPAM Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_IPAM +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature WIDInstall + { + Ensure = 'Present' + Name = 'Windows-Internal-Database' + } + + WindowsFeature IPAMInstall + { + Ensure = 'Present' + Name = 'IPAM' + DependsOn = '[WindowsFeature]WIDInstall' + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_JENKINS.DSC.ps1 b/source/dsclibrary/MEMBER_JENKINS.DSC.ps1 new file mode 100644 index 00000000..e45ebc05 --- /dev/null +++ b/source/dsclibrary/MEMBER_JENKINS.DSC.ps1 @@ -0,0 +1,124 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_JENKINS +.Desription + Builds a Windows Server, joins it to a Domain and installs Jenkins CI on it. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + JenkinsPort = 80 +###################################################################################################> + +Configuration MEMBER_JENKINS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName cChoco + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature NetFrameworkCore + { + Ensure = 'Present' + Name = 'NET-Framework-Core' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Install Chocolatey + cChocoInstaller installChoco + { + InstallDir = 'c:\choco' + DependsOn = '[WindowsFeature]NetFrameworkCore' + } + + # Install JDK8 + cChocoPackageInstaller installJdk8 + { + Name = 'jdk8' + DependsOn = '[cChocoInstaller]installChoco' + } + + # Install Jenkins + cChocoPackageInstaller installJenkins + { + Name = 'Jenkins' + DependsOn = '[cChocoInstaller]installChoco' + } + + # Set the Jenkins Port + $JenkinsPort = 8080 + if ($Node.JenkinsPort) + { + $JenkinsPort = $Node.JenkinsPort + } + Script SetJenkinsPort + { + SetScript = { + Write-Verbose -Message "Setting Jenkins Port to $Using:JenkinsPort" + $Config = Get-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" + $NewConfig = $Config ` + -replace '--httpPort=[0-9]*\s', "--httpPort=$Using:JenkinsPort " + Set-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" ` + -Value $NewConfig ` + -Force + Write-Verbose -Message 'Restarting Jenkins' + Restart-Service ` + -Name Jenkins + } + GetScript = { + $Config = Get-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" + $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) + $CurrentPort = $Matches.Groups[1].Value + Return @{ + 'JenkinsPort' = $CurrentPort + } + } + TestScript = { + $Config = Get-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" + $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) + $CurrentPort = $Matches.Groups[1].Value + + if ($Using:JenkinsPort -ne $CurrentPort) + { + # Jenkins port must be changed + Return $false + } + # Jenkins is already on correct port + Return $true + } + DependsOn = '[cChocoPackageInstaller]installJenkins' + } + } +} diff --git a/source/dsclibrary/MEMBER_NANO.DSC.ps1 b/source/dsclibrary/MEMBER_NANO.DSC.ps1 new file mode 100644 index 00000000..4be2c839 --- /dev/null +++ b/source/dsclibrary/MEMBER_NANO.DSC.ps1 @@ -0,0 +1,36 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_NANO +.Desription + Builds a Nano Server and joins it to a Domain using an ODJ Request File. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + ODJRequestFile = 'C:\ODJRequest.txt' +###################################################################################################> + +Configuration MEMBER_NANO +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + OfflineDomainJoin JoinDomain + { + IsSingleInstance = 'Yes' + RequestFile = $Node.ODJRequestFile + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_NLB.DSC.ps1 b/source/dsclibrary/MEMBER_NLB.DSC.ps1 new file mode 100644 index 00000000..5bc98ae8 --- /dev/null +++ b/source/dsclibrary/MEMBER_NLB.DSC.ps1 @@ -0,0 +1,66 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_NLB +.Desription + Builds a Network Load Balancing cluster node. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_NLB +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + + WindowsFeature InstallWebServer + { + Ensure = 'Present' + Name = 'Web-Server' + } + + WindowsFeature InstallWebMgmtService + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + } + + WindowsFeature InstallNLB + { + Ensure = 'Present' + Name = 'NLB' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_NPS.DSC.ps1 b/source/dsclibrary/MEMBER_NPS.DSC.ps1 new file mode 100644 index 00000000..f2912c60 --- /dev/null +++ b/source/dsclibrary/MEMBER_NPS.DSC.ps1 @@ -0,0 +1,68 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_NPS +.Desription + Builds a Server that is joined to a domain and then contains NPS/Radius components. +.Requires + Windows Server 2012 R2 Full (Server core not supported). +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_NPS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature NPASPolicyServerInstall + { + Ensure = 'Present' + Name = 'NPAS-Policy-Server' + } + + WindowsFeature NPASHealthInstall + { + Ensure = 'Present' + Name = 'NPAS-Health' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' + } + + WindowsFeature RSATNPAS + { + Ensure = 'Present' + Name = 'RSAT-NPAS' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 b/source/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 new file mode 100644 index 00000000..86024ee1 --- /dev/null +++ b/source/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 @@ -0,0 +1,92 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_NPS_SPECIAL +.Desription + Builds a Server that is joined to a domain and then contains NPS/Radius components. + + ** This is a special version that is used for testing the DFSDsc resource because + ** it requires a full server (not core) installation to work. +.Requires + Windows Server 2012 R2 Full (Server core not supported). +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_NPS_DFSTEST +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName DFSDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature NPASPolicyServerInstall + { + Ensure = 'Present' + Name = 'NPAS-Policy-Server' + } + + WindowsFeature NPASHealthInstall + { + Ensure = 'Present' + Name = 'NPAS-Health' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' + } + + WindowsFeature RSATNPAS + { + Ensure = 'Present' + Name = 'RSAT-NPAS' + DependsOn = '[WindowsFeature]NPASPolicyServerInstall' + } + + WindowsFeature RSATDFSMgmtConInstall + { + Ensure = 'Present' + Name = 'RSAT-DFS-Mgmt-Con' + DependsOn = '[WindowsFeature]RSATNPAS' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + DFSReplicationGroup RGPublic + { + GroupName = 'Public' + Description = 'Public files for use by all departments' + Ensure = 'Present' + Members = 'SA_FS1', 'SA_FS2' + Folders = 'Software', 'Misc' + Topology = 'Fullmesh' + ContentPaths = 'd:\public\Software', 'd:\public\Misc' + PSDSCRunAsCredential = $DomainAdminCredential + DependsOn = '[Computer]JoinDomain' + } # End of RGPublic Resource + } +} diff --git a/source/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 b/source/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 new file mode 100644 index 00000000..467a95aa --- /dev/null +++ b/source/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 @@ -0,0 +1,59 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_REMOTEACCESS +.Desription + Builds a Server that is joined to a domain and then contains Remote Access components. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_REMOTEACCESS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature DirectAccessVPNInstall + { + Ensure = 'Present' + Name = 'DirectAccess-VPN' + } + + WindowsFeature RoutingInstall + { + Ensure = 'Present' + Name = 'Routing' + DependsOn = '[WindowsFeature]DirectAccessVPNInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 b/source/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 new file mode 100644 index 00000000..e71aa746 --- /dev/null +++ b/source/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 @@ -0,0 +1,67 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_REMOTEACCESS_WAP +.Desription + Builds a Server that is joined to a domain and then contains Remote Access and + Web Application Proxy components. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_REMOTEACCESS_WAP +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature DirectAccessVPNInstall + { + Ensure = 'Present' + Name = 'DirectAccess-VPN' + } + + WindowsFeature RoutingInstall + { + Ensure = 'Present' + Name = 'Routing' + DependsOn = '[WindowsFeature]DirectAccessVPNInstall' + } + + WindowsFeature WebApplicationProxyInstall + { + Ensure = 'Present' + Name = 'Web-Application-Proxy' + DependsOn = '[WindowsFeature]RoutingInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + } +} diff --git a/source/dsclibrary/MEMBER_ROOTCA.DSC.ps1 b/source/dsclibrary/MEMBER_ROOTCA.DSC.ps1 new file mode 100644 index 00000000..985bf08b --- /dev/null +++ b/source/dsclibrary/MEMBER_ROOTCA.DSC.ps1 @@ -0,0 +1,343 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_ROOTCA +.Desription + Builds an Enterprise Root CA. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + CACommonName = 'LABBUILDER.COM Root CA' + CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' + CRLPublicationURLs = '65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' + CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 +###################################################################################################> + +Configuration MEMBER_ROOTCA +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName ActiveDirectoryCSDsc + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + # Install the CA Service + WindowsFeature ADCSCA + { + Name = 'ADCS-Cert-Authority' + Ensure = 'Present' + } + + # Install the Web Enrollment Service + WindowsFeature ADCSWebEnrollment + { + Name = 'ADCS-Web-Enrollment' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + + WindowsFeature InstallWebMgmtService + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + DependsOn = '[WindowsFeature]ADCSWebEnrollment' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-AD-Tools' + DependsOn = '[WindowsFeature]ADCSCA' + } + } + + if ($Node.InstallOnlineResponder) + { + # Install the Online Responder Service + WindowsFeature OnlineResponderCA + { + Name = 'ADCS-Online-Cert' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + } + + if ($Node.InstallEnrollmentWebService) + { + # Install the Enrollment Web Service/Enrollment Policy Web Service + WindowsFeature EnrollmentWebSvc + { + Name = 'ADCS-Enroll-Web-Svc' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + + WindowsFeature EnrollmentWebPol + { + Name = 'ADCS-Enroll-Web-Pol' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. + File CAPolicy + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\CAPolicy.inf' + Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" + Type = 'File' + DependsOn = '[Computer]JoinDomain' + } + + <# + Make a CertEnroll folder to put the Root CA certificate into. + The CA Web Enrollment server would also create this but we need it now. + #> + File CertEnrollFolder + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' + Type = 'Directory' + DependsOn = '[File]CAPolicy' + } + + <# + Configure the Root CA which will create the Certificate REQ file that Root CA will use + to issue a certificate for this Sub CA. + #> + ADCSCertificationAuthority ConfigCA + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $DomainAdminCredential + CAType = 'EnterpriseRootCA' + CACommonName = $Node.CACommonName + CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix + OverwriteExistingCAinDS = $true + CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' + HashAlgorithmName = 'SHA256' + KeyLength = 4096 + DependsOn = '[File]CertEnrollFolder' + } + + # Configure the Web Enrollment Feature + ADCSWebEnrollment ConfigWebEnrollment + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + DependsOn = '[ADCSCertificationAuthority]ConfigCA' + } + + <# + Perform final configuration of the CA which will cause the CA service to startup + Set the advanced CA properties + #> + Script ADCSAdvConfig + { + SetScript = { + if ($Using:Node.CADistinguishedNameSuffix) + { + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" + } + + if ($Using:Node.CRLPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) + } + + if ($Using:Node.CACertPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) + } + + if ($Using:Node.CRLPeriodUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" + } + + if ($Using:Node.CRLOverlapUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" + } + + if ($Using:Node.ValidityPeriodUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" + } + + if ($Using:Node.AuditFilter) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) + } + + Restart-Service -Name CertSvc + New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue + Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' + } + + GetScript = { + return @{ + 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); + 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); + 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); + 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') + 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') + 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') + 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') + 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') + 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') + 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') + 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') + } + } + + TestScript = { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) + { + return $false + } + + if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) + { + return $false + } + + if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) + { + return $false + } + + if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) + { + return $false + } + + if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) + { + return $false + } + + if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) + { + return $false + } + + if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) + { + return $false + } + + if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) + { + return $false + } + + if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) + { + return $false + } + + return $true + } + + DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' + } + + if ($Node.InstallOnlineResponder) + { + # Configure the Online Responder Feature + ADCSOnlineResponder ConfigOnlineResponder + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + DependsOn = '[Script]ADCSAdvConfig' + } + + # Enable Online Responder FireWall rules so we can remote manage Online Responder + Firewall OnlineResponderFirewall1 + { + Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In' + Enabled = 'True' + DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' + } + + Firewall OnlineResponderirewall2 + { + Name = 'Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In' + Enabled = 'True' + DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' + } + + Firewall OnlineResponderFirewall3 + { + Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out' + Enabled = 'True' + DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 b/source/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 new file mode 100644 index 00000000..2cdf1b7f --- /dev/null +++ b/source/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 @@ -0,0 +1,142 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_SQLSERVER2014 +.Desription + Builds a Server that is joined to a domain and then installs SQL Server 2014. + It will install SQLServer from a locally mounted ISO file. +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallerUsername = 'Administrator' + InstallerPassword = 'P@ssword!1' + SQLAdminAccount = 'Administrator' + SQLDataDrive = 'E' + SourcePath = 'D:\' + Instances = @( + @{ + Name = 'MSSQLSERVER' + Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } + ) + InstallManagementTools = $true +###################################################################################################> + +Configuration MEMBER_SQLSERVER2014 +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName SQLServerDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + if ($Node.InstallerPassword) + { + $InstallerCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) + } + + # Install the SQL Server Dependencies + WindowsFeature Net35Install + { + Name = 'NET-Framework-Core' + Ensure = 'Present' + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = $Node.SQLDataDrive + DependsOn = '[WaitforDisk]Disk2' + } + + foreach ($Instance in $Node.Instances) + { + $Features = $Instance.Features + + if ([System.String]::IsNullOrEmpty($Features)) + { + $Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } # if + + SqlServerSetup ($Instance.Name) + { + SourcePath = $Node.SourcePath + InstanceName = $Instance.Name + Features = $Features + SQLSysAdminAccounts = "$($Node.DomainName)\$($Node.SQLAdminAccount)" + InstallSharedDir = "C:\Program Files\Microsoft SQL Server" + InstallSharedWOWDir = "C:\Program Files (x86)\Microsoft SQL Server" + InstanceDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server" + InstallSQLDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLUserDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLUserDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLTempDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLTempDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + ASDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Data" + ASLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Log" + ASBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Backup" + ASTempDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Temp" + ASConfigDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Config" + PsDscRunAsCredential = $InstallerCredential + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' + } + + SqlServerFirewall ($Instance.Name) + { + SourcePath = $Node.SourcePath + InstanceName = $Instance.Name + Features = $Features + DependsOn = "[SqlServerSetup]$($Instance.Name)" + } + } + + if ($Node.InstallManagementTools) + { + SqlServerSetup SQLMT + { + SourcePath = $Node.SourcePath + InstanceName = 'NULL' + Features = 'SSMS,ADV_SSMS' + PsDscRunAsCredential = $InstallerCredential + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 b/source/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 new file mode 100644 index 00000000..8c4b752e --- /dev/null +++ b/source/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 @@ -0,0 +1,140 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_SQLSERVER2016 +.Desription + Builds a Server that is joined to a domain and then installs SQL Server 2016. + It will install SQLServer from a locally mounted ISO file. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallerUsername = 'Administrator' + InstallerPassword = 'P@ssword!1' + SQLAdminAccount = 'Administrator' + SQLDataDrive = 'E' + SourcePath = 'D:\' + Instances = @( + @{ + Name = 'MSSQLSERVER' + Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } + ) + InstallManagementTools = $true +###################################################################################################> + +Configuration MEMBER_SQLSERVER2016 +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName SQLServerDsc + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + if ($Node.InstallerPassword) + { + $InstallerCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) + } + + # Install the SQL Server Dependencies + WindowsFeature Net35Install + { + Name = 'NET-Framework-Core' + Ensure = 'Present' + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = $Node.SQLDataDrive + DependsOn = '[WaitforDisk]Disk2' + } + + foreach ($Instance in $Node.Instances) + { + $Features = $Instance.Features + if ([System.String]::IsNullOrEmpty($Features)) + { + $Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } # if + SqlServerSetup ($Instance.Name) + { + SourcePath = $Node.SourcePath + InstanceName = $Instance.Name + Features = $Features + SQLSysAdminAccounts = "$($Node.DomainName)\$($Node.SQLAdminAccount)" + InstallSharedDir = "C:\Program Files\Microsoft SQL Server" + InstallSharedWOWDir = "C:\Program Files (x86)\Microsoft SQL Server" + InstanceDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server" + InstallSQLDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLUserDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLUserDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLTempDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLTempDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + SQLBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" + ASDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Data" + ASLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Log" + ASBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Backup" + ASTempDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Temp" + ASConfigDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Config" + PsDscRunAsCredential = $InstallerCredential + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' + } + + SqlServerFirewall ($Instance.Name) + { + SourcePath = $Node.SourcePath + InstanceName = $Instance.Name + Features = $Features + DependsOn = "[SqlServerSetup]$($Instance.Name)" + } + } + + if ($Node.InstallManagementTools) + { + SqlServerSetup SQLMT + { + SourcePath = $Node.SourcePath + InstanceName = 'NULL' + Features = 'SSMS,ADV_SSMS' + PsDscRunAsCredential = $InstallerCredential + DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_SUBCA.DSC.ps1 b/source/dsclibrary/MEMBER_SUBCA.DSC.ps1 new file mode 100644 index 00000000..a3ddd952 --- /dev/null +++ b/source/dsclibrary/MEMBER_SUBCA.DSC.ps1 @@ -0,0 +1,396 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_SUBCA +.Desription + Builds a Enterprise Subordinate\Issuing CA. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true + CACommonName = 'LABBUILDER.COM Issuing CA' + CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' + CRLPublicationURLs = '65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' + CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' + RootCAName = 'SS_ROOTCA' + RootCACommonName = 'LABBUILDER.COM Root CA' +###################################################################################################> + +Configuration MEMBER_SUBCA +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName ActiveDirectoryCSDsc + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + # Install the CA Service + WindowsFeature ADCSCA + { + Name = 'ADCS-Cert-Authority' + Ensure = 'Present' + } + + # Install the Web Enrollment Service + WindowsFeature WebEnrollmentCA + { + Name = 'ADCS-Web-Enrollment' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + + WindowsFeature InstallWebMgmtService + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + DependsOn = '[WindowsFeature]ADCSWebEnrollment' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-AD-Tools' + DependsOn = '[WindowsFeature]ADCSCA' + } + } + + if ($Node.InstallOnlineResponder) + { + # Install the Online Responder Service + WindowsFeature OnlineResponderCA + { + Name = 'ADCS-Online-Cert' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + } + + if ($Node.InstallEnrollmentWebService) + { + # Install the Enrollment Web Service/Enrollment Policy Web Service + WindowsFeature EnrollmentWebSvc + { + Name = 'ADCS-Enroll-Web-Svc' + Ensure = 'Present' + DependsOn = '[WindowsFeature]ADCSCA' + } + + WindowsFeature EnrollmentWebPol + { + Name = 'ADCS-Enroll-Web-Pol' + Ensure = 'Present' + DependsOn = '[WindowsFeature]WebEnrollmentCA' + } + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. + File CAPolicy + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\CAPolicy.inf' + Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=10`r`n AlternateSignatureAlgorithm=1`r`n CNGHashAlgorithm=SHA256`r`n LoadDefaultTemplates=0`r`n" + Type = 'File' + DependsOn = '[Computer]JoinDomain' + } + + <# + Make a CertEnroll folder to put the Root CA certificate into. + The CA Web Enrollment server would also create this but we need it now. + #> + File CertEnrollFolder + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' + Type = 'Directory' + DependsOn = '[File]CAPolicy' + } + + <# + Wait for the RootCA Web Enrollment to complete so we can grab the Root CA + certificate file. + #> + WaitForAny RootCA + { + ResourceName = '[ADCSWebEnrollment]ConfigWebEnrollment' + NodeName = $Node.RootCAName + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[File]CertEnrollFolder' + } + + # Download the Root CA certificate file. + xRemoteFile DownloadRootCACRTFile + { + DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCAName)_$($Node.RootCACommonName).crt" + Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCAName)_$($Node.RootCACommonName).crt" + DependsOn = '[WaitForAny]RootCA' + } + + # Download the Root CA certificate revocation list. + xRemoteFile DownloadRootCACRLFile + { + DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" + Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCACommonName).crl" + DependsOn = '[xRemoteFile]DownloadRootCACRTFile' + } + + # Install the Root CA Certificate to the LocalMachine Root Store and DS + Script InstallRootCACert + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Write-Verbose -Message "Registering the Root CA Certificate C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt in DS..." + & "$($ENV:SystemRoot)\system32\certutil.exe" -f -dspublish "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt" RootCA + Write-Verbose -Message "Registering the Root CA CRL C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl in DS..." + & "$($ENV:SystemRoot)\system32\certutil.exe" -f -dspublish "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" "$($Using:Node.RootCAName)" + Write-Verbose -Message "Installing the Root CA Certificate C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt..." + & "$($ENV:SystemRoot)\system32\certutil.exe" -addstore -f root "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt" + Write-Verbose -Message "Installing the Root CA CRL C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl..." + & "$($ENV:SystemRoot)\system32\certutil.exe" -addstore -f root "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" + } + GetScript = { + return @{ + Installed = ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) + } + } + TestScript = { + if ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) + { + Write-Verbose -Message 'Root CA Certificate Needs to be installed...' + return $false + } + return $true + } + DependsOn = '[xRemoteFile]DownloadRootCACRTFile' + } + + <# + Configure the Sub CA which will create the Certificate REQ file that Root CA will use + to issue a certificate for this Sub CA. + #> + ADCSCertificationAuthority ConfigCA + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $DomainAdminCredential + CAType = 'EnterpriseSubordinateCA' + CACommonName = $Node.CACommonName + CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix + OverwriteExistingCAinDS = $true + OutputCertRequestFile = "c:\windows\system32\certsrv\certenroll\$($Node.NodeName).req" + CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' + HashAlgorithmName = 'SHA256' + KeyLength = 2048 + DependsOn = '[Script]InstallRootCACert' + } + + # Configure the Web Enrollment Feature + ADCSWebEnrollment ConfigWebEnrollment + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + DependsOn = '[ADCSCertificationAuthority]ConfigCA' + } + + # Set the IIS Mime Type to allow the REQ request to be downloaded by the Root CA + Script SetREQMimeType + { + SetScript = { + Add-WebConfigurationProperty -PSPath IIS:\ -Filter //staticContent -Name "." -Value @{fileExtension = '.req'; mimeType = 'application/pkcs10' } + } + GetScript = { + return @{ + 'MimeType' = ((Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *).mimeType); + } + } + TestScript = { + if (-not (Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *)) + { + # Mime type is not set + return $false + } + # Mime Type is already set + return $true + } + DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' + } + + # Wait for the Root CA to have completed issuance of the certificate for this SubCA. + WaitForAny SubCACer + { + ResourceName = "[Script]IssueCert_$($Node.NodeName)" + NodeName = $Node.RootCAName + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[Script]SetREQMimeType' + } + + # Download the Certificate for this SubCA but rename it so that it'll match the name expected by the CA + xRemoteFile DownloadSubCACERFile + { + DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.NodeName)_$($Node.CACommonName).crt" + Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.NodeName).crt" + DependsOn = '[WaitForAny]SubCACer' + } + + # Register the Sub CA Certificate with the Certification Authority + Script RegisterSubCA + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Write-Verbose -Message "Registering the Sub CA Certificate with the Certification Authority C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.NodeName)_$($Using:Node.CACommonName).crt..." + & "$($ENV:SystemRoot)\system32\certutil.exe" -installCert "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.NodeName)_$($Using:Node.CACommonName).crt" + } + GetScript = { + return @{ + } + } + TestScript = { + if (-not (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertHash')) + { + Write-Verbose -Message 'Sub CA Certificate needs to be registered with the Certification Authority...' + return $false + } + return $true + } + DependsOn = '[xRemoteFile]DownloadSubCACERFile' + } + + <# + Perform final configuration of the CA which will cause the CA service to startup + It should be able to start up once the SubCA certificate has been installed. + #> + Script ADCSAdvConfig + { + SetScript = { + if ($Using:Node.CADistinguishedNameSuffix) + { + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" + } + + if ($Using:Node.CRLPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) + } + + if ($Using:Node.CACertPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) + } + + Restart-Service -Name CertSvc + New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue + Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' + } + + GetScript = { + return @{ + 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); + 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); + 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); + 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') + } + } + + TestScript = { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) + { + return $false + } + + if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) + { + return $false + } + + return $true + } + + DependsOn = '[Script]RegisterSubCA' + } + + if ($Node.InstallOnlineResponder) + { + # Configure the Online Responder Feature + ADCSOnlineResponder ConfigOnlineResponder + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + DependsOn = '[Script]ADCSAdvConfig' + } + + # Enable Online Responder FireWall rules so we can remote manage Online Responder + Firewall OnlineResponderFirewall1 + { + Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In' + Enabled = 'True' + DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' + } + + Firewall OnlineResponderirewall2 + { + Name = 'Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In' + Enabled = 'True' + DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' + } + + Firewall OnlineResponderFirewall3 + { + Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out' + Enabled = 'True' + DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' + } + } + } +} diff --git a/source/dsclibrary/MEMBER_WDS.DSC.ps1 b/source/dsclibrary/MEMBER_WDS.DSC.ps1 new file mode 100644 index 00000000..069cec06 --- /dev/null +++ b/source/dsclibrary/MEMBER_WDS.DSC.ps1 @@ -0,0 +1,89 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_WDS +.Desription + Builds a Server that is joined to a domain and then installs WSUS components. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_WDS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature WDSDeploymentInstall + { + Ensure = 'Present' + Name = 'WDS-Deployment' + } + + WindowsFeature WDSTransportInstall + { + Ensure = 'Present' + Name = 'WDS-Transport' + DependsOn = '[WindowsFeature]WDSDeploymentInstall' + } + + WindowsFeature BitLockerNetworkUnlockInstall + { + Ensure = 'Present' + Name = 'BitLocker-NetworkUnlock' + DependsOn = '[WindowsFeature]WDSTransportInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + } +} diff --git a/source/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 b/source/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 new file mode 100644 index 00000000..e862b1a4 --- /dev/null +++ b/source/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 @@ -0,0 +1,238 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_WEBSERVER +.Desription + Builds a Server that is joined to a domain and then made into an IIS Web Application Server. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + WebAppPools = @( + @{ Name = 'MyAppPool'; + State = 'Started'; + Ensure = 'Present'; + } + ) + WebSites = @( + @{ Name = 'MySite1'; + Ensure = 'Present'; + State = 'Started'; + SourcePath = '\\fileserver\MySite1'; + PhysicalPath = 'c:\MySite1'; + BindingInfo = @( + MSFT_xWebBindingInformation + { + Protocol = 'HTTPS' + Port = 8443 + CertificateThumbprint = '71AD93562316F21F74606F1096B85D66289ED60F' + CertificateStoreName = 'WebHosting' + }, + MSFT_xWebBindingInformation + { + Protocol = 'HTTPS' + Port = 8444 + CertificateThumbprint = 'DEDDD963B28095837F558FE14DA1FDEFB7FA9DA7' + CertificateStoreName = 'MY' + } + ) + ApplicationPool = 'MyAppPool'; + } + ) + WebApplications = @( + @{ WebSite = 'MySite1'; + Name = 'MyWebApp'; + WebAppPool = 'MyAppPool'; + PhysicalPath = 'c:\MyApp1'; + SourcePath = '\\fileserver\MyApp1'; + Ensure = 'Present'; + } + ) + WebVirtualDirectories = @( + @{ WebSite = 'MySite1' + WebApplication = 'MyWebApp'; + PhysicalPath = 'c:\Images'; + SourcePath = '\\fileserver\MySite1\Images; + Name = 'Images'; + Ensure = 'Present'; + } + ) +###################################################################################################> + +Configuration MEMBER_WEBSERVER +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xWebAdministration + + Node $AllNodes.NodeName { + # Assemble the Admin Credentials + if ($Node.DomainAdminPassword) { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature IISInstall + { + Ensure = 'Present' + Name = 'Web-Server' + } + + WindowsFeature AspNet45Install + { + Ensure = 'Present' + Name = 'Web-Asp-Net45' + } + + WindowsFeature WebMgmtServiceInstall + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + } + + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + # Create the Web App Pools + $count=0 + foreach ($WebAppPool in $Node.WebAppPools) { + $count++ + xWebAppPool "WebAppPool$count" + { + Ensure = $WebAppPool.Ensure + Name = $WebAppPool.Name + State = $WebAppPool.State + } + } + + # Create the Web Sites + $count=0 + foreach ($WebSite in $Node.WebSites) { + $count++ + + # Create an empty folder or copy content from Source Path + if ($WebSite.SourcePath) + { + File "WebSiteContent$count" + { + Ensure = 'Present' + SourcePath = $WebSite.SourcePath + DestinationPath = $WebSite.PhysicalPath + Recurse = $true + Type = 'Directory' + } + } + else + { + File "WebSiteContent$count" + { + Ensure = 'Present' + Type = 'Directory' + DestinationPath = $WebSite.PhysicalPath + } + } # if + + xWebsite "WebSite$count" + { + Ensure = $WebSite.Ensure + Name = $WebSite.Name + State = $WebSite.State + PhysicalPath = $WebSite.PhysicalPath + BindingInfo = $WebSite.BindingInfo + ApplicationPool = $WebSite.ApplicationPool + DependsOn = "[File]WebSiteContent$count" + } + } + + # Create the Web Applications + $count=0 + foreach ($WebApplication in $Node.WebApplications) { + $count++ + + # Create an empty folder or copy content from Source Path + if ($WebApplication.SourcePath) + { + File "WebApplicationContent$count" + { + Ensure = 'Present' + SourcePath = $WebApplication.SourcePath + DestinationPath = $WebApplication.PhysicalPath + Recurse = $true + Type = 'Directory' + } + } + else + { + File "WebApplicationContent$count" + { + Ensure = 'Present' + Type = 'Directory' + DestinationPath = $WebApplication.PhysicalPath + } + } # if + + xWebApplication "WebApplication$count" + { + Ensure = $WebApplication.Ensure + WebSite = $WebApplication.WebSite + Name = $WebApplication.Name + WebAppPool = $WebApplication.WebAppPool + PhysicalPath = $WebApplication.PhysicalPath + DependsOn = "[File]WebApplicationContent$count" + } + } + + # Create the Web Virtual Directories + $count=0 + foreach ($WebVirtualDirectory in $Node.WebVirtualDirectories) { + $count++ + + # Create an empty folder or copy content from Source Path + if ($WebVirtualDirectory.SourcePath) + { + File "WebVirtualDirectoryContent$count" + { + Ensure = 'Present' + SourcePath = $WebVirtualDirectory.SourcePath + DestinationPath = $WebVirtualDirectory.PhysicalPath + Recurse = $true + Type = 'Directory' + } + } + else + { + File "WebVirtualDirectoryContent$count" + { + Ensure = 'Present' + Type = 'Directory' + DestinationPath = $WebVirtualDirectory.PhysicalPath + } + } # if + + xWebVirtualDirectory "WebVirtualDirectory$count" + { + Ensure = $WebVirtualDirectory.Ensure + WebSite = $WebVirtualDirectory.WebSite + WebApplication = $WebVirtualDirectory.WebApplication + PhysicalPath = $WebVirtualDirectory.PhysicalPath + Name = $WebVirtualDirectory.Name + DependsOn = "[File]WebVirtualDirectoryContent$count" + } + } + } +} diff --git a/source/dsclibrary/MEMBER_WSUS.DSC.ps1 b/source/dsclibrary/MEMBER_WSUS.DSC.ps1 new file mode 100644 index 00000000..83bafb9e --- /dev/null +++ b/source/dsclibrary/MEMBER_WSUS.DSC.ps1 @@ -0,0 +1,84 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_WSUS +.Desription + Builds a Server that is joined to a domain and then installs WSUS components. + Requires cMicrosoftUpdate resource from https://github.com/fabiendibot/cMicrosoftUpdate +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true +###################################################################################################> + +Configuration MEMBER_WSUS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName xWindowsUpdate + Import-DscResource -ModuleName StorageDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature UpdateServicesWIDDBInstall + { + Ensure = 'Present' + Name = 'UpdateServices-WidDB' + } + + WindowsFeature UpdateServicesServicesInstall + { + Ensure = 'Present' + Name = 'UpdateServices-Services' + DependsOn = '[WindowsFeature]UpdateServicesWIDDBInstall' + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + # Join this Server to the Domain + Computer JoinDomain + { + Name = $Node.NodeName + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + DependsOn = '[WaitForAll]DC' + } + + WaitforDisk Disk2 + { + DiskId = 1 + RetryIntervalSec = 60 + RetryCount = 60 + DependsOn = '[Computer]JoinDomain' + } + + Disk DVolume + { + DiskId = 1 + DriveLetter = 'D' + DependsOn = '[WaitforDisk]Disk2' + } + } +} diff --git a/source/dsclibrary/RODC_SECONDARY.DSC.ps1 b/source/dsclibrary/RODC_SECONDARY.DSC.ps1 new file mode 100644 index 00000000..e77976cd --- /dev/null +++ b/source/dsclibrary/RODC_SECONDARY.DSC.ps1 @@ -0,0 +1,91 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + RODC_SECONDARY +.Desription + Builds a Read Only Domain Controller and adds it to the existing domain provided in the Parameter DomainName. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' + DCName = 'SA-DC1' + PSDscAllowDomainUser = $true + InstallRSATTools = $true +###################################################################################################> + +Configuration RODC_SECONDARY +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + if ($Node.DomainAdminPassword) + { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature BackupInstall + { + Ensure = 'Present' + Name = 'Windows-Server-Backup' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + WindowsFeature ADDSInstall + { + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' + } + + WindowsFeature RSAT-AD-PowerShellInstall + { + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' + } + + if ($InstallRSATTools) + { + WindowsFeature RSAT-ManagementTools + { + Ensure = 'Present' + Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' + DependsOn = '[WindowsFeature]ADDSInstall' + } + } + + # Wait for the Domain to be available so we can join it. + WaitForAll DC + { + ResourceName = '[ADDomain]PrimaryDC' + NodeName = $Node.DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + + ADDomainController SecondaryDC + { + DomainName = $Node.DomainName + Credential = $DomainAdminCredential + SafemodeAdministratorPassword = $LocalAdminCredential + ReadOnlyReplica = $true + DependsOn = '[WaitForADDomain]DscDomainWait' + } + } +} diff --git a/source/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 b/source/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 new file mode 100644 index 00000000..46954ce4 --- /dev/null +++ b/source/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 @@ -0,0 +1,23 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + STANDALONE_DEFAULT +.Desription + Builds a Standalone computer with no additional DSC resources. +.Parameters: +###################################################################################################> + +Configuration STANDALONE_DEFAULT +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + } +} diff --git a/source/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 b/source/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 new file mode 100644 index 00000000..0bddeb61 --- /dev/null +++ b/source/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 @@ -0,0 +1,178 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + STANDALONE_DHCPDNS +.Desription + Builds a Standalone DHCP and DNS Server. +.Parameters: + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + Forwarders = @('8.8.8.8','8.8.4.4') + ADZones = @( + @{ Name = 'ALPHA.LOCAL'; + DynamicUpdate = 'Secure'; + ReplicationScope = 'Forest'; + } + ) + PrimaryZones = @( + @{ Name = 'BRAVO.LOCAL'; + ZoneFile = 'bravo.local.dns'; + DynamicUpdate = 'None'; + } + ) +###################################################################################################> + +Configuration STANDALONE_DHCPDNS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + Node $AllNodes.NodeName { + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + <# + Add the DHCP Scope, Reservation and Options from + the node configuration + #> + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + # DNS Server Settings + if ($Node.Forwarders) + { + xDnsServerForwarder DNSForwarders + { + IsSingleInstance = 'Yes' + IPAddresses = $Node.Forwarders + DependsOn = '[Computer]JoinDomain' + } + } + + $count = 0 + foreach ($ADZone in $Node.ADZones) + { + $count++ + xDnsServerADZone "ADZone$count" + { + Ensure = 'Present' + Name = $ADZone.Name + DynamicUpdate = $ADZone.DynamicUpdate + ReplicationScope = $ADZone.ReplicationScope + Credential = $DomainAdminCredential + DependsOn = '[Computer]JoinDomain' + } + } + + $count = 0 + foreach ($PrimaryZone in $Node.PrimaryZones) + { + $count++ + xDnsServerPrimaryZone "PrimaryZone$count" + { + Ensure = 'Present' + Name = $PrimaryZone.Name + ZoneFile = $PrimaryZone.ZoneFile + DynamicUpdate = $PrimaryZone.DynamicUpdate + DependsOn = '[Computer]JoinDomain' + } + } + } +} diff --git a/source/dsclibrary/STANDALONE_INTERNET.DSC.ps1 b/source/dsclibrary/STANDALONE_INTERNET.DSC.ps1 new file mode 100644 index 00000000..ede854e3 --- /dev/null +++ b/source/dsclibrary/STANDALONE_INTERNET.DSC.ps1 @@ -0,0 +1,103 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + STANDALONE_INTERNET +.Desription + Builds a Standalone DHCP, DNS and IIS Server to simulate the Internet. + See http://blog.superuser.com/2011/05/16/windows-7-network-awareness/ + for details on how Windows computers detect Internet connectivity. +.Parameters: +###################################################################################################> + +Configuration STANDALONE_INTERNET +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + Import-DscResource -ModuleName xWebAdministration + + Node $AllNodes.NodeName { + WindowsFeature WebServerInstall + { + Ensure = 'Present' + Name = 'Web-WebServer' + } + + WindowsFeature DHCPInstall + { + Ensure = 'Present' + Name = 'DHCP' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + # Create the default ncsi.txt. + File CAPolicy + { + Ensure = 'Present' + DestinationPath = 'c:\inetpub\wwwroot\ncsi.txt' + Contents = 'Microsoft NCSI' + Type = 'File' + DependsOn = '[WindowsFeature]WebServerInstall' + } + + <# + Add the DHCP Scope, Reservation and Options from + the node configuration + #> + $count = 0 + foreach ($Scope in $Node.Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + $count = 0 + foreach ($Reservation in $Node.Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + + $count = 0 + foreach ($ScopeOption in $Node.ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $Node.DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + DependsOn = '[WindowsFeature]DHCPInstall' + } + } + } +} diff --git a/source/dsclibrary/STANDALONE_JENKINS.DSC.ps1 b/source/dsclibrary/STANDALONE_JENKINS.DSC.ps1 new file mode 100644 index 00000000..4173d3b8 --- /dev/null +++ b/source/dsclibrary/STANDALONE_JENKINS.DSC.ps1 @@ -0,0 +1,93 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + STANDALONE_JENKINS +.Desription + Builds a Windows Server and installs Jenkins CI on it. +.Parameters: + JenkinsPort = 80 +###################################################################################################> + +Configuration STANDALONE_JENKINS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName cChoco + Import-DscResource -ModuleName NetworkingDsc + + Node $AllNodes.NodeName { + WindowsFeature NetFrameworkCore + { + Ensure = 'Present' + Name = 'NET-Framework-Core' + } + + # Install Chocolatey + cChocoInstaller installChoco + { + InstallDir = 'c:\choco' + DependsOn = '[WindowsFeature]NetFrameworkCore' + } + + # Install JDK8 + cChocoPackageInstaller installJdk8 + { + Name = 'jdk8' + DependsOn = '[cChocoInstaller]installChoco' + } + + # Install Jenkins + cChocoPackageInstaller installJenkins + { + Name = 'Jenkins' + DependsOn = '[cChocoInstaller]installChoco' + } + + # Set the Jenkins Port + $JenkinsPort = 8080 + if ($Node.JenkinsPort) + { + $JenkinsPort = $Node.JenkinsPort + } + Script SetJenkinsPort + { + SetScript = { + Write-Verbose -Message "Setting Jenkins Port to $Using:JenkinsPort" + $Config = Get-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" + $NewConfig = $Config ` + -replace '--httpPort=[0-9]*\s', "--httpPort=$Using:JenkinsPort " + Set-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" ` + -Value $NewConfig ` + -Force + Write-Verbose -Message "Restarting Jenkins" + Restart-Service ` + -Name Jenkins + } + GetScript = { + $Config = Get-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" + $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) + $CurrentPort = $Matches.Groups[1].Value + Return @{ + 'JenkinsPort' = $CurrentPort + } + } + TestScript = { + $Config = Get-Content ` + -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" + $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) + $CurrentPort = $Matches.Groups[1].Value + + if ($Using:JenkinsPort -ne $CurrentPort) + { + # Jenkins port must be changed + Return $false + } + # Jenkins is already on correct port + Return $true + } + DependsOn = '[cChocoPackageInstaller]installJenkins' + } + } +} diff --git a/source/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 b/source/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 new file mode 100644 index 00000000..62ae7f3f --- /dev/null +++ b/source/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 @@ -0,0 +1,326 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + STANDALONE_ROOTCA +.Desription + Builds a Standalone Root CA and creates Issuing CA certificates for Sub CAs. +.Parameters: + CACommonName = 'LABBUILDER.COM Root CA' + CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' + CRLPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' + CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + SubCAs = @('SA_SUBCA') +###################################################################################################> + +Configuration STANDALONE_ROOTCA +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryCSDsc + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + # Install the ADCS Certificate Authority + WindowsFeature ADCSCA + { + Name = 'ADCS-Cert-Authority' + Ensure = 'Present' + } + + <# + Install ADCS Web Enrollment - only required because it creates the CertEnroll virtual folder + Which we use to pass certificates to the Issuing/Sub CAs + #> + WindowsFeature ADCSWebEnrollment + { + Ensure = 'Present' + Name = 'ADCS-Web-Enrollment' + DependsOn = '[WindowsFeature]ADCSCA' + } + + WindowsFeature InstallWebMgmtService + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + DependsOn = '[WindowsFeature]ADCSWebEnrollment' + } + + # Create the CAPolicy.inf file which defines basic properties about the ROOT CA certificate + File CAPolicy + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\CAPolicy.inf' + Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" + Type = 'File' + DependsOn = '[WindowsFeature]ADCSCA' + } + + # Configure the CA as Standalone Root CA + ADCSCertificationAuthority ConfigCA + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + CAType = 'StandaloneRootCA' + CACommonName = $Node.CACommonName + CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix + ValidityPeriod = 'Years' + ValidityPeriodUnits = 20 + CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' + HashAlgorithmName = 'SHA256' + KeyLength = 4096 + DependsOn = '[File]CAPolicy' + } + + # Configure the ADCS Web Enrollment + ADCSWebEnrollment ConfigWebEnrollment + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + CAConfig = 'CertSrv' + Credential = $LocalAdminCredential + DependsOn = '[ADCSCertificationAuthority]ConfigCA' + } + + # Set the advanced CA properties + Script ADCSAdvConfig + { + SetScript = { + if ($Using:Node.CADistinguishedNameSuffix) + { + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" + } + + if ($Using:Node.CRLPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) + } + + if ($Using:Node.CACertPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) + } + + if ($Using:Node.CRLPeriodUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" + } + + if ($Using:Node.CRLOverlapUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" + } + + if ($Using:Node.ValidityPeriodUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" + } + + if ($Using:Node.AuditFilter) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) + } + + Restart-Service -Name CertSvc + New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue + Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' + } + + GetScript = { + return @{ + 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); + 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); + 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); + 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') + 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') + 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') + 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') + 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') + 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') + 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') + 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') + } + } + + TestScript = { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) + { + return $false + } + + if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) + { + return $false + } + + if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) + { + return $false + } + + if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) + { + return $false + } + + if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) + { + return $false + } + + if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) + { + return $false + } + + if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) + { + return $false + } + + if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) + { + return $false + } + + if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) + { + return $false + } + + return $true + } + + DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' + } + + # Generate Issuing certificates for any SubCAs + foreach ($SubCA in $Node.SubCAs) + { + + # Wait for SubCA to generate REQ + WaitForAny "WaitForSubCA_$SubCA" + { + ResourceName = '[ADCSCertificationAuthority]ConfigCA' + NodeName = $SubCA + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = '[Script]ADCSAdvConfig' + } + + # Download the REQ from the SubCA + xRemoteFile "DownloadSubCA_$SubCA" + { + DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$SubCA.req" + Uri = "http://$SubCA/CertEnroll/$SubCA.req" + DependsOn = "[WaitForAny]WaitForSubCA_$SubCA" + } + + # Generate the Issuing Certificate from the REQ + Script "IssueCert_$SubCA" + { + SetScript = { + Write-Verbose -Message "Submitting C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.req to $($Using:Node.CACommonName)" + [System.String]$RequestResult = & "$($ENV:SystemRoot)\System32\Certreq.exe" -Config ".\$($Using:Node.CACommonName)" -Submit "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.req" + $Matches = [Regex]::Match($RequestResult, 'RequestId:\s([0-9]*)') + + if ($Matches.Groups.Count -lt 2) + { + Write-Verbose -Message 'Error getting Request ID from SubCA certificate submission.' + Throw 'Error getting Request ID from SubCA certificate submission.' + } + + [System.Int32]$RequestId = $Matches.Groups[1].Value + Write-Verbose -Message "Issuing $RequestId in $($Using:Node.CACommonName)" + [System.String]$SubmitResult = & "$($ENV:SystemRoot)\System32\CertUtil.exe" -Resubmit $RequestId + + if ($SubmitResult -notlike 'Certificate issued.*') + { + Write-Verbose -Message 'Unexpected result issuing SubCA request.' + throw 'Unexpected result issuing SubCA request.' + } + + Write-Verbose -Message "Retrieving C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.req from $($Using:Node.CACommonName)" + [System.String]$RetrieveResult = & "$($ENV:SystemRoot)\System32\Certreq.exe" -Config ".\$($Using:Node.CACommonName)" -Retrieve $RequestId "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.crt" + } + + GetScript = { + return @{ + 'Generated' = (Test-Path -Path "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.crt"); + } + } + + TestScript = { + if (-not (Test-Path -Path "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.crt")) + { + # SubCA Cert is not yet created + return $false + } + + # SubCA Cert has been created + return $true + } + + DependsOn = "[xRemoteFile]DownloadSubCA_$SubCA" + } + + # Wait for SubCA to install the CA Certificate + WaitForAny "WaitForComplete_$SubCA" + { + ResourceName = '[Script]RegisterSubCA' + NodeName = $SubCA + RetryIntervalSec = 30 + RetryCount = 30 + DependsOn = "[Script]IssueCert_$SubCA" + } + + # Shutdown the Root CA - it is no longer needed because it has issued all SubCAs + Script ShutdownRootCA + { + SetScript = { + Stop-Computer + } + + GetScript = { + return @{ + } + } + + TestScript = { + # SubCA Cert is not yet created + return $false + } + + DependsOn = "[WaitForAny]WaitForComplete_$SubCA" + } + } + } +} diff --git a/source/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 b/source/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 new file mode 100644 index 00000000..9d1b8ff5 --- /dev/null +++ b/source/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 @@ -0,0 +1,216 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + STANDALONE_ROOTCA_NOSUBCA +.Desription + Builds a Standalone Root CA with no Sub CAs. +.Parameters: + CACommonName = 'LABBUILDER.COM Root CA' + CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' + CRLPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' + CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' +###################################################################################################> + +Configuration STANDALONE_ROOTCA_NOSUBCA +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryCSDsc + + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) + { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + + # Install the ADCS Certificate Authority + WindowsFeature ADCSCA + { + Name = 'ADCS-Cert-Authority' + Ensure = 'Present' + } + + <# + Install ADCS Web Enrollment - only required because it creates the CertEnroll virtual folder + Which we use to pass certificates to the Issuing/Sub CAs + #> + WindowsFeature ADCSWebEnrollment + { + Ensure = 'Present' + Name = 'ADCS-Web-Enrollment' + DependsOn = '[WindowsFeature]ADCSCA' + } + + WindowsFeature InstallWebMgmtService + { + Ensure = 'Present' + Name = 'Web-Mgmt-Service' + DependsOn = '[WindowsFeature]ADCSWebEnrollment' + } + + # Create the CAPolicy.inf file which defines basic properties about the ROOT CA certificate + File CAPolicy + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\CAPolicy.inf' + Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" + Type = 'File' + DependsOn = '[WindowsFeature]ADCSCA' + } + + # Configure the CA as Standalone Root CA + ADCSCertificationAuthority ConfigCA + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + CAType = 'StandaloneRootCA' + CACommonName = $Node.CACommonName + CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix + ValidityPeriod = 'Years' + ValidityPeriodUnits = 20 + CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' + HashAlgorithmName = 'SHA256' + KeyLength = 4096 + DependsOn = '[File]CAPolicy' + } + + # Configure the ADCS Web Enrollment + ADCSWebEnrollment ConfigWebEnrollment + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + CAConfig = 'CertSrv' + Credential = $LocalAdminCredential + DependsOn = '[ADCSCertificationAuthority]ConfigCA' + } + + # Set the advanced CA properties + Script ADCSAdvConfig + { + SetScript = { + if ($Using:Node.CADistinguishedNameSuffix) + { + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" + } + + if ($Using:Node.CRLPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) + } + + if ($Using:Node.CACertPublicationURLs) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) + } + + if ($Using:Node.CRLPeriodUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" + } + + if ($Using:Node.CRLOverlapUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" + } + + if ($Using:Node.ValidityPeriodUnits) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" + } + + if ($Using:Node.AuditFilter) + { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) + } + + Restart-Service -Name CertSvc + New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue + Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' + } + + GetScript = { + return @{ + 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); + 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); + 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); + 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') + 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') + 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') + 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') + 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') + 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') + 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') + 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') + } + } + + TestScript = { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) + { + return $false + } + + if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) + { + return $false + } + + if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) + { + return $false + } + + if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) + { + return $false + } + + if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) + { + return $false + } + + if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) + { + return $false + } + + if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) + { + return $false + } + + if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) + { + return $false + } + + if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) + { + return $false + } + + if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) + { + return $false + } + + return $true + } + + DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' + } + } +} diff --git a/source/dsclibrary/modules/LabDSCResources.psd1 b/source/dsclibrary/modules/LabDSCResources.psd1 new file mode 100644 index 00000000..e4e6a2eb --- /dev/null +++ b/source/dsclibrary/modules/LabDSCResources.psd1 @@ -0,0 +1,7 @@ +RootModule=xCertAuthorityServer.DSC.Schema.psm1 +RootModule=xDC.DSC.Schema.psm1 +RootModule=xDHCPServer.DSC.Schema.psm1 +RootModule=xFileServer.DSC.Schema.psm1 +RootModule=xJoinDomain.DSC.Schema.psm1 +RootModule=xNPSServer.DSC.Schema.psm1 +RootModule=xRemoteAccessServer.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 new file mode 100644 index 00000000..f85e7928 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xCertAuthorityServer.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 new file mode 100644 index 00000000..719073f2 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 @@ -0,0 +1,250 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_ROOTCA +.Desription + Builds an Enterprise Root CA. +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + CACommonName = "LABBUILDER.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 +###################################################################################################> + +Configuration MEMBER_ROOTCA +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName ActiveDirectoryCSDsc + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName NetworkingDsc + Node $AllNodes.NodeName { + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) { + $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + if ($Node.DomainAdminPassword) { + $DomainAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + # Install the CA Service + WindowsFeature ADCSCA { + Name = 'ADCS-Cert-Authority' + Ensure = 'Present' + } + + # Install the Web Enrollment Service + WindowsFeature ADCSWebEnrollment { + Name = 'ADCS-Web-Enrollment' + Ensure = 'Present' + DependsOn = "[WindowsFeature]ADCSCA" + } + + WindowsFeature InstallWebMgmtService + { + Ensure = "Present" + Name = "Web-Mgmt-Service" + DependsOn = '[WindowsFeature]ADCSWebEnrollment' + } + + if ($Node.InstallOnlineResponder) { + # Install the Online Responder Service + WindowsFeature OnlineResponderCA { + Name = 'ADCS-Online-Cert' + Ensure = 'Present' + DependsOn = "[WindowsFeature]ADCSCA" + } + } + + if ($Node.InstallEnrollmentWebService) { + # Install the Enrollment Web Service/Enrollment Policy Web Service + WindowsFeature EnrollmentWebSvc { + Name = 'ADCS-Enroll-Web-Svc' + Ensure = 'Present' + DependsOn = "[WindowsFeature]ADCSCA" + } + + WindowsFeature EnrollmentWebPol { + Name = 'ADCS-Enroll-Web-Pol' + Ensure = 'Present' + DependsOn = "[WindowsFeature]ADCSCA" + } + } + + + # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. + File CAPolicy + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\CAPolicy.inf' + Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" + Type = 'File' + DependsOn = '[Computer]JoinDomain' + } + + # Make a CertEnroll folder to put the Root CA certificate into. + # The CA Web Enrollment server would also create this but we need it now. + File CertEnrollFolder + { + Ensure = 'Present' + DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' + Type = 'Directory' + DependsOn = '[File]CAPolicy' + } + + # Configure the Root CA which will create the Certificate REQ file that Root CA will use + # to issue a certificate for this Sub CA. + ADCSCertificationAuthority ConfigCA + { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $DomainAdminCredential + CAType = 'EnterpriseRootCA' + CACommonName = $Node.CACommonName + CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix + OverwriteExistingCAinDS = $true + CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' + HashAlgorithmName = 'SHA256' + KeyLength = 4096 + DependsOn = '[File]CertEnrollFolder' + } + + # Configure the Web Enrollment Feature + ADCSWebEnrollment ConfigWebEnrollment { + Ensure = 'Present' + Name = 'ConfigWebEnrollment' + Credential = $LocalAdminCredential + DependsOn = '[ADCSCertificationAuthority]ConfigCA' + } + + # Perform final configuration of the CA which will cause the CA service to startup + # Set the advanced CA properties + Script ADCSAdvConfig + { + SetScript = { + if ($Using:Node.CADistinguishedNameSuffix) { + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" + & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" + } + if ($Using:Node.CRLPublicationURLs) { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) + } + if ($Using:Node.CACertPublicationURLs) { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) + } + if ($Using:Node.CRLPeriodUnits) { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" + } + if ($Using:Node.CRLOverlapUnits) { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" + } + if ($Using:Node.ValidityPeriodUnits) { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" + } + if ($Using:Node.AuditFilter) { + & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) + } + Restart-Service -Name CertSvc + Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value "Certificate Service Restarted ..." + } + GetScript = { + Return @{ + 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); + 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); + 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); + 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') + 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') + 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') + 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') + 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') + 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') + 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') + 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') + } + } + TestScript = { + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) { + Return $false + } + if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) { + Return $false + } + if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) { + Return $false + } + if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) { + Return $false + } + if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) { + Return $false + } + if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) { + Return $false + } + if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) { + Return $false + } + if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) { + Return $false + } + if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) { + Return $false + } + if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) { + Return $false + } + if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) { + Return $false + } + Return $true + } + DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' + } + + if ($Node.InstallOnlineResponder) { + # Configure the Online Responder Feature + ADCSOnlineResponder ConfigOnlineResponder { + Ensure = 'Present' + IsSingleInstance = 'Yes' + Credential = $LocalAdminCredential + DependsOn = '[Script]ADCSAdvConfig' + } + + # Enable Online Responder FireWall rules so we can remote manage Online Responder + Firewall OnlineResponderFirewall1 + { + Name = "Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In" + Enabled = "True" + DependsOn = "[ADCSOnlineResponder]ConfigOnlineResponder" + } + + Firewall OnlineResponderirewall2 + { + Name = "Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In" + Enabled = "True" + DependsOn = "[ADCSOnlineResponder]ConfigOnlineResponder" + } + + Firewall OnlineResponderFirewall3 + { + Name = "Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out" + Enabled = "True" + DependsOn = "[ADCSOnlineResponder]ConfigOnlineResponder" + } + } + } +} diff --git a/source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 new file mode 100644 index 00000000..29e79fa8 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xDC.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 new file mode 100644 index 00000000..8a116abd --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 @@ -0,0 +1,111 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + DC_FORESTPRIMARY +.Desription + Builds a Domain Controller as the first DC in a forest with the name of the Domain Name parameter passed. +.Parameters: + DomainName = 'LABBUILDER.COM' + DomainAdminPassword = 'P@ssword!1' +###################################################################################################> + +Configuration DC +{ + Param + ( + # Set the Domain Name + [Parameter(Mandatory=$true,Position=1)] + [System.String] + $DomainName, + + # Set the Domain Controller Name + [Parameter(Mandatory=$true)] + [System.String] + $DCName, + + # Local Administrator Credentials + [Parameter(Mandatory=$true)] + [System.String] + $LocalAdminPassword, + + # Domain Administrator Credentials + [Parameter(Mandatory=$true)] + [System.String] + $DomainAdminPassword, + + #OUs to Create + [Parameter(Mandatory=$false)] + [System.String] + $OUName + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 + Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 + + # Assemble the Local Admin Credentials + if ($LocalAdminPassword) { + $LocalAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) + } + + if ($DomainAdminPassword) { + $DomainAdminCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ('Administrator', (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature BackupInstall + { + Ensure = 'Present' + Name = 'Windows-Server-Backup' + } + + WindowsFeature DNSInstall + { + Ensure = 'Present' + Name = 'DNS' + } + + WindowsFeature ADDSInstall + { + Ensure = 'Present' + Name = 'AD-Domain-Services' + DependsOn = '[WindowsFeature]DNSInstall' + } + + WindowsFeature RSAT-AD-PowerShellInstall + { + Ensure = 'Present' + Name = 'RSAT-AD-PowerShell' + DependsOn = '[WindowsFeature]ADDSInstall' + } + + ADDomain ADDomainCreateDC + { + DomainName = $DomainName + Credential = $DomainAdminCredential + SafemodeAdministratorPassword = $LocalAdminCredential + DependsOn = '[WindowsFeature]ADDSInstall' + } + + WaitForADDomain DscDomainWait + { + DomainName = $Node.ParentDomainName + Credential = $DomainAdminCredential + WaitTimeout = 300 + RestartCount = 5 + DependsOn = '[WindowsFeature]ADDSInstall' + } + + ADOrganizationalUnit NewOU + { + Name = $OUName + Path = $OUPath + ProtectedFromAccidentalDeletion = $true + Description = $OUDescription + Ensure = 'Present' + DependsOn = '[WaitForADDomain]DscDomainWait' + } +} diff --git a/source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 new file mode 100644 index 00000000..fb6fd6c0 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xDHCPServer.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 new file mode 100644 index 00000000..4a1bc236 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 @@ -0,0 +1,119 @@ +Configuration DHCP +{ + Param + ( + # Domain Admin Password + [Parameter(Mandatory = $true)] + [System.String] + $DomainAdminPassword, + + # Local Admin Password + [Parameter(Mandatory = $true)] + [System.String] + $LocalAdminPassword, + + # Domain Name + [Parameter(Mandatory = $true)] + [System.String] + $DomainName, + + # Scope Options to add + [Parameter(AttributeValues)] + [hashtable] + $ScopeOptions, + + # Scopes to create + [Parameter(AttributeValues)] + [hashtable] + $Scopes, + + # Scope Reservations + [Parameter(AttributeValues)] + [hashtable] + $Reservations + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 + + # Assemble the Local Admin Credentials + if ($LocalAdminPassword) + { + [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) + } + if ($DomainAdminPassword) + { + [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$DomainName\Administrator", (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature DHCPInstall + { + Ensure = "Present" + Name = "DHCP" + } + + Script DHCPAuthorize + { + PSDSCRunAsCredential = $DomainAdminCredential + SetScript = { + Add-DHCPServerInDC + } + GetScript = { + Return @{ + 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); + } + } + TestScript = { + Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) + } + DependsOn = '[WindowsFeature]DHCPInstall' + } + + $count = 0 + foreach ($Scope in $Scopes) + { + $count++ + xDhcpServerScope "Scope$count" + { + Ensure = 'Present' + ScopeId = $Scope.Name + IPStartRange = $Scope.Start + IPEndRange = $Scope.End + Name = $Scope.Name + SubnetMask = $Scope.SubnetMask + State = 'Active' + LeaseDuration = '00:08:00' + AddressFamily = $Scope.AddressFamily + } + } + + $count = 0 + foreach ($Reservation in $Reservations) + { + $count++ + xDhcpServerReservation "Reservation$count" + { + Ensure = 'Present' + ScopeID = $Reservation.ScopeId + ClientMACAddress = $Reservation.ClientMACAddress + IPAddress = $Reservation.IPAddress + Name = $Reservation.Name + AddressFamily = $Reservation.AddressFamily + } + } + + $count = 0 + foreach ($ScopeOption in $ScopeOptions) + { + $count++ + xDhcpServerOption "ScopeOption$count" + { + Ensure = 'Present' + ScopeID = $ScopeOption.ScopeId + DnsDomain = $DomainName + DnsServerIPAddress = $ScopeOption.DNServerIPAddress + Router = $ScopeOption.Router + AddressFamily = $ScopeOption.AddressFamily + } + } +} diff --git a/source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 new file mode 100644 index 00000000..b13d327a --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xFileServer.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 new file mode 100644 index 00000000..a8749b30 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 @@ -0,0 +1,184 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_FILESERVER +.Desription + Builds a Server that is joined to a domain and then made into a File Server. +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" +###################################################################################################> + +Configuration FILESERVER +{ + + Param + ( + # Set the Domain Name + [Parameter(Mandatory=$true,Position=1)] + [System.String] + $DomainName, + + # Local Administrator Credentials + [Parameter(Mandatory=$true)] + [System.String] + $LocalAdminPassword, + + # Domain Administrator Credentials + [Parameter(Mandatory=$true)] + [ParameterType] + $DomainAdminPassword, + + # Disks for File server use + [Parameter(AttributeValues)] + [hashtable] + $Disks + + + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName StorageDsc + Import-DscResource -ModuleName NetworkingDsc + + # Assemble the Local Admin Credentials + if ($Node.LocalAdminPassword) { + $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) + } + if ($Node.DomainAdminPassword) { + $DomainAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) + } + + WindowsFeature FileServerInstall + { + Ensure = "Present" + Name = "FS-FileServer" + } + + WindowsFeature DataDedupInstall + { + Ensure = "Present" + Name = "FS-Data-Deduplication" + DependsOn = "[WindowsFeature]FileServerInstall" + } + + WindowsFeature DFSNameSpaceInstall + { + Ensure = "Present" + Name = "FS-DFS-Namespace" + DependsOn = "[WindowsFeature]DataDedupInstall" + } + + WindowsFeature DFSReplicationInstall + { + Ensure = "Present" + Name = "FS-DFS-Replication" + DependsOn = "[WindowsFeature]DFSNameSpaceInstall" + } + + WindowsFeature FSResourceManagerInstall + { + Ensure = "Present" + Name = "FS-Resource-Manager" + DependsOn = "[WindowsFeature]DFSReplicationInstall" + } + + WindowsFeature FSSyncShareInstall + { + Ensure = "Present" + Name = "FS-SyncShareService" + DependsOn = "[WindowsFeature]FSResourceManagerInstall" + } + + WindowsFeature StorageServicesInstall + { + Ensure = "Present" + Name = "Storage-Services" + DependsOn = "[WindowsFeature]FSSyncShareInstall" + } + + WindowsFeature ISCSITargetServerInstall + { + Ensure = "Present" + Name = "FS-iSCSITarget-Server" + DependsOn = "[WindowsFeature]StorageServicesInstall" + } + + + # Enable FSRM FireWall rules so we can remote manage FSRM + Firewall FSRMFirewall1 + { + Name = "FSRM-WMI-ASYNC-In-TCP" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall2 + { + Name = "FSRM-WMI-WINMGMT-In-TCP" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall3 + { + Name = "FSRM-RemoteRegistry-In (RPC)" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall4 + { + Name = "FSRM-Task-Scheduler-In (RPC)" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall5 + { + Name = "FSRM-SrmReports-In (RPC)" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall6 + { + Name = "FSRM-RpcSs-In (RPC-EPMAP)" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall7 + { + Name = "FSRM-System-In (TCP-445)" + Ensure = 'Present' + Enabled = 'True' + } + + Firewall FSRMFirewall8 + { + Name = "FSRM-SrmSvc-In (RPC)" + Ensure = 'Present' + Enabled = 'True' + } + + [System.Int32]$count=0 + ForEach ($Disk in $Disks) { + $count++ + + WaitforDisk Disk$count + { + DiskNumber = $Disk.Number + RetryIntervalSec = 60 + RetryCount = 60 + } + + Disk Volume$count + { + DiskNumber = $Disk.Number + DriveLetter = $Disk.Letter + DependsOn = "[WaitforDisk]Disk$count" + } + } +} diff --git a/source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 new file mode 100644 index 00000000..c808a252 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xJoindomain.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 new file mode 100644 index 00000000..82c7c49a --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 @@ -0,0 +1,72 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + Join Domain DSC Module +.Desription + Joins Server to Domain +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + ComputerName = "Server01" + DomainControllerName = "DC01" +###################################################################################################> + +Configuration JOINDOMAIN +{ + Param + ( + # Set the Domain Name + [Parameter(Mandatory=$true,Position=1)] + [System.String] + $DomainName, + + + # Set the Domain Controller Name + [Parameter(Mandatory=$true)] + [System.String] + $DCName, + + # Domain Administrator Credentials + [Parameter(Mandatory=$true)] + [System.String] + $DomainAdminPassword, + + # Set the Computer Name + [Parameter(Mandatory=$true)] + [System.String] + $ComputerName + + + + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 + Import-DscResource -ModuleName NetworkingDsc + + # Assemble the Local Admin Credentials + if ($LocalAdminPassword) { + [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) + } + if ($Node.DomainAdminPassword) { + [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$DomainName\Administrator", (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) + } + + WaitForAll DC + { + ResourceName = '[ADDomain]CreateDC' + NodeName = $DCname + RetryIntervalSec = 15 + RetryCount = 60 + } + + + Computer JoinDomain + { + Name = $ComputerName + DomainName = $DomainName + Credential = $DomainAdminCredential + DependsOn = "[WaitForAll]DC" + } + +} diff --git a/source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 new file mode 100644 index 00000000..404cfee8 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xNPSServer.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 new file mode 100644 index 00000000..79d1a1e2 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 @@ -0,0 +1,40 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_NPS +.Desription + Builds a Server that is joined to a domain and then contains NPS/Radius components. +.Requires + Windows Server 2012 R2 Full (Server core not supported). +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" +###################################################################################################> + +Configuration NPS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + + + WindowsFeature NPASPolicyServerInstall + { + Ensure = "Present" + Name = "NPAS-Policy-Server" + } + + WindowsFeature NPASHealthInstall + { + Ensure = "Present" + Name = "NPAS-Health" + DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + } + + WindowsFeature RSATNPAS + { + Ensure = "Present" + Name = "RSAT-NPAS" + DependsOn = "[WindowsFeature]NPASPolicyServerInstall" + } + + +} diff --git a/source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 b/source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 new file mode 100644 index 00000000..26b54472 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 @@ -0,0 +1 @@ +RootModule=xRemoteAccessServer.DSC.Schema.psm1 diff --git a/source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 b/source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 new file mode 100644 index 00000000..73da8730 --- /dev/null +++ b/source/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 @@ -0,0 +1,31 @@ +<################################################################################################### +DSC Template Configuration File For use by LabBuilder +.Title + MEMBER_EDGE +.Desription + Builds a Server that is joined to a domain and then contains Remote Access components. +.Parameters: + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" +###################################################################################################> + +Configuration REMOTEACCESS +{ + Import-DscResource -ModuleName PSDesiredStateConfiguration + + + WindowsFeature DirectAccessVPNInstall + { + Ensure = "Present" + Name = "DirectAccess-VPN" + } + + WindowsFeature RoutingInstall + { + Ensure = "Present" + Name = "Routing" + DependsOn = "[WindowsFeature]DirectAccessVPNInstall" + } + + +} diff --git a/source/en-US/LabBuilder_LocalizedData.psd1 b/source/en-US/LabBuilder_LocalizedData.psd1 new file mode 100644 index 00000000..008d98fd --- /dev/null +++ b/source/en-US/LabBuilder_LocalizedData.psd1 @@ -0,0 +1,241 @@ +# culture = "en-US" +ConvertFrom-StringData -StringData @' + FileNotFoundError = The {0} file '{1}' was not found. + InitializeVHDNotInitializedError = The VHD '{0}' failed to initialize because a Partition Style was not provided. + InitializeVHDNotFormattedError = The VHD '{0}' failed to format because a File System was not provided. + InitializeVHDAccessPathNotFoundError = The VHD '{0}' could not be assigned to the access path '{1}' because it does not exist. + FileDownloadError = Error downloading {0} from '{1}'; {2}. + FileExtractError = Error extracting {0}; {1}. + ConfigurationFileNotFoundError = Configuration file {0} is not found. + ConfigurationFileEmptyError = Configuration file {0} is empty. + RequiredBuildNotMetError = The Windows build of this host '{0}' does not meet the minimum required build of '{1}' to install this Lab. + ConfigurationFileAlreadyExistsError = Configuration file {0} already exists. + ConfigurationInvalidError = Configuration is invalid. + PathNotFoundError = {0} path '{1}' is not found. + ResourceModuleNameIsEmptyError = Resource Module Name is missing or empty. + ResourceMSUNameIsEmptyError = Resource MSU Name is missing or empty. + ResourceISONameIsEmptyError = Resource ISO Name is missing or empty. + ResourceISOPathIsEmptyError = Resource ISO '{0}' path is missing or empty. + ResourceISOFileNotFoundAndNoURLError = Resource ISO '{0}' file '{1}' is not found and no URL provided. + ResourceISOFileNotDownloadedError = Resource ISO '{0}' file '{1}' was not downloaded successfully from '{2}'. Please download this file manually and check the content and filename. + ModuleNotAvailableError = Error installing Module '{0}' ({1}); {2}. + SwitchNameIsEmptyError = Switch name is missing or empty. + UnknownSwitchTypeError = Unknown switch type '{0}' specified for switch '{1}'. + AdapterSpecifiedError = Adater specified on '{0}' switch '{1}'. + NatSwitchNotSupportedError = NAT Switch '{0}' is not supported. NAT Switches are only supported on build Windows 10 or Windows Server 2016 build 14295 and above. + NatSubnetEmptyError = NAT Switch '{0}' subnet is empty. + NatSubnetInvalidError = NAT Switch '{0}' subnet format '{1}' is invalid. It must contain IP address and prefix length. E.g '192.168.1.1/24'. + NatSubnetAddressInvalidError = NAT Switch '{0}' subnet address '{1}' is invalid. + NatSubnetPrefixLengthInvalidError = NAT Switch '{0}' subnet prefix length '{1}' is invalid. + NatSwitchDefaultAdapterMacEmptyError = NAT Switch '{0}' default virtual network adapter MAC address is empty. + EmptyVMTemplateVHDNameError = Template VHD name is missing or empty. + EmptyVMTemplateVHDISOPathError = The ISO Path in VM Template VHD '{0}' is empty. + EmptyVMTemplateVHDPathError = The VHD Path in VM Template VHD '{0}' is empty. + VMTemplateVHDISORootPathNotFoundError = The default ISO Folder '{0}' for VM template VHDs is not found. + VMTemplateVHDISOPathNotFoundError = The ISO file '{1}' for VM Template VHD '{0}' could not be found. + VMTemplateVHDRootPathNotFoundError = The default VHD Folder '{0}' for VM template VHDs is not found. + InvalidVMTemplateVHDOSTypeError = The OSType '{1}' in VM template VHD '{0}' is invalid. Valid settings are Server, Client or Nano. + InvalidVMTemplateVHDVHDFormatError = The VHDFormat '{1}' in VM template VHD '{0}' is invalid. Valid settings are VHDx or VHD. + InvalidVMTemplateVHDVHDTypeError = The VHDType '{1}' in VM template VHD '{0}' is invalid. Valid settings are Dynamic or Fixed. + InvalidVMTemplateVHDGenerationError = The Generation '{1}' in VM template VHD '{0}' is invalid. Valid settings are 1 or 2. + EmptyTemplateNameError = Template Name is missing or empty. + TemplateSourceVHDAndTemplateVHDConflictError = Both the Template SourceVHD and TemplateVHD parameters are set for Template '{0}'. Only one of these may be set for each Template. + TemplateSourceVHDandTemplateVHDMissingError = Either the Template SourceVHD or TemplateVHD parameter must be set in Template '{0}'. + TemplateTemplateVHDNotFoundError = The Template VHD '{1}' in Template '{0}' could not be found. + TemplateSourceVHDNotFoundError = The Template Source VHD '{1}' in Template '{0}' could not be found. + DSCModuleDownloadError = Module '{2}' required by DSC Config File '{0}' in VM '{1}' could not be found or downloaded. + DSCModuleNotFoundError = Module '{2}' required by DSC Config File '{0}' in VM '{1}' could not be found in the module path. + CertificateCreateError = The self-signed certificate for VM '{0}' could not be created and downloaded. + CertificateDownloadError = The self-signed certificate for VM '{0}' could not be downloaded. + DSCConfigMetaMOFCreateError = A Meta MOF File was not created by the DSC LCM Config for VM '{0}'. + DSCConfigMoreThanOneNodeError = A single Node element cannot be found in the DSC Config File '{0}' in VM '{1}'. + DSCConfigMOFCreateError = A MOF File was not created by the DSC Config File '{0}' in VM '{1}'. + NetworkAdapterNotFoundError = VM Network Adapter '{0}' could not be found attached to VM '{1}'. + NetworkAdapterBlankMacError = VM Network Adapter '{0}' attached to VM '{1}' has a blank MAC Address. + ManagmentIPAddressError = An IPv4 address for the network adapter connected to the {0} for VM '{1}' could not be identified. + DSCInitializationError = An error occurred initializing DSC for VM '{0}'. + RemotingConnectionError = An error occurred connecting to VM '{0}' using PowerShell Remoting. + InitialSetupCompleteError = The Initial Setup for VM '{0}' did not complete before the timeout occurred. + InitializationDidNotCompleteError = Initialization for VM '{0}' did not complete. + SetupCompleteScriptMissingError = The Setup Complete Script file '{1}' specified in VM '{0}' could not be found. + UnattendFileMissingError = The Unattend file '{1}' specified in VM '{0}' could not be found. + SetupCompleteFileMissingError = The Setup Complete file '{1}' specified in VM '{0}' could not be found. + SetupCompleteFileBadTypeError = The Setup Complete file '{1}' specified in VM '{0}' must be either a PS1 or CMD file. + DSCConfigFileMissingError = The DSC Config file '{1}' specified in VM '{0}' could not be found. + DSCConfigFileBadTypeError = The DSC Config file '{1}' specified in VM '{0}' must be a PS1 file. + DSCConfigNameIsEmptyError = The DSC Config Name specified in VM '{0}' is empty. + VMNameError = The VM name cannot be 'VM' or empty. + VMTemplateNameEmptyError = The template name in VM '{0}' is empty. + VMTemplateNotFoundError = The template '{1}' specified in VM '{0}' could not be found. + VMTemplateVHDPathEmptyError = The template VHD path set in template '{0}' is empty. + VMAdapterNameError = The Adapter Name in VM '{0}' cannot be 'adapter' or empty. + VMAdapterSwitchNameError = The Switch Name specified in adapter '{1}' specified in VM '{0}' cannot be empty. + VMAdapterSwitchNotFoundError = The switch '{2}' specified in adapter '{1}' in VM '{0}' could not be found in Switches. + VMDataDiskVHDEmptyError = The Data Disk VHD in VM '{0}' cannot be 'datavhd' or empty. + VMDataDiskCantBeCreatedError = The Data Disk VHD '{1}' specified in VM '{0}' does not exist but the size and type or Source VHD was not provided so it not be created. + VMDataDiskParentVHDNotFoundError = The Data Disk Parent VHD '{1}' specified in VM '{0}' could not be found. + VMDataDiskParentVHDMissingError = The Differencing Data Disk Parent VHD specified in VM '{0}' is empty. + VMDataDiskSourceVHDNotFoundError = The Data Disk Source VHD '{1}' specified in VM '{0}' could not be found. + VMDataDiskUnknownTypeError = Unknown Data Disk type '{2}' specified in VM '{0}' for VHD '{1}'. + VMDataDiskSharedDifferencingError = The Differencing Data Disk VHD '{1}' specified in VM '{0}' can not be set as Shared. + VMDataDiskSourceVHDIfMoveError = The Data Disk VHD '{1}' specified in VM '{0}' must have a Source VHD specified if MoveSourceVHD is set. + VMDataDiskVHDConvertError = The Data Disk '{1}' in VM '{0}' cannot be converted to a {2} type. + VMDataDiskVHDShrinkError = The Data Disk '{1}' in VM '{0}' cannot be shrunk to {2}. + DownloadFolderDoesNotExistError = The folder '{0}' to download '{1}' to does not exist. + VMDataDiskPartitionStyleError = '{2}' is not a valid partition style for the Data Disk '{1}' in VM '{0}'. + VMDataDiskFileSystemError = '{2}' is not a valid file system for the Data Disk '{1}' in VM '{0}'. + VMDataDiskPartitionStyleMissingError = The Data Disk '{1}' in VM '{0}' does not have a partition style definied. + VMDataDiskFileSystemMissingError = The Data Disk '{1}' in VM '{0}' does not have a file format definied. + VMDataDiskCopyFolderMissingError = The CopyFolder '{2}' that should be copied to Data Disk '{1}' in VM '{0}' does not exist. + VMDVDDriveISOResourceNotFOundError = The ISO Resource '{1}' to be mounted into a Virtual DVD Drive specified in VM '{0}' does not exist. + NanoServerPackagesFolderMissingError = The NanoServerPackages folder '{0}' does not exist. + VMDoesNotExistError = The VM '{0}' does not exist. + VMVirtualizationExtError = The VM '{0}' requires Virtualization Extensions to be exposed, but this is not supported by your version of Windows. Either set the ExposeVirtualizationExtensions attribute to 'N' for the VM or update to a Windows Build 10565 or above. + NanoPackageNotFoundError = The Nano Server Package '{0}' could not be found. + PackageNotFoundError = The Package MSU '{0}' is not listed in the Lab Resource MSU list. + PackageMSUNotFoundError = The file '{1}' for Package MSU '{0}' does not exist. + BootPhaseStartVMsTimeoutError = One or more Virtual Machines with Bootorder '{0}' failed to start completely in the required time. + ConfigurationXMLValidationError = Lab Configuration XML '{0}'- {1}. + DSCConfiguartionMissingError = Start of Configuration could not be correctly identified in DSC Config. + VolumeNotAvailableAfterMountError = The volume was not found after ISO File '{0}' was mounted. + DriveLetterNotAssignedError = The volume was not found after ISO File '{0}' was mounted but a Drive Letter was not assigned. + ConvertWindowsImageError = An error occured converting {2} in '{1}' from ISO File '{0}' to a bootable {3}; {4}. + BindingAdapterNotFoundError = A physical network adapter {1}was not found to bind to the External Switch {0}. + BindingAdapterUsedError = Error binding physical network adapter '{1}' to External Switch '{0}' because it is already bound to another External Switch. + IPAddressError = The IP Address '{0}' is invalid. + WSManNotEnabledError = WS-Man is not enabled. + WinRMServiceFailedToStartError = The WinRM service failed to start. + PackageProviderNotInstalledError = The required package provider '{0}' is not installed. + PackageSourceNotTrustedError = The required package source '{0}' is not trusted. + PackageSourceNotRegisteredError = The required package source '{0}' is not registered. + ODJCopyError = Error copying Offline Domain Join file '{1}' to VM '{0}'. + VMNotRunningHeartbeatError = Virtual Machine '{0}' is not running, so waiting for heartbeat failed. + + ImportingLibFileMessage = Importing function library '{0}'. + EnablingWSManMessage = Enabling WS-Man for communication with Lab Guests. + InstallingHyperVComponentsMesage = Installing {0} Hyper-V Components. + InitializingLabFoldersMesage = Initializing Lab Folders. + CreatingLabFolderMessage = Creating {0} folder '{1}' for Lab. + InitializingHyperVComponentsMesage = Initializing Hyper-V Components. + InitializeVHDMountingMessage = Mounting VHD {0} for Initialization. + InitializeVHDInitializingMessage = Initializing {1} partition table on VHD {0}. + InitializeVHDCreatePartitionMessage = Creating partition on VHD {0}. + InitializeVHDFormatVolumeMessage = Formatting volume on partition {2} as {1} on VHD '{0}'. + InitializeVHDSetLabelVolumeMessage = Setting volume label to {1} on VHD '{0}'. + InitializeVHDDriveLetterMessage = Assigning drive letter {1}: to VHD {0}. + InitializeVHDAccessPathMessage = Assigning access path {1} to VHD {0}. + DownloadingFileMessage = Downloading File '{0}' from '{1}' to '{2}'. + ExtractingFileMessage = Extracting downloaded File '{0}' to '{1}'. + DownloadingResourceModuleMessage = Downloading Lab Resource Module '{0}' from '{1}'. + DownloadingResourceMSUMessage = Downloading Lab Resource MSU Package '{0}' from '{1}'. + DownloadingResourceISOMessage = Downloading Lab Resource ISO File '{0}' from '{1}'. + InitializingLabManagementVirtualNetworkMesage = Initializing Lab Management virtual switch '{0}'. + CreatingLabManagementSwitchMessage = Creating Lab Management Switch '{0}' on Vlan {1}. + UpdatingLabManagementSwitchMessage = Updating Lab Management Switch '{0}' to Vlan {1}. + RemovingLabManagementSwitchMessage = Removing Lab Management Switch '{0}'. + ModuleNotInstalledMessage = Module {0} ({1}) is not installed. + DownloadingLabResourceWebMessage = Downloading Module {0} ({1}) from '{2}'. + InstalledLabResourceWebMessage = Installed Module {0} ({1}) to '{2}'. + CreatingVirtualSwitchMessage = Creating {0} Virtual Switch '{1}'. + DeleteingVirtualSwitchMessage = Deleting {0} Virtual Switch '{1}'. + CopyingTemplateSourceVHDMessage = Copying template source VHD '{0}' to '{1}'. + OptimizingParentVHDMessage = Optimizing parent VHD '{0}'. + SettingParentVHDReadonlyMessage = Setting parent VHD '{0}' as readonly. + SkipParentVHDFileMessage = Skip copying parent VHD file '{1}' for '{0}' because it already exists. + SkipVMTemplateVHDFileMessage = Skip building VM template VHD file '{1}' for '{0}' because it already exists. + DeletingVMTemplateVHDFileMessage = Deleting VM template VHD file '{1}' for '{0}'. + DeletingParentVHDMessage = Deleting Parent VHD '{0}'. + DSCConfigIdentifyModulesMessage = Identifying Modules used by DSC Config File '{0}' in VM '{1}'. + DSCConfigSearchingForModuleMessage = Searching for Module '{2}' required by DSC Config File '{0}' in VM '{1}'. + DSCConfigInstallingModuleMessage = Installing Module '{2}' required by DSC Config File '{0}' in VM '{1}'. + DSCConfigSavingModuleMessage = Saving Module '{2}' required by DSC Config File '{0}' in VM '{1}' to LabBuilder files. + DSCConfigCopyingModuleMessage = Copying Module '{2}' required by DSC Config File '{0}' in VM '{1}' from '{3}' to '{4}'. + DSCConfigCreatingLCMMOFMessage = Creating DSC LCM Config file '{0}' in VM '{1}'. + DSCConfigPrepareMessage = Preparing to compile DSC Config '{0}' for VM '{1}'. + DSCConfigCreatingMOFMessage = Creating DSC Config file '{0}' in VM '{1}'. + DSCConfigMOFCreatedMessage = DSC MOF File '{0}' for VM '{1}'. was created successfully. + ConnectingVMMessage = Connecting to VM '{0}' on '{1}'. + DisconnectingVMMessage = Disconnecting from VM '{0}' on '{1}'. + VMSessionDoesNotExistMessage = LabBuilder Remoting Session to VM '{0}' does not exist. + ConnectingVMFailedMessage = Connection to VM '{0}' failed ({2}), retrying in {1} seconds. + ConnectingVMAccessDeniedMessage = Access Denied connecting to VM '{0}', the connection will not be retried. + CopyingFilesToVMMessage = Copying {1} Files to VM '{0}'. + CopyingFilesToVMFailedMessage = Copying {1} Files to VM '{0}' failed, retrying in {2} seconds. + CreatingVMMessage = Creating VM '{0}'. + CreatingVMDiskMessage = Creating {2} disk '{1}' for VM '{0}'. + CreatingVMDiskByMovingSourceVHDMessage = Creating disk {1} for VM '{0}' by moving Source VHD '{2}'. + CreatingVMDiskByCopyingSourceVHDMessage = Creating disk {1} for VM '{0}' by copying Source VHD '{2}'. + VMDiskAlreadyExistsMessage = {2} disk '{1}' for VM '{0}' already exists. + ExpandingVMDiskMessage = Expanding {2} disk '{1}' for VM '{0}' to {3}. + AddingVMDiskMessage = Adding {2} disk '{1}' to VM '{0}'. + AddingVMDVDDriveMessage = Adding DVD Drive to VM '{0}'. + MountingVMDVDDriveISOMessage = Mounting ISO '{1}' to DVD Drive in VM '{0}'. + DismountingVMDVDDriveISOMessage = Dismounting ISO '{1}' from DVD Drive in VM '{0}'. + CopyingFoldersToVMDiskMessage = Copying folder '{2}' to VM Disk '{1}' for VM '{0}'. + InitializingVMDiskMessage = Initializing VM Disk '{1}' for VM '{0}'. + MountingVMDiskMessage = Mounting VM Disk '{1}' for VM '{0}' to '{2}'. + DismountingVMDiskMessage = Dismounting VM Disk '{1}' for VM '{0}'. + DeletingVMFolderMessage = Deleting folder for VM '{0}'. + AddingVMNetworkAdapterMessage = Adding {2} network adapter {1} to VM '{0}'. + SettingVMNetworkAdapterVlanMessage = Setting VLAN on {2} network adapter {1} in VM '{0}' to {3}. + ClearingVMNetworkAdapterVlanMessage = Clearing VLAN on {2} network adapter {1} in VM '{0}'. + StartingVMMessage = Starting VM '{0}'. + StoppingVMMessage = Stopping VM '{0}'. + RemovingVMMessage = Removing VM '{0}'. + RemovedVMMessage = Removed VM '{0}'. + StartingBootPhaseVMsMessage = Starting Virtual Machines with Bootorder '{0}'. + AllBootPhaseVMsStartedMessage = All Virtual Machines with Bootorder '{0}' have started. + StoppingBootPhaseVMsMessage = Stopping Virtual Machines with Bootorder '{0}'. + AllBootPhaseVMsStoppedMessage = All Virtual Machines with Bootorder '{0}' have stopped. + StartingDSCMessage = Starting DSC on VM '{0}'. + MountingVMBootDiskMessage = Mounting VM '{0}' VHD Boot Disk '{1}'. + DownloadingVMBootDiskFileMessage = Downloading VM '{0}' {1} file '{2}'. + ApplyingVMBootDiskFileMessage = Applying {1} file '{2}' to VHD Boot Disk for VM '{0}'. + CreatingVMBootDiskPantherFolderMessage = Creating Panther folder to VHD Boot Disk for VM '{0}'. + DismountingVMBootDiskMessage = Dismounting VM '{0}' VHD Boot Disk '{1}'. + MountingTemplateBootDiskMessage = Mounting Template '{0}' VHD Boot Disk '{1}'. + ApplyingTemplateBootDiskFileMessage = Applying {1} file '{2}' to VHD Boot Disk for Template '{0}'. + DismountingTemplateBootDiskMessage = Dismounting Template '{0}' VHD Boot Disk '{1}'. + AddingIPAddressToTrustedHostsMessage = Adding IP Address '{1}' to WS-Man Trusted Hosts to allow remoting to '{0}'. + RemovingIPAddressFromTrustedHostsMessage = Removing IP Address '{1}' from WS-Man Trusted Hosts. + WaitingForIPAddressAssignedMessage = Waiting for valid IP Address to be assigned to VM '{0}', retrying in {1} seconds. + WaitingForInitialSetupCompleteMessage = Waiting for Initial Setup to be complete on VM '{0}', retrying in {1} seconds. + WaitingForCertificateMessage = Waiting for Certificate file on VM '{0}', retrying in {1} seconds. + FailedToUploadCertificateCreateScriptMessage = Failed to upload certificate create script to VM '{0}', retrying in {1} seconds. + FailedToDownloadCertificateMessage = Failed to download certificate from VM '{0}', retrying in {1} seconds. + FailedToExecuteCertificateCreateScriptMessage = Failed to execute certificate create script to VM '{0}', retrying in {1} seconds. + InitialSetupIsAlreadyCompleteMessaage = Initial Setup on VM '{0}' has already been completed. + CertificateDownloadStartedMessage = Certificate download from VM '{0}' started. + CertificateDownloadCompleteMessage = Certificate download from VM '{0}' complete. + VMNotFoundMessage = VM '{0}' was not found in Hyper-V server. + EnableVMIntegrationServiceMessage = The '{1}' Integration Service has been enabled in VM '{0}'. + DisableVMIntegrationServiceMessage = The '{1}' Integration Service has been disabled in VM '{0}'. + ISONotFoundDownloadURLMessage = The ISO '{1}' for VM template VHD '{0}' could not be found. It can be downloaded from '{2}'. + CreatingMountFolderMessage = Creating a temporary mount folder '{0}'. + CreatingVMTemplateVHDMessage = Creating the '{0}' VM Template VHD '{1}'. + CachingNanoServerPackagesMessage = Caching Nano Server packages from '{0}' to '{1}'. + ConvertingWIMtoVHDMessage = Converting '{3}' in '{0}' to a bootable {4} {5} {2} '{1}'. + CreatedVMInitializationFiles = Created Initialization files for VM '{0}'. + MountingVMTemplateVHDISOMessage = Mounting {1} to use source WIM to create Template VHD {0} + LabInstallCompleteMessage = The Lab '{0}' has been installed into folder '{1}'. + LabUpdateCompleteMessage = The Lab '{0}' in folder '{1}' has been updated. + LabUninstallCompleteMessage = The Lab '{0}' has been uninstalled from folder '{1}'. + LabStartCompleteMessage = The Lab '{0}' in folder '{1}' has been started. + LabStopCompleteMessage = The Lab '{0}' in folder '{1}' has been stopped. + ConfigurationXMLValidationMessage = Lab Configuration XML '{0}'- {1} + InstallPackageProviderMessage = Installing Package Provider '{0}'. + RegisterPackageSourceMessage = Registering Package Source '{0}' with '{1}'. + WaitingForVMHeartbeatMessage = Waiting for Virtual Machine '{0}' heartbeat, retrying in {1} seconds. + + ShouldUninstallLab = Uninstall the Lab '{0}' in folder '{1}' + ShouldRemoveVMTemplate = Delete the Parent VM Template VHDs used by Lab '{0}' in folder '{1}' + ShouldRemoveSwitch = Delete the virtual switches used by Lab '{0}' + ShouldRemoveVMTemplateVHD = Delete the VM Template VHDs used by Lab '{0}'. + ShouldRemoveLabFolder = Delete the folder '{1}' containing Lab '{0}' + ShouldOverwriteLab = Install a new Lab into the existing folder '{0}' + ShouldOverwriteLabConfig = Overwrite the existing Lab Configuration file '{0}' + DismountingVMTemplateVHDISOMessage = Dismounting {1} used for source WIM to create Template VHD {0} + ShouldInstallPackageProvider = Install Package Provider '{0}' + ShouldRegisterPackageSource = Register Package Source '{0}' with '{1}' + ShouldTrustPackageSource = Trust Package Source '{0}' with '{1}' +'@ diff --git a/source/en-US/about_LabBuilder.help.txt b/source/en-US/about_LabBuilder.help.txt new file mode 100644 index 00000000..5197dad9 --- /dev/null +++ b/source/en-US/about_LabBuilder.help.txt @@ -0,0 +1,24 @@ +TOPIC + about_CosmosDB + +SHORT DESCRIPTION + PowerShell module for working with CosmosDB. + +LONG DESCRIPTION + This module provides cmdlets for working with Azure Cosmos DB databases, collections, + documents, attachments, offers, users, permissions, triggers, stored procedures + and user defined functions. + +EXAMPLES + PS C:\> Get-Command -Module CosmosDB + +TROUBLESHOOTING NOTE: + Go to the Github repository for read about issues, submit a new issue, and read + about new releases. https://github.com/PlagueHO/CosmosDB + +SEE ALSO + - https://github.com/PlagueHO/CosmosDB + +KEYWORDS + CosmosDB, DocumentDb, Azure + diff --git a/source/prefix.ps1 b/source/prefix.ps1 new file mode 100644 index 00000000..b6f33f49 --- /dev/null +++ b/source/prefix.ps1 @@ -0,0 +1,539 @@ +<# +.EXTERNALHELP LabBuilder-help.xml +#> +#Requires -version 5.1 +#Requires -RunAsAdministrator + +$script:moduleRoot = Split-Path ` + -Path $MyInvocation.MyCommand.Path ` + -Parent + +#region LocalizedData +$culture = $PSUICulture + +if ([System.String]::IsNullOrEmpty($culture)) +{ + $culture = 'en-US' +} +else +{ + if (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath $culture)) + { + $culture = 'en-US' + } +} + +Import-LocalizedData ` + -BindingVariable LocalizedData ` + -Filename 'CosmosDB.strings.psd1' ` + -BaseDirectory $script:moduleRoot ` + -UICulture $culture +#endregion + +#region Types +Enum LabOStype { + Server = 1 + Nano = 2 + Client = 3 +} # Enum LabOStype + +Enum LabVHDType { + Fixed = 1 + Dynamic = 2 + Differencing = 3 +} # Enum LabVHDType + +Enum LabVHDFormat { + VHD = 1 + VHDx = 2 +} # Enum LabVHDFormat + +Enum LabSwitchType { + Private = 1 + Internal = 2 + External = 3 + NAT = 4 +} # Enum LabSwitchType + +Enum LabPartitionStyle { + MBR = 1 + GPT = 2 +} # Enum LabPartitionStyle + +Enum LabFileSystem { + FAT32 = 1 + exFAT = 2 + NTFS = 3 + ReFS = 4 +} # Enum LabFileSystem + +Enum LabCertificateSource { + Guest = 1 + Host = 2 +} # Enum LabCertificateSource + +class LabResourceModule:System.ICloneable { + [System.String] $Name + [System.String] $URL + [System.String] $Folder + [System.String] $MinimumVersion + [System.String] $RequiredVersion + + LabResourceModule() {} + + LabResourceModule($Name) { + $this.Name = $Name + } # Constructor + + [Object] Clone () { + $New = [LabResourceModule]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabResourceModule + +class LabResourceMSU:System.ICloneable { + [System.String] $Name + [System.String] $URL + [System.String] $Path + [System.String] $Filename + + LabResourceMSU() {} + + LabResourceMSU($Name) { + $this.Name = $Name + } # Constructor + + LabResourceMSU($Name,$URL) { + $this.Name = $Name + $this.URL = $URL + } # Constructor + + [Object] Clone () { + $New = [LabResourceMSU]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabResourceMSU + +class LabResourceISO:System.ICloneable { + [System.String] $Name + [System.String] $URL + [System.String] $Path + + LabResourceISO() {} + + LabResourceISO($Name) { + $this.Name = $Name + } # Constructor + + [Object] Clone () { + $New = [LabResourceISO]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabResourceISO + +class LabSwitchAdapter:System.ICloneable { + [System.String] $Name + [System.String] $MACAddress + [Byte] $Vlan + + LabSwitchAdapter() {} + + LabSwitchAdapter($Name) { + $this.Name = $Name + } # Constructor + + LabSwitchAdapter($Name,$Type) { + $this.Name = $Name + $this.Type = $Type + } # Constructor + + [Object] Clone () { + $New = [LabSwitchAdapter]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabSwitchAdapter + +class LabVMAdapterIPv4:System.ICloneable { + [System.String] $Address + [System.String] $DefaultGateway + [Byte] $SubnetMask + [System.String] $DNSServer + + LabVMAdapterIPv4() {} + + LabVMAdapterIPv4($Address,$SubnetMask) { + $this.Address = $Address + $this.SubnetMask = $SubnetMask + } # Constructor + + [Object] Clone () { + $New = [LabVMAdapterIPv4]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabVMAdapterIPv4 + +class LabVMAdapterIPv6:System.ICloneable { + [System.String] $Address + [System.String] $DefaultGateway + [Byte] $SubnetMask + [System.String] $DNSServer + + LabVMAdapterIPv6() {} + + LabVMAdapterIPv6($Address,$SubnetMask) { + $this.Address = $Address + $this.SubnetMask = $SubnetMask + } # Constructor + + [Object] Clone () { + $New = [LabVMAdapterIPv6]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabVMAdapterIPv6 + +class LabVMAdapter:System.ICloneable { + [System.String] $Name + [System.String] $SwitchName + [System.String] $MACAddress + [System.Boolean] $MACAddressSpoofing + [Byte] $Vlan + [LabVMAdapterIPv4] $IPv4 + [LabVMAdapterIPv6] $IPv6 + + LabVMAdapter() {} + + LabVMAdapter($Name) { + $this.Name = $Name + } # Constructor + + [Object] Clone () { + $New = [LabVMAdapter]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabVMAdapter + +class LabDataVHD:System.ICloneable { + [System.String] $VHD + [LabVHDType] $VHDType + [Uint64] $Size + [System.String] $SourceVHD + [System.String] $ParentVHD + [System.Boolean] $MoveSourceVHD + [System.String] $CopyFolders + [LabFileSystem] $FileSystem + [LabPartitionStyle] $PartitionStyle + [System.String] $FileSystemLabel + [System.Boolean] $Shared = $false + [System.Boolean] $SupportPR = $false + + LabDataVHD() {} + + LabDataVHD($VHD) { + $this.VHD = $VHD + } # Constructor + + [Object] Clone () { + $New = [LabDataVHD]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabDataVHD + +class LabDVDDrive:System.ICloneable { + [System.String] $ISO + [System.String] $Path + + LabDVDDrive() {} + + LabDVDDrive($ISO) { + $this.ISO = $ISO + } # Constructor + + [Object] Clone () { + $New = [LabDVDDrive]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabDVDDrive + +class LabVMTemplateVHD:System.ICloneable { + [System.String] $Name + [System.String] $ISOPath + [System.String] $VHDPath + [LabOStype] $OSType = [LabOStype]::Server + [System.String] $Edition + [Byte] $Generation = 2 + [LabVHDFormat] $VHDFormat = [LabVHDFormat]::VHDx + [LabVHDType] $VHDType = [LabVHDType]::Dynamic + [Uint64] $VHDSize = 0 + [System.String[]] $Packages + [System.String[]] $Features + + LabVMTemplateVHD() {} + + LabVMTemplateVHD($Name) { + $this.Name = $Name + } # Constructor + + [Object] Clone () { + $New = [LabVMTemplateVHD]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabVMTemplateVHD + +class LabVMTemplate:System.ICloneable { + [System.String] $Name + [System.String] $VHD + [System.String] $SourceVHD + [System.String] $ParentVHD + [System.String] $TemplateVHD + [Uint64] $MemoryStartupBytes = 1GB + [System.Boolean] $DynamicMemoryEnabled = $true + [System.Boolean] $ExposeVirtualizationExtensions = $false + [Byte] $ProcessorCount = 1 + [System.String] $AdministratorPassword + [System.String] $ProductKey + [System.String] $Timezone="Pacific Standard Time" + [LabOStype] $OSType = [LabOStype]::Server + [System.String[]] $IntegrationServices = @('Guest Service Interface','Heartbeat','Key-Value Pair Exchange','Shutdown','Time Synchronization','VSS') + [System.String[]] $Packages + [ValidateRange(1,2)][Byte] $Generation = 2 + [ValidateSet("5.0","6.2","7.0","7.1","8.0","254.0","255.0")][System.String] $Version = '8.0' + + LabVMTemplate() {} + + LabVMTemplate($Name) { + $this.Name = $Name + } # Constructor + + [Object] Clone () { + $New = [LabVMTemplate]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabVMTemplate + +class LabSwitch:System.ICloneable { + [System.String] $Name + [LabSwitchType] $Type + [Byte] $VLAN + [System.String] $BindingAdapterName + [System.String] $BindingAdapterMac + [System.String] $NatSubnet + [System.String] $NatGatewayAddress + [LabSwitchAdapter[]] $Adapters + + LabSwitch() {} + + LabSwitch($Name) { + $this.Name = $Name + } # Constructor + + LabSwitch($Name,$Type) { + $this.Name = $Name + $this.Type = $Type + } # Constructor + + [Object] Clone () { + $New = [LabSwitch]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabSwitch + +class LabDSC:System.ICloneable { + [System.String] $ConfigName + [System.String] $ConfigFile + [System.String] $Parameters + [System.Boolean] $Logging = $false + + LabDSC() {} + + LabDSC($ConfigName) { + $this.ConfigName = $ConfigName + } # Constructor + + LabDSC($ConfigName,$ConfigFile) { + $this.ConfigName = $ConfigName + $this.ConfigFile = $ConfigFile + } # Constructor + + [Object] Clone () { + $New = [LabDSC]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabDSC + +class LabVM:System.ICloneable { + [System.String] $Name + [System.String] $Template + [System.String] $ComputerName + [Byte] $ProcessorCount + [Uint64] $MemoryStartupBytes = 1GB + [System.Boolean] $DynamicMemoryEnabled = $true + [System.Boolean] $ExposeVirtualizationExtensions = $false + [System.String] $ParentVHD + [System.Boolean] $UseDifferencingDisk = $true + [System.String] $AdministratorPassword + [System.String] $ProductKey + [System.String] $Timezone="Pacific Standard Time" + [LabOStype] $OSType = [LabOStype]::Server + [System.String] $UnattendFile + [System.String] $SetupComplete + [System.String[]] $Packages + [ValidateRange(1,2)][Byte] $Generation = 2 + [ValidateSet("5.0","6.2","7.0","7.1","8.0","254.0","255.0")][System.String] $Version = '8.0' + [System.Int32] $BootOrder + [System.String[]] $IntegrationServices = @('Guest Service Interface','Heartbeat','Key-Value Pair Exchange','Shutdown','Time Synchronization','VSS') + [LabVMAdapter[]] $Adapters + [LabDataVHD[]] $DataVHDs + [LabDVDDrive[]] $DVDDrives + [LabDSC] $DSC + [System.String] $VMRootPath + [System.String] $LabBuilderFilesPath + [LabCertificateSource] $CertificateSource = [LabCertificateSource]::Guest + [System.String] $NanoODJPath + + LabVM() {} + + LabVM($Name) { + $this.Name = $Name + } # Constructor + + LabVM($Name,$ComputerName) { + $this.Name = $Name + $this.ComputerName = $ComputerName + } # Constructor + + [Object] Clone () { + $New = [LabVM]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabVM + +class LabDSCModule:System.ICloneable { + [System.String] $ModuleName + [Version] $ModuleVersion + [Version] $MinimumVersion + + LabDSCModule() {} + + LabDSCModule($ModuleName) { + $this.ModuleName = $ModuleName + } # Constructor + + LabDSCModule($ModuleName,$ModuleVersion) { + $this.ModuleName = $ModuleName + $this.ModuleVersion = [Version] $ModuleVersion + } # Constructor + + [Object] Clone () { + $New = [LabDSCModule]::New() + foreach ($Property in ($this | Get-Member -MemberType Property)) + { + $New.$($Property.Name) = $this.$($Property.Name) + } # foreach + return $New + } # Clone +} # class LabDSCModule +#endregion + +#region ModuleVariables +[System.String] $Script:WorkingFolder = $ENV:Temp + +# Supporting files +[System.String] $Script:SupportConvertWindowsImagePath = Join-Path ` + -Path $PSScriptRoot ` + -ChildPath 'support\Convert-WindowsImage.ps1' +[System.String] $Script:SupportGertGenPath = Join-Path ` + -Path $PSScriptRoot ` + -ChildPath 'support\New-SelfSignedCertificateEx.ps1' + +# DSC Library +[System.String] $Script:DSCLibraryPath = Join-Path ` + -Path $PSScriptRoot ` + -ChildPath 'dsclibrary' + +# Virtual Networking Parameters +[System.Int32] $Script:DefaultManagementVLan = 99 + +# Self-signed Certificate Parameters +[System.Int32] $Script:SelfSignedCertKeyLength = 2048 +# Warning - using KSP causes the Private Key to not be accessible to PS. +[System.String] $Script:SelfSignedCertProviderName = 'Microsoft Enhanced Cryptographic Provider v1.0' # 'Microsoft Software Key Storage Provider' +[System.String] $Script:SelfSignedCertAlgorithmName = 'RSA' # 'ECDH_P256' Or 'ECDH_P384' Or 'ECDH_P521' +[System.String] $Script:SelfSignedCertSignatureAlgorithm = 'SHA256' # 'SHA1' +[System.String] $Script:DSCEncryptionCert = 'DSCEncryption.cer' +[System.String] $Script:DSCEncryptionPfxCert = 'DSCEncryption.pfx' +[System.String] $Script:DSCCertificateFriendlyName = 'DSC Credential Encryption' +[System.String] $Script:DSCCertificatePassword = 'E3jdNkd903mDn43NEk2nbDENjw' +[System.Int32] $Script:RetryConnectSeconds = 5 +[System.Int32] $Script:RetryHeartbeatSeconds = 1 +[System.Int32] $Script:StartupTimeout = 90 + +# System Info +[System.Int32] $Script:CurrentBuild = (Get-ItemProperty ` + -Path 'hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuild + +# XML Stuff +[System.String] $Script:ConfigurationXMLSchema = Join-Path ` + -Path $PSScriptRoot ` + -ChildPath 'schema\labbuilderconfig-schema.xsd' +[System.String] $Script:ConfigurationXMLTemplate = Join-Path ` + -Path $PSScriptRoot ` + -ChildPath 'template\labbuilderconfig-template.xml' + +# Nano Stuff +[System.String] $Script:NanoPackageCulture = 'en-us' diff --git a/source/private/Assert-LabValidConfigurationXMLSchema.ps1 b/source/private/Assert-LabValidConfigurationXMLSchema.ps1 new file mode 100644 index 00000000..5ec7b6a3 --- /dev/null +++ b/source/private/Assert-LabValidConfigurationXMLSchema.ps1 @@ -0,0 +1,88 @@ +<# + .SYNOPSIS + Validates the provided configuration XML against the Schema. + + .DESCRIPTION + This function will ensure that the provided Configration XML + is compatible with the LabBuilderConfig.xsd Schema file. + + .PARAMETER ConfigPath + Contains the path to the Configuration XML file. + + .EXAMPLE + Assert-LabValidConfigurationXMLSchema -ConfigPath c:\mylab\config.xml + Validates the XML configuration and downloads any resources required by it. + + .OUTPUTS + None. If the XML is invalid an exception will be thrown. +#> +function Assert-LabValidConfigurationXMLSchema +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ConfigPath + ) + + # Define these variables so they are accesible inside the event handler. + $Script:XMLErrorCount = 0 + $Script:XMLFirstError = '' + $Script:XMLPath = $ConfigPath + $Script:ConfigurationXMLValidationMessage = $LocalizedData.ConfigurationXMLValidationMessage + + # Perform the XSD Validation + $readerSettings = New-Object -TypeName System.Xml.XmlReaderSettings + $readerSettings.ValidationType = [System.Xml.ValidationType]::Schema + $null = $readerSettings.Schemas.Add("labbuilderconfig", $Script:ConfigurationXMLSchema) + $readerSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessInlineSchema -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation + $readerSettings.add_ValidationEventHandler( + { + # Triggered each time an error is found in the XML file + if ([System.String]::IsNullOrWhitespace($Script:XMLFirstError)) + { + $Script:XMLFirstError = $_.Message + } # if + Write-LabMessage -Message ($Script:ConfigurationXMLValidationMessage ` + -f $Script:XMLPath, $_.Message) + $Script:XMLErrorCount++ + }) + + $reader = [System.Xml.XmlReader]::Create([System.String] $ConfigPath, $readerSettings) + + try + { + while ($reader.Read()) + { + } # while + } # try + catch + { + # XML is NOT valid + $exceptionParameters = @{ + errorId = 'ConfigurationXMLValidationError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ConfigurationXMLValidationError ` + -f $ConfigPath, $_.Exception.Message) + } + New-LabException @exceptionParameters + } # catch + finally + { + $null = $reader.Close() + } # finally + + # Verify the results of the XSD validation + if ($script:XMLErrorCount -gt 0) + { + # XML is NOT valid + $exceptionParameters = @{ + errorId = 'ConfigurationXMLValidationError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ConfigurationXMLValidationError -f $ConfigPath, $Script:XMLFirstError) + } + New-LabException @exceptionParameters + } # if +} diff --git a/source/private/Assert-LabValidIpAddress.ps1 b/source/private/Assert-LabValidIpAddress.ps1 new file mode 100644 index 00000000..98420efa --- /dev/null +++ b/source/private/Assert-LabValidIpAddress.ps1 @@ -0,0 +1,41 @@ +<# + .SYNOPSIS + Validates the IP Address. + + .PARAMETER IpAddress + Contains the IP Address to validate. + + .EXAMPLE + Assert-LabValidIpAddress -IpAddress '192.168.123.44' + Does not throw an exception and returns '192.168.123.44'. + + .EXAMPLE + Assert-LabValidIpAddress -IpAddress '192.168.123.4432' + Throws an exception. + + .OUTPUTS + The IP address if valid. +#> +function Assert-LabValidIpAddress +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $IpAddress + ) + + $ip = [System.Net.IPAddress]::Any + if (-not [System.Net.IPAddress]::TryParse($IpAddress, [ref] $ip)) + { + $exceptionParameters = @{ + errorId = 'IPAddressError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.IPAddressError -f $IpAddress) + } + New-LabException @exceptionParameters + } + return $ip +} diff --git a/source/private/ConvertTo-LabAbsolutePath.ps1 b/source/private/ConvertTo-LabAbsolutePath.ps1 new file mode 100644 index 00000000..2b446007 --- /dev/null +++ b/source/private/ConvertTo-LabAbsolutePath.ps1 @@ -0,0 +1,37 @@ +<# + .SYNOPSIS + Convert a path to be an absolute by adding it to the base path. + If the path is already absolute then it is just returned as is. + + .PARAMETER Path + The path to convert to an absolute path. + + .PARAMETER BasePath + The full path to the lab. + + .OUTPUTS + The path converted to an absolute path. +#> +function ConvertTo-LabAbsolutePath +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Path, + + [Parameter(Mandatory = $true)] + [System.String] + $BasePath + ) + + if (-not [System.IO.Path]::IsPathRooted($Path)) + { + $Path = Join-Path ` + -Path $BasePath ` + -ChildPath $Path + } # if + + return $Path +} diff --git a/source/private/Copy-LabOdjFile.ps1 b/source/private/Copy-LabOdjFile.ps1 new file mode 100644 index 00000000..6c0dc735 --- /dev/null +++ b/source/private/Copy-LabOdjFile.ps1 @@ -0,0 +1,149 @@ +<# + .SYNOPSIS + Uploads Precreated ODJ files to Nano systems or others as required. + + .DESCRIPTION + This function will perform the following tasks: + 1. Connect to the VM via remoting. + 2. Upload the ODJ file to c:\windows\setup\ODJFiles folder of the VM. + If the ODJ file does not exist in the LabFiles folder for the VM then the + copy will not be performed. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .PARAMETER Timeout + The maximum amount of time that this function can take to perform the copy. + If the timeout is reached before the process is complete an error will be thrown. + The timeout defaults to 300 seconds. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Copy-LabOdjFile -Lab $Lab -VM $VMs[0] + + .OUTPUTS + None. +#> +function Copy-LabOdjFile +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [System.Int32] + $Timeout = 300 + ) + + $startTime = Get-Date + $session = $null + $complete = $false + $odjCopyComplete = $false + $odjFilename = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath "$($VM.ComputerName).txt" + + # If ODJ file does not exist then return + if (-not (Test-Path -Path $odjFilename)) + { + return + } # if + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + # Connect to the VM + $session = Connect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + # Failed to connnect to the VM + if (-not $session) + { + $exceptionParameters = @{ + errorId = 'ODJCopyError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.ODJCopyError ` + -f $VM.Name,$odjFilename) + } + New-LabException @exceptionParameters + return + } # if + + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $odjCopyComplete)) + { + $CopyParameters = @{ + Destination = 'C:\Windows\Setup\ODJFiles\' + ToSession = $session + Force = $true + ErrorAction = 'Stop' + } + + # Connection has been made OK, upload the ODJ files + while ((-not $odjCopyComplete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + try + { + Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMMessage ` + -f $VM.Name,'ODJ') + + Copy-Item ` + @CopyParameters ` + -Path (Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath "$($VM.ComputerName).txt") ` + -Verbose + $odjCopyComplete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` + -f $VM.Name,'ODJ',$Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # try + } # while + } # if + + # If the copy didn't complete and we're out of time throw an exception + if ((-not $odjCopyComplete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $exceptionParameters = @{ + errorId = 'ODJCopyError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.ODJCopyError ` + -f $VM.Name,$odjFilename) + } + New-LabException @exceptionParameters + } # if + + + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $complete = $true + } # while +} diff --git a/source/private/Enable-LabWSMan.ps1 b/source/private/Enable-LabWSMan.ps1 new file mode 100644 index 00000000..44e1c45b --- /dev/null +++ b/source/private/Enable-LabWSMan.ps1 @@ -0,0 +1,71 @@ +<# + .SYNOPSIS + Ensures the WS-Man is configured on this system. + + .DESCRIPTION + If WS-Man is not enabled on this system it will be enabled. + This is required to communicate with the managed Lab Virtual Machines. + + .EXAMPLE + Enable-LabWSMan + Enables WS-Man on this machine. + + .OUTPUTS + None +#> +function Enable-LabWSMan +{ + [CmdLetBinding()] + param + ( + [Parameter()] + [Switch] + $Force + ) + + if (-not (Get-PSPRovider -PSProvider WSMan -ErrorAction SilentlyContinue)) + { + Write-LabMessage -Message ($LocalizedData.EnablingWSManMessage) + + try + { + Start-Service -Name WinRm -ErrorAction Stop + } + catch + { + $null = Enable-PSRemoting ` + @PSBoundParameters ` + -SkipNetworkProfileCheck ` + -ErrorAction Stop + } + + # Check WS-Man was enabled + if (-not (Get-PSProvider -PSProvider WSMan -ErrorAction SilentlyContinue)) + { + $exceptionParameters = @{ + errorId = 'WSManNotEnabledError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.WSManNotEnabledError) + } + New-LabException @exceptionParameters + } # if + } # if + + # Make sure the WinRM service is running + if ((Get-Service -Name WinRM).Status -ne 'Running') + { + try + { + Start-Service -Name WinRm -ErrorAction Stop + } + catch + { + $exceptionParameters = @{ + errorId = 'WinRMServiceFailedToStartError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.WinRMServiceFailedToStartError) + } + New-LabException @exceptionParameters + } + } +} diff --git a/source/private/Get-LabBuilderModulePath.ps1 b/source/private/Get-LabBuilderModulePath.ps1 new file mode 100644 index 00000000..e0127175 --- /dev/null +++ b/source/private/Get-LabBuilderModulePath.ps1 @@ -0,0 +1,15 @@ +<# + .SYNOPSIS + Returns the path of the currently loaded LabBuilder module. + + .OUTPUTS + The path to the currently loaded LabBuilder module. +#> +function Get-LabBuilderModulePath +{ + [CmdLetBinding()] + [OutputType([System.String])] + param () + + return $script:LabBuidlerModuleRoot +} diff --git a/source/private/Get-LabCertificatePsFileContent.ps1 b/source/private/Get-LabCertificatePsFileContent.ps1 new file mode 100644 index 00000000..1601f983 --- /dev/null +++ b/source/private/Get-LabCertificatePsFileContent.ps1 @@ -0,0 +1,119 @@ +<# + .SYNOPSIS + Assemble the the PowerShell commands required to create a self-signed certificate. + + .DESCRIPTION + This function creates the content that can be written into a PS1 file to create a self-signed + certificate. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + $CertificatePS = Get-LabCertificatePsFileContent -Lab $Lab -VM $VMs[0] + Return the Create Self-Signed Certificate script for the first VM in the + Lab c:\mylab\config.xml for DSC configuration. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .PARAMETER CertificateSource + A CertificateSource to use instead of the one contained in the VM. + + .OUTPUTS + A string containing the Create Self-Signed Certificate PowerShell code. +#> +function Get-LabCertificatePsFileContent +{ + [CmdLetBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [Parameter()] + [LabCertificateSource] + $CertificateSource + ) + + # If a CertificateSource is not provided get it from the VM. + if (-not $CertificateSource) + { + $CertificateSource = $VM.CertificateSource + } # if + + if ($CertificateSource -eq [LabCertificateSource]::Guest) + { + $createCertificatePs = @" +`$CertificateFriendlyName = '$($Script:DSCCertificateFriendlyName)' +`$Cert = Get-ChildItem -Path cert:\LocalMachine\My `` + | Where-Object { `$_.FriendlyName -eq `$CertificateFriendlyName } `` + | Select-Object -First 1 +if (-not `$Cert) +{ + . `"`$(`$ENV:SystemRoot)\Setup\Scripts\New-SelfSignedCertificateEx.ps1`" + New-SelfsignedCertificateEx `` + -Subject 'CN=$($VM.ComputerName)' `` + -EKU '1.3.6.1.4.1.311.80.1','1.3.6.1.5.5.7.3.1','1.3.6.1.5.5.7.3.2' `` + -KeyUsage 'DigitalSignature, KeyEncipherment, DataEncipherment' `` + -SAN '$($VM.ComputerName)' `` + -FriendlyName `$CertificateFriendlyName `` + -Exportable `` + -StoreLocation 'LocalMachine' `` + -StoreName 'My' `` + -KeyLength $($Script:SelfSignedCertKeyLength) `` + -ProviderName '$($Script:SelfSignedCertProviderName)' `` + -AlgorithmName $($Script:SelfSignedCertAlgorithmName) `` + -SignatureAlgorithm $($Script:SelfSignedCertSignatureAlgorithm) + # There is a slight delay before new cert shows up in Cert: + # So wait for it to show. + While (-not `$Cert) + { + `$Cert = Get-ChildItem -Path cert:\LocalMachine\My `` + | Where-Object { `$_.FriendlyName -eq `$CertificateFriendlyName } + } +} +Export-Certificate `` + -Type CERT `` + -Cert `$Cert `` + -FilePath `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionCert`" +Add-Content `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'Encryption Certificate Imported from CER ...' `` + -Encoding Ascii +"@ + } + else + { + [System.String] $createCertificatePs = @" +if (Test-Path -Path `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`") +{ + `$CertificatePassword = ConvertTo-SecureString `` + -String '$Script:DSCCertificatePassword' `` + -Force `` + -AsPlainText + Add-Content `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'Importing Encryption Certificate from PFX ...' `` + -Encoding Ascii + Import-PfxCertificate `` + -Password '$Script:DSCCertificatePassword' `` + -FilePath `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`" `` + -CertStoreLocation cert:\localMachine\root + Add-Content `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'Encryption Certificate from PFX Imported...' `` + -Encoding Ascii +} +"@ + } # if + + return $createCertificatePs +} diff --git a/source/private/Get-LabDSCNetworkingConfig.ps1 b/source/private/Get-LabDSCNetworkingConfig.ps1 new file mode 100644 index 00000000..6f67ee96 --- /dev/null +++ b/source/private/Get-LabDSCNetworkingConfig.ps1 @@ -0,0 +1,201 @@ +<# + .SYNOPSIS + Assemble the content of the Networking DSC config file. + + .DESCRIPTION + This function creates the content that will be written to the Networking DSC Config file + from the networking details stored in the VM object. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + $NetworkingDsc = Get-LabDSCNetworkingConfig -Lab $Lab -VM $VMs[0] + Return the Networking DSC for the first VM in the Lab c:\mylab\config.xml for DSC configuration. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .OUTPUTS + A string containing the DSC Networking config. +#> +function Get-LabDSCNetworkingConfig +{ + [CmdLetBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + $NetworkingDscVersion = (` + Get-Module -Name NetworkingDsc -ListAvailable ` + | Sort-Object version -Descending ` + | Select-Object -First 1 ` + ).Version.ToString() + + $dscNetworkingConfig = @" +Configuration Networking { + Import-DscResource -ModuleName NetworkingDsc -ModuleVersion $NetworkingDscVersion + +"@ + $adapterCount = 0 + + foreach ($Adapter in $VM.Adapters) + { + $adapterCount++ + + if ($adapter.IPv4) + { + if (-not [System.String]::IsNullOrWhitespace($adapter.IPv4.Address)) + { + $dscNetworkingConfig += @" + IPAddress IPv4_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv4' + IPAddress = '$($adapter.IPv4.Address.Replace(',',"','"))/$($adapter.IPv4.SubnetMask)' + } + +"@ + if (-not [System.String]::IsNullOrWhitespace($adapter.IPv4.DefaultGateway)) + { + $dscNetworkingConfig += @" + DefaultGatewayAddress IPv4G_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv4' + Address = '$($adapter.IPv4.DefaultGateway)' + } + +"@ + } + else + { + $dscNetworkingConfig += @" + DefaultGatewayAddress IPv4G_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv4' + } + +"@ + } # if + } + else + { + $dscNetworkingConfig += @" + NetIPInterface IPv4DHCP_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv4' + Dhcp = 'Enabled' + } + +"@ + + } # if + + if (-not [System.String]::IsNullOrWhitespace($adapter.IPv4.DNSServer)) + { + $dscNetworkingConfig += @" + DnsServerAddress IPv4D_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv4' + Address = '$($adapter.IPv4.DNSServer.Replace(',',"','"))' + } + +"@ + } # if + } # if + + if ($adapter.IPv6) + { + if (-not [System.String]::IsNullOrWhitespace($adapter.IPv6.Address)) + { + $dscNetworkingConfig += @" + IPAddress IPv6_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv6' + IPAddress = '$($adapter.IPv6.Address.Replace(',',"','"))/$($adapter.IPv6.SubnetMask)' + } + +"@ + if (-not [System.String]::IsNullOrWhitespace($adapter.IPv6.DefaultGateway)) + { + $dscNetworkingConfig += @" + DefaultGatewayAddress IPv6G_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv6' + Address = '$($adapter.IPv6.DefaultGateway)' + } + +"@ + } + else + { + $dscNetworkingConfig += @" + DefaultGatewayAddress IPv6G_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv6' + } + +"@ + } # if + } + else + { + $dscNetworkingConfig += @" + NetIPInterface IPv6DHCP_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv6' + Dhcp = 'Enabled' + } + +"@ + + } # if + + if (-not [System.String]::IsNullOrWhitespace($adapter.IPv6.DNSServer)) + { + $dscNetworkingConfig += @" + DnsServerAddress IPv6D_$adapterCount { + InterfaceAlias = '$($adapter.Name)' + AddressFamily = 'IPv6' + Address = '$($adapter.IPv6.DNSServer.Replace(',',"','"))' + } + +"@ + } # if + } # if + } # endfor + + $dscNetworkingConfig += @" +} +"@ + + return $dscNetworkingConfig +} # Get-LabDSCNetworkingConfig + +[DSCLocalConfigurationManager()] +Configuration ConfigLCM { + param ( + [System.String] $ComputerName, + [System.String] $Thumbprint + ) + Node $ComputerName { + Settings + { + RefreshMode = 'Push' + ConfigurationMode = 'ApplyAndAutoCorrect' + CertificateId = $Thumbprint + ConfigurationModeFrequencyMins = 15 + RefreshFrequencyMins = 30 + RebootNodeIfNeeded = $true + ActionAfterReboot = 'ContinueConfiguration' + } + } +} diff --git a/source/private/Get-LabIntegrationServiceName.ps1 b/source/private/Get-LabIntegrationServiceName.ps1 new file mode 100644 index 00000000..dad9421b --- /dev/null +++ b/source/private/Get-LabIntegrationServiceName.ps1 @@ -0,0 +1,46 @@ +<# + .SYNOPSIS + Get list of Integration Service names (localized) + + .DESCRIPTION + This cmdlet will get the list of Integration services available on a Hyper-V host. + The list of Integration Services will contain the localized names. + + .EXAMPLE + Get-LabIntegrationServiceName + + .OUTPUTS + An array of localized Integration Serivce names. +#> +function Get-LabIntegrationServiceName +{ + [CmdLetBinding()] + param + ( + ) + + $captions = @() + $classes = @( + 'Msvm_VssComponentSettingData' + 'Msvm_ShutdownComponentSettingData' + 'Msvm_TimeSyncComponentSettingData' + 'Msvm_HeartbeatComponentSettingData' + 'Msvm_GuestServiceInterfaceComponentSettingData' + 'Msvm_KvpExchangeComponentSettingData' + ) + + <# + This Integration Service is registered in CIM but is not exposed in Hyper-V: + 'Msvm_RdvComponentSettingData' + #> + + foreach ($class in $classes) + { + $captions += (Get-CimInstance ` + -Class $class ` + -Namespace Root\Virtualization\V2 ` + -Property Caption | Select-Object -First 1).Caption + } # foreach + + return $captions +} diff --git a/source/private/Get-LabManagementSwitchName.ps1 b/source/private/Get-LabManagementSwitchName.ps1 new file mode 100644 index 00000000..bbfacd07 --- /dev/null +++ b/source/private/Get-LabManagementSwitchName.ps1 @@ -0,0 +1,42 @@ +<# + .SYNOPSIS + Returns the name of the Management Switch to use for this lab. + + .DESCRIPTION + Each lab has a unique private management switch created for it. + All Virtual Machines in the Lab are connected to the switch. + This function returns the name of this swtich for the provided + lab configuration. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $ManagementSwitch = Get-LabManagementSwitchName -Lab $Lab + Returns the Management Switch for the Lab c:\mylab\config.xml. + + .OUTPUTS + A management switch name. +#> +function Get-LabManagementSwitchName +{ + [CmdLetBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + $Lab + ) + + $LabId = $Lab.labbuilderconfig.settings.labid + + if (-not $LabId) + { + $LabId = $Lab.labbuilderconfig.name + } # if + + $managementSwitchName = ('{0}Lab Management' -f $LabId) + + return $managementSwitchName +} diff --git a/source/private/Get-LabModulesInDSCConfig.ps1 b/source/private/Get-LabModulesInDSCConfig.ps1 new file mode 100644 index 00000000..62a4f302 --- /dev/null +++ b/source/private/Get-LabModulesInDSCConfig.ps1 @@ -0,0 +1,82 @@ +<# + .SYNOPSIS + Get a list of all Resources imported in a DSC Config + + .DESCRIPTION + Uses RegEx to pull a list of Resources that are imported in a DSC Configuration using the + Import-DSCResource cmdlet. + + If The -ModuleVersion parameter is included then the ModuleVersion property in the returned + LabDSCModule object will be set, otherwise it will be null. + + .PARAMETER DscConfigFile + Contains the path to the DSC Config file to extract resource module names from. + + .PARAMETER DscConfigContent + Contains the content of the DSC Config to extract resource module names from. + + .EXAMPLE + Get-LabModulesInDSCConfig -DscConfigFile c:\mydsc\Server01.ps1 + Return the DSC Resource module list from file c:\mydsc\server01.ps1 + + .EXAMPLE + Get-LabModulesInDSCConfig -DscConfigContent $DSCConfig + Return the DSC Resource module list from the DSC Config in $DSCConfig. + + .OUTPUTS + An array of LabDSCModule objects containing the DSC Resource modules required by this DSC + configuration file. +#> +function Get-LabModulesInDSCConfig +{ + [CmdLetBinding(DefaultParameterSetName = "Content")] + [OutputType([Object[]])] + param + ( + [parameter( + Position = 1, + ParameterSetName = "Content", + Mandatory = $true)] + [System.String] + $DscConfigContent, + + [parameter( + Position = 2, + ParameterSetName = "File", + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $DscConfigFile + ) + + [LabDSCModule[]] $modules = $null + + if ($PSCmdlet.ParameterSetName -eq 'File') + { + $DscConfigContent = Get-Content -Path $DscConfigFile -Raw + } # if + + $regex = "[ \t]*?Import\-DscResource[ \t]+(?:\-ModuleName[ \t])?'?`"?([A-Za-z0-9._-]+)`"?'?(([ \t]+-ModuleVersion)?[ \t]+'?`"?([0-9.]+)`"?`?)?[ \t]*?[\r\n]+?" + $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') + + foreach ($moduleMatch in $moduleMatches) + { + $moduleName = $moduleMatch.Groups[1].Value + $moduleVersion = $moduleMatch.Groups[4].Value + # Make sure this module isn't already in the list + + if ($moduleName -notin $Modules.ModuleName) + { + $module = [LabDSCModule]::New($moduleName) + + if (-not [System.String]::IsNullOrWhitespace($moduleVersion)) + { + $module.moduleVersion = [Version] $moduleVersion + } # if + + $modules += @( $module ) + } # if + } # foreach + + return $modules +} diff --git a/source/private/Get-LabNextIpAddress.ps1 b/source/private/Get-LabNextIpAddress.ps1 new file mode 100644 index 00000000..0fc50019 --- /dev/null +++ b/source/private/Get-LabNextIpAddress.ps1 @@ -0,0 +1,61 @@ +<# + .SYNOPSIS + Increases the IP Address. + + .PARAMETER IpAddress + Contains the IP Address to increase. + + .PARAMETER Step + Contains the number of steps to increase the IP address by. + + .EXAMPLE + Get-LabNextIpAddress -IpAddress '192.168.123.44' -Step 2 + Returns the IP Address '192.168.123.44' + + .EXAMPLE + Get-LabNextIpAddress -IpAddress 'fe80::15b4:b934:5d23:1a2f' -Step 2 + Returns the IP Address 'fe80::15b4:b934:5d23:1a31' + + .OUTPUTS + The increased IP Address. +#> +function Get-LabNextIpAddress +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $IpAddress, + + [Parameter()] + [System.Byte] + $Step = 1 + ) + + # Check the IP Address is valid + $ip = Assert-LabValidIpAddress -IpAddress $IpAddress + + # This code will increase the next IP address by the step amount. + # It uses the IP Address byte array to do this. + $bytes = $ip.GetAddressBytes() + $position = $bytes.Length - 1 + + while ($Step -gt 0) + { + if ($bytes[$position] + $Step -gt 255) + { + $bytes[$position] = $bytes[$position] + $Step - 256 + $Step = $Step - $bytes[$position] + $position-- + } + else + { + $bytes[$position] = $bytes[$position] + $Step + $Step = 0 + } # if + } # while + + return [System.Net.IPAddress]::new($bytes).IPAddressToString +} diff --git a/source/private/Get-LabNextMacAddress.ps1 b/source/private/Get-LabNextMacAddress.ps1 new file mode 100644 index 00000000..7383f42f --- /dev/null +++ b/source/private/Get-LabNextMacAddress.ps1 @@ -0,0 +1,33 @@ +<# + .SYNOPSIS + Increases the MAC Address. + + .PARAMETER MACAddress + Contains the MAC Address to increase. + + .PARAMETER Step + Contains the number of steps to increase the MAC address by. + + .EXAMPLE + Get-NextMacAddress -MacAddress '00155D0106ED' -Step 2 + Returns the MAC Address '00155D0106EF' + + .OUTPUTS + The increased MAC Address. +#> +function Get-NextMacAddress +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $MacAddress, + + [System.Byte] + $Step = 1 + ) + + return [System.String]::Format("{0:X}", [Convert]::ToUInt64($MACAddress, 16) + $Step).PadLeft(12, '0') +} diff --git a/source/private/Get-LabUnattendFileContent.ps1 b/source/private/Get-LabUnattendFileContent.ps1 new file mode 100644 index 00000000..c6135864 --- /dev/null +++ b/source/private/Get-LabUnattendFileContent.ps1 @@ -0,0 +1,169 @@ +<# + .SYNOPSIS + Assembles the content of a Unattend XML file that should be used to initialize + Windows on the specified VM. + + .DESCRIPTION + This function will return the content of a standard Windows Unattend XML file + that can be written to an VHD containing a copy of Windows that is still in + OOBE mode. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Get-LabUnattendFileContent -Lab $Lab -VM $VMs[0] + Returns the content of the Unattend File for the first VM in the Lab c:\mylab\config.xml. + + .OUTPUTS + The content of the Unattend File for the VM. +#> +function Get-LabUnattendFileContent +{ + [CmdLetBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + if ($VM.UnattendFile) + { + $unattendContent = Get-Content -Path $VM.UnattendFile + } + else + { + $domainName = $Lab.labbuilderconfig.settings.domainname + $email = $Lab.labbuilderconfig.settings.email + $unattendContent = @" + + + + + false + + + + + 1 + + + + + 0409:00000409 + en-US + en-US + en-US + en-US + + + true + + + 0 + + + $($VM.ComputerName) + + +"@ + + + if ($VM.OSType -eq [LabOSType]::Client) + { + $unattendContent += @" + + + + 1 + net user administrator /active:yes + + + 2 + powershell.exe -Command "Enable-PSRemoting -SkipNetworkProfileCheck -Force" + + + + +"@ + } # If + + $unattendContent += @" + + + +"@ + if ($VM.OSType -eq [LabOSType]::Client) + { + $unattendContent += @" + + + $($VM.AdministratorPassword) + true</PlainText> + </Password> + <Username>Administrator</Username> + <Enabled>true</Enabled> + <LogonCount>2</LogonCount> + </AutoLogon> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> + <Description>Set Execution Policy 64 Bit</Description> + <Order>1</Order> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> + <Description>Set Execution Policy 32 Bit</Description> + <Order>2</Order> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + </FirstLogonCommands> + +"@ +} # If +$unattendContent += @" + <OOBE> + <HideEULAPage>true</HideEULAPage> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <NetworkLocation>Work</NetworkLocation> + <ProtectYourPC>1</ProtectYourPC> + <SkipUserOOBE>true</SkipUserOOBE> + <SkipMachineOOBE>true</SkipMachineOOBE> + </OOBE> + <UserAccounts> + <AdministratorPassword> + <Value>$($VM.AdministratorPassword)</Value> + <PlainText>true</PlainText> + </AdministratorPassword> + </UserAccounts> + <RegisteredOrganization>$($domainName)</RegisteredOrganization> + <RegisteredOwner>$($email)</RegisteredOwner> + <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> + <TimeZone>$($VM.TimeZone)</TimeZone> + </component> + <component name="Microsoft-Windows-ehome-reg-inf" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <RestartEnabled>true</RestartEnabled> + </component> + <component name="Microsoft-Windows-ehome-reg-inf" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <RestartEnabled>true</RestartEnabled> + </component> + </settings> +</unattend> +"@ + } + + return $unattendContent +} diff --git a/source/private/Get-LabVMManagementIPAddress.ps1 b/source/private/Get-LabVMManagementIPAddress.ps1 new file mode 100644 index 00000000..c99ae1ae --- /dev/null +++ b/source/private/Get-LabVMManagementIPAddress.ps1 @@ -0,0 +1,58 @@ +<# + .SYNOPSIS + Gets the Management IP Address for a running Lab VM. + + .DESCRIPTION + This function will return the IPv4 address assigned to the network adapter that + is connected to the Management switch for the specified VM. The VM must be + running, otherwise an error will be thrown. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + $IPAddress = Get-LabVMManagementIPAddress -Lab $Lab -VM $VM[0] + + .OUTPUTS + The IP Managment IP Address. +#> +function Get-LabVMManagementIPAddress +{ + [CmdLetBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + $managementSwitchName = Get-LabManagementSwitchName -Lab $Lab + $managementAdapter = Get-VMNetworkAdapter -VMName $VM.Name | + Where-Object -Property SwitchName -EQ -Value $managementSwitchName + $managementAdapterIpAddresses = $managementAdapter.IPAddresses + $managementAdapterIpAddress = $managementAdapterIpAddresses | + Where-Object -FilterScript { + $_.Contains('.') + } + + if (-not $managementAdapterIpAddress) { + $exceptionParameters = @{ + errorId = 'ManagmentIPAddressError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ManagmentIPAddressError ` + -f $managementSwitchName,$VM.Name) + } + New-LabException @exceptionParameters + } # if + + return $managementAdapterIpAddress +} diff --git a/source/private/Initialize-LabBootVHD.ps1 b/source/private/Initialize-LabBootVHD.ps1 new file mode 100644 index 00000000..ca4bce72 --- /dev/null +++ b/source/private/Initialize-LabBootVHD.ps1 @@ -0,0 +1,312 @@ +<# + .SYNOPSIS + Initialized a VM VHD for first boot by applying any required files to the image. + + .DESCRIPTION + This function mounts a VM boot VHD image and applies the following files from the + LabBuilder Files folder to it: + 1. Unattend.xml - a Windows Unattend.xml file. + 2. SetupComplete.cmd - the command file that gets run after the Windows OOBE is complete. + 3. SetupComplete.ps1 - this PowerShell script file that is run at the the end of the + SetupComplete.cmd. + The files should have already been prepared by the New-LabVMInitializationFile function. + The VM VHD image should contain an installed copy of Windows still in OOBE mode. + + This function also applies optional MSU package files from the Lab resource folder if + specified in the packages list in the VM. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A VMLab object pulled from the Lab Configuration file using Get-LabVM. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Initialize-LabBootVHD ` + -Lab $Lab ` + -VM $VMs[0] ` + -VMBootDiskPath $BootVHD[0] + Prepare the boot VHD in for the first VM in the Lab c:\mylab\config.xml for initial boot. + + .OUTPUTS + None. +#> +function Initialize-LabBootVHD +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [Parameter(Mandatory = $true)] + [System.String] + $VMBootDiskPath + ) + + # Get path to Lab + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + # Get Path to LabBuilder files + [System.String] $VMLabBuilderFiles = $VM.LabBuilderFilesPath + + # Mount the VMs Boot VHD so that files can be loaded into it + Write-LabMessage -Message $($LocalizedData.MountingVMBootDiskMessage ` + -f $VM.Name,$VMBootDiskPath) + + # Create a mount point for mounting the Boot VHD + [System.String] $MountPoint = Join-Path ` + -Path $VMLabBuilderFiles ` + -ChildPath 'Mount' + + if (-not (Test-Path -Path $MountPoint -PathType Container)) + { + $null = New-Item ` + -Path $MountPoint ` + -ItemType Directory + } + + # Mount the VHD to the Mount point + $null = Mount-WindowsImage ` + -ImagePath $VMBootDiskPath ` + -Path $MountPoint ` + -Index 1 + + try + { + $Packages = $VM.Packages + if ($VM.OSType -eq [LabOSType]::Nano) + { + # Now specify the Nano Server packages to add. + [System.String] $NanoPackagesFolder = Join-Path ` + -Path $LabPath ` + -ChildPath 'NanoServerPackages' + if (-not (Test-Path -Path $NanoPackagesFolder)) + { + $exceptionParameters = @{ + errorId = 'NanoServerPackagesFolderMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NanoServerPackagesFolderMissingError ` + -f $NanoPackagesFolder) + } + New-LabException @exceptionParameters + } + # Add DSC Package to packages list if missing + if ([System.String]::IsNullOrWhitespace($Packages)) + { + $Packages = 'Microsoft-NanoServer-DSC-Package.cab' + } + else + { + if (@($Packages -split ',') -notcontains 'Microsoft-NanoServer-DSC-Package.cab') + { + $Packages = "$Packages,Microsoft-NanoServer-DSC-Package.cab" + } # if + } # if + } # if + + # Apply any listed packages to the Image + if (-not [System.String]::IsNullOrWhitespace($Packages)) + { + # Get the list of Lab Resource MSUs + $ResourceMSUs = Get-LabResourceMSU ` + -Lab $Lab + + foreach ($Package in @($Packages -split ',')) + { + if (([System.IO.Path]::GetExtension($Package) -eq '.cab') ` + -and ($VM.OSType -eq [LabOSType]::Nano)) + { + # This is a Nano Server .CAB package + # Generate the path to the Nano Package + $PackagePath = Join-Path ` + -Path $NanoPackagesFolder ` + -ChildPath $Package + + # Does it exist? + if (-not (Test-Path -Path $PackagePath)) + { + $exceptionParameters = @{ + errorId = 'NanoPackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NanoPackageNotFoundError ` + -f $PackagePath) + } + New-LabException @exceptionParameters + } + + # Add the package + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,$Package,$PackagePath) + + $null = Add-WindowsPackage ` + -PackagePath $PackagePath ` + -Path $MountPoint + + # Generate the path to the Nano Language Package + $PackageLangFile = $Package -replace '.cab',"_$($Script:NanoPackageCulture).cab" + $PackageLangFile = Join-Path ` + -Path $NanoPackagesFolder ` + -ChildPath "$($Script:NanoPackageCulture)\$PackageLangFile" + + # Does it exist? + if (-not (Test-Path -Path $PackageLangFile)) + { + $exceptionParameters = @{ + errorId = 'NanoPackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NanoPackageNotFoundError ` + -f $PackageLangFile) + } + New-LabException @exceptionParameters + } + + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,$Package,$PackageLangFile) + + # Add the package + $null = Add-WindowsPackage ` + -PackagePath $PackageLangFile ` + -Path $MountPoint + } + else + { + # Tihs is a ResourceMSU type package + [System.Boolean] $Found = $false + foreach ($ResourceMSU in $ResourceMSUs) + { + if ($ResourceMSU.Name -eq $Package) + { + # Found the package + $Found = $true + break + } # if + } # foreach + if (-not $Found) + { + $exceptionParameters = @{ + errorId = 'PackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageNotFoundError ` + -f $Package) + } + New-LabException @exceptionParameters + } # if + + $PackagePath = $ResourceMSU.Filename + if (-not (Test-Path -Path $PackagePath)) + { + $exceptionParameters = @{ + errorId = 'PackageMSUNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageMSUNotFoundError ` + -f $Package,$PackagePath) + } + New-LabException @exceptionParameters + } # if + # Apply a Package + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,$Package,$PackagePath) + + $null = Add-WindowsPackage ` + -PackagePath $PackagePath ` + -Path $MountPoint + } # if + } # foreach + } # if + } + catch + { + # Dismount Disk Image before throwing exception + Write-LabMessage -Message $($LocalizedData.DismountingVMBootDiskMessage ` + -f $VM.Name,$VMBootDiskPath) + $null = Dismount-WindowsImage -Path $MountPoint -Save + $null = Remove-Item -Path $MountPoint -Recurse -Force + + Throw $_ + } # try + + # Create the scripts folder where setup scripts will be put + $null = New-Item ` + -Path "$MountPoint\Windows\Setup\Scripts" ` + -ItemType Directory + + # Create the ODJ folder where Offline domain join files can be put + $null = New-Item ` + -Path "$MountPoint\Windows\Setup\ODJFiles" ` + -ItemType Directory + + # Apply an unattended setup file + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,'Unattend','Unattend.xml') + + if (-not (Test-Path -Path "$MountPoint\Windows\Panther" -PathType Container)) + { + Write-LabMessage -Message $($LocalizedData.CreatingVMBootDiskPantherFolderMessage ` + -f $VM.Name) + + $null = New-Item ` + -Path "$MountPoint\Windows\Panther" ` + -ItemType Directory + } # if + $null = Copy-Item ` + -Path (Join-Path -Path $VMLabBuilderFiles -ChildPath 'Unattend.xml') ` + -Destination "$MountPoint\Windows\Panther\Unattend.xml" ` + -Force + + # If a Certificate PFX file is available, copy it into the c:\Windows + # folder of the VM. + $CertificatePfxPath = Join-Path ` + -Path $VMLabBuilderFiles ` + -ChildPath $Script:DSCEncryptionPfxCert + if (Test-Path -Path $CertificatePfxPath) + { + # Apply the CMD Setup Complete File + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,'Credential Certificate PFX',$Script:DSCEncryptionPfxCert) + $null = Copy-Item ` + -Path $CertificatePfxPath ` + -Destination "$MountPoint\Windows\$Script:DSCEncryptionPfxCert" ` + -Force + } + + # Apply the CMD Setup Complete File + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,'Setup Complete CMD','SetupComplete.cmd') + $null = Copy-Item ` + -Path (Join-Path -Path $VMLabBuilderFiles -ChildPath 'SetupComplete.cmd') ` + -Destination "$MountPoint\Windows\Setup\Scripts\SetupComplete.cmd" ` + -Force + + # Apply the PowerShell Setup Complete file + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,'Setup Complete PowerShell','SetupComplete.ps1') + $null = Copy-Item ` + -Path (Join-Path -Path $VMLabBuilderFiles -ChildPath 'SetupComplete.ps1') ` + -Destination "$MountPoint\Windows\Setup\Scripts\SetupComplete.ps1" ` + -Force + + # Apply the Certificate Generator script if not a Nano Server + if ($VM.OSType -ne [LabOSType]::Nano) + { + $CertGenFilename = Split-Path -Path $Script:SupportGertGenPath -Leaf + Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` + -f $VM.Name,'Certificate Create Script',$CertGenFilename) + $null = Copy-Item ` + -Path $Script:SupportGertGenPath ` + -Destination "$MountPoint\Windows\Setup\Scripts\"` + -Force + } + + # Dismount the VHD in preparation for boot + Write-LabMessage -Message $($LocalizedData.DismountingVMBootDiskMessage ` + -f $VM.Name,$VMBootDiskPath) + $null = Dismount-WindowsImage -Path $MountPoint -Save + $null = Remove-Item -Path $MountPoint -Recurse -Force +} diff --git a/source/private/Initialize-LabDSC.ps1 b/source/private/Initialize-LabDSC.ps1 new file mode 100644 index 00000000..a5895f5a --- /dev/null +++ b/source/private/Initialize-LabDSC.ps1 @@ -0,0 +1,50 @@ +<# + .SYNOPSIS + This function prepares all files require to configure a VM using Desired State + Configuration (DSC). + + .DESCRIPTION + Calling this function will cause the LabBuilder folder to be populated/updated + with all files required to configure a Virtual Machine with DSC. + This includes: + 1. Required DSC Resouce Modules. + 2. DSC Credential Encryption certificate. + 3. DSC Configuration files. + 4. DSC MOF Files for general config and for LCM config. + 5. Start up scripts. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Initialize-LabDSC -Lab $Lab -VM $VMs[0] + Prepares all files required to start up Desired State Configuration for the + first VM in the Lab c:\mylab\config.xml for DSC start up. + + .OUTPUTS + None. +#> +function Initialize-LabDSC +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + # Are there any DSC Settings to manage? + Update-LabDSC -Lab $Lab -VM $VM + + # Generate the DSC Start up Script file + Set-LabDSC -Lab $Lab -VM $VM +} diff --git a/source/private/Initialize-LabManagementSwitch.ps1 b/source/private/Initialize-LabManagementSwitch.ps1 new file mode 100644 index 00000000..d2f51d17 --- /dev/null +++ b/source/private/Initialize-LabManagementSwitch.ps1 @@ -0,0 +1,91 @@ +<# + .SYNOPSIS + Create the LabBuilder Management Network switch and assign VLAN + + .DESCRIPTION + Each lab needs a unique private management switch created for it. + All Virtual Machines in the Lab are connected to the switch. + This function creates the virtual switch and attaches an adapter + to it and assigns it to a VLAN. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + Initialize-LabManagementSwitch -Lab $Lab + Creates or updates the Management Switch for the Lab c:\mylab\config.xml. + + .OUTPUTS + None. +#> +function Initialize-LabManagementSwitch +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab + ) + + # Used by host to communicate with Lab VMs + $managementSwitchName = Get-LabManagementSwitchName -Lab $Lab + + Write-LabMessage -Message $($LocalizedData.InitializingLabManagementVirtualNetworkMesage ` + -f $managementSwitchName) + + if ($Lab.labbuilderconfig.switches.ManagementVlan) + { + $requiredManagementVlan = $Lab.labbuilderconfig.switches.ManagementVlan + } + else + { + $requiredManagementVlan = $Script:DefaultManagementVLan + } + + $managementSwitch = Get-VMSwitch | Where-Object -Property Name -eq $managementSwitchName + + if ($managementSwitch.Count -eq 0) + { + $null = New-VMSwitch ` + -SwitchType Internal ` + -Name $managementSwitchName ` + -ErrorAction Stop + + Write-LabMessage -Message $($LocalizedData.CreatingLabManagementSwitchMessage ` + -f $managementSwitchName, $requiredManagementVlan) + } + + # Check the Vlan ID of the adapter on the switch + $existingManagementAdapter = Get-VMNetworkAdapter ` + -ManagementOS ` + -Name $managementSwitchName ` + -SwitchName $managementSwitchName ` + -ErrorAction SilentlyContinue + + if ($null -eq $existingManagementAdapter) + { + $existingManagementAdapter = Add-VMNetworkAdapter ` + -ManagementOS ` + -Name $managementSwitchName ` + -SwitchName $managementSwitchName ` + -ErrorAction Stop + } + + $existingManagementAdapterVlan = Get-VMNetworkAdapterVlan ` + -VMNetworkAdapter $existingManagementAdapter + + $existingManagementVlan = $existingManagementAdapterVlan.AccessVlanId + + if ($existingManagementVlan -ne $requiredManagementVlan) + { + Write-LabMessage -Message $($LocalizedData.UpdatingLabManagementSwitchMessage ` + -f $managementSwitchName, $requiredManagementVlan) + + Set-VMNetworkAdapterVlan ` + -VMNetworkAdapter $existingManagementAdapter ` + -Access ` + -VlanId $requiredManagementVlan ` + -ErrorAction Stop + } +} diff --git a/source/private/Initialize-LabVHD.ps1 b/source/private/Initialize-LabVHD.ps1 new file mode 100644 index 00000000..aa274303 --- /dev/null +++ b/source/private/Initialize-LabVHD.ps1 @@ -0,0 +1,331 @@ +<# + .SYNOPSIS + This function mounts the VHDx passed and ensures it is OK to be written to. + + .DESCRIPTION + The function checks that the disk has been paritioned and that it contains + a volume that has been formatted. + + This function will work for the following situations: + 0. VHDx is not mounted. + 1. VHDx is not initialized and PartitionStyle is passed. + 2. VHDx is initialized but has 0 partitions and FileSystem is passed. + 3. VHDx has 1 partition but 0 volumes and FileSystem is passed. + 4. VHDx has 1 partition and 1 volume that is unformatted and FileSystem is passed. + 5. VHDx has 1 partition and 1 volume that is formatted. + + If the VHDx is any other state an exception will be thrown. + + If the FileSystemLabel passed is different to the current label then it will + be updated. + + This function will not change the File System and/or Partition Type on the VHDx + if it is different to the values provided. + + .PARAMETER Path + This is the path to the VHD/VHDx file to mount and initialize. + + .PARAMETER PartitionStyle + The Partition Style to set an uninitialized VHD/VHDx to. It can be MBR or GPT. + If it is not passed and the VHD is not initialized then an exception will be + thrown. + + .PARAMETER FileSystem + The File System to format the new parition with on an VHD/VHDx. It can be + FAT, FAT32, exFAT, NTFS, ReFS. + If it is not passed and the VHD does not contain any formatted volumes then + an exception will be thrown. + + .PARAMETER FileSystemLabel + This parameter will allow the File System Label of the disk to be changed to this + value. + + .PARAMETER DriveLetter + Setting this parameter to a drive letter that is not in use will cause the VHD + to be assigned to this drive letter. + + .PARAMETER AccessPath + Setting this parameter to an existing folder will cause the VHD to be assigned + to the AccessPath defined. The folder must already exist otherwise an exception + will be thrown. + + .EXAMPLE + Initialize-LabVHD -Path c:\VMs\Tools.VHDx -AccessPath c:\mount + The VHDx c:\VMs\Tools.VHDx will be mounted and and assigned to the c:\mount folder + if it is initialized and contains a formatted partition. + + .EXAMPLE + Initialize-LabVHD -Path c:\VMs\Tools.VHDx -PartitionStyle GPT -FileSystem NTFS + The VHDx c:\VMs\Tools.VHDx will be mounted and initialized with GPT if not already + initialized. It will also be partitioned and formatted with NTFS if no partitions + already exist. + + .EXAMPLE + Initialize-LabVHD ` + -Path c:\VMs\Tools.VHDx ` + -PartitionStyle GPT ` + -FileSystem NTFS ` + -FileSystemLabel ToolsDisk + -DriveLetter X + The VHDx c:\VMs\Tools.VHDx will be mounted and initialized with GPT if not already + initialized. It will also be partitioned and formatted with NTFS if no partitions + already exist. The File System label will also be set to ToolsDisk and the disk + will be mounted to X drive. + + .OUTPUTS + It will return the Volume object that can then be mounted to a Drive Letter + or path. +#> +function Initialize-LabVHD +{ + [OutputType([Microsoft.Management.Infrastructure.CimInstance])] + [CmdletBinding(DefaultParameterSetName = 'AssignDriveLetter')] + param + ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Path, + + [Parameter()] + [LabPartitionStyle] + $PartitionStyle, + + [Parameter()] + [LabFileSystem] + $FileSystem, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $FileSystemLabel, + + [Parameter(ParameterSetName = 'DriveLetter')] + [ValidateNotNullOrEmpty()] + [System.String] + $DriveLetter, + + [Parameter(ParameterSetName = 'AccessPath')] + [ValidateNotNullOrEmpty()] + [System.String] + $AccessPath + ) + + # Check file exists + if (-not (Test-Path -Path $Path)) + { + $exceptionParameters = @{ + errorId = 'FileNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.FileNotFoundError ` + -f "VHD",$Path) + } + New-LabException @exceptionParameters + } # if + + # Check disk is not already mounted + $VHD = Get-VHD ` + -Path $Path + if (-not $VHD.Attached) + { + Write-LabMessage -Message ($LocalizedData.InitializeVHDMountingMessage ` + -f $Path) + + $null = Mount-VHD ` + -Path $Path ` + -ErrorAction Stop + $VHD = Get-VHD ` + -Path $Path + } # if + + # Check partition style + $DiskNumber = $VHD.DiskNumber + if ((Get-Disk -Number $DiskNumber).PartitionStyle -eq 'RAW') + { + if (-not $PartitionStyle) + { + $exceptionParameters = @{ + errorId = 'InitializeVHDNotInitializedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InitializeVHDNotInitializedError ` + -f $Path) + } + New-LabException @exceptionParameters + } # if + Write-LabMessage -Message ($LocalizedData.InitializeVHDInitializingMessage ` + -f $Path,$PartitionStyle) + + $null = Initialize-Disk ` + -Number $DiskNumber ` + -PartitionStyle $PartitionStyle ` + -ErrorAction Stop + } # if + + # Check for a partition that is not 'reserved' + $Partitions = @(Get-Partition ` + -DiskNumber $DiskNumber ` + -ErrorAction SilentlyContinue) + if (-not ($Partitions) ` + -or (($Partitions | Where-Object -Property Type -ne 'Reserved').Count -eq 0)) + { + Write-LabMessage -Message ($LocalizedData.InitializeVHDCreatePartitionMessage ` + -f $Path) + + $Partitions = @(New-Partition ` + -DiskNumber $DiskNumber ` + -UseMaximumSize ` + -ErrorAction Stop) + } # if + + # Find the best partition to work with + # This will usually be the one just created if it was + # Otherwise we'll try and match by FileSystem and then + # format and failing that the first partition. + foreach ($Partition in $Partitions) + { + $VolumeFileSystem = (Get-Volume ` + -Partition $Partition).FileSystem + if ($FileSystem) + { + if (-not [System.String]::IsNullOrWhitespace($VolumeFileSystem)) + { + # Found a formatted partition + $FoundFormattedPartition = $Partition + } # if + if ($FileSystem -eq $VolumeFileSystem) + { + # Found a parition with a matching file system + $FoundPartition = $Partition + break + } # if + } + else + { + if (-not [System.String]::IsNullOrWhitespace($VolumeFileSystem)) + { + # Found an formatted partition + $FoundFormattedPartition = $Partition + break + } # if + } # if + } # foreach + if ($FoundPartition) + { + # Use the formatted partition + $Partition = $FoundPartition + } + elseif ($FoundFormattedPartition) + { + # An unformatted partition was found + $Partition = $FoundFormattedPartition + } + else + { + # There are no formatted partitions so use the first one + $Partition = $Partitions[0] + } # if + + $PartitionNumber = $Partition.PartitionNumber + + # Check for volume + $Volume = Get-Volume ` + -Partition $Partition + + # Check for file system + if ([System.String]::IsNullOrWhitespace($Volume.FileSystem)) + { + # This volume is not formatted + if (-not $FileSystem) + { + # A File System wasn't specified so can't continue + $exceptionParameters = @{ + errorId = 'InitializeVHDNotFormattedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InitializeVHDNotFormattedError ` + -f $Path) + } + New-LabException @exceptionParameters + } + + # Format the volume + Write-LabMessage -Message ($LocalizedData.InitializeVHDFormatVolumeMessage ` + -f $Path,$FileSystem,$PartitionNumber) + $FormatProperties = @{ + InputObject = $Volume + FileSystem = $FileSystem + } + if ($FileSystemLabel) + { + $FormatProperties += @{ + NewFileSystemLabel = $FileSystemLabel + } + } + $Volume = Format-Volume ` + @FormatProperties ` + -ErrorAction Stop + } + else + { + # Check the File System Label + if (($FileSystemLabel) -and ` + ($Volume.FileSystemLabel -ne $FileSystemLabel)) + { + Write-LabMessage -Message ($LocalizedData.InitializeVHDSetLabelVolumeMessage ` + -f $Path,$FileSystemLabel) + $Volume = Set-Volume ` + -InputObject $Volume ` + -NewFileSystemLabel $FileSystemLabel ` + -ErrorAction Stop + } + } + + # Assign an access path or Drive letter + if ($DriveLetter -or $AccessPath) + { + switch ($PSCmdlet.ParameterSetName) + { + 'DriveLetter' + { + # Mount the partition to a Drive Letter + $null = Set-Partition ` + -DiskNumber $Disknumber ` + -PartitionNumber $PartitionNumber ` + -NewDriveLetter $DriveLetter ` + -ErrorAction Stop + + $Volume = Get-Volume ` + -Partition $Partition + + Write-LabMessage -Message ($LocalizedData.InitializeVHDDriveLetterMessage ` + -f $Path,$DriveLetter.ToUpper()) + } + 'AccessPath' + { + # Check the Access folder exists + if (-not (Test-Path -Path $AccessPath -Type Container)) + { + $exceptionParameters = @{ + errorId = 'InitializeVHDAccessPathNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InitializeVHDAccessPathNotFoundError ` + -f $Path,$AccessPath) + } + New-LabException @exceptionParameters + } + + # Add the Partition Access Path + $null = Add-PartitionAccessPath ` + -DiskNumber $DiskNumber ` + -PartitionNumber $partitionNumber ` + -AccessPath $AccessPath ` + -ErrorAction Stop + + Write-LabMessage -Message ($LocalizedData.InitializeVHDAccessPathMessage ` + -f $Path,$AccessPath) + } + } + } + + # Return the Volume to the pipeline + return $Volume +} diff --git a/source/private/Initialize-LabVMPath.ps1 b/source/private/Initialize-LabVMPath.ps1 new file mode 100644 index 00000000..e844b9b1 --- /dev/null +++ b/source/private/Initialize-LabVMPath.ps1 @@ -0,0 +1,65 @@ +<# + .SYNOPSIS + Creates the folder structure that will contain a Lab Virtual Machine. + + .DESCRIPTION + Creates a standard Hyper-V Virtual Machine folder structure as well as additional folders + for containing configuration files for DSC. + + .PARAMETER vmpath + The path to the folder where the Virtual Machine files are stored. + + .EXAMPLE + Initialize-LabVMPath -VMPath 'c:\VMs\Lab\Virtual Machine 1' + The command will create the Virtual Machine structure for a Lab VM in the folder: + 'c:\VMs\Lab\Virtual Machine 1' + + .OUTPUTS + None. +#> +function Initialize-LabVMPath +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $VMPath + ) + + if (-not (Test-Path -Path $VMPath)) + { + $null = New-Item ` + -Path $VMPath ` + -ItemType Directory + } # if + + if (-not (Test-Path -Path "$VMPath\Virtual Machines")) + { + $null = New-Item ` + -Path "$VMPath\Virtual Machines" ` + -ItemType Directory + } # if + + if (-not (Test-Path -Path "$VMPath\Virtual Hard Disks")) + { + $null = New-Item ` + -Path "$VMPath\Virtual Hard Disks" ` + -ItemType Directory + } # if + + if (-not (Test-Path -Path "$VMPath\LabBuilder Files")) + { + $null = New-Item ` + -Path "$VMPath\LabBuilder Files" ` + -ItemType Directory + } # if + + if (-not (Test-Path -Path "$VMPath\LabBuilder Files\DSC Modules")) + { + $null = New-Item ` + -Path "$VMPath\LabBuilder Files\DSC Modules" ` + -ItemType Directory + } # if +} diff --git a/source/private/Install-LabHyperV.ps1 b/source/private/Install-LabHyperV.ps1 new file mode 100644 index 00000000..0cbbf89e --- /dev/null +++ b/source/private/Install-LabHyperV.ps1 @@ -0,0 +1,51 @@ +<# + .SYNOPSIS + Ensures the Hyper-V features are installed onto the system. + + .DESCRIPTION + If the Hyper-V features are not installed onto this system they will be installed. + + .EXAMPLE + Install-LabHyperV + Installs the appropriate Hyper-V features if they are not currently installed. + + .OUTPUTS + None +#> +function Install-LabHyperV +{ + [CmdLetBinding()] + param + ( + ) + + # Install Hyper-V Components + if ((Get-CimInstance Win32_OperatingSystem).ProductType -eq 1) + { + # Desktop OS + [Array] $feature = Get-WindowsOptionalFeature -Online -FeatureName '*Hyper-V*' ` + | Where-Object -Property State -Eq 'Disabled' + if ($feature.Count -gt 0 ) + { + Write-LabMessage -Message ($LocalizedData.InstallingHyperVComponentsMesage ` + -f 'Desktop') + $feature.Foreach( { + Enable-WindowsOptionalFeature -Online -FeatureName $_.FeatureName + } ) + } + } + Else + { + # Server OS + [Array] $feature = Get-WindowsFeature -Name Hyper-V ` + | Where-Object -Property Installed -EQ $false + if ($feature.Count -gt 0 ) + { + Write-LabMessage -Message ($LocalizedData.InstallingHyperVComponentsMesage ` + -f 'Desktop') + $feature.Foreach( { + Install-WindowsFeature -IncludeAllSubFeature -IncludeManagementTools -Name $_.Name + } ) + } + } +} diff --git a/source/private/Install-LabPackageProvider.ps1 b/source/private/Install-LabPackageProvider.ps1 new file mode 100644 index 00000000..c80e1ea8 --- /dev/null +++ b/source/private/Install-LabPackageProvider.ps1 @@ -0,0 +1,67 @@ +<# + .SYNOPSIS + Ensures the Package Providers required by LabBuilder are installed. + + .DESCRIPTION + This function will check that both the NuGet and the PowerShellGet package + providers are installed. + If either of them are missing the function will attempt to install them. + + .EXAMPLE + Install-LabPackageProvider + Ensures the required Package Providers for LabBuilder are installed. + + .OUTPUTS + None +#> +function Install-LabPackageProvider +{ + [CmdLetBinding(SupportsShouldProcess = $true, + ConfirmImpact = 'High')] + param + ( + [Parameter()] + [Switch] + $Force + ) + + $requiredPackageProviders = @('PowerShellGet', 'NuGet') + $currentPackageProviders = Get-PackageProvider ` + -ListAvailable ` + -ErrorAction Stop + + foreach ($requiredPackageProvider in $requiredPackageProviders) + { + $packageProvider = $currentPackageProviders | + Where-Object { $_.Name -eq $requiredPackageProvider } + + if (-not $packageProvider) + { + # The Package provider is not installed so install it + if ($Force -or $PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldInstallPackageProvider ` + -f $packageProvider ))) + { + Write-LabMessage -Message ($LocalizedData.InstallPackageProviderMessage ` + -f $requiredPackageProvider) + + $null = Install-PackageProvider ` + -Name $requiredPackageProvider ` + -ForceBootstrap ` + -Force ` + -ErrorAction Stop + } + else + { + # Can't continue if the package provider is not installed. + $exceptionParameters = @{ + errorId = 'PackageProviderNotInstalledError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageProviderNotInstalledError ` + -f $requiredPackageProvider) + } + New-LabException @exceptionParameters + } # if + } # if + } # foreach +} diff --git a/source/private/Invoke-LabDownloadAndUnzipFile.ps1 b/source/private/Invoke-LabDownloadAndUnzipFile.ps1 new file mode 100644 index 00000000..583e4a3d --- /dev/null +++ b/source/private/Invoke-LabDownloadAndUnzipFile.ps1 @@ -0,0 +1,101 @@ +<# + .SYNOPSIS + Download the a file to a folder and optionally unzip it. + + .DESCRIPTION + If the file is a zip file the file will be downloaded to a temporary + working folder and then unzipped to the destination, otherwise it + will be downloaded straight to the destination folder. +#> +function Invoke-LabDownloadAndUnzipFile +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $URL, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $DestinationPath + ) + + $fileName = [System.IO.Path]::GetFileName($URL) + + if (-not (Test-Path -Path $DestinationPath)) + { + $exceptionParameters = @{ + errorId = 'DownloadFolderDoesNotExistError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DownloadFolderDoesNotExistError ` + -f $DestinationPath, $fileName) + } + New-LabException @exceptionParameters + } + + $extension = [System.IO.Path]::GetExtension($fileName) + + if ($extension -eq '.zip') + { + # Download to a temp folder and unzip + $downloadPath = Join-Path -Path $Script:WorkingFolder -ChildPath $fileName + } + else + { + # Download to a temp folder and unzip + $downloadPath = Join-Path -Path $DestinationPath -ChildPath $fileName + } + + Write-LabMessage -Message ($LocalizedData.DownloadingFileMessage ` + -f $fileName, $URL, $downloadPath) + + try + { + Invoke-WebRequest ` + -Uri $URL ` + -OutFile $downloadPath ` + -ErrorAction Stop + } + catch + { + $exceptionParameters = @{ + errorId = 'FileDownloadError' + errorCategory = 'InvalidOperation' + errorMessage = $($LocalizedData.FileDownloadError -f $fileName, $URL, $_.Exception.Message) + } + New-LabException @exceptionParameters + } # try + + if ($extension -eq '.zip') + { + Write-LabMessage -Message ($LocalizedData.ExtractingFileMessage ` + -f $fileName, $downloadPath) + + # Extract this to the destination folder + try + { + Expand-Archive ` + -Path $downloadPath ` + -DestinationPath $DestinationPath ` + -Force ` + -ErrorAction Stop + } + catch + { + $exceptionParameters = @{ + errorId = 'FileExtractError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.FileExtractError -f $fileName, $_.Exception.Message) + } + New-LabException @exceptionParameters + } + finally + { + # Remove the downloaded zip file + Remove-Item -Path $downloadPath + } # try + } +} diff --git a/source/private/Invoke-LabDownloadResourceModule.ps1 b/source/private/Invoke-LabDownloadResourceModule.ps1 new file mode 100644 index 00000000..178c0351 --- /dev/null +++ b/source/private/Invoke-LabDownloadResourceModule.ps1 @@ -0,0 +1,189 @@ +<# + .SYNOPSIS + Downloads a resource module. + + .DESCRIPTION + It will download a specific resource module, either from PowerShell Gallery + or from a URL if the module does not already exist. + + .PARAMETER Name + Contains the Name of the module to download. + + .PARAMETER URL + If this parameter is specified, the resource module will be downloaded from a URL + rather than via PowerShell Gallery. This is a the URL to use to download a zip + file containing this resource module. + + .PARAMETER Folder + If this resource module is downloaded using a URL, this is the folder in the zip + file that contains the resource and will need to be renamed to the name of the + resource. + + .PARAMETER RequiredVersion + This is the required version of the Resource Module that is required. + If this version is not installed the a new version will be downloaded. + + .PARAMETER MinimumVersion + This is the minimum version of the Resource Module that is required. + If at least this version is not installed then a new version will be downloaded. + + .EXAMPLE + Invoke-LabDownloadResourceModule ` + -Name NetworkingDsc ` + -RequiredVersion 2.7.0.0 + Downloads the Resource Module xNetowrking version 2.7.0.0 + + .OUTPUTS + None. +#> +function Invoke-LabDownloadResourceModule +{ + [CmdLetBinding()] + param + ( + [Parameter( + position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter( + position = 2)] + [System.String] + $URL, + + [Parameter( + position = 3)] + [System.String] + $Folder, + + [Parameter( + position = 4)] + [System.String] + $RequiredVersion, + + [Parameter( + position = 5)] + [System.String] + $MinimumVersion + ) + + $installedModules = @(Get-Module -ListAvailable) + + # Determine a query that will be used to decide if the module is already installed + if ($RequiredVersion) + { + [ScriptBlock] $Query = { + ($_.Name -eq $Name) -and ($_.Version -eq $RequiredVersion) + } + + $versionMessage = $RequiredVersion + } + elseif ($MinimumVersion) + { + [ScriptBlock] $Query = { + ($_.Name -eq $Name) -and ($_.Version -ge $MinimumVersion) + } + + $versionMessage = "min ${MinimumVersion}" + } + else + { + [ScriptBlock] $Query = { + $_.Name -eq $Name + } + + $versionMessage = 'any version' + } + + # Is the module installed? + if ($installedModules.Where($Query).Count -eq 0) + { + Write-LabMessage -Message ($LocalizedData.ModuleNotInstalledMessage ` + -f $Name, $versionMessage) + + # If a URL was specified, download this module via HTTP + if ($URL) + { + # The module is not installed - so download it + # This is usually for downloading modules directly from github + Write-LabMessage -Message ($LocalizedData.DownloadingLabResourceWebMessage ` + -f $Name, $versionMessage, $URL) + + $modulesFolder = "$($ENV:ProgramFiles)\WindowsPowerShell\Modules\" + + Invoke-LabDownloadAndUnzipFile ` + -URL $URL ` + -DestinationPath $modulesFolder ` + -ErrorAction Stop + + if ($Folder) + { + # This zip file contains a folder that is not the name of the module so it must be + # renamed. This is usually the case with source downloaded directly from GitHub + $modulePath = Join-Path -Path $modulesFolder -ChildPath $Name + + if (Test-Path -Path $modulePath) + { + Remove-Item -Path $modulePath -Recurse -Force + } + + Rename-Item ` + -Path (Join-Path -Path $modulesFolder -ChildPath $Folder) ` + -NewName $Name ` + -Force + } # if + + Write-LabMessage -Message ($LocalizedData.InstalledLabResourceWebMessage ` + -f $Name, $versionMessage, $modulePath) + } + else + { + # Install the package via PowerShellGet from the PowerShellGallery + # Make sure the Nuget Package provider is initialized. + $null = Get-PackageProvider ` + -name nuget ` + -ForceBootStrap ` + -Force + + # Make sure PSGallery is trusted + Set-PSRepository ` + -Name PSGallery ` + -InstallationPolicy Trusted + + # Install the module + $installModuleParameters = [PSObject] @{ Name = $Name } + + if ($RequiredVersion) + { + # Is a specific module version required? + $installModuleParameters += [PSObject] @{ + RequiredVersion = $RequiredVersion + } + } + elseif ($MinimumVersion) + { + # Is a specific module version minimum version? + $installModuleParameters += [PSObject] @{ + MinimumVersion = $MinimumVersion + } + } + + try + { + Install-Module @installModuleParameters -Force -ErrorAction Stop + } + catch + { + $exceptionParameters = @{ + errorId = 'ModuleNotAvailableError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ModuleNotAvailableError ` + -f $Name, $versionMessage, $_.Exception.Message) + } + New-LabException @exceptionParameters + } + } # If + } # If +} diff --git a/source/private/New-LabCredential.ps1 b/source/private/New-LabCredential.ps1 new file mode 100644 index 00000000..d9af4574 --- /dev/null +++ b/source/private/New-LabCredential.ps1 @@ -0,0 +1,27 @@ +<# + .SYNOPSIS + Generates a credential object from a username and password. +#> +function New-LabCredential() +{ + [CmdletBinding()] + [OutputType([PSCredential])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Username, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Password + ) + + $credential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ($Username, (ConvertTo-SecureString $Password -AsPlainText -Force)) + + return $credential +} diff --git a/source/private/New-LabException.ps1 b/source/private/New-LabException.ps1 new file mode 100644 index 00000000..623462dd --- /dev/null +++ b/source/private/New-LabException.ps1 @@ -0,0 +1,69 @@ +<# + .SYNOPSIS + Throws a custom exception. + + .DESCRIPTION + This cmdlet throws a terminating or non-terminating exception. + + .PARAMETER errorId + The Id of the exception. + + .PARAMETER errorCategory + The category of the exception. It must be a valid [System.Management.Automation.ErrorCategory] + value. + + .PARAMETER errorMessage + The exception message. + + .PARAMETER terminate + This switch will cause the exception to terminate the cmdlet. + + .EXAMPLE + $exceptionParameters = @{ + errorId = 'ConnectionFailure' + errorCategory = 'ConnectionError' + errorMessage = 'Could not connect' + } + New-LabException @exceptionParameters + Throw a ConnectionError exception with the message 'Could not connect'. + + .OUTPUTS + None +#> +function New-LabException +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ErrorId, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.ErrorCategory] + $ErrorCategory, + + [Parameter(Mandatory = $true)] + [System.String] + $ErrorMessage, + + [Switch] + $Terminate + ) + + $exception = New-Object -TypeName System.Exception ` + -ArgumentList $errorMessage + $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord ` + -ArgumentList $exception, $errorId, $errorCategory, $null + + if ($Terminate) + { + # This is a terminating exception. + throw $errorRecord + } + else + { + # Note: Although this method is called ThrowTerminatingError, it doesn't terminate. + $PSCmdlet.ThrowTerminatingError($errorRecord) + } +} diff --git a/source/private/New-LabHostSelfSignedCertificate.ps1 b/source/private/New-LabHostSelfSignedCertificate.ps1 new file mode 100644 index 00000000..dd943c19 --- /dev/null +++ b/source/private/New-LabHostSelfSignedCertificate.ps1 @@ -0,0 +1,99 @@ +<# + .SYNOPSIS + Generate a new credential encryption certificate on the Host for a VM. + + .DESCRIPTION + This function will create a new self-signed certificate on the host that can be uploaded + to the VM that it is created for. The certificate will be created in the LabBuilder files + folder for the specified VM. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + New-LabHostSelfSignedCertificate -Lab $Lab -VM $VMs[0] + Causes a new self-signed certificate for the VM and stores it to the Labbuilder files folder + of th VM. + + .OUTPUTS + The path to the certificate file that was created. +#> +function New-LabHostSelfSignedCertificate +{ + [CmdLetBinding()] + [OutputType([System.IO.FileInfo])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + $certificateFriendlyName = $Script:DSCCertificateFriendlyName + $certificateSubject = "CN=$($VM.ComputerName)" + + # Create the self-signed certificate for the destination VM + . $Script:SupportGertGenPath + New-SelfsignedCertificateEx ` + -Subject $certificateSubject ` + -EKU 'Document Encryption','Server Authentication','Client Authentication' ` + -KeyUsage 'DigitalSignature, KeyEncipherment, DataEncipherment' ` + -SAN $VM.ComputerName ` + -FriendlyName $certificateFriendlyName ` + -Exportable ` + -StoreLocation 'LocalMachine' ` + -StoreName 'My' ` + -KeyLength $Script:SelfSignedCertKeyLength ` + -ProviderName $Script:SelfSignedCertProviderName ` + -AlgorithmName $Script:SelfSignedCertAlgorithmName ` + -SignatureAlgorithm $Script:SelfSignedCertSignatureAlgorithm ` + -ErrorAction Stop + + # Locate the newly created certificate + $certificate = Get-ChildItem -Path cert:\LocalMachine\My ` + | Where-Object { + ($_.FriendlyName -eq $certificateFriendlyName) ` + -and ($_.Subject -eq $certificateSubject) + } | Select-Object -First 1 + + # Export the certificate with the Private key in + # preparation for upload to the VM + $certificatePassword = ConvertTo-SecureString ` + -String $Script:DSCCertificatePassword ` + -Force ` + -AsPlainText + $certificatePfxDestination = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath $Script:DSCEncryptionPfxCert + $null = Export-PfxCertificate ` + -FilePath $certificatePfxDestination ` + -Cert $certificate ` + -Password $certificatePassword ` + -ErrorAction Stop + + # Export the certificate without a private key + $certificateDestination = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath $Script:DSCEncryptionCert + $null = Export-Certificate ` + -Type CERT ` + -FilePath $certificateDestination ` + -Cert $certificate ` + -ErrorAction Stop + + # Remove the certificate from the Local Machine store + $certificate | Remove-Item + + return (Get-Item -Path $certificateDestination) +} diff --git a/source/private/New-LabVMInitializationFile.ps1 b/source/private/New-LabVMInitializationFile.ps1 new file mode 100644 index 00000000..c3a25c19 --- /dev/null +++ b/source/private/New-LabVMInitializationFile.ps1 @@ -0,0 +1,200 @@ +<# + .SYNOPSIS + Prepares the the files for initializing a new VM. + + .DESCRIPTION + This function creates the following files in the LabBuilder Files for the a VM in preparation + for them to be applied to the VM VHD before it is booted up for the first time: + 1. Unattend.xml - a Windows Unattend.xml file. + 2. SetupComplete.cmd - the command file that gets run after the Windows OOBE is complete. + 3. SetupComplete.ps1 - this PowerShell script file that is run at the the end of the + SetupComplete.cmd. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + New-LabVMInitializationFile -Lab $Lab -VM $VMs[0] + Prepare the first VM in the Lab c:\mylab\config.xml for initial boot. + + .OUTPUTS + None. +#> +function New-LabVMInitializationFile +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + # Generate an unattended setup file + $unattendFile = Get-LabUnattendFileContent ` + -Lab $Lab ` + -VM $VM + $null = Set-Content ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'Unattend.xml') ` + -Value $unattendFile -Force + + # Assemble the SetupComplete.* scripts. + $setupCompleteCmd = '' + + # Write out the PS1 Setup Complete File + if ($VM.OSType -eq [LabOSType]::Nano) + { + # For a Nano Server we also need to create the certificates + # to upload to it (because it Nano Server can't generate them) + $null = New-LabHostSelfSignedCertificate ` + -Lab $Lab ` + -VM $VM + + # PowerShell currently can't find any basic Cmdlets when executed by + # SetupComplete.cmd during the initialization phase, so create an empty + # a SetupComplete.ps1 + $setupCompletePs = '' + } + else + { + if ($VM.CertificateSource -eq [LabCertificateSource]::Host) + { + # Generate the PFX certificate on the host + $null = New-LabHostSelfSignedCertificate ` + -Lab $Lab ` + -VM $VM + } + + $getCertPs = Get-LabCertificatePsFileContent ` + -Lab $Lab ` + -VM $VM + $setupCompletePs = @" +Add-Content `` + -Path "C:\WINDOWS\Setup\Scripts\SetupComplete.log" `` + -Value 'SetupComplete.ps1 Script Started...' `` + -Encoding Ascii +Start-Sleep -Seconds 30 +$getCertPs +Add-Content `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'Certificate identified and saved to C:\Windows\$Script:DSCEncryptionCert ...' `` + -Encoding Ascii +Enable-PSRemoting -SkipNetworkProfileCheck -Force +Add-Content `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'Windows Remoting Enabled ...' `` + -Encoding Ascii +"@ + } # if + + if ($VM.SetupComplete) + { + $setupComplete = $VM.SetupComplete + + if (-not (Test-Path -Path $setupComplete)) + { + $exceptionParameters = @{ + errorId = 'SetupCompleteScriptMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.SetupCompleteScriptMissingError ` + -f $VM.name,$setupComplete) + } + New-LabException @exceptionParameters + } + + $extension = [System.IO.Path]::GetExtension($setupComplete) + + switch ($extension.ToLower()) + { + '.ps1' + { + $setupCompletePs += Get-Content -Path $setupComplete + Break + } # 'ps1' + + '.cmd' + { + $setupCompleteCmd += Get-Content -Path $setupComplete + Break + } # 'cmd' + } # Switch + } # If + + # Write out the CMD Setup Complete File + if ($VM.OSType -eq [LabOSType]::Nano) + { + $setupCompleteCmd = @" +@echo SetupComplete.cmd Script Started... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log +$setupCompleteCmd +certoc.exe -ImportPFX -p $Script:DSCCertificatePassword root $ENV:SystemRoot\$Script:DSCEncryptionPfxCert >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log +@echo SetupComplete.cmd Script Finished... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log +@echo Initial Setup Completed - this file indicates that setup has completed. >> %SYSTEMROOT%\Setup\Scripts\InitialSetupCompleted.txt +"@ + } + else + { + $setupCompleteCmd = @" +@echo SetupComplete.cmd Script Started... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log`r +$setupCompleteCmd +@echo SetupComplete.cmd Execute SetupComplete.ps1... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log`r +powerShell.exe -ExecutionPolicy Unrestricted -Command `"%SYSTEMROOT%\Setup\Scripts\SetupComplete.ps1`" `r +@echo SetupComplete.cmd Script Finished... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log +@echo Initial Setup Completed - this file indicates that setup has completed. >> %SYSTEMROOT%\Setup\Scripts\InitialSetupCompleted.txt +"@ + } + + $null = Set-Content ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'SetupComplete.cmd') ` + -Value $setupCompleteCmd -Force + + # Write out the PowerShell Setup Complete file + $setupCompletePs = @" +Add-Content `` + -Path `"$($ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'SetupComplete.ps1 Script Started...' `` + -Encoding Ascii +$setupCompletePs +Add-Content `` + -Path `"$($ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` + -Value 'SetupComplete.ps1 Script Finished...' `` + -Encoding Ascii +"@ + $null = Set-Content ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'SetupComplete.ps1') ` + -Value $setupCompletePs -Force + + # If ODJ file specified copy it to the labuilder path. + if ($VM.OSType -eq [LabOSType]::Nano ` + -and -not [System.String]::IsNullOrWhiteSpace($VM.NanoODJPath)) + { + if ([System.IO.Path]::IsPathRooted($VM.NanoODJPath)) + { + $nanoODJPath = $VM.NanoODJPath + } + else + { + $nanoODJPath = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $VM.NanoODJPath + } # if + + $null = Copy-Item ` + -Path (Join-Path -Path $nanoODJPath -ChildPath "$($VM.ComputerName).txt") ` + -Destination $vmLabBuilderFiles ` + -ErrorAction Stop + } # if + + Write-LabMessage -Message $($LocalizedData.CreatedVMInitializationFiles ` + -f $VM.Name) +} diff --git a/source/private/Recieve-LabSelfSignedCertificate.ps1 b/source/private/Recieve-LabSelfSignedCertificate.ps1 new file mode 100644 index 00000000..7e6dcfc3 --- /dev/null +++ b/source/private/Recieve-LabSelfSignedCertificate.ps1 @@ -0,0 +1,135 @@ +<# + .SYNOPSIS + Download the existing self-signed certificate from a running VM. + + .DESCRIPTION + This function uses PS Remoting to connect to a running VM and download the an existing + Self-Signed certificate file that was written to the c:\windows folder of the guest operating + system by the SetupComplete.ps1 script on the. The certificate will be downloaded to the VM's + Labbuilder files folder. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .PARAMETER Timeout + The maximum amount of time that this function can take to download the certificate. + If the timeout is reached before the process is complete an error will be thrown. + The timeout defaults to 300 seconds. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Recieve-LabSelfSignedCertificate -Lab $Lab -VM $VMs[0] + Downloads the existing Self-signed certificate for the VM to the Labbuilder files folder of the + VM. + + .OUTPUTS + The path to the certificate file that was downloaded. +#> +function Recieve-LabSelfSignedCertificate +{ + [CmdLetBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [Parameter()] + [System.Int32] + $Timeout = 300 + ) + + $startTime = Get-Date + $session = $null + $complete = $false + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + $session = Connect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + # Failed to connnect to the VM + if (-not $session) + { + $exceptionParameters = @{ + errorId = 'CertificateDownloadError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.CertificateDownloadError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + return + } # if + + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $complete)) + { + # We connected OK - download the Certificate file + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + try + { + $null = Copy-Item ` + -Path "c:\windows\$Script:DSCEncryptionCert" ` + -Destination $vmLabBuilderFiles ` + -FromSession $session ` + -ErrorAction Stop + $complete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.WaitingForCertificateMessage ` + -f $VM.Name,$Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # try + } # while + } # if + + # If the copy didn't complete and we're out of time throw an exception + if ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $exceptionParameters = @{ + errorId = 'CertificateDownloadError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.CertificateDownloadError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } # if + + # Close the Session if it is opened and the download is complete + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and ($complete)) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + } # if + } # while + + return (Get-Item -Path "$vmLabBuilderFiles\$($Script:DSCEncryptionCert)") +} diff --git a/source/private/Register-LabPackageSource.ps1 b/source/private/Register-LabPackageSource.ps1 new file mode 100644 index 00000000..af7fda23 --- /dev/null +++ b/source/private/Register-LabPackageSource.ps1 @@ -0,0 +1,112 @@ +<# + .SYNOPSIS + Ensures the Package Sources required by LabBuilder are registered. + + .DESCRIPTION + This function will check that both the NuGet.org and the PSGallery package + sources are registered. + If either of them are missing the function will attempt to register them. + + .EXAMPLE + Register-LabPackageSource + Ensures the required Package Sources for LabBuilder are required. + + .OUTPUTS + None +#> +function Register-LabPackageSource +{ + [CmdLetBinding(SupportsShouldProcess = $true, + ConfirmImpact = 'High')] + param + ( + [Parameter()] + [Switch] + $Force + ) + + $requiredPackageSources = @( + @{ + Name = 'nuget.org' + ProviderName = 'NuGet' + Location = 'https://www.nuget.org/api/v2/' + }, + @{ + Name = 'PSGallery' + ProviderName = 'PowerShellGet' + Location = 'https://www.powershellgallery.com/api/v2/' + } + ) + + $currentPackageSources = Get-PackageSource -ErrorAction Stop + + foreach ($requiredPackageSource in $requiredPackageSources) + { + $packageSource = $currentPackageSources | + Where-Object -FilterScript { + $_.Name -eq $requiredPackageSource.Name + } + + if ($packageSource) + { + if (-not $packageSource.IsTrusted) + { + if ($Force -or $PSCmdlet.ShouldProcess( 'Localhost', ` + ($LocalizedData.ShouldTrustPackageSource ` + -f $requiredPackageSource.Name, $requiredPackageSource.Location ))) + { + # The Package source is not trusted so trust it + Write-LabMessage -Message ($LocalizedData.RegisterPackageSourceMessage ` + -f $requiredPackageSource.Name, $requiredPackageSource.Location) + + $null = Set-PackageSource ` + -Name $requiredPackageSource.Name ` + -Trusted ` + -Force ` + -ErrorAction Stop + } + else + { + # Can't continue if the package source is not trusted. + $exceptionParameters = @{ + errorId = 'PackageSourceNotTrustedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageSourceNotTrustedError ` + -f $requiredPackageSource.Name) + } + New-LabException @exceptionParameters + } # if + } # if + } + else + { + # The Package source is not registered so register it + if ($Force -or $PSCmdlet.ShouldProcess( 'Localhost', ` + ($LocalizedData.ShouldRegisterPackageSource ` + -f $requiredPackageSource.Name, $requiredPackageSource.Location ))) + { + Write-LabMessage -Message ($LocalizedData.RegisterPackageSourceMessage ` + -f $requiredPackageSource.Name, $requiredPackageSource.Location) + + $null = Register-PackageSource ` + -Name $requiredPackageSource.Name ` + -Location $requiredPackageSource.Location ` + -ProviderName $requiredPackageSource.ProviderName ` + -Trusted ` + -Force ` + -ErrorAction Stop + } + else + { + # Can't continue if the package source is not registered. + $exceptionParameters = @{ + errorId = 'PackageSourceNotRegisteredError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageSourceNotRegisteredError ` + -f $requiredPackageSource.Name) + } + New-LabException @exceptionParameters + } # if + } # if + } # foreach +} diff --git a/source/private/Request-LabSelfSignedCertificate.ps1 b/source/private/Request-LabSelfSignedCertificate.ps1 new file mode 100644 index 00000000..ba61afb3 --- /dev/null +++ b/source/private/Request-LabSelfSignedCertificate.ps1 @@ -0,0 +1,203 @@ +<# + .SYNOPSIS + Generate and download a new credential encryption certificate from a running VM. + + .DESCRIPTION + This function uses PS Remoting to connect to a running VM and upload the GetDSCEncryptionCert.ps1 + script and then run it. This wil create a new self-signed certificate that is written to the + c:\windows folder of the guest operating system. The certificate will be downloaded to the VM's + Labbuilder files folder. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .PARAMETER Timeout + The maximum amount of time that this function can take to download the certificate. + If the timeout is reached before the process is complete an error will be thrown. + The timeout defaults to 300 seconds. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Request-LabSelfSignedCertificate -Lab $Lab -VM $VMs[0] + Causes a new self-signed certificate on the VM and download it to the Labbuilder files folder + of th VM. + + .OUTPUTS + The path to the certificate file that was downloaded. +#> +function Request-LabSelfSignedCertificate +{ + [CmdLetBinding()] + [OutputType([System.IO.FileInfo])] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [Parameter()] + [System.Int32] + $Timeout = 300 + ) + + $startTime = Get-Date + $session = $null + $complete = $false + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + # Ensure the certificate generation script has been created + $getCertPs = Get-LabCertificatePsFileContent ` + -Lab $Lab ` + -VM $VM ` + -CertificateSource Guest + + $null = Set-Content ` + -Path "$VMLabBuilderFiles\GetDSCEncryptionCert.ps1" ` + -Value $getCertPs ` + -Force + + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + $session = Connect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + # Failed to connnect to the VM + if (-not $session) + { + $exceptionParameters = @{ + errorId = 'CertificateDownloadError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.CertificateDownloadError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + return + } # if + + $complete = $false + + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $complete)) + { + # We connected OK - Upload the script + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + try + { + Copy-Item ` + -Path "$VMLabBuilderFiles\GetDSCEncryptionCert.ps1" ` + -Destination 'c:\windows\setup\scripts\' ` + -ToSession $session ` + -Force ` + -ErrorAction Stop + $complete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.FailedToUploadCertificateCreateScriptMessage ` + -f $VM.Name,$Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # try + } # while + } # if + + $complete = $false + + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $complete)) + { + # Script uploaded, run it + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + try + { + Invoke-Command -Session $session -ScriptBlock { + C:\Windows\Setup\Scripts\GetDSCEncryptionCert.ps1 + } + + $complete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.FailedToExecuteCertificateCreateScriptMessage ` + -f $VM.Name,$Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # try + } # while + } # if + + $complete = $false + + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $complete)) + { + # Now download the Certificate + while ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + try { + $null = Copy-Item ` + -Path "c:\windows\$($Script:DSCEncryptionCert)" ` + -Destination $vmLabBuilderFiles ` + -FromSession $session ` + -ErrorAction Stop + + $complete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.FailedToDownloadCertificateMessage ` + -f $VM.Name,$Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # Try + } # While + } # If + + # If the process didn't complete and we're out of time throw an exception + if ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) + { + if ($session) + { + Remove-PSSession -Session $session + } + + $exceptionParameters = @{ + errorId = 'CertificateDownloadError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.CertificateDownloadError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } + + # Close the Session if it is opened and the download is complete + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and ($complete)) + { + Remove-PSSession -Session $session + } # If + } # While + + return (Get-Item -Path "$vmLabBuilderFiles\$($Script:DSCEncryptionCert)") +} diff --git a/source/private/Set-LabDSC.ps1 b/source/private/Set-LabDSC.ps1 new file mode 100644 index 00000000..7f3bd22a --- /dev/null +++ b/source/private/Set-LabDSC.ps1 @@ -0,0 +1,158 @@ +<# + .SYNOPSIS + This function prepares the PowerShell scripts used for starting up DSC on a VM. + + .DESCRIPTION + Two PowerShell scripts will be created by this function in the LabBuilder Files + folder of the VM: + 1. StartDSC.ps1 - the script that is called automatically to start up DSC. + 2. StartDSCDebug.ps1 - a debug script that will start up DSC in debug mode. + These scripts will contain code to perform the following operations: + 1. Configure the names of the Network Adapters so that they will match the + names in the DSC Configuration files. + 2. Enable/Disable DSC Event Logging. + 3. Apply Configuration to the Local Configuration Manager. + 4. Start DSC. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Set-LabDSC -Lab $Lab -VM $VMs[0] + Prepare the first VM in the Lab c:\mylab\config.xml for DSC start up. + + .OUTPUTS + None. +#> +function Set-LabDSC +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + $dscStartPs = '' + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + <# + Relabel the Network Adapters so that they match what the DSC Networking config will use + This is because unfortunately the Hyper-V Device Naming feature doesn't work. + #> + $managementSwitchName = Get-LabManagementSwitchName -Lab $Lab + $adapters = [System.String[]] ($VM.Adapters).Name + $adapters += @($managementSwitchName) + + foreach ($adapter in $adapters) + { + $netAdapter = Get-VMNetworkAdapter -VMName $($VM.Name) -Name $adapter + + if (-not $netAdapter) + { + $exceptionParameters = @{ + errorId = 'NetworkAdapterNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NetworkAdapterNotFoundError ` + -f $adapter, $VM.Name) + } + New-LabException @exceptionParameters + } # if + + $macAddress = $netAdapter.MacAddress + + if (-not $macAddress) + { + $exceptionParameters = @{ + errorId = 'NetworkAdapterBlankMacError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NetworkAdapterBlankMacError ` + -f $adapter, $VM.Name) + } + New-LabException @exceptionParameters + } # If + + $dscStartPs += @" +Get-NetAdapter `` + | Where-Object { `$_.MacAddress.Replace('-','') -eq '$macAddress' } `` + | Rename-NetAdapter -NewName '$($adapter)' + +"@ + } # Foreach + + <# + Enable DSC logging (as long as it hasn't been already) + Nano Server doesn't have the Microsoft-Windows-Dsc/Analytic channels so + Logging can't be enabled. + #> + if ($VM.OSType -ne [LabOSType]::Nano) + { + $logging = ($VM.DSC.Logging).ToString() + + $dscStartPs += @" +`$Result = & "wevtutil.exe" get-log "Microsoft-Windows-Dsc/Analytic" +if (-not (`$Result -like '*enabled: true*')) { + & "wevtutil.exe" set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:$logging +} +`$Result = & "wevtutil.exe" get-log "Microsoft-Windows-Dsc/Debug" +if (-not (`$Result -like '*enabled: true*')) { + & "wevtutil.exe" set-log "Microsoft-Windows-Dsc/Debug" /q:true /e:$logging +} + +"@ + } # if + + # Start the actual DSC Configuration + $dscStartPs += @" +Set-DscLocalConfigurationManager `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` + -Verbose *>> `"`$(`$ENV:SystemRoot)\Setup\Scripts\DSC.log`" +Start-DSCConfiguration `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` + -Force `` + -Verbose *>> `"`$(`$ENV:SystemRoot)\Setup\Scripts\DSC.log`" + +"@ + $null = Set-Content ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSC.ps1') ` + -Value $dscStartPs -Force + + $dscStartPsDebug = @" +param ( + [System.Boolean] `$WaitForDebugger +) +Set-DscLocalConfigurationManager `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` + -Verbose +if (`$WaitForDebugger) +{ + Enable-DscDebug `` + -BreakAll +} +Start-DSCConfiguration `` + -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` + -Force `` + -Debug `` + -Wait `` + -Verbose +if (`$WaitForDebugger) +{ + Disable-DscDebug +} +"@ + + $null = Set-Content ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSCDebug.ps1') ` + -Value $dscStartPsDebug -Force +} diff --git a/source/private/Set-LabModulesInDSCConfig.ps1 b/source/private/Set-LabModulesInDSCConfig.ps1 new file mode 100644 index 00000000..93025358 --- /dev/null +++ b/source/private/Set-LabModulesInDSCConfig.ps1 @@ -0,0 +1,129 @@ +<# + .SYNOPSIS + Sets the Modules Resources that should be imported in a DSC Config. + + .DESCRIPTION + It will completely replace the list of Imported DSCResources with this new list. + + .PARAMETER DscConfigFile + Contains the path to the DSC Config file to set resource module names in. + + .PARAMETER DscConfigContent + Contains the content of the DSC Config to set resource module names in. + + .PARAMETER Modules + Contains an array of LabDSCModule objects to replace set in the Configuration. + + .EXAMPLE + Set-LabModulesInDSCConfig -DscConfigFile c:\mydsc\Server01.ps1 -Modules $Modules + Set the DSC Resource module in the content from file c:\mydsc\server01.ps1 + + .EXAMPLE + Set-LabModulesInDSCConfig -DscConfigContent $DSCConfig -Modules $Modules + Set the DSC Resource module in the content $DSCConfig + + .OUTPUTS + A string containing the content of the DSC Config file with the updated + module names in it. +#> +function Set-LabModulesInDSCConfig +{ + [CmdLetBinding(DefaultParameterSetName = "Content")] + [OutputType([System.String])] + param + ( + [parameter( + Position = 1, + ParameterSetName = "Content", + Mandatory = $true)] + [System.String] + $DscConfigContent, + + [parameter( + Position = 2, + ParameterSetName = "File", + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $DscConfigFile, + + [parameter( + Position = 3, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [LabDSCModule[]] + $Modules + ) + + if ($PSCmdlet.ParameterSetName -eq 'File') + { + $DscConfigContent = Get-Content -Path $DscConfigFile -Raw + } # if + + $regex = "[ \t]*?Import\-DscResource[ \t]+(?:\-ModuleName[ \t]+)?'?`"?([A-Za-z0-9._-]+)`"?'?([ \t]+(-ModuleVersion[ \t]+)?'?`"?([0-9.]+)`"?'?)?[ \t]*[\r\n]+" + $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') + + foreach ($module in $Modules) + { + $importCommand = "Import-DscResource -ModuleName '$($module.ModuleName)'" + if ($module.ModuleVersion) + { + $importCommand = "$importCommand -ModuleVersion '$($module.ModuleVersion)'" + } # if + + $importCommand = " $importCommand`r`n" + + # is this module already in there? + $found = $false + + foreach ($moduleMatch in $moduleMatches) + { + if ($moduleMatch.Groups[1].Value -eq $module.ModuleName) + { + # Found the module - so replace it + $DscConfigContent = ("{0}{1}{2}" -f ` + $DscConfigContent.Substring(0, $moduleMatch.Index), ` + $importCommand, ` + $DscConfigContent.Substring($moduleMatch.Index + $moduleMatch.Length)) + + $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') + $found = $true + break + } # if + } # foreach + + if (-not $found) + { + if ($moduleMatches.Count -gt 0) + { + # Add this to the end of the existing Import-DSCResource lines + $moduleMatch = $moduleMatches[$moduleMatches.count - 1] + } + else + { + # There are no existing DSC Resource lines, so add it after + # Configuration ... { line + $moduleMatch = [regex]::matches($DscConfigContent, "[ \t]*?Configuration[ \t]+?'?`"?[A-Za-z0-9._-]+`"?'?[ \t]*?[\r\n]*?{[\r\n]*?", 'IgnoreCase') + + if (-not $moduleMatch) + { + $exceptionParameters = @{ + errorId = 'DSCConfiguartionMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfiguartionMissingError) + } + New-LabException @exceptionParameters + } + } # if + + $DscConfigContent = ("{0}{1}{2}" -f ` + $DscConfigContent.Substring(0, $moduleMatch.Index + $moduleMatch.Length), ` + $importCommand, ` + $DscConfigContent.Substring($moduleMatch.Index + $moduleMatch.Length)) + + $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') + } # Module not found so add it to the end + } # foreach + + return $DscConfigContent +} diff --git a/source/private/Set-LabSwitchAdapter.ps1 b/source/private/Set-LabSwitchAdapter.ps1 new file mode 100644 index 00000000..22ecaecc --- /dev/null +++ b/source/private/Set-LabSwitchAdapter.ps1 @@ -0,0 +1,134 @@ +<# + .SYNOPSIS + Ensures that the virtual adapter is attached to a Virtual Switch + and configured correctly. + + .DESCRIPTION + This function is used to add or update the specified virtual network adapter + that is used by the Management OS to connect to the specifed virtual switch. + + .PARAMETER Name + Contains the name of the virtual network adapter to add. + + .PARAMETER SwitchName + Contains the name of the virtual switch to connect this adapter to. + + .PARAMETER ManagementOS + Whether or not this adapter is attached to the Management OS. + + .PARAMETER StaticMacAddress + This optional parameter contains the static MAC address to assign to the virtual + network adapter. + + .PARAMETER VlanId + This optional parameter contains the VLan Id to assign to this network adapter. + + .EXAMPLE + Set-LabSwitchAdapter -Name 'Domain Nat SMB' -SwitchName 'Domain Nat' -VlanId 25 + + .OUTPUTS + None. +#> +function Set-LabSwitchAdapter +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $SwitchName, + + [Parameter()] + [Switch] + $ManagementOS, + + [Parameter()] + [System.String] + $StaticMacAddress, + + [Parameter()] + [AllowNull()] + [Nullable[System.Int32]] + $VlanId + ) + + # Determine if we should set the MAC address and VLan Id + $setVlanId = $PSBoundParameters.ContainsKey('VlanId') + $setMacAddress = $PSBoundParameters.ContainsKey('StaticMacAddress') + + # Remove VlanId Parameter so this can be splatted + $null = $PSBoundParameters.Remove('VlanId') + $null = $PSBoundParameters.Remove('StaticMacAddress') + + $existingManagementAdapter = Get-VMNetworkAdapter ` + @PSBoundParameters ` + -ErrorAction SilentlyContinue + + if (-not $existingManagementAdapter) + { + # Adapter does not exist so add it + if ($SetMacAddress) + { + # For a management adapter a Static MAC address can only be assigned at creation time. + if (-not ([System.String]::IsNullOrEmpty($StaticMacAddress))) + { + $PSBoundParameters.Add('StaticMacAddress', $StaticMacAddress) + } + } + + $existingManagementAdapter = Add-VMNetworkAdapter ` + @PSBoundParameters ` + -Passthru + } + else + { + <# + The MAC Address for an existing Management Adapter can not be changed + This shouldn't ever happen unless the configuration is changed. + Not sure of the solution to this problem. + #> + } + + # Set or clear the VlanId + if ($setVlanId) + { + $existingManagementAdapterVlan = Get-VMNetworkAdapterVlan ` + -VMNetworkAdapter $existingManagementAdapter + + $existingManagementVlan = $existingManagementAdapterVlan.AccessVlanId + + if ($null -eq $VlanId) + { + if ($null -eq $existingManagementVlan) + { + $setVMNetworkAdapterVlanParameters = @{ + VMNetworkAdapter = $existingManagementAdapter + Untagged = $true + } + + $null = Set-VMNetworkAdapterVlan ` + @setVMNetworkAdapterVlanParameters ` + -ErrorAction Stop + } + } + else + { + if ($VlanId -ne $existingManagementVlan) + { + $setVMNetworkAdapterVlanParameters = @{ + VMNetworkAdapter = $existingManagementAdapter + Access = $true + VlanId = $VlanId + } + + $null = Set-VMNetworkAdapterVlan ` + @setVMNetworkAdapterVlanParameters ` + -ErrorAction Stop + } + } + } +} diff --git a/source/private/Start-LabDSC.ps1 b/source/private/Start-LabDSC.ps1 new file mode 100644 index 00000000..accfd136 --- /dev/null +++ b/source/private/Start-LabDSC.ps1 @@ -0,0 +1,239 @@ +<# + .SYNOPSIS + Uploads prepared Modules and MOF files to a VM and starts up Desired State + Configuration (DSC) on it. + + .DESCRIPTION + This function will perform the following tasks: + 1. Connect to the VM via remoting. + 2. Upload the DSC and LCM MOF files to the c:\windows\setup\scripts folder of the VM. + 3. Upload DSC Start up scripts to the c:\windows\setup\scripts folder of the VM. + 4. Upload all required modules to the c:\program files\WindowsPowerShell\Modules\ folder + of the VM. + 5. Invoke the StartDSC.ps1 script on the VM to start DSC processing. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM. + + .PARAMETER Timeout + The maximum amount of time that this function can take to perform DSC start-up. + If the timeout is reached before the process is complete an error will be thrown. + The timeout defaults to 300 seconds. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Start-LabDSC -Lab $Lab -VM $VMs[0] + Starts up Desired State Configuration for the first VM in the Lab c:\mylab\config.xml. + + .OUTPUTS + None. +#> +function Start-LabDSC +{ + [CmdLetBinding()] + param ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [Parameter()] + [System.Int32] + $Timeout = 300 + ) + + $startTime = Get-Date + $session = $null + $complete = $false + $configCopyComplete = $false + $moduleCopyComplete = $false + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + While ((-not $complete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + # Connect to the VM + $session = Connect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + # Failed to connnect to the VM + if (-not $session) + { + $exceptionParameters = @{ + errorId = 'DSCInitializationError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.DSCInitializationError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + + return + } + + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $configCopyComplete)) + { + $copyParameters = @{ + Destination = 'c:\Windows\Setup\Scripts' + ToSession = $session + Force = $true + ErrorAction = 'Stop' + } + + # Connection has been made OK, upload the DSC files + While ((-not $configCopyComplete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) + { + Try + { + Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMMessage ` + -f $VM.Name, 'DSC') + + $null = Copy-Item ` + @copyParameters ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).mof") + + if (Test-Path ` + -Path "$vmLabBuilderFiles\$($VM.ComputerName).meta.mof") + { + $null = Copy-Item ` + @copyParameters ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).meta.mof") + } # If + + $null = Copy-Item ` + @copyParameters ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSC.ps1') + + $null = Copy-Item ` + @copyParameters ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSCDebug.ps1') + + $configCopyComplete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` + -f $VM.Name, 'DSC', $Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # try + } # while + } # if + + # If the copy didn't complete and we're out of time throw an exception + if ((-not $configCopyComplete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $exceptionParameters = @{ + errorId = 'DSCInitializationError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.DSCInitializationError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } # if + + # Upload any required modules to the VM + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and (-not $moduleCopyComplete)) + { + $dscContent = Get-Content ` + -Path $($VM.DSC.ConfigFile) ` + -Raw + [LabDSCModule[]] $dscModules = Get-LabModulesInDSCConfig -DSCConfigContent $dscContent + + # Add the NetworkingDsc DSC Resource because it is always used + $module = [LabDSCModule]::New('NetworkingDsc') + $dscModules += @( $module ) + + foreach ($dscModule in $dscModules) + { + $moduleName = $dscModule.ModuleName + + # Upload all but PSDesiredStateConfiguration because it + # should always exist on client node. + if ($moduleName -ne 'PSDesiredStateConfiguration') + { + try + { + Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMMessage ` + -f $VM.Name, "DSC Module $moduleName") + + $null = Copy-Item ` + -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath "DSC Modules\$moduleName\") ` + -Destination "$($env:ProgramFiles)\WindowsPowerShell\Modules\" ` + -ToSession $session ` + -Force ` + -Recurse ` + -ErrorAction Stop + } + catch + { + Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` + -f $VM.Name, "DSC Module $moduleName", $Script:RetryConnectSeconds) + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # try + } # if + } # foreach + + $moduleCopyComplete = $true + } # if + + # If the copy didn't complete and we're out of time throw an exception + if ((-not $moduleCopyComplete) ` + -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $exceptionParameters = @{ + errorId = 'DSCInitializationError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.DSCInitializationError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } # if + + # Finally, Start DSC up! + if (($session) ` + -and ($session.State -eq 'Opened') ` + -and ($configCopyComplete) ` + -and ($moduleCopyComplete)) + { + Write-LabMessage -Message $($LocalizedData.StartingDSCMessage ` + -f $VM.Name) + + Invoke-Command -Session $session { + c:\windows\setup\scripts\StartDSC.ps1 + } + + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $complete = $true + } # if + } # while +} diff --git a/source/private/Update-LabDSC.ps1 b/source/private/Update-LabDSC.ps1 new file mode 100644 index 00000000..803a283e --- /dev/null +++ b/source/private/Update-LabDSC.ps1 @@ -0,0 +1,413 @@ +<# + .SYNOPSIS + This function prepares all the files and modules necessary for a VM to be configured using + Desired State Configuration (DSC). + + .DESCRIPTION + This funcion performs the following tasks in preparation for starting Desired State + Configuration on a Virtual Machine: + 1. Ensures the folder structure for the Virtual Machine DSC files is available. + 2. Gets a list of all Modules required by the DSC configuration to be applied. + 3. Download and Install any missing DSC modules required for the DSC configuration. + 4. Copy all modules required for the DSC configuration to the VM folder. + 5. Cause a self-signed cetficiate to be created and downloaded on the Lab VM. + 6. Create a Networking DSC configuration file and ensure the DSC config file calss it. + 7. Create the MOF file from the config and an LCM config. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Update-LabDSC -Lab $Lab -VM $VMs[0] + Prepare the first VM in the Lab c:\mylab\config.xml for DSC configuration. + + .OUTPUTS + None. +#> +function Update-LabDSC +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + $Lab, + + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + $dscMOFFile = '' + $dscMOFMetaFile = '' + + # Get Path to LabBuilder files + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + if (-not $VM.DSC.ConfigFile) + { + # This VM doesn't have a DSC Configuration + return + } + + # Make sure all the modules required to create the MOF file are installed + $installedModules = Get-Module -ListAvailable + + Write-LabMessage -Message $($LocalizedData.DSCConfigIdentifyModulesMessage ` + -f $VM.DSC.ConfigFile, $VM.Name) + + $dscConfigContent = Get-Content ` + -Path $($VM.DSC.ConfigFile) ` + -Raw + + [LabDSCModule[]] $dscModules = Get-LabModulesInDSCConfig ` + -DSCConfigContent $dscConfigContent + + # Add the NetworkingDsc DSC Resource because it is always used + $module = [LabDSCModule]::New('NetworkingDsc') + + # It must be 7.0.0.0 or greater + $module.MinimumVersion = [Version] '7.0.0.0' + $dscModules += @( $module ) + + foreach ($dscModule in $dscModules) + { + $moduleName = $dscModule.ModuleName + $moduleParameters = @{ Name = $ModuleName } + $moduleVersion = $dscModule.ModuleVersion + $minimumVersion = $dscModule.MinimumVersion + + if ($moduleVersion) + { + $filterScript = { + ($_.Name -eq $ModuleName) -and ($moduleVersion -eq $_.Version) + } + + $moduleParameters += @{ + RequiredVersion = $moduleVersion + } + } + elseif ($minimumVersion) + { + $filterScript = { + ($_.Name -eq $ModuleName) -and ($_.Version -ge $minimumVersion) + } + + $moduleParameters += @{ + MinimumVersion = $minimumVersion + } + } + else + { + $filterScript = { + $_.Name -eq $ModuleName + } + } + + $module = ($installedModules | + Where-Object -FilterScript $filterScript | + Sort-Object -Property Version -Descending | + Select-Object -First 1) + + if ($module) + { + # The module already exists, load the version number into the Module + # to force the version number to be set in the DSC Config file + $dscModule.ModuleVersion = $module.Version + } + else + { + # The Module isn't available on this computer, so try and install it + Write-LabMessage -Message $($LocalizedData.DSCConfigSearchingForModuleMessage ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) + + $newModule = Find-Module ` + @moduleParameters + + if ($newModule) + { + Write-LabMessage -Message $($LocalizedData.DSCConfigInstallingModuleMessage ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) + + try + { + $newModule | Install-Module + } + catch + { + $exceptionParameters = @{ + errorId = 'DSCModuleDownloadError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCModuleDownloadError ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) + } + New-LabException @exceptionParameters + } + } + else + { + $exceptionParameters = @{ + errorId = 'DSCModuleDownloadError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCModuleDownloadError ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) + } + New-LabException @exceptionParameters + } + + $dscModule.ModuleVersion = $newModule.Version + } # if + + Write-LabMessage -Message $($LocalizedData.DSCConfigSavingModuleMessage ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) + + # Find where the module is actually stored + $modulePath = '' + + foreach ($Path in $ENV:PSModulePath.Split(';')) + { + if (-not [System.String]::IsNullOrEmpty($Path)) + { + $modulePath = Join-Path ` + -Path $Path ` + -ChildPath $ModuleName + + if (Test-Path -Path $modulePath) + { + break + } # If + } + } # Foreach + + if (-not (Test-Path -Path $modulePath)) + { + $exceptionParameters = @{ + errorId = 'DSCModuleNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCModuleNotFoundError ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) + } + New-LabException @exceptionParameters + } + + $destinationPath = Join-Path -Path $vmLabBuilderFiles -ChildPath 'DSC Modules\' + + if (-not (Test-Path -Path $destinationPath)) + { + # Create the DSC Modules folder if it doesn't exist. + $null = New-Item -Path $destinationPath -ItemType Directory -Force + } # if + + Write-LabMessage -Message $($LocalizedData.DSCConfigCopyingModuleMessage ` + -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName, $modulePath, $destinationPath) + Copy-Item ` + -Path $modulePath ` + -Destination $destinationPath ` + -Recurse ` + -Force ` + -ErrorAction Continue + } # Foreach + + if ($VM.CertificateSource -eq [LabCertificateSource]::Guest) + { + # Recreate the certificate if it the source is the Guest + if (-not (Request-LabSelfSignedCertificate -Lab $Lab -VM $VM)) + { + $exceptionParameters = @{ + errorId = 'CertificateCreateError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.CertificateCreateError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } + + # Remove any old self-signed certifcates for this VM + Get-ChildItem -Path cert:\LocalMachine\My | + Where-Object { $_.FriendlyName -eq $Script:DSCCertificateFriendlyName } | + Remove-Item + } # if + + # Add the VM Self-Signed Certificate to the Local Machine store and get the Thumbprint + $certificateFile = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath $Script:DSCEncryptionCert + $certificate = Import-Certificate ` + -FilePath $certificateFile ` + -CertStoreLocation 'Cert:LocalMachine\My' + $certificateThumbprint = $certificate.Thumbprint + + # Set the predicted MOF File name + $dscMOFFile = Join-Path ` + -Path $ENV:Temp ` + -ChildPath "$($VM.ComputerName).mof" + $dscMOFMetaFile = ([System.IO.Path]::ChangeExtension($dscMOFFile, 'meta.mof')) + + # Generate the LCM MOF File + Write-LabMessage -Message $($LocalizedData.DSCConfigCreatingLCMMOFMessage -f $dscMOFMetaFile, $VM.Name) + + $null = ConfigLCM ` + -OutputPath $ENV:Temp ` + -ComputerName $($VM.ComputerName) ` + -Thumbprint $certificateThumbprint + + if (-not (Test-Path -Path $dscMOFMetaFile)) + { + $exceptionParameters = @{ + errorId = 'DSCConfigMetaMOFCreateError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfigMetaMOFCreateError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } # If + + # A DSC Config File was provided so create a MOF File out of it. + Write-LabMessage -Message $($LocalizedData.DSCConfigCreatingMOFMessage -f $VM.DSC.ConfigFile, $VM.Name) + + # Now create the Networking DSC Config file + $dscNetworkingConfig = Get-LabDSCNetworkingConfig ` + -Lab $Lab -VM $VM + $NetworkingDscFile = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath 'DSCNetworking.ps1' + $null = Set-Content ` + -Path $NetworkingDscFile ` + -Value $dscNetworkingConfig + . $NetworkingDscFile + $dscFile = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath 'DSC.ps1' + + # Set the Modules List in the DSC Configuration + $dscConfigContent = Set-LabModulesInDSCConfig ` + -DSCConfigContent $dscConfigContent ` + -Modules $dscModules + + if (-not ($dscConfigContent -match 'Networking Network {}')) + { + # Add the Networking Configuration item to the base DSC Config File + # Find the location of the line containing "Node $AllNodes.NodeName {" + [System.String] $Regex = '\s*Node\s.*{.*' + $Matches = [regex]::matches($dscConfigContent, $Regex, 'IgnoreCase') + + if ($Matches.Count -eq 1) + { + $dscConfigContent = $dscConfigContent.` + Insert($Matches[0].Index + $Matches[0].Length, "`r`nNetworking Network {}`r`n") + } + else + { + $exceptionParameters = @{ + errorId = 'DSCConfigMoreThanOneNodeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfigMoreThanOneNodeError ` + -f $VM.DSC.ConfigFile, $VM.Name) + } + New-LabException @exceptionParameters + } # if + } # if + + # Save the DSC Content + $null = Set-Content ` + -Path $dscFile ` + -Value $dscConfigContent ` + -Force + + # Hook the Networking DSC File into the main DSC File + . $dscFile + + $dscConfigName = $VM.DSC.ConfigName + + Write-LabMessage -Message $($LocalizedData.DSCConfigPrepareMessage -f $dscConfigName, $VM.Name) + + # Generate the Configuration Nodes data that always gets passed to the DSC configuration. + $dscConfigData = @" +@{ + AllNodes = @( + @{ + NodeName = '$($VM.ComputerName)' + CertificateFile = '$certificateFile' + Thumbprint = '$certificateThumbprint' + LocalAdminPassword = '$($VM.administratorpassword)' + $($VM.DSC.Parameters) + } + ) +} +"@ + # Write it to a temp file + $dscConfigFile = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath 'DSCConfigData.psd1' + + if (Test-Path -Path $dscConfigFile) + { + $null = Remove-Item ` + -Path $dscConfigFile ` + -Force + } + + $null = Set-Content -Path $dscConfigFile -Value $dscConfigData + + # Read the config data into a Hash Table + $dscConfigData = Import-LocalizedData -BaseDirectory $vmLabBuilderFiles -FileName 'DSCConfigData.psd1' + + # Generate the MOF file from the configuration + $null = & $dscConfigName ` + -OutputPath $ENV:Temp ` + -ConfigurationData $dscConfigData ` + -ErrorAction Stop + + if (-not (Test-Path -Path $dscMOFFile)) + { + $exceptionParameters = @{ + errorId = 'DSCConfigMOFCreateError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfigMOFCreateError -f $VM.DSC.ConfigFile, $VM.Name) + } + New-LabException @exceptionParameters + } # If + + # Remove the VM Self-Signed Certificate from the Local Machine Store + $null = Remove-Item ` + -Path "Cert:LocalMachine\My\$certificateThumbprint" ` + -Force + + Write-LabMessage -Message $($LocalizedData.DSCConfigMOFCreatedMessage -f $VM.DSC.ConfigFile, $VM.Name) + + # Copy the files to the LabBuilder Files folder + $dscMOFDestinationFile = Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).mof" + $null = Copy-Item ` + -Path $dscMOFFile ` + -Destination $dscMOFDestinationFile ` + -Force + + if (-not $VM.DSC.MOFFile) + { + # Remove Temporary files created by DSC + $null = Remove-Item ` + -Path $dscMOFFile ` + -Force + } + + if (Test-Path -Path $dscMOFMetaFile) + { + $dscMOFMetaDestinationFile = Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).meta.mof" + $null = Copy-Item ` + -Path $dscMOFMetaFile ` + -Destination $dscMOFMetaDestinationFile ` + -Force + + if (-not $VM.DSC.MOFFile) + { + # Remove Temporary files created by DSC + $null = Remove-Item ` + -Path $dscMOFMetaFile ` + -Force + } + } # if +} diff --git a/source/private/Update-LabVMDataDisk.ps1 b/source/private/Update-LabVMDataDisk.ps1 new file mode 100644 index 00000000..a1c4d79f --- /dev/null +++ b/source/private/Update-LabVMDataDisk.ps1 @@ -0,0 +1,397 @@ +<# + .SYNOPSIS + Updates the VM Data Disks to match the VM Configuration. + + .DESCRIPTION + This cmdlet will take the VM configuration provided and ensure that that data disks that are + attached to the VM. + + The function will use the array of items in the DataVHDs property of the VM to create and + attach any data disk VHDs that are missing. + + If the data disk VHD file exists but is not attached it will be attached to the VM. If the + data disk VHD file does not exist then it will be created and attached. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Update-LabVMDataDisk -Lab $Lab -VM VM[0] + This will update the data disks for the first VM in the configuration file c:\mylab\config.xml. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .OUTPUTS + None. +#> +function Update-LabVMDataDisk +{ + [CmdLetBinding()] + param + ( + [Parameter( + Mandatory, + Position=0)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Mandatory, + Position=1)] + [ValidateNotNullOrEmpty()] + [LabVM] + $VM + ) + + # If there are no data VHDs just return + if (-not $VM.DataVHDs) + { + return + } + + # Get the root path of the VM + $vmRootPath = $VM.VMRootPath + + # Get the Virtual Hard Disk Path + $vhdPath = Join-Path ` + -Path $vmRootPath ` + -ChildPath 'Virtual Hard Disks' + + foreach ($dataVhd in @($VM.DataVHDs)) + { + $vhd = $dataVhd.Vhd + if (Test-Path -Path $vhd) + { + Write-LabMessage -Message $($LocalizedData.VMDiskAlreadyExistsMessage ` + -f $VM.Name,$vhd,'Data') + + # Check the parameters of the VHD match + $existingVhd = Get-VHD -Path $vhd + + # Check the VHD Type + if (($dataVhd.VhdType) ` + -and ($existingVhd.VhdType.ToString() -ne $dataVhd.VhdType.ToString())) + { + # The type of disk can't be changed. + $exceptionParameters = @{ + errorId = 'VMDataDiskVHDConvertError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskVHDConvertError ` + -f $VM.name,$vhd,$dataVhd.VhdType) + } + New-LabException @exceptionParameters + } + + # Check the size + if ($dataVhd.Size) + { + if ($existingVhd.Size -lt $dataVhd.Size) + { + # Expand the disk + Write-LabMessage -Message $($LocalizedData.ExpandingVMDiskMessage ` + -f $VM.Name,$vhd,'Data',$dataVhd.Size) + + $null = Resize-VHD ` + -Path $vhd ` + -SizeBytes $dataVhd.Size + } + elseif ($existingVhd.Size -gt $dataVhd.Size) + { + <# + The disk size can't be reduced. + This could be revisited later. + #> + $exceptionParameters = @{ + errorId = 'VMDataDiskVHDShrinkError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskVHDShrinkError ` + -f $VM.name,$vhd,$dataVhd.Size) + } + New-LabException @exceptionParameters + } # if + } # if + } + else + { + # The data disk VHD does not exist so create it + $SourceVhd = $dataVhd.SourceVhd + if ($SourceVhd) + { + # A source VHD was specified to create the new VHD using + if (! (Test-Path -Path $SourceVhd)) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskSourceVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskSourceVHDNotFoundError ` + -f $VM.name,$SourceVhd) + } + New-LabException @exceptionParameters + } # if + + # Should the Source VHD be copied or moved + if ($dataVhd.MoveSourceVHD) + { + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskByMovingSourceVHDMessage ` + -f $VM.Name,$vhd,$SourceVhd) + + $null = Move-Item ` + -Path $SourceVhd ` + -Destination $vhdPath ` + -Force ` + -ErrorAction Stop + } + else + { + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskByCopyingSourceVHDMessage ` + -f $VM.Name,$vhd,$SourceVhd) + + $null = Copy-Item ` + -Path $SourceVhd ` + -Destination $vhdPath ` + -Force ` + -ErrorAction Stop + } # if + } + else + { + $size = $dataVhd.size + + switch ($dataVhd.VhdType) + { + 'fixed' + { + # Create a new Fixed VHD + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` + -f $VM.Name,$vhd,'Fixed Data') + + $null = New-VHD ` + -Path $vhd ` + -SizeBytes $size ` + -Fixed ` + -ErrorAction Stop + break; + } # 'fixed' + + 'dynamic' + { + # Create a new Dynamic VHD + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` + -f $VM.Name,$vhd,'Dynamic Data') + + $null = New-VHD ` + -Path $vhd ` + -SizeBytes $size ` + -Dynamic ` + -ErrorAction Stop + break; + } # 'dynamic' + + 'differencing' + { + <# + A differencing disk is specified so check the Parent VHD + is specified and exists. + #> + $ParentVhd = $dataVhd.ParentVhd + if (-not $ParentVhd) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskParentVHDMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskParentVHDMissingError ` + -f $VM.name) + } + New-LabException @exceptionParameters + } # if + if (-not (Test-Path -Path $ParentVhd)) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskParentVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskParentVHDNotFoundError ` + -f $VM.name,$ParentVhd) + } + New-LabException @exceptionParameters + } # if + + # Create a new Differencing VHD + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` + -f $VM.Name,$vhd,"Differencing Data using Parent '$ParentVhd'") + + $null = New-VHD ` + -Path $vhd ` + -SizeBytes $size ` + -Differencing ` + -ParentPath $ParentVhd ` + -ErrorAction Stop + break; + } # 'differencing' + + default + { + $exceptionParameters = @{ + errorId = 'VMDataDiskUnknownTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskUnknownTypeError ` + -f $VM.Name,$vhd,$dataVhd.VhdType) + } + New-LabException @exceptionParameters + } # default + } # switch + } # if + + # Do folders need to be copied to this Data Disk? + if ($null -ne $dataVhd.CopyFolders) + { + <# + Files need to be copied to this Data VHD so + set up a mount folder for it to be mounted to. + Get Path to LabBuilder files + #> + $vmLabBuilderFiles = $VM.LabBuilderFilesPath + + $mountPoint = Join-Path ` + -Path $vmLabBuilderFiles ` + -ChildPath 'VHDMount' + + if (-not (Test-Path -Path $mountPoint -PathType Container)) + { + $null = New-Item ` + -Path $mountPoint ` + -ItemType Directory + } + + # Yes, initialize the disk (or check it is) + $initializeLabVHDParams = @{ + Path = $vhd + AccessPath = $mountPoint + } + + # Are we allowed to initialize/format the disk? + if ($dataVhd.PartitionStyle -and $dataVhd.FileSystem) + { + # Yes, initialize the disk + $initializeLabVHDParams += @{ + PartitionStyle = $dataVhd.PartitionStyle + FileSystem = $dataVhd.FileSystem + } + + # Set a FileSystemLabel too? + if ($dataVhd.FileSystemLabel) + { + $initializeLabVHDParams += @{ + FileSystemLabel = $dataVhd.FileSystemLabel + } + } + } + + Write-LabMessage -Message $($LocalizedData.InitializingVMDiskMessage ` + -f $VM.Name,$vhd) + + Initialize-LabVHD ` + @initializeLabVHDParams ` + -ErrorAction Stop + + # Copy each folder to the VM Data Disk + foreach ($copyFolder in @($dataVhd.CopyFolders)) + { + Write-LabMessage -Message $($LocalizedData.CopyingFoldersToVMDiskMessage ` + -f $VM.Name,$vhd,$copyFolder) + + Copy-item ` + -Path $copyFolder ` + -Destination $mountPoint ` + -Recurse ` + -Force + } + + # Dismount the VM Data Disk + Write-LabMessage -Message $($LocalizedData.DismountingVMDiskMessage ` + -f $VM.Name,$vhd) + + Dismount-VHD ` + -Path $vhd ` + -ErrorAction Stop + } + else + { + <# + No folders need to be copied but check if we + need to initialize the new disk. + #> + if ($dataVhd.PartitionStyle -and $dataVhd.FileSystem) + { + $InitializeVHDParams = @{ + Path = $vhd + PartitionStyle = $dataVhd.PartitionStyle + FileSystem = $dataVhd.FileSystem + } + + if ($dataVhd.FileSystemLabel) + { + $InitializeVHDParams += @{ + FileSystemLabel = $dataVhd.FileSystemLabel + } + } # if + + Write-LabMessage -Message $($LocalizedData.InitializingVMDiskMessage ` + -f $VM.Name,$vhd) + + Initialize-LabVHD ` + @InitializeVHDParams ` + -ErrorAction Stop + + # Dismount the VM Data Disk + Write-LabMessage -Message $($LocalizedData.DismountingVMDiskMessage ` + -f $VM.Name,$vhd) + + Dismount-VHD ` + -Path $vhd ` + -ErrorAction Stop + } # if + } # if + } # if + + # Get a list of disks attached to the VM + $VMHardDiskDrives = Get-VMHardDiskDrive ` + -VMName $VM.Name + + # The data disk VHD will now exist so ensure it is attached + if (($VMHardDiskDrives | Where-Object -Property Path -eq $vhd).Count -eq 0) + { + # The data disk is not yet attached + Write-LabMessage -Message $($LocalizedData.AddingVMDiskMessage ` + -f $VM.Name,$vhd,'Data') + + <# + Determine the ControllerLocation and ControllerNumber to + attach the VHD to. + #> + $controllerLocation = ($VMHardDiskDrives | + Measure-Object -Property ControllerLocation -Maximum).Maximum + 1 + + $newHardDiskParams = @{ + VMName = $VM.Name + Path = $vhd + ControllerType = 'SCSI' + ControllerLocation = $controllerLocation + ControllerNumber = 0 + ErrorAction = 'Stop' + } + if ($dataVhd.Shared -or $dataVhd.SupportPR) + { + $newHardDiskParams += @{ + SupportPersistentReservations = $true + } + } # if + + Write-Verbose -Message ($newHardDiskParams | Out-String | Fl *) -Verbose + + $null = Add-VMHardDiskDrive @newHardDiskParams + } # if + } # foreach +} diff --git a/source/private/Update-LabVMDvdDrive.ps1 b/source/private/Update-LabVMDvdDrive.ps1 new file mode 100644 index 00000000..53845192 --- /dev/null +++ b/source/private/Update-LabVMDvdDrive.ps1 @@ -0,0 +1,108 @@ +<# + .SYNOPSIS + Updates the VM DVD Drives to match the VM Configuration. + + .DESCRIPTION + This cmdlet will take the VM configuration provided and ensure that the DVD Drives are + attached to the VM and with the specified ISO. + + The function will use the array of items in the DVDDrives property of the VM to create and + attach any DVD Drives that are missing. + + If an ISO File is specified in the DVD Drive then it will be mounted to the DVD Drive. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Update-LabVMDvdDrive -Lab $Lab -VM VM[0] + This will update the DVD Drives for the first VM in the configuration file c:\mylab\config.xml. + + .PARAMETER Lab + Contains the Lab object that was produced by the Get-Lab cmdlet. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .OUTPUTS + None. +#> +function Update-LabVMDvdDrive +{ + [CmdLetBinding()] + param + ( + [Parameter( + Mandatory, + Position=0)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Mandatory, + Position=1)] + [ValidateNotNullOrEmpty()] + [LabVM] + $VM + ) + + # If there are no DVD Drives just return + if (-not $VM.DVDDrives) + { + return + } + + [System.Int32] $DVDDriveCount = 0 + foreach ($DVDDrive in @($VM.DVDDrives)) + { + # Get a list of DVD Drives attached to the VM + $VMDVDDrives = @(Get-VMDVDDrive ` + -VMName $VM.Name) + + # The DVD Drive will now exist so ensure it is attached + if ($VMDVDDrives[$DVDDriveCount]) + { + # The DVD Drive is already attached then make sure the correct ISO + if ($VMDVDDrives[$DVDDriveCount].Path -ne $DVDDrive.Path) + { + if ($DVDDrive.Path) + { + Write-LabMessage -Message $($LocalizedData.MountingVMDVDDriveISOMessage ` + -f $VM.Name,$DVDDrive.Path) + } + else + { + Write-LabMessage -Message $($LocalizedData.DismountingVMDVDDriveISOMessage ` + -f $VM.Name,$VMDVDDrives[$DVDDriveCount].Path) + } # if + Set-VMDVDDrive ` + -VMName $VM.Name ` + -ControllerNumber $VMDVDDrives[$DVDDriveCount].ControllerNumber ` + -ControllerLocation $VMDVDDrives[$DVDDriveCount].ControllerLocation ` + -Path $DVDDrive.Path + } # if + } + else + { + # The DVD Drive does not exist + Write-LabMessage -Message $($LocalizedData.AddingVMDVDDriveMessage ` + -f $VM.Name) + + $NewDVDDriveParams = @{ + VMName = $VM.Name + ErrorAction = 'Stop' + } + + if ($DVDDrive.Path) + { + Write-LabMessage -Message $($LocalizedData.MountingVMDVDDriveISOMessage ` + -f $VM.Name,$DVDDrive.Path) + + $NewDVDDriveParams += @{ + Path = $DVDDrive.Path + } + } # if + $null = Add-VMDVDDrive @NewDVDDriveParams + } # if + $DVDDriveCount++ + } # foreach +} diff --git a/source/private/Update-LabVMIntegrationService.ps1 b/source/private/Update-LabVMIntegrationService.ps1 new file mode 100644 index 00000000..0c3cace5 --- /dev/null +++ b/source/private/Update-LabVMIntegrationService.ps1 @@ -0,0 +1,95 @@ +<# + .SYNOPSIS + Updates the VM Integration Services to match the VM Configuration. + + .DESCRIPTION + This cmdlet will take the VM object provided and ensure the integration services specified + in it are enabled. + + The function will use comma delimited list of integration services in the VM object passed + and enable the integration services listed for this VM. + + If the IntegrationServices property of the VM is not set or set to null then ALL integration + services will be ENABLED. + + If the IntegrationServices property of the VM is set but is blank then ALL integration + services will be DISABLED. + + The IntegrationServices property should contain a comma delimited list of Integration Services + that should be enabled. + + The currently available Integration Services are: + - Guest Service Interface + - Heartbeat + - Key-Value Pair Exchange + - Shutdown + - Time Synchronization + - VSS + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Update-LabVMIntegrationService -VM VM[0] + This will update the Integration Services for the first VM in the configuration file c:\mylab\config.xml. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .OUTPUTS + None. +#> +function Update-LabVMIntegrationService +{ + [CmdLetBinding()] + param + ( + [Parameter( + Mandatory, + Position=1)] + [ValidateNotNullOrEmpty()] + [LabVM] + $VM + ) + + # Configure the Integration services + $integrationServices = $VM.IntegrationServices + if ($null -eq $integrationServices) + { + # Get the full list of Integration Service names localized + $integrationServices = ((Get-LabIntegrationServiceName) -Join ',') + } + + $enabledIntegrationServices = $integrationServices -split ',' + $existingIntegrationServices = Get-VMIntegrationService ` + -VMName $VM.Name ` + -ErrorAction Stop + + # Loop through listed integration services and enable them + foreach ($existingIntegrationService in $existingIntegrationServices) + { + if ($existingIntegrationService.Name -in $enabledIntegrationServices) + { + # This integration service should be enabled + if (-not $existingIntegrationService.Enabled) + { + # It is disabled so enable it + $existingIntegrationService | Enable-VMIntegrationService + + Write-LabMessage -Message $($LocalizedData.EnableVMIntegrationServiceMessage ` + -f $VM.Name,$existingIntegrationService.Name) + } # if + } + else + { + # This integration service should be disabled + if ($existingIntegrationService.Enabled) + { + # It is enabled so disable it + $existingIntegrationService | Disable-VMIntegrationService + + Write-LabMessage -Message $($LocalizedData.DisableVMIntegrationServiceMessage ` + -f $VM.Name,$existingIntegrationService.Name) + } # if + } # if + } # foreach +} diff --git a/source/private/Wait-LabVMInitializationComplete.ps1 b/source/private/Wait-LabVMInitializationComplete.ps1 new file mode 100644 index 00000000..10e593fd --- /dev/null +++ b/source/private/Wait-LabVMInitializationComplete.ps1 @@ -0,0 +1,151 @@ +<# + .SYNOPSIS + Waits for a VM to complete setup. + + .DESCRIPTION + When a VM starts up for the first time various scripts are run that prepare the Virtual Machine + to be managed as part of a Lab. This function will wait for these scripts to complete. + It determines if the setup has been completed by using PowerShell remoting to connect to the + VM and downloading the c:\windows\Setup\Scripts\InitialSetupCompleted.txt file. If this file + does not exist then the initial setup has not been completed. + + The cmdlet will wait for a maximum of 300 seconds for this process to be completed. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .PARAMETER Timeout + The maximum amount of time that this function will wait for the setup to complete. + If the timeout is reached before the process is complete an error will be thrown. + The timeout defaults to 300 seconds. + + .EXAMPLE + $Lab = Get-Lab -ConfigPath c:\mylab\config.xml + $VMs = Get-LabVM -Lab $Lab + Wait-LabVMInitializationComplete -VM $VMs[0] + Waits for the initial setup to complete on the first VM in the config.xml. + + .OUTPUTS + The path to the local copy of the Initial Setup complete file in the Labbuilder files folder + for this VM. +#> +function Wait-LabVMInitializationComplete +{ + [CmdLetBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + [LabVM] + $VM, + + [Parameter()] + [System.Int32] + $Timeout = 300 + ) + + [DateTime] $StartTime = Get-Date + [System.Management.Automation.Runspaces.PSSession] $Session = $null + [System.Boolean] $Complete = $false + + # Get the root path of the VM + [System.String] $VMRootPath = $VM.VMRootPath + + # Get Path to LabBuilder files + [System.String] $VMLabBuilderFiles = $VM.LabBuilderFilesPath + + # Make sure the VM has started + Wait-LabVMStarted -VM $VM + + [System.String] $InitialSetupCompletePath = Join-Path ` + -Path $VMLabBuilderFiles ` + -ChildPath 'InitialSetupCompleted.txt' + + # Check the initial setup on this VM hasn't already completed + if (Test-Path -Path $InitialSetupCompletePath) + { + Write-LabMessage -Message $($LocalizedData.InitialSetupIsAlreadyCompleteMessaage ` + -f $VM.Name) + return $InitialSetupCompletePath + } + + while ((-not $Complete) ` + -and (((Get-Date) - $StartTime).TotalSeconds) -lt $TimeOut) + { + # Connect to the VM + $Session = Connect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + # Failed to connnect to the VM + if (-not $Session) + { + $exceptionParameters = @{ + errorId = 'InitialSetupCompleteError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.InitialSetupCompleteError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + return + } + + if (($Session) ` + -and ($Session.State -eq 'Opened') ` + -and (-not $Complete)) + { + # We connected OK - Download the script + while ((-not $Complete) ` + -and (((Get-Date) - $StartTime).TotalSeconds) -lt $TimeOut) + { + try + { + $null = Copy-Item ` + -Path "c:\windows\Setup\Scripts\InitialSetupCompleted.txt" ` + -Destination $VMLabBuilderFiles ` + -FromSession $Session ` + -Force ` + -ErrorAction Stop + $Complete = $true + } + catch + { + Write-LabMessage -Message $($LocalizedData.WaitingForInitialSetupCompleteMessage ` + -f $VM.Name, $Script:RetryConnectSeconds) + Start-Sleep ` + -Seconds $Script:RetryConnectSeconds + } # try + } # while + } # if + + # If the process didn't complete and we're out of time throw an exception + if ((-not $Complete) ` + -and (((Get-Date) - $StartTime).TotalSeconds) -ge $TimeOut) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + + $exceptionParameters = @{ + errorId = 'InitialSetupCompleteError' + errorCategory = 'OperationTimeout' + errorMessage = $($LocalizedData.InitialSetupCompleteError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } + + # Close the Session if it is opened + if (($Session) ` + -and ($Session.State -eq 'Opened')) + { + # Disconnect from the VM + Disconnect-LabVM ` + -VM $VM ` + -ErrorAction Continue + } # if + } # while + + return $InitialSetupCompletePath +} diff --git a/source/private/Wait-LabVMOff.ps1 b/source/private/Wait-LabVMOff.ps1 new file mode 100644 index 00000000..7e2286e7 --- /dev/null +++ b/source/private/Wait-LabVMOff.ps1 @@ -0,0 +1,27 @@ +<# + .SYNOPSIS + Wait for VM to enter the Off state. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM. + + .OUTPUTS + None. +#> +function Wait-LabVMOff +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + $runningVM = Get-VM -Name $VM.Name + while ($runningVM.State -ne 'Off') + { + $runningVM = Get-VM -Name $VM.Name + Start-Sleep -Seconds $Script:RetryHeartbeatSeconds + } # while +} diff --git a/source/private/Wait-LabVMStarted.ps1 b/source/private/Wait-LabVMStarted.ps1 new file mode 100644 index 00000000..aea90963 --- /dev/null +++ b/source/private/Wait-LabVMStarted.ps1 @@ -0,0 +1,45 @@ +<# + .SYNOPSIS + Wait for the VM to enter the running state. + + .PARAMETER VM + A LabVM object pulled from the Lab Configuration file using Get-LabVM + + .OUTPUTS + None. +#> +function Wait-LabVMStarted +{ + [CmdLetBinding()] + param + ( + [Parameter(Mandatory = $true)] + [LabVM] + $VM + ) + + # If the VM is not running then throw an exception + if ((Get-VM -VMName $VM.Name).State -ne 'Running') { + $exceptionParameters = @{ + errorId = 'VMNotRunningHeartbeatMessage' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMNotRunningHeartbeatMessage ` + -f $VM.name) + } + New-LabException @exceptionParameters + } # if + + # Names of IntegrationServices are not culture neutral, but have an ID + $heartbeatCultureNeutral = ( Get-VMIntegrationService -VMName $VM.Name | Where-Object { $_.ID -match "84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47" } ).Name + $heartbeat = Get-VMIntegrationService -VMName $VM.Name -Name $heartbeatCultureNeutral + + while (($heartbeat.PrimaryStatusDescription -ne 'OK') -and (-not [System.String]::IsNullOrEmpty($heartbeat.PrimaryStatusDescription))) + { + $heartbeat = Get-VMIntegrationService -VMName $VM.Name -Name $heartbeatCultureNeutral + + Write-LabMessage -Message $($LocalizedData.WaitingForVMHeartbeatMessage ` + -f $VM.Name,$Script:RetryHeartbeatSeconds) + + Start-Sleep -Seconds $Script:RetryHeartbeatSeconds + } # while +} diff --git a/source/private/Write-LabMessage.ps1 b/source/private/Write-LabMessage.ps1 new file mode 100644 index 00000000..09ff436f --- /dev/null +++ b/source/private/Write-LabMessage.ps1 @@ -0,0 +1,93 @@ +<# + .SYNOPSIS + Writes a Message of the specified Type. + + .DESCRIPTION + This cmdlet will write a message along with the time to the specified output stream. + + .PARAMETER Type + This can be one of the following: + Error - Writes to the Error Stream. + Warning - Writes to the Warning Stream. + Verbose - Writes to the Verbose Stream (default) + Debug - Writes to the Debug Stream. + Information - Writes to the Information Stream. + Output - Writes to the Output Stream (so should be used for a terminating message) + + .PARAMETER Message + The Message to output. + + .PARAMETER ForegroundColor + The foreground color of the message if being writen to the output stream. + + .EXAMPLE + Write-LabMessage -Type Verbose -Message 'Downloading file' + New-LabException @exceptionParameters + Outputs the message 'Downloading file' to the Verbose stream. + + .OUTPUTS + None +#> +function Write-LabMessage +{ + [CmdLetBinding()] + param + ( + [Parameter()] + [ValidateSet('Error', 'Warning', 'Verbose', 'Debug', 'Info', 'Alert')] + [System.String] + $Type = 'Verbose', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [System.String] + $ForegroundColor = 'Yellow' + ) + + $time = Get-Date -UFormat %T + + switch ($Type) + { + 'Error' + { + Write-Error -Message $Message + break + } + + 'Warning' + { + Write-Warning -Message ('[{0}]: {1}' -f $time, $Message) + break + } + + 'Verbose' + { + Write-Verbose -Message ('[{0}]: {1}' -f $time, $Message) + break + } + + 'Debug' + { + Write-Debug -Message ('[{0}]: {1}' -f $time, $Message) + break + } + + 'Info' + { + Write-Information -MessageData ('INFO: [{0}]: {1}' -f $time, $Message) + break + } + + 'Alert' + { + Write-Host ` + -ForegroundColor $ForegroundColor ` + -Object $Message + break + } + } # switch +} diff --git a/source/public/Connect-LabVm.ps1 b/source/public/Connect-LabVm.ps1 new file mode 100644 index 00000000..293b303b --- /dev/null +++ b/source/public/Connect-LabVm.ps1 @@ -0,0 +1,118 @@ +function Connect-LabVM +{ + [OutputType([System.Management.Automation.Runspaces.PSSession])] + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [LabVM] + $VM, + + [Parameter( + Position = 2)] + [System.Int32] + $ConnectTimeout = 300 + ) + + $startTime = Get-Date + $session = $null + $adminCredential = New-LabCredential ` + -Username '.\Administrator' ` + -Password $VM.AdministratorPassword + $fatalException = $false + + while (($null -eq $session) ` + -and (((Get-Date) - $startTime).TotalSeconds) -lt $ConnectTimeout ` + -and -not $fatalException) + { + try + { + <# + Get the Management IP Address of the VM + We repeat this because the IP Address will only be assiged + once the VM is fully booted. + #> + $ipAddress = Get-LabVMManagementIPAddress ` + -Lab $Lab ` + -VM $VM + + <# + Add the IP Address to trusted hosts if not already in it + This could be avoided if able to use SSL or if PS Direct is used. + Also, don't add if TrustedHosts is already * + #> + $trustedHosts = (Get-Item -Path WSMAN::localhost\Client\TrustedHosts).Value + + if (($trustedHosts -notlike "*$ipAddress*") -and ($trustedHosts -ne '*')) + { + if ([System.String]::IsNullOrWhitespace($trustedHosts)) + { + $trustedHosts = "$ipAddress" + } + else + { + $trustedHosts = "$trustedHosts,$ipAddress" + } + + Set-Item ` + -Path WSMAN::localhost\Client\TrustedHosts ` + -Value $trustedHosts ` + -Force + Write-LabMessage -Message $($LocalizedData.AddingIPAddressToTrustedHostsMessage ` + -f $VM.Name, $ipAddress) + } + + if (Test-WSMan -ComputerName $ipAddress -ErrorAction SilentlyContinue) + { + Write-LabMessage -Message $($LocalizedData.ConnectingVMMessage ` + -f $VM.Name, $ipAddress) + + $session = New-PSSession ` + -Name 'LabBuilder' ` + -ComputerName $ipAddress ` + -Credential $adminCredential ` + -ErrorAction Stop + } + else + { + Write-LabMessage -Message $($LocalizedData.WaitingForIPAddressAssignedMessage ` + -f $VM.Name, $Script:RetryConnectSeconds) + } + } + catch + { + if (-not $ipAddress) + { + Write-LabMessage -Message $($LocalizedData.WaitingForIPAddressAssignedMessage ` + -f $VM.Name, $Script:RetryConnectSeconds) + } + else + { + Write-LabMessage -Message $($LocalizedData.ConnectingVMFailedMessage ` + -f $VM.Name, $Script:RetryConnectSeconds, $_.Exception.Message) + } + + Start-Sleep -Seconds $Script:RetryConnectSeconds + } # Try + } # While + + <# + If a fatal exception occured or the connection just couldn't be established + then throw an exception so it can be caught by the calling code. + #> + if ($fatalException -or ($null -eq $session)) + { + # The connection failed so throw an error + $exceptionParameters = @{ + errorId = 'RemotingConnectionError' + errorCategory = 'ConnectionError' + errorMessage = $($LocalizedData.RemotingConnectionError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } + + return $session +} # Connect-LabVM diff --git a/source/public/Disconnect-LabVm.ps1 b/source/public/Disconnect-LabVm.ps1 new file mode 100644 index 00000000..01d72d4f --- /dev/null +++ b/source/public/Disconnect-LabVm.ps1 @@ -0,0 +1,73 @@ +function Disconnect-LabVM +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position=1, + Mandatory=$true)] + [LabVM] $VM + ) + + $adminCredential = New-LabCredential ` + -Username '.\Administrator' ` + -Password $VM.AdministratorPassword + + # Get the Management IP Address of the VM + $ipAddress = Get-LabVMManagementIPAddress ` + -Lab $Lab ` + -VM $VM + + try + { + # Look for the session + $session = Get-PSSession ` + -Name 'LabBuilder' ` + -ComputerName $ipAddress ` + -Credential $adminCredential ` + -ErrorAction Stop + + if (-not $session) + { + # No session found to this machine so nothing to do. + Write-LabMessage -Message $($LocalizedData.VMSessionDoesNotExistMessage ` + -f $VM.Name) + } + else + { + if ($session.State -eq 'Opened') + { + # Disconnect the session + $null = $session | Disconnect-PSSession + Write-LabMessage -Message $($LocalizedData.DisconnectingVMMessage ` + -f $VM.Name,$IPAddress) + } + # Remove the session + $null = $session | Remove-PSSession -ErrorAction SilentlyContinue + } + } + catch + { + Throw $_ + } + finally + { + # Remove the entry from TrustedHosts + $trustedHosts = (Get-Item -Path WSMAN::localhost\Client\TrustedHosts).Value + + if (($trustedHosts -like "*$ipAddress*") -and ($trustedHosts -ne '*')) + { + $ipAddresses = @($trustedHosts -split ',') + $trustedHosts = ($ipAddresses | Where-Object -FilterScript { + $_ -ne $ipAddress + }) -join ',' + + Set-Item ` + -Path WSMAN::localhost\Client\TrustedHosts ` + -Value $trustedHosts ` + -Force + Write-LabMessage -Message $($LocalizedData.RemovingIPAddressFromTrustedHostsMessage ` + -f $VM.Name,$ipAddress) + } + } # try +} # Disconnect-LabVM diff --git a/source/public/Get-Lab.ps1 b/source/public/Get-Lab.ps1 new file mode 100644 index 00000000..b03a048b --- /dev/null +++ b/source/public/Get-Lab.ps1 @@ -0,0 +1,173 @@ +function Get-Lab +{ + [CmdLetBinding()] + [OutputType([XML])] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ConfigPath, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String] + $labPath, + + [Parameter( + Position = 3)] + [Switch] + $SkipXMLValidation + ) + + <# + If a relative path to the config has been specified + then convert it to absolute path + #> + if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) + { + $ConfigPath = Join-Path ` + -Path (Get-Location).Path ` + -ChildPath $ConfigPath + } # if + + if (-not (Test-Path -Path $ConfigPath)) + { + $exceptionParameters = @{ + errorId = 'ConfigurationFileNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ConfigurationFileNotFoundError ` + -f $ConfigPath) + } + New-LabException @exceptionParameters + } # if + + $content = Get-Content -Path $ConfigPath -Raw + + if (-not $content) + { + $exceptionParameters = @{ + errorId = 'ConfigurationFileEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ConfigurationFileEmptyError ` + -f $ConfigPath) + } + New-LabException @exceptionParameters + } # if + + if (-not $SkipXMLValidation) + { + # Validate the XML + Assert-LabValidConfigurationXMLSchema ` + -ConfigPath $ConfigPath ` + -ErrorAction Stop + } + + # The XML passes the Schema check so load it. + $lab = New-Object -TypeName System.Xml.XmlDocument + $lab.PreserveWhitespace = $true + $lab.LoadXML($content) + + # Check the Required Windows Build + $requiredWindowsBuild = $lab.labbuilderconfig.settings.requiredwindowsbuild + + if ($requiredWindowsBuild -and ` + ($Script:CurrentBuild -lt $requiredWindowsBuild)) + { + $exceptionParameters = @{ + errorId = 'RequiredBuildNotMetError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.RequiredBuildNotMetError ` + -f $Script:CurrentBuild, $requiredWindowsBuild) + } + New-LabException @exceptionParameters + } # if + + <# + Figure out the Config path and load it into the XML object (if we can) + This path is used to find any additional configuration files that might + be provided with config + #> + [System.String] $ConfigPath = [System.IO.Path]::GetDirectoryName($ConfigPath) + [System.String] $xmlConfigPath = $lab.labbuilderconfig.settings.configpath + + if ($xmlConfigPath) + { + $xmlConfigPath = ConvertTo-LabAbsolutePath -Path $xmlConfigPath -BasePath $labPath + } + else + { + [System.String] $fullConfigPath = $ConfigPath + } + + $lab.labbuilderconfig.settings.setattribute('fullconfigpath', $fullConfigPath) + + # if the LabPath was passed as a parameter, set it in the config + if ($labPath) + { + $lab.labbuilderconfig.settings.SetAttribute('labpath', $labPath) + } + else + { + [System.String] $labPath = $lab.labbuilderconfig.settings.labpath + } + + # Get the VHDParentPathFull - if it isn't supplied default + [System.String] $vhdParentPath = $lab.labbuilderconfig.settings.vhdparentpath + + if (-not $vhdParentPath) + { + $vhdParentPath = 'Virtual Hard Disk Templates' + } + + # if the resulting parent path is not rooted make the root the Lab Path + $vhdParentPath = ConvertTo-LabAbsolutePath -Path $vhdParentPath -BasePath $labPath + $lab.labbuilderconfig.settings.setattribute('vhdparentpathfull', $vhdParentPath) + + # Get the DSCLibraryPathFull - if it isn't supplied default + [System.String] $dscLibraryPath = $lab.labbuilderconfig.settings.dsclibrarypath + + if (-not $dscLibraryPath) + { + $dscLibraryPath = Get-LabBuilderModulePath | Join-Path -ChildPath 'dsclibrary' + } # if + + # if the resulting parent path is not rooted make the root the Full config path + $dscLibraryPath = ConvertTo-LabAbsolutePath -Path $dscLibraryPath -BasePath $labPath + $lab.labbuilderconfig.settings.setattribute('dsclibrarypathfull', $dscLibraryPath) + + # Get the ResourcePathFull - if it isn't supplied default + [System.String] $resourcePath = $lab.labbuilderconfig.settings.resourcepath + + if (-not $resourcePath) + { + $resourcePath = 'Resource' + } # if + + # if the resulting Resource path is not rooted make the root the Lab Path + $resourcePath = ConvertTo-LabAbsolutePath -Path $resourcePath -BasePath $labPath + $lab.labbuilderconfig.settings.setattribute('resourcepathfull', $resourcePath) + + <# + Determine the ModulePath where alternate Lab PowerShell Modules can be found. + If a path is specified but it is relative, make it relative to the lab path. + Otherwise use it as is. + #> + [System.String] $modulePath = $lab.labbuilderconfig.settings.modulepath + + if ($modulePath) + { + $modulePath = ConvertTo-LabAbsolutePath -Path $modulePath -BasePath $labPath + + # If the path is not included in the PSModulePath add it + if (-not $env:PSModulePath.ToLower().Contains($modulePath.ToLower() + ';')) + { + $env:PSModulePath = "$modulePath;" + $env:PSModulePath + } # if + } # if + + return $lab +} # Get-Lab diff --git a/source/public/Get-LabResourceIso.ps1 b/source/public/Get-LabResourceIso.ps1 new file mode 100644 index 00000000..228b4cd3 --- /dev/null +++ b/source/public/Get-LabResourceIso.ps1 @@ -0,0 +1,92 @@ +function Get-LabResourceISO +{ + [OutputType([LabResourceISO[]])] + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] + $Name + ) + + <# + Determine the ISORootPath where the ISO files should be found. + If no path is specified then look in the resource path. + If a path is specified but it is relative, make it relative to the resource path. + Otherwise use it as is. + #> + [System.String] $isoRootPath = $Lab.labbuilderconfig.Resources.ISOPath + + if ($isoRootPath) + { + $isoRootPath = ConvertTo-LabAbsolutePath -Path $isoRootPath ` + -BasePath $Lab.labbuilderconfig.settings.resourcepathfull + } + else + { + $isoRootPath = $Lab.labbuilderconfig.settings.resourcepathfull + } # if + + [LabResourceISO[]] $resourceISOs = @() + + if ($Lab.labbuilderconfig.resources) + { + foreach ($iso in $Lab.labbuilderconfig.resources.iso) + { + $isoName = $iso.Name + + if ($Name -and ($isoName -notin $Name)) + { + # A names list was passed but this ISO wasn't included + continue + } # if + + if ($isoName -eq 'iso') + { + $exceptionParameters = @{ + errorId = 'ResourceISONameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ResourceISONameIsEmptyError) + } + New-LabException @exceptionParameters + } # if + + $resourceISO = [LabResourceISO]::New($isoName) + $path = $iso.Path + + if ($path) + { + $path = ConvertTo-LabAbsolutePath -Path $path -BasePath $isoRootPath + } + else + { + # A Path is not provided + $exceptionParameters = @{ + errorId = 'ResourceISOPathIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ResourceISOPathIsEmptyError ` + -f $isoName) + } + New-LabException @exceptionParameters + } + + if ($iso.URL) + { + $resourceISO.URL = $iso.URL + } # if + + $resourceISO.Path = $path + $resourceISOs += @( $resourceISO ) + } # foreach + } # if + + return $resourceISOs +} # Get-LabResourceISO diff --git a/source/public/Get-LabResourceModule.ps1 b/source/public/Get-LabResourceModule.ps1 new file mode 100644 index 00000000..774d548f --- /dev/null +++ b/source/public/Get-LabResourceModule.ps1 @@ -0,0 +1,49 @@ +function Get-LabResourceModule +{ + [OutputType([LabResourceModule[]])] + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name + ) + + [LabResourceModule[]] $ResourceModules = @() + if ($Lab.labbuilderconfig.resources) + { + foreach ($Module in $Lab.labbuilderconfig.resources.module) + { + $ModuleName = $Module.Name + if ($Name -and ($ModuleName -notin $Name)) + { + # A names list was passed but this Module wasn't included + continue + } # if + + if ($ModuleName -eq 'module') + { + $exceptionParameters = @{ + errorId = 'ResourceModuleNameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ResourceModuleNameIsEmptyError) + } + New-LabException @exceptionParameters + } # if + $ResourceModule = [LabResourceModule]::New($ModuleName) + $ResourceModule.URL = $Module.URL + $ResourceModule.Folder = $Module.Folder + $ResourceModule.MinimumVersion = $Module.MinimumVersion + $ResourceModule.RequiredVersion = $Module.RequiredVersion + $ResourceModules += @( $ResourceModule ) + } # foreach + } # if + return $ResourceModules +} # Get-LabResourceModule diff --git a/source/public/Get-LabResourceMsu.ps1 b/source/public/Get-LabResourceMsu.ps1 new file mode 100644 index 00000000..f2073f05 --- /dev/null +++ b/source/public/Get-LabResourceMsu.ps1 @@ -0,0 +1,64 @@ +function Get-LabResourceMSU +{ + [OutputType([LabResourceMSU[]])] + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name + ) + + [LabResourceMSU[]] $ResourceMSUs = @() + if ($Lab.labbuilderconfig.resources) + { + foreach ($MSU in $Lab.labbuilderconfig.resources.msu) + { + $MSUName = $MSU.Name + if ($Name -and ($MSUName -notin $Name)) + { + # A names list was passed but this MSU wasn't included + continue + } # if + + if ($MSUName -eq 'msu') + { + $exceptionParameters = @{ + errorId = 'ResourceMSUNameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ResourceMSUNameIsEmptyError) + } + New-LabException @exceptionParameters + } # if + $ResourceMSU = [LabResourceMSU]::New($MSUName, $MSU.URL) + $Path = $MSU.Path + if ($Path) + { + if (-not [System.IO.Path]::IsPathRooted($Path)) + { + $Path = Join-Path ` + -Path $Lab.labbuilderconfig.settings.resourcepathfull ` + -ChildPath $Path + } + } + else + { + $Path = $Lab.labbuilderconfig.settings.resourcepathfull + } + $FileName = Join-Path ` + -Path $Path ` + -ChildPath $MSU.URL.Substring($MSU.URL.LastIndexOf('/') + 1) + $ResourceMSU.Path = $Path + $ResourceMSU.Filename = $Filename + $ResourceMSUs += @( $ResourceMSU ) + } # foreach + } # if + return $ResourceMSUs +} # Get-LabResourceMSU diff --git a/source/public/Get-LabSwitch.ps1 b/source/public/Get-LabSwitch.ps1 new file mode 100644 index 00000000..966232f9 --- /dev/null +++ b/source/public/Get-LabSwitch.ps1 @@ -0,0 +1,116 @@ +function Get-LabSwitch +{ + [OutputType([LabSwitch[]])] + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name + ) + + [System.String] $LabId = $Lab.labbuilderconfig.settings.labid + [LabSwitch[]] $Switches = @() + $ConfigSwitches = $Lab.labbuilderconfig.Switches.Switch + + foreach ($ConfigSwitch in $ConfigSwitches) + { + # It can't be switch because if the name attrib/node is missing the name property on the + # XML object defaults to the name of the parent. So we can't easily tell if no name was + # specified or if they actually specified 'switch' as the name. + $SwitchName = $ConfigSwitch.Name + if ($Name -and ($SwitchName -notin $Name)) + { + # A names list was passed but this swtich wasn't included + continue + } # if + + if ($SwitchName -eq 'switch') + { + $exceptionParameters = @{ + errorId = 'SwitchNameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.SwitchNameIsEmptyError) + } + New-LabException @exceptionParameters + } + + # Convert the switch type string to a LabSwitchType + $SwitchType = [LabSwitchType]::$($ConfigSwitch.Type) + + # If the SwitchType string doesn't match any enum value it will be + # set to null. + if (-not $SwitchType) + { + $exceptionParameters = @{ + errorId = 'UnknownSwitchTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.UnknownSwitchTypeError ` + -f $ConfigSwitch.Type, $SwitchName) + } + New-LabException @exceptionParameters + } # if + + # if a LabId is set for the lab, prepend it to the Switch name as long as it isn't + # an external switch. + if ($LabId -and ($SwitchType -ne [LabSwitchType]::External)) + { + $SwitchName = "$LabId$SwitchName" + } # if + + # Assemble the list of Mangement OS Adapters if any are specified for this switch + # Only Intenal and External switches are allowed Management OS adapters. + if ($ConfigSwitch.Adapters) + { + [LabSwitchAdapter[]] $ConfigAdapters = @() + foreach ($Adapter in $ConfigSwitch.Adapters.Adapter) + { + $AdapterName = $Adapter.Name + # if a LabId is set for the lab, prepend it to the adapter name. + # But only if it is not an External switch. + if ($LabId -and ($SwitchType -ne [LabSwitchType]::External)) + { + $AdapterName = "$LabId$AdapterName" + } + + $ConfigAdapter = [LabSwitchAdapter]::New($AdapterName) + $ConfigAdapter.MACAddress = $Adapter.MacAddress + $ConfigAdapters += @( $ConfigAdapter ) + } # foreach + if (($ConfigAdapters.Count -gt 0) ` + -and ($SwitchType -notin [LabSwitchType]::External, [LabSwitchType]::Internal)) + { + $exceptionParameters = @{ + errorId = 'AdapterSpecifiedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.AdapterSpecifiedError ` + -f $SwitchType, $SwitchName) + } + New-LabException @exceptionParameters + } # if + } + else + { + $ConfigAdapters = $null + } # if + + # Create the new Switch object + [LabSwitch] $NewSwitch = [LabSwitch]::New($SwitchName, $SwitchType) + $NewSwitch.VLAN = $ConfigSwitch.VLan + $NewSwitch.BindingAdapterName = $ConfigSwitch.BindingAdapterName + $NewSwitch.BindingAdapterMac = $ConfigSwitch.BindingAdapterMac + $NewSwitch.BindingAdapterMac = $ConfigSwitch.BindingAdapterMac + $NewSwitch.NatSubnet = $ConfigSwitch.NatSubnet + $NewSwitch.NatGatewayAddress = $ConfigSwitch.NatGatewayAddress + $NewSwitch.Adapters = $ConfigAdapters + $Switches += @( $NewSwitch ) + } # foreach + return $Switches +} # Get-LabSwitch diff --git a/source/public/Get-LabVMTemplate.ps1 b/source/public/Get-LabVMTemplate.ps1 new file mode 100644 index 00000000..a16aae98 --- /dev/null +++ b/source/public/Get-LabVMTemplate.ps1 @@ -0,0 +1,296 @@ +function Get-LabVMTemplate +{ + [OutputType([LabVMTemplate[]])] + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabVMTemplateVHD[]] $VMTemplateVHDs + ) + + # if VMTeplateVHDs array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMTemplateVHDs')) + { + [LabVMTemplateVHD[]] $VMTemplateVHDs = Get-LabVMTemplateVHD ` + -Lab $Lab + } # if + + [LabVMTemplate[]] $VMTemplates = @() + [System.String] $VHDParentPath = $Lab.labbuilderconfig.settings.vhdparentpathfull + + # Get a list of all templates in the Hyper-V system matching the phrase found in the fromvm + # config setting + [System.String] $FromVM = $Lab.labbuilderconfig.templates.fromvm + if ($FromVM) + { + $Templates = @(Get-VM -Name $FromVM) + foreach ($Template in $Templates) + { + if ($Name -and ($Template.Name -notin $Name)) + { + # A names list was passed but this VM Template wasn't included + continue + } # if + + [System.String] $VHDFilepath = (Get-VMHardDiskDrive -VMName $Template.Name).Path + [System.String] $VHDFilename = [System.IO.Path]::GetFileName($VHDFilepath) + [LabVMTemplate] $VMTemplate = [LabVMTemplate]::New($Template.Name) + $VMTemplate.Vhd = $VHDFilename + $VMTemplate.SourceVhd = $VHDFilepath + $VMTemplate.ParentVhd = (Join-Path -Path $VHDParentPath -ChildPath $VHDFilename) + $VMTemplates += @( $VMTemplate ) + } # foreach + } # if + + # Read the list of templates from the configuration file + $Templates = $Lab.labbuilderconfig.templates.template + foreach ($Template in $Templates) + { + # It can't be template because if the name attrib/node is missing the name property on + # the XML object defaults to the name of the parent. So we can't easily tell if no name + # was specified or if they actually specified 'template' as the name. + $TemplateName = $Template.Name + if ($Name -and ($TemplateName -notin $Name)) + { + # A names list was passed but this VM Template wasn't included + continue + } # if + + if ($TemplateName -eq 'template') + { + $exceptionParameters = @{ + errorId = 'EmptyTemplateNameError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.EmptyTemplateNameError) + } + New-LabException @exceptionParameters + } # if + + # Does the template already exist in the list? + [System.Boolean] $Found = $false + foreach ($VMTemplate in $VMTemplates) + { + if ($VMTemplate.Name -eq $TemplateName) + { + # The template already exists - so don't add it again + $Found = $true + Break + } # if + } # foreach + if (-not $Found) + { + # The template wasn't found in the list of templates so add it + $VMTemplate = [LabVMTemplate]::New($TemplateName) + # Add the new Template to the Templates Array + $VMTemplates += @( $VMTemplate ) + } # if + + # Determine the Source VHD, Template VHD and VHD + [System.String] $SourceVHD = $Template.SourceVHD + [System.String] $TemplateVHD = $Template.TemplateVHD + + # Throw an error if both a TemplateVHD and SourceVHD are provided + if ($TemplateVHD -and $SourceVHD) + { + $exceptionParameters = @{ + errorId = 'TemplateSourceVHDAndTemplateVHDConflictError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.TemplateSourceVHDAndTemplateVHDConflictError ` + -f $TemplateName) + } + New-LabException @exceptionParameters + } # if + + if ($TemplateVHD) + { + # A TemplateVHD was provided so look it up. + $VMTemplateVHD = ` + $VMTemplateVHDs | Where-Object -Property Name -EQ $TemplateVHD + if ($VMTemplateVHD) + { + # The TemplateVHD was found + $VMTemplate.Sourcevhd = $VMTemplateVHD.VHDPath + + # if a VHD filename wasn't specified in the TemplateVHD + # Just use the leaf of the SourceVHD + if ($VMTemplateVHD.VHD) + { + $VMTemplate.Vhd = $VMTemplateVHD.VHD + } + else + { + $VMTemplate.Vhd = Split-Path ` + -Path $VMTemplate.sourcevhd ` + -Leaf + } # if + } + else + { + # The TemplateVHD could not be found in the list + $exceptionParameters = @{ + errorId = 'TemplateTemplateVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.TemplateTemplateVHDNotFoundError ` + -f $TemplateName, $TemplateVHD) + } + New-LabException @exceptionParameters + } # if + } + elseif ($SourceVHD) + { + # A Source VHD was provided so use that. + # if this is a relative path, add it to the config path + if ([System.IO.Path]::IsPathRooted($SourceVHD)) + { + $VMTemplate.SourceVhd = $SourceVHD + } + else + { + $VMTemplate.SourceVhd = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $SourceVHD + } + + # A Source VHD file was specified - does it exist? + if (-not (Test-Path -Path $VMTemplate.sourcevhd)) + { + $exceptionParameters = @{ + errorId = 'TemplateSourceVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.TemplateSourceVHDNotFoundError ` + -f $TemplateName, $VMTemplate.sourcevhd) + } + New-LabException @exceptionParameters + } # if + + # if a VHD filename wasn't specified in the Template + # Just use the leaf of the SourceVHD + if ($Template.VHD) + { + $VMTemplate.vhd = $Template.VHD + } + else + { + $VMTemplate.vhd = Split-Path ` + -Path $VMTemplate.sourcevhd ` + -Leaf + } # if + } + elseif ($VMTemplate.SourceVHD) + { + # A SourceVHD is already set + # Usually because it was pulled From a Hyper-V VM template. + } + else + { + # Neither a SourceVHD or TemplateVHD was provided + # So throw an exception + $exceptionParameters = @{ + errorId = 'TemplateSourceVHDandTemplateVHDMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.TemplateSourceVHDandTemplateVHDMissingError ` + -f $TemplateName) + } + New-LabException @exceptionParameters + } # if + + # Ensure the ParentVHD is up-to-date + $VMTemplate.parentvhd = Join-Path ` + -Path $VHDParentPath ` + -ChildPath ([System.IO.Path]::GetFileName($VMTemplate.vhd)) + + # Write any template specific default VM attributes + [System.Int64] $MemoryStartupBytes = 1GB + if ($Template.MemoryStartupBytes) + { + $MemoryStartupBytes = (Invoke-Expression $Template.MemoryStartupBytes) + } # if + if ($MemoryStartupBytes -gt 0) + { + $VMTemplate.memorystartupbytes = $MemoryStartupBytes + } # if + if ($Template.DynamicMemoryEnabled) + { + $VMTemplate.DynamicMemoryEnabled = ($Template.DynamicMemoryEnabled -eq 'Y') + } + elseif (-not $VMTemplate.DynamicMemoryEnabled) + { + $VMTemplate.DynamicMemoryEnabled = $true + } # if + if ($Template.version) + { + $VMTemplate.version = $Template.version + } + elseif (-not $Template.version) + { + $VMTemplate.version = "8.0" + } # if + if ($Template.generation) + { + $VMTemplate.generation = $Template.generation + } + elseif (-not $Template.generation) + { + $VMTemplate.generation = 2 + } # if + + if ($Template.ProcessorCount) + { + $VMTemplate.ProcessorCount = $Template.ProcessorCount + } # if + if ($Template.ExposeVirtualizationExtensions) + { + $VMTemplate.ExposeVirtualizationExtensions = ($Template.ExposeVirtualizationExtensions -eq 'Y') + } # if + if ($Template.AdministratorPassword) + { + $VMTemplate.AdministratorPassword = $Template.AdministratorPassword + } # if + if ($Template.ProductKey) + { + $VMTemplate.ProductKey = $Template.ProductKey + } # if + if ($Template.TimeZone) + { + $VMTemplate.TimeZone = $Template.TimeZone + } # if + + if ($Template.OSType) + { + $VMTemplate.OSType = [LabOSType]::$($Template.OSType) + } + elseif (-not $VMTemplate.OSType) + { + $VMTemplate.OSType = [LabOStype]::Server + } # if + if ($Template.IntegrationServices) + { + $VMTemplate.IntegrationServices = $Template.IntegrationServices + } + else + { + $VMTemplate.IntegrationServices = $null + } # if + if ($Template.Packages) + { + $VMTemplate.Packages = $Template.Packages + } + else + { + $VMTemplate.Packages = $null + } # if + } # foreach + Return $VMTemplates +} # Get-LabVMTemplate diff --git a/source/public/Get-LabVm.ps1 b/source/public/Get-LabVm.ps1 new file mode 100644 index 00000000..2513b00c --- /dev/null +++ b/source/public/Get-LabVm.ps1 @@ -0,0 +1,989 @@ +function Get-LabVM +{ + [OutputType([LabVM[]])] + [CmdLetBinding()] + param ( + [Parameter( + Position=1, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position=2)] + [ValidateNotNullOrEmpty()] + [System.String[]] + $Name, + + [Parameter( + Position=3)] + [LabVMTemplate[]] + $vmTemplates, + + [Parameter( + Position=4)] + [LabSwitch[]] + $switches + ) + + # If VMTeplates array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMTemplates')) + { + [LabVMTemplate[]] $vmTemplates = Get-LabVMTemplate ` + -Lab $Lab + } + + # If Switches array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('Switches')) + { + [LabSwitch[]] $switches = Get-LabSwitch ` + -Lab $Lab + } + + [LabVM[]] $labVMs = @() + [System.String] $labPath = $Lab.labbuilderconfig.settings.labpath + [System.String] $labId = $Lab.labbuilderconfig.settings.labid + $vms = $Lab.labbuilderconfig.vms.vm + + foreach ($vm in $vms) + { + if ($vm.Name -eq 'VM') + { + $exceptionParameters = @{ + errorId = 'VMNameError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMNameError) + } + New-LabException @exceptionParameters + } # if + + # Get the Instance Count attribute + $instanceCount = $vm.InstanceCount + + if (-not $instanceCount) + { + $instanceCount = 1 + } + + foreach ($instance in 1..$instanceCount) + { + # If InstanceCount is 1 then don't increment the IP or MAC addresses or append count to the name + if ($instanceCount -eq 1) + { + $vmName = $vm.Name + $computerName = $vm.ComputerName + $incNetIds = 0 + } + else + { + $vmName = "$($vm.Name)$instance" + $computerName = "$($vm.ComputerName)$instance" + # This value is used to increment IP and MAC addresses + $incNetIds = $instance - 1 + } # if + + if ($Name -and ($vmName -notin $Name)) + { + # A names list was passed but this VM wasn't included + continue + } # if + + # If a LabId is set for the lab, prepend it to the VM name. + if ($labId) + { + $vmName = "$labId$vmName" + } + + if (-not $vm.Template) + { + $exceptionParameters = @{ + errorId = 'VMTemplateNameEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMTemplateNameEmptyError ` + -f $vmName) + } + New-LabException @exceptionParameters + } # if + + # Find the template that this VM uses and get the VHD Path + [System.String] $parentVHDPath = '' + [System.Boolean] $found = $false + + foreach ($vmTemplate in $vmTemplates) { + if ($vmTemplate.Name -eq $vm.Template) { + $parentVHDPath = $vmTemplate.ParentVHD + $found = $true + Break + } # if + } # foreach + + if (-not $found) + { + $exceptionParameters = @{ + errorId = 'VMTemplateNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMTemplateNotFoundError ` + -f $vmName,$vm.template) + } + New-LabException @exceptionParameters + } # if + + # Get path to Offline Domain Join file if it exists + [System.String] $nanoODJPath = $null + + if ($vm.NanoODJPath) + { + $nanoODJPath = $vm.NanoODJPath + } # if + + # Assemble the Network adapters that this VM will use + [LabVMAdapter[]] $vmAdapters = @() + [System.Int32] $adapterCount = 0 + + foreach ($vmAdapter in $vm.Adapters.Adapter) + { + $adapterCount++ + $adapterName = $vmAdapter.Name + $adapterSwitchName = $vmAdapter.SwitchName + + if ($adapterName -eq 'adapter') + { + $exceptionParameters = @{ + errorId = 'VMAdapterNameError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMAdapterNameError ` + -f $vmName) + } + New-LabException @exceptionParameters + } # if + + if (-not $adapterSwitchName) + { + $exceptionParameters = @{ + errorId = 'VMAdapterSwitchNameError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMAdapterSwitchNameError ` + -f $vmName,$adapterName) + } + New-LabException @exceptionParameters + } # if + + <# + If a LabId is set for the lab, prepend it to the adapter name + name and switch name. + #> + if ($labId) + { + $adapterName = "$labId$adapterName" + $adapterSwitchName = "$labId$adapterSwitchName" + } # if + + # Check the switch is in the switch list + $found = $false + + foreach ($switch in $switches) + { + <# + Match the switch name to the Adapter Switch Name or + the LabId and Adapter Switch Name + #> + if ($switch.Name -eq $adapterSwitchName) + { + # The switch is found in the switch list - record the VLAN (if there is one) + $found = $true + $switchVLan = $switch.Vlan + break + } # if + elseif ($switch.Name -eq $vmAdapter.SwitchName) + { + # The switch is found in the switch list - record the VLAN (if there is one) + $found = $true + $switchVLan = $switch.Vlan + + if ($switch.Type -eq [LabSwitchType]::External) + { + $adapterName = $vmAdapter.Name + $adapterSwitchName = $vmAdapter.SwitchName + } # if + + break + } + } # foreach + + if (-not $found) + { + $exceptionParameters = @{ + errorId = 'VMAdapterSwitchNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMAdapterSwitchNotFoundError ` + -f $vmName,$adapterName,$adapterSwitchName) + } + New-LabException @exceptionParameters + } # if + + # Figure out the VLan - If defined in the VM use it, otherwise use the one defined in the Switch, otherwise keep blank. + [System.String] $vLan = $vmAdapter.VLan + + if (-not $vLan) + { + $vLan = $switchVLan + } # if + + [System.Boolean] $MACAddressSpoofing = ($vmAdapter.macaddressspoofing -eq 'On') + + # Have we got any IPv4 settings? + Remove-Variable -Name IPv4 -ErrorAction SilentlyContinue + + if ($vmAdapter.IPv4) + { + if ($vmAdapter.IPv4.Address) + { + $ipv4 = [LabVMAdapterIPv4]::New(` + (Get-LabNextIpAddress ` + -IpAddress $vmAdapter.IPv4.Address` + -Step $incNetIds)` + ,$vmAdapter.IPv4.SubnetMask) + } # if + + $ipv4.defaultgateway = $vmAdapter.IPv4.DefaultGateway + $ipv4.dnsserver = $vmAdapter.IPv4.DNSServer + } # if + + # Have we got any IPv6 settings? + Remove-Variable -Name IPv6 -ErrorAction SilentlyContinue + + if ($vmAdapter.IPv6) + { + if ($vmAdapter.IPv6.Address) + { + $ipv6 = [LabVMAdapterIPv6]::New(` + (Get-LabNextIpAddress ` + -IpAddress $vmAdapter.IPv6.Address` + -Step $incNetIds)` + ,$vmAdapter.IPv6.SubnetMask) + } # if + + $ipv6.defaultgateway = $vmAdapter.IPv6.DefaultGateway + $ipv6.dnsserver = $vmAdapter.IPv6.DNSServer + } # if + + $newVMAdapter = [LabVMAdapter]::New($adapterName) + $newVMAdapter.SwitchName = $adapterSwitchName + + if ($vmAdapter.macaddress) + { + $newVMAdapter.MACAddress = Get-NextMacAddress ` + -MacAddress $vmAdapter.macaddress ` + -Step $incNetIds + } # if + + $newVMAdapter.MACAddressSpoofing = $MACAddressSpoofing + $newVMAdapter.VLan = $vLan + $newVMAdapter.IPv4 = $ipv4 + $newVMAdapter.IPv6 = $ipv6 + $vmAdapters += @( $newVMAdapter ) + } # foreach + + # Assemble the Data Disks this VM will use + [LabDataVHD[]] $dataVhds = @() + [System.Int32] $dataVhdCount = 0 + + foreach ($vmDataVhd in $vm.DataVhds.DataVhd) + { + $dataVhdCount++ + + # Load all the VHD properties and check they are valid + [System.String] $vhd = $vmDataVhd.Vhd + + if (-not $vmDataVhd.Vhd) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskVHDEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskVHDEmptyError ` + -f $vmName) + } + New-LabException @exceptionParameters + } # if + + <# + Adjust the path to be relative to the Virtual Hard Disks folder of the VM + if it doesn't contain a root (e.g. c:\) + #> + if (-not [System.IO.Path]::IsPathRooted($vhd)) + { + $vhd = Join-Path ` + -Path $labPath ` + -ChildPath "$($vmName)\Virtual Hard Disks\$vhd" + } # if + + # Does the VHD already exist? + $exists = Test-Path ` + -Path $vhd + + # Create the new Data VHD object + $newDataVHD = [LabDataVHD]::New($vhd) + + # Get the Parent VHD and check it exists if passed + if ($vmDataVhd.ParentVHD) + { + $newDataVHD.ParentVhd = $vmDataVhd.ParentVHD + <# + Adjust the path to be relative to the Virtual Hard Disks folder of the VM + if it doesn't contain a root (e.g. c:\) + #> + if (-not [System.IO.Path]::IsPathRooted($newDataVHD.ParentVhd)) + { + $newDataVHD.ParentVhd = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $newDataVHD.ParentVhd + } + + if (-not (Test-Path -Path $newDataVHD.ParentVhd)) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskParentVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskParentVHDNotFoundError ` + -f $vmName,$newDataVHD.ParentVhd) + } + New-LabException @exceptionParameters + } # if + } # if + + # Get the Source VHD and check it exists if passed + if ($vmDataVhd.SourceVHD) + { + $newDataVHD.SourceVhd = $vmDataVhd.SourceVHD + <# + Adjust the path to be relative to the Virtual Hard Disks folder of the VM + if it doesn't contain a root (e.g. c:\) + #> + if (-not [System.IO.Path]::IsPathRooted($newDataVHD.SourceVhd)) + { + $newDataVHD.SourceVhd = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $newDataVHD.SourceVhd + } # if + + if (-not (Test-Path -Path $newDataVHD.SourceVhd)) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskSourceVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskSourceVHDNotFoundError ` + -f $vmName,$newDataVHD.SourceVhd) + } + New-LabException @exceptionParameters + } # if + } # if + + # Get the disk size if provided + if ($vmDataVhd.Size) + { + $newDataVHD.Size = (Invoke-Expression -Command $vmDataVhd.Size) + } # if + + # Get the Shared flag + $newDataVHD.Shared = ($vmDataVhd.Shared -eq 'Y') + + # Get the Support Persistent Reservations + $newDataVHD.SupportPR = ($vmDataVhd.SupportPR -eq 'Y') + + # Validate the data disk type specified + if ($vmDataVhd.Type) + { + switch ($vmDataVhd.Type) + { + 'fixed' + { + break + } + + 'dynamic' + { + break + } + + 'differencing' + { + if (-not $newDataVHD.ParentVhd) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskParentVHDMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskParentVHDMissingError ` + -f $vmName) + } + New-LabException @exceptionParameters + } # if + + if ($newDataVHD.Shared) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskSharedDifferencingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskSharedDifferencingError ` + -f $vmName,$VHD) + } + New-LabException @exceptionParameters + } # if + + break + } + + default + { + $exceptionParameters = @{ + errorId = 'VMDataDiskUnknownTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskUnknownTypeError ` + -f $vmName,$VHD,$vmDataVhd.Type) + } + New-LabException @exceptionParameters + } + } # switch + + $newDataVHD.VHDType = [LabVHDType]::$($vmDataVhd.Type) + } # if + + # Get Partition Style for the new disk. + if ($vmDataVhd.PartitionStyle) + { + $PartitionStyle = [LabPartitionStyle]::$($vmDataVhd.PartitionStyle) + + if (-not $PartitionStyle) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskPartitionStyleError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskPartitionStyleError ` + -f $vmName,$VHD,$vmDataVhd.PartitionStyle) + } + New-LabException @exceptionParameters + } # if + $newDataVHD.PartitionStyle = $PartitionStyle + } # if + + # Get file system for the new disk. + if ($vmDataVhd.FileSystem) + { + $FileSystem = [LabFileSystem]::$($vmDataVhd.FileSystem) + + if (-not $FileSystem) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskFileSystemError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskFileSystemError ` + -f $vmName,$VHD,$vmDataVhd.FileSystem) + } + New-LabException @exceptionParameters + } # if + $newDataVHD.FileSystem = $FileSystem + } # if + + # Has a file system label been provided? + if ($vmDataVhd.FileSystemLabel) + { + $newDataVHD.FileSystemLabel = $vmDataVhd.FileSystemLabel + } # if + + <# + If the Partition Style, File System or File System Label has been + provided then ensure Partition Style and File System are set. + #> + if ($newDataVHD.PartitionStyle ` + -or $newDataVHD.FileSystem ` + -or $newDataVHD.FileSystemLabel) + { + if (-not $newDataVHD.PartitionStyle) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskPartitionStyleMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskPartitionStyleMissingError ` + -f $vmName,$VHD) + } + New-LabException @exceptionParameters + } # if + + if (-not $newDataVHD.FileSystem) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskFileSystemMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskFileSystemMissingError ` + -f $vmName,$VHD) + } + New-LabException @exceptionParameters + } # if + } # if + + # Get the Folder to copy and check it exists if passed + if ($vmDataVhd.CopyFolders) + { + foreach ($CopyFolder in ($vmDataVhd.CopyFolders -Split ',')) + { + <# + Adjust the path to be relative to the configuration folder + if it doesn't contain a root (e.g. c:\) + #> + if (-not [System.IO.Path]::IsPathRooted($CopyFolder)) + { + $CopyFolder = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $CopyFolder + } # if + + if (-not (Test-Path -Path $CopyFolder -Type Container)) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskCopyFolderMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskCopyFolderMissingError ` + -f $vmName,$VHD,$CopyFolder) + } + New-LabException @exceptionParameters + } + } # foreach + + $newDataVHD.CopyFolders = $vmDataVhd.CopyFolders + } # if + + # Should the Source VHD be moved rather than copied + if ($vmDataVhd.MoveSourceVHD) + { + $newDataVHD.MoveSourceVHD = ($vmDataVhd.MoveSourceVHD -eq 'Y') + if (-not $newDataVHD.SourceVHD) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskSourceVHDIfMoveError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskSourceVHDIfMoveError ` + -f $vmName,$VHD) + } + New-LabException @exceptionParameters + } # if + } # if + + # if the data disk file doesn't exist then some basic parameters MUST be provided + if (-not $exists ` + -and ( ( ( -not $newDataVHD.VhdType ) -or ( $newDataVHD.Size -eq 0) ) ` + -and -not $newDataVHD.SourceVhd ) ) + { + $exceptionParameters = @{ + errorId = 'VMDataDiskCantBeCreatedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDataDiskCantBeCreatedError ` + -f $vmName,$VHD) + } + New-LabException @exceptionParameters + } # if + + $dataVHDs += @( $newDataVHD ) + } # foreach + + # Assemble the DVD Drives this VM will use + [LabDVDDrive[]] $dvdDrives = @() + [System.Int32] $dvdDriveCount = 0 + + foreach ($vmDVDDrive in $vm.DVDDrives.DVDDrive) + { + $dvdDriveCount++ + + # Create the new DVD Drive object + $newDVDDrive = [LabDVDDRive]::New() + + # Load all the DVD Drive properties and check they are valid + if ($vmDVDDrive.ISO) + { + <# + Look the ISO up in the ISO Resources + Pull the list of Resource ISOs available if not already pulled from Lab. + #> + if (-not $resourceISOs) + { + $resourceISOs = Get-LabResourceISO ` + -Lab $Lab + } # if + + # Lookup the Resource ISO record + $resourceISO = $resourceISOs | Where-Object -Property Name -eq $vmDVDDrive.ISO + + if (-not $resourceISO) + { + # The ISO Resource was not found + $exceptionParameters = @{ + errorId = 'VMDVDDriveISOResourceNotFOundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDVDDriveISOResourceNotFOundError ` + -f $vmName,$vmDVDDrive.ISO) + } + New-LabException @exceptionParameters + } # if + + # The ISO resource was found so populate the ISO details + $newDVDDrive.ISO = $vmDVDDrive.ISO + $newDVDDrive.Path = $resourceISO.Path + } # if + + $dvdDrives += @( $newDVDDrive ) + } # foreach + + # Does the VM have an Unattend file specified? + $unattendFile = '' + + if ($vm.UnattendFile) + { + if ([System.IO.Path]::IsPathRooted($vm.UnattendFile)) + { + $unattendFile = $vm.UnattendFile + } + else + { + $unattendFile = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $vm.UnattendFile + } # if + + if (-not (Test-Path $unattendFile)) + { + $exceptionParameters = @{ + errorId = 'UnattendFileMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.UnattendFileMissingError ` + -f $vmName,$unattendFile) + } + New-LabException @exceptionParameters + } # if + } # if + + # Does the VM specify a Setup Complete Script? + $setupComplete = '' + + if ($vm.SetupComplete) + { + if ([System.IO.Path]::IsPathRooted($vm.SetupComplete)) + { + $setupComplete = $vm.SetupComplete + } + else + { + $setupComplete = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $vm.SetupComplete + } # if + + if ([System.IO.Path]::GetExtension($setupComplete).ToLower() -notin '.ps1','.cmd' ) + { + $exceptionParameters = @{ + errorId = 'SetupCompleteFileBadTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.SetupCompleteFileBadTypeError ` + -f $vmName,$setupComplete) + } + New-LabException @exceptionParameters + } # if + + if (-not (Test-Path $setupComplete)) + { + $exceptionParameters = @{ + errorId = 'SetupCompleteFileMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.SetupCompleteFileMissingError ` + -f $vmName,$setupComplete) + } + New-LabException @exceptionParameters + } # if + } # if + + # Create the Lab DSC object + $labDSC = [LabDSC]::New($vm.DSC.ConfigName) + + # Load the DSC Config File setting and check it + $labDSC.ConfigFile = '' + + if ($vm.DSC.ConfigFile) + { + if (-not [System.IO.Path]::IsPathRooted($vm.DSC.ConfigFile)) + { + $labDSC.ConfigFile = Join-Path ` + -Path $Lab.labbuilderconfig.settings.dsclibrarypathfull ` + -ChildPath $vm.DSC.ConfigFile + } + else + { + $labDSC.ConfigFile = $vm.DSC.ConfigFile + } # if + + if ([System.IO.Path]::GetExtension($labDSC.ConfigFile).ToLower() -ne '.ps1' ) + { + $exceptionParameters = @{ + errorId = 'DSCConfigFileBadTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfigFileBadTypeError ` + -f $vmName,$labDSC.ConfigFile) + } + New-LabException @exceptionParameters + } # if + + if (-not (Test-Path $labDSC.ConfigFile)) + { + $exceptionParameters = @{ + errorId = 'DSCConfigFileMissingError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfigFileMissingError ` + -f $vmName,$labDSC.ConfigFile) + } + New-LabException @exceptionParameters + } # if + + if (-not $vm.DSC.ConfigName) + { + $exceptionParameters = @{ + errorId = 'DSCConfigNameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DSCConfigNameIsEmptyError ` + -f $vmName) + } + New-LabException @exceptionParameters + } # if + } # if + + # Load the DSC Parameters + $labDSC.Parameters = '' + + if ($vm.DSC.Parameters) + { + <# + Correct any LFs into CRLFs to ensure the new line format is the same when + pulled from the XML. + #> + $labDSC.Parameters = ($vm.DSC.Parameters -replace "`r`n","`n") -replace "`n","`r`n" + } # if + + # Load the DSC Parameters + $labDSC.Logging = ($vm.DSC.Logging -eq 'Y') + + # Get the Memory Startup Bytes (from the template or VM) + [System.Int64] $memoryStartupBytes = 1GB + + if ($vm.memorystartupbytes) + { + $memoryStartupBytes = (Invoke-Expression -Command $vm.memorystartupbytes) + } + elseif ($vmTemplate.memorystartupbytes) + { + $memoryStartupBytes = $vmTemplate.memorystartupbytes + } # if + + # Get the Dynamic Memory Enabled flag + $dynamicMemoryEnabled = $true + + if ($vm.DynamicMemoryEnabled) + { + $dynamicMemoryEnabled = ($vm.DynamicMemoryEnabled -eq 'Y') + } + elseif ($vmTemplate.DynamicMemoryEnabled) + { + $dynamicMemoryEnabled = $vmTemplate.DynamicMemoryEnabled + } # if + + # Get the Number of vCPUs (from the template or VM) + [System.Int32] $processorCount = 1 + + if ($vm.processorcount) + { + $processorCount = (Invoke-Expression $vm.processorcount) + } + elseif ($vmTemplate.processorcount) + { + $processorCount = $vmTemplate.processorcount + } # if + + # Get the Expose Virtualization Extensions flag + if ($vm.ExposeVirtualizationExtensions) + { + $exposeVirtualizationExtensions = ($vm.ExposeVirtualizationExtensions -eq 'Y') + } + elseif ($vmTemplate.ExposeVirtualizationExtensions) + { + $exposeVirtualizationExtensions = $vmTemplate.ExposeVirtualizationExtensions + } # if + + <# + If VM requires ExposeVirtualizationExtensions but + it is not supported on Host then throw an exception. + #> + if ($exposeVirtualizationExtensions -and ($Script:CurrentBuild -lt 10565)) + { + $exceptionParameters = @{ + errorId = 'VMVirtualizationExtError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMVirtualizationExtError ` + -f $vmName) + } + New-LabException @exceptionParameters + } # if + + $useDifferencingDisk = $true + + if ($vm.UseDifferencingDisk -eq 'N') + { + $useDifferencingDisk = $false + } # if + + # Get the Integration Services flags + if ($null -ne $vm.IntegrationServices) + { + $integrationServices = $vm.IntegrationServices + } + elseif ($null -ne $vmTemplate.IntegrationServices) + { + $integrationServices = $vmTemplate.IntegrationServices + } # if + + # Get the Administrator password (from the template or VM) + $administratorPassword = '' + + if ($vm.administratorpassword) + { + $administratorPassword = $vm.administratorpassword + } + elseif ($vmTemplate.administratorpassword) + { + $administratorPassword = $vmTemplate.administratorpassword + } # if + + # Get the Product Key (from the template or VM) + $productKey = '' + + if ($vm.productkey) + { + $productKey = $vm.productkey + } + elseif ($vmTemplate.productkey) + { + $productKey = $vmTemplate.productkey + } # if + + # Get the Timezone (from the template or VM) + $timezone = 'Pacific Standard Time' + + if ($vm.timezone) + { + $timezone = $vm.timezone + } + elseif ($vmTemplate.timezone) + { + $timezone = $vmTemplate.timezone + } # if + + # Get the OS Type + $osType = [LabOStype]::Server + + if ($vm.OSType) + { + $osType = $vm.OSType + } + elseif ($vmTemplate.OSType) + { + $osType = $vmTemplate.OSType + } # if + + # Get the Bootorder + [Byte] $bootorder = [Byte]::MaxValue + + if ($vm.bootorder) + { + $bootorder = $vm.bootorder + } # if + + # Get the Packages + [System.String] $packages = $null + + if ($vm.packages) + { + $packages = $vm.packages + } + elseif ($vmTemplate.packages) + { + $packages = $vmTemplate.packages + } # if + + # Get the Version (from the template or VM) + $version = '8.0' + + if ($vm.version) + { + $version = $vm.version + } + elseif ($vmTemplate.version) + { + $version = $vmTemplate.version + } # if + + # Get the Generation (from the template or VM) + $generation = '2' + + if ($vm.generation) + { + $generation = $vm.generation + } + elseif ($vmTemplate.generation) + { + $generation = $vmTemplate.generation + } # if + + # Get the Certificate Source + $certificateSource = [LabCertificateSource]::Guest + + if ($osType -eq [LabOSType]::Nano) + { + # Nano Server can't generate certificates so must always be set to Host + $certificateSource = [LabCertificateSource]::Host + } + elseif ($vm.CertificateSource) + { + $certificateSource = $vm.CertificateSource + } # if + + + $labVM = [LabVM]::New($vmName,$computerName) + $labVM.Template = $vm.Template + $labVM.ParentVHD = $parentVHDPath + $labVM.UseDifferencingDisk = $useDifferencingDisk + $labVM.MemoryStartupBytes = $memoryStartupBytes + $labVM.DynamicMemoryEnabled = $dynamicMemoryEnabled + $labVM.ProcessorCount = $processorCount + $labVM.ExposeVirtualizationExtensions = $exposeVirtualizationExtensions + $labVM.IntegrationServices = $integrationServices + $labVM.AdministratorPassword = $administratorPassword + $labVM.ProductKey = $productKey + $labVM.TimeZone =$timezone + $labVM.UnattendFile = $unattendFile + $labVM.SetupComplete = $setupComplete + $labVM.OSType = $osType + $labVM.CertificateSource = $certificateSource + $labVM.Bootorder = $bootorder + $labVM.Packages = $packages + $labVM.Version = $version + $labVM.Generation = $generation + $labVM.Adapters = $vmAdapters + $labVM.DataVHDs = $dataVHDs + $labVM.DVDDrives = $dvdDrives + $labVM.DSC = $labDSC + $labVM.NanoODJPath = $nanoODJPath + $labVM.VMRootPath = Join-Path ` + -Path $labPath ` + -ChildPath $vmName + $labVM.LabBuilderFilesPath = Join-Path ` + -Path $labPath ` + -ChildPath "$vmName\LabBuilder Files" + $labVMs += @( $labVM ) + } # foreach + } # foreach + + return $labVMs +} # Get-LabVM diff --git a/source/public/Get-LabVmTemplateVhd.ps1 b/source/public/Get-LabVmTemplateVhd.ps1 new file mode 100644 index 00000000..f2be7f57 --- /dev/null +++ b/source/public/Get-LabVmTemplateVhd.ps1 @@ -0,0 +1,290 @@ +function Get-LabVMTemplateVHD +{ + [OutputType([LabVMTemplateVHD[]])] + [CmdLetBinding()] + param + ( + [Parameter ( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name + ) + + # return null if the TemplateVHDs node does not exist + if (-not $Lab.labbuilderconfig.TemplateVHDs) + { + return + } + + # Determine the ISORootPath where the ISO files should be found + # if no path is specified then look in the same path as the config + # if a path is specified but it is relative, make it relative to the + # config path. Otherwise use it as is. + [System.String] $ISORootPath = $Lab.labbuilderconfig.TemplateVHDs.ISOPath + if (-not $ISORootPath) + { + $ISORootPath = $Lab.labbuilderconfig.settings.fullconfigpath + } + else + { + if (-not [System.IO.Path]::IsPathRooted($ISORootPath)) + { + $ISORootPath = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $ISORootPath + } # if + } # if + if (-not (Test-Path -Path $ISORootPath -Type Container)) + { + $exceptionParameters = @{ + errorId = 'VMTemplateVHDISORootPathNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMTemplateVHDISORootPathNotFoundError ` + -f $ISORootPath) + } + New-LabException @exceptionParameters + } # if + + # Determine the VHDRootPath where the VHD files should be put + # if no path is specified then look in the same path as the config + # if a path is specified but it is relative, make it relative to the + # config path. Otherwise use it as is. + [System.String] $VHDRootPath = $Lab.labbuilderconfig.TemplateVHDs.VHDPath + if (-not $VHDRootPath) + { + $VHDRootPath = $Lab.labbuilderconfig.settings.fullconfigpath + } + else + { + if (-not [System.IO.Path]::IsPathRooted($VHDRootPath)) + { + $VHDRootPath = Join-Path ` + -Path $Lab.labbuilderconfig.settings.fullconfigpath ` + -ChildPath $VHDRootPath + } # if + } # if + if (-not (Test-Path -Path $VHDRootPath -Type Container)) + { + $exceptionParameters = @{ + errorId = 'VMTemplateVHDRootPathNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMTemplateVHDRootPathNotFoundError ` + -f $VHDRootPath) + } + New-LabException @exceptionParameters + } # if + + $TemplatePrefix = $Lab.labbuilderconfig.templatevhds.prefix + + # Read the list of templateVHD from the configuration file + $TemplateVHDs = $Lab.labbuilderconfig.templatevhds.templatevhd + [LabVMTemplateVHD[]] $VMTemplateVHDs = @() + foreach ($TemplateVHD in $TemplateVHDs) + { + # It can't be template because if the name attrib/node is missing the name property on + # the XML object defaults to the name of the parent. So we can't easily tell if no name + # was specified or if they actually specified 'templatevhd' as the name. + $TemplateVHDName = $TemplateVHD.Name + if ($Name -and ($TemplateVHDName -notin $Name)) + { + # A names list was passed but this VM Template VHD wasn't included + continue + } # if + + if (($TemplateVHDName -eq 'TemplateVHD') ` + -or ([System.String]::IsNullOrWhiteSpace($TemplateVHDName))) + { + $exceptionParameters = @{ + errorId = 'EmptyVMTemplateVHDNameError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.EmptyVMTemplateVHDNameError) + } + New-LabException @exceptionParameters + } # if + + # Get the ISO Path + [System.String] $ISOPath = $TemplateVHD.ISO + if (-not $ISOPath) + { + $exceptionParameters = @{ + errorId = 'EmptyVMTemplateVHDISOPathError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.EmptyVMTemplateVHDISOPathError ` + -f $TemplateVHD.Name) + } + New-LabException @exceptionParameters + } # if + + # Adjust the ISO Path if required + if (-not [System.IO.Path]::IsPathRooted($ISOPath)) + { + $ISOPath = Join-Path ` + -Path $ISORootPath ` + -ChildPath $ISOPath + } # if + + # Does the ISO Exist? + if (-not (Test-Path -Path $ISOPath)) + { + $URL = $TemplateVHD.URL + if ($URL) + { + Write-LabMessage ` + -Type Alert ` + -Message $($LocalizedData.ISONotFoundDownloadURLMessage ` + -f $TemplateVHD.Name, $ISOPath, $URL) + } # if + $exceptionParameters = @{ + errorId = 'VMTemplateVHDISOPathNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMTemplateVHDISOPathNotFoundError ` + -f $TemplateVHD.Name, $ISOPath) + } + New-LabException @exceptionParameters + } # if + + # Get the VHD Path + [System.String] $VHDPath = $TemplateVHD.VHD + if (-not $VHDPath) + { + $exceptionParameters = @{ + errorId = 'EmptyVMTemplateVHDPathError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.EmptyVMTemplateVHDPathError ` + -f $TemplateVHD.Name) + } + New-LabException @exceptionParameters + } # if + + # Adjust the VHD Path if required + if (-not [System.IO.Path]::IsPathRooted($VHDPath)) + { + $VHDPath = Join-Path ` + -Path $VHDRootPath ` + -ChildPath $VHDPath + } # if + + # Add the template prefix to the VHD name. + if (-not ([System.String]::IsNullOrWhitespace($TemplatePrefix))) + { + $VHDPath = Join-Path ` + -Path (Split-Path -Path $VHDPath)` + -ChildPath ("$TemplatePrefix$(Split-Path -Path $VHDPath -Leaf)") + } # if + + # Get the Template OS Type + $OSType = [LabOStype]::Server + if ($TemplateVHD.OSType) + { + $OSType = [LabOStype]::$($TemplateVHD.OSType) + } # if + if (-not $OSType) + { + $exceptionParameters = @{ + errorId = 'InvalidVMTemplateVHDOSTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InvalidVMTemplateVHDOSTypeError ` + -f $TemplateVHD.Name, $TemplateVHD.OSType) + } + New-LabException @exceptionParameters + } # if + + # Get the Template Wim Image to use + $Edition = $null + if ($TemplateVHD.Edition) + { + $Edition = $TemplateVHD.Edition + } # if + + # Get the Template VHD Format + $VHDFormat = [LabVHDFormat]::VHDx + if ($TemplateVHD.VHDFormat) + { + $VHDFormat = [LabVHDFormat]::$($TemplateVHD.VHDFormat) + } # if + if (-not $VHDFormat) + { + $exceptionParameters = @{ + errorId = 'InvalidVMTemplateVHDVHDFormatError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InvalidVMTemplateVHDVHDFormatError ` + -f $TemplateVHD.Name, $TemplateVHD.VHDFormat) + } + New-LabException @exceptionParameters + } + + # Get the Template VHD Type + $VHDType = [LabVHDType]::Dynamic + if ($TemplateVHD.VHDType) + { + $VHDType = [LabVHDType]::$($TemplateVHD.VHDType) + } # if + if (-not $VHDType) + { + $exceptionParameters = @{ + errorId = 'InvalidVMTemplateVHDVHDTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InvalidVMTemplateVHDVHDTypeError ` + -f $TemplateVHD.Name, $TemplateVHD.VHDType) + } + New-LabException @exceptionParameters + } # if + + # Get the disk size if provided + [System.Int64] $VHDSize = 25GB + if ($TemplateVHD.VHDSize) + { + $VHDSize = (Invoke-Expression $TemplateVHD.VHDSize) + } # if + + # Get the Template VM Generation + [System.Int32] $Generation = 2 + if ($TemplateVHD.Generation) + { + $Generation = $TemplateVHD.Generation + } # if + if ($Generation -notin @(1, 2) ) + { + $exceptionParameters = @{ + errorId = 'InvalidVMTemplateVHDGenerationError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InvalidVMTemplateVHDGenerationError ` + -f $TemplateVHD.Name, $Generation) + } + New-LabException @exceptionParameters + } + + # Get the Template Packages + if ($TemplateVHD.packages) + { + $Packages = $TemplateVHD.Packages + } # if + + # Get the Template Features + if ($TemplateVHD.features) + { + $Features = $TemplateVHD.Features + } # if + + # Add template VHD to the list + $NewVMTemplateVHD = [LabVMTemplateVHD]::New($TemplateVHDName) + $NewVMTemplateVHD.ISOPath = $ISOPath + $NewVMTemplateVHD.VHDPath = $VHDPath + $NewVMTemplateVHD.OSType = $OSType + $NewVMTemplateVHD.Edition = $Edition + $NewVMTemplateVHD.Generation = $Generation + $NewVMTemplateVHD.VHDFormat = $VHDFormat + $NewVMTemplateVHD.VHDType = $VHDType + $NewVMTemplateVHD.VHDSize = $VHDSize + $NewVMTemplateVHD.Packages = $Packages + $NewVMTemplateVHD.Features = $Features + $VMTemplateVHDs += @( $NewVMTemplateVHD ) + } # foreach + Return $VMTemplateVHDs +} # Get-LabVMTemplateVHD diff --git a/source/public/Initialize-LabResourceIso.ps1 b/source/public/Initialize-LabResourceIso.ps1 new file mode 100644 index 00000000..0867445c --- /dev/null +++ b/source/public/Initialize-LabResourceIso.ps1 @@ -0,0 +1,78 @@ +function Initialize-LabResourceISO +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabResourceISO[]] $ResourceISOs + ) + + # if resource ISOs was not passed, pull it. + if (-not $PSBoundParameters.ContainsKey('resourceisos')) + { + $ResourceMSUs = Get-LabResourceISO ` + @PSBoundParameters + } # if + + if ($ResourceISOs) + { + foreach ($ResourceISO in $ResourceISOs) + { + if (-not (Test-Path -Path $ResourceISO.Path)) + { + # The Resource ISO does not exist + if (-not ($ResourceISO.URL)) + { + $exceptionParameters = @{ + errorId = 'ResourceISOFileNotFoundAndNoURLError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ResourceISOFileNotFoundAndNoURLError ` + -f $ISOName, $Path) + } + New-LabException @exceptionParameters + } # if + + $URLLeaf = [System.IO.Path]::GetFileName($ResourceISO.URL) + $URLExtension = [System.IO.Path]::GetExtension($URLLeaf) + if ($URLExtension -in @('.zip', '.iso')) + { + Write-LabMessage -Message $($LocalizedData.DownloadingResourceISOMessage ` + -f $ResourceISO.Name, $ResourceISO.URL) + + Invoke-LabDownloadAndUnzipFile ` + -URL $ResourceISO.URL ` + -DestinationPath (Split-Path -Path $ResourceISO.Path) + } + elseif ([System.String]::IsNullOrEmpty($URLExtension)) + { + Write-LabMessage ` + -Type Alert ` + -Message $($LocalizedData.ISONotFoundDownloadURLMessage ` + -f $ResourceISO.Name, $ResourceISO.Path, $ResourceISO.URL) + } # if + if (-not (Test-Path -Path $ResourceISO.Path)) + { + $exceptionParameters = @{ + errorId = 'ResourceISOFileNotDownloadedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ResourceISOFileNotDownloadedError ` + -f $ResourceISO.Name, $ResourceISO.Path, $ResourceISO.URL) + } + New-LabException @exceptionParameters + } # if + } # if + } # foreach + } # if +} # Initialize-LabResourceISO diff --git a/source/public/Initialize-LabResourceModule.ps1 b/source/public/Initialize-LabResourceModule.ps1 new file mode 100644 index 00000000..f732fde5 --- /dev/null +++ b/source/public/Initialize-LabResourceModule.ps1 @@ -0,0 +1,57 @@ +function Initialize-LabResourceModule +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabResourceModule[]] $ResourceModules + ) + + # if resource modules was not passed, pull it. + if (-not $PSBoundParameters.ContainsKey('resourcemodules')) + { + $ResourceModules = Get-LabResourceModule ` + @PSBoundParameters + } + + if ($ResourceModules) + { + foreach ($Module in $ResourceModules) + { + $Splat = [PSObject] @{ Name = $Module.Name } + if ($Module.URL) + { + $Splat += [PSObject] @{ URL = $Module.URL } + } + if ($Module.Folder) + { + $Splat += [PSObject] @{ Folder = $Module.Folder } + } + if ($Module.RequiredVersion) + { + $Splat += [PSObject] @{ RequiredVersion = $Module.RequiredVersion } + } + if ($Module.MiniumVersion) + { + $Splat += [PSObject] @{ MiniumVersion = $Module.MiniumVersion } + } + + Write-LabMessage -Message $($LocalizedData.DownloadingResourceModuleMessage ` + -f $Name, $URL) + + Invoke-LabDownloadResourceModule @Splat + } # foreach + } # if +} # Initialize-LabResourceModule diff --git a/source/public/Initialize-LabResourceMsu.ps1 b/source/public/Initialize-LabResourceMsu.ps1 new file mode 100644 index 00000000..36f3977b --- /dev/null +++ b/source/public/Initialize-LabResourceMsu.ps1 @@ -0,0 +1,44 @@ +function Initialize-LabResourceMSU +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabResourceMSU[]] $ResourceMSUs + ) + + # if resource MSUs was not passed, pull it. + if (-not $PSBoundParameters.ContainsKey('resourcemsus')) + { + $ResourceMSUs = Get-LabResourceMSU ` + @PSBoundParameters + } + + if ($ResourceMSUs) + { + foreach ($MSU in $ResourceMSUs) + { + if (-not (Test-Path -Path $MSU.Filename)) + { + Write-LabMessage -Message $($LocalizedData.DownloadingResourceMSUMessage ` + -f $MSU.Name, $MSU.URL) + + Invoke-LabDownloadAndUnzipFile ` + -URL $MSU.URL ` + -DestinationPath (Split-Path -Path $MSU.Filename) + } # if + } # foreach + } # if +} # Initialize-LabResourceMSU diff --git a/source/public/Initialize-LabSwitch.ps1 b/source/public/Initialize-LabSwitch.ps1 new file mode 100644 index 00000000..bc75d067 --- /dev/null +++ b/source/public/Initialize-LabSwitch.ps1 @@ -0,0 +1,320 @@ +function Initialize-LabSwitch +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabSwitch[]] $Switches + ) + + # if switches was not passed, pull it. + if (-not $PSBoundParameters.ContainsKey('switches')) + { + [LabSwitch[]] $Switches = Get-LabSwitch ` + @PSBoundParameters + } + + # Create Hyper-V Switches + foreach ($VMSwitch in $Switches) + { + if ($Name -and ($VMSwitch.name -notin $Name)) + { + # A names list was passed but this swtich wasn't included + continue + } # if + + if ((Get-VMSwitch | Where-Object -Property Name -eq $($VMSwitch.Name)).Count -eq 0) + { + [System.String] $SwitchName = $VMSwitch.Name + if (-not $SwitchName) + { + $exceptionParameters = @{ + errorId = 'SwitchNameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.SwitchNameIsEmptyError) + } + New-LabException @exceptionParameters + } + [LabSwitchType] $SwitchType = $VMSwitch.Type + Write-LabMessage -Message $($LocalizedData.CreatingVirtualSwitchMessage ` + -f $SwitchType, $SwitchName) + Switch ($SwitchType) + { + 'External' + { + # Determine which Physical Adapter to bind this switch to + if ($VMSwitch.BindingAdapterMac) + { + $BindingAdapter = Get-NetAdapter | Where-Object { + ($_.MacAddress -replace '-', '') -eq $VMSwitch.BindingAdapterMac + } + $ErrorDetail = "with a MAC address '$($VMSwitch.BindingAdapterMac)' " + } + elseif ($VMSwitch.BindingAdapterName) + { + $BindingAdapter = Get-NetAdapter ` + -Name $VMSwitch.BindingAdapterName ` + -ErrorAction SilentlyContinue + $ErrorDetail = "with a name '$($VMSwitch.BindingAdapterName)' " + } + else + { + $BindingAdapter = Get-NetAdapter | ` + Where-Object { + ($_.Status -eq 'Up') ` + -and (-not $_.Virtual) ` + } | Select-Object -First 1 + $ErrorDetail = '' + } # if + + # Check that a Binding Adapter was found + if (-not $BindingAdapter) + { + $exceptionParameters = @{ + errorId = 'BindingAdapterNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.BindingAdapterNotFoundError ` + -f $SwitchName, $ErrorDetail) + } + New-LabException @exceptionParameters + } # if + + # Check this adapter is not already bound to a switch + $VMSwitchNames = (Get-VMSwitch | Where-Object { + $_.SwitchType -eq 'External' + }).Name + $MacAddress = @() + + foreach ($VmSwitchName in $VmSwitchNames) + { + $MacAddress += (Get-VMNetworkAdapter ` + -ManagementOS ` + -SwitchName $VmSwitchName ` + -Name $VmSwitchName ` + -ErrorAction SilentlyContinue).MacAddress + } # foreach + + $UsedAdapters = @((Get-NetAdapter | Where-Object { + ($_.MacAddress -replace '-', '') -in $MacAddress + }).Name) + if ($BindingAdapter.Name -in $UsedAdapters) + { + $exceptionParameters = @{ + errorId = 'BindingAdapterUsedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.BindingAdapterUsedError ` + -f $SwitchName, $BindingAdapter.Name) + } + New-LabException @exceptionParameters + } # if + + # Create the swtich + $null = New-VMSwitch ` + -Name $SwitchName ` + -NetAdapterName $BindingAdapter.Name + break + } # 'External' + + 'Private' + { + $null = New-VMSwitch ` + -Name $SwitchName ` + -SwitchType Private + Break + } # 'Private' + + 'Internal' + { + $null = New-VMSwitch ` + -Name $SwitchName ` + -SwitchType Internal + Break + } # 'Internal' + + 'NAT' + { + if ($Script:CurrentBuild -lt 14295) + { + $exceptionParameters = @{ + errorId = 'NatSwitchNotSupportedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSwitchNotSupportedError -f $SwitchName) + } + New-LabException @exceptionParameters + } + + $NatSubnet = $VMSwitch.NatSubnet + # Check Nat Subnet is set + if (-not $NatSubnet) + { + $exceptionParameters = @{ + errorId = 'NatSubnetEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSubnetEmptyError ` + -f $SwitchName) + } + New-LabException @exceptionParameters + } # if + # Ensure Nat Subnet looks valid + if ($NatSubnet -notmatch '[0-9]+.[0-9]+.[0-9]+.[0-9]+/[0-9]+') + { + $exceptionParameters = @{ + errorId = 'NatSubnetInvalidError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSubnetInvalidError ` + -f $SwitchName, $NatSubnet) + } + New-LabException @exceptionParameters + } # if + $NatSubnetComponents = ($NatSubnet -split '/') + $NatSubnetAddress = $NatSubnetComponents[0] + # Validate the Nat Subnet Address + if (-not ([System.Net.Ipaddress]::TryParse($NatSubnetAddress, [ref]0))) + { + $exceptionParameters = @{ + errorId = 'NatSubnetAddressInvalidError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSubnetAddressInvalidError ` + -f $SwitchName, $NatSubnetAddress) + } + New-LabException @exceptionParameters + } # if + # Validate the Nat Subnet Prefix Length + [System.Int32] $NatSubnetPrefixLength = $NatSubnetComponents[1] + if (($NatSubnetPrefixLength -lt 1) -or ($NatSubnetPrefixLength -gt 31)) + { + $exceptionParameters = @{ + errorId = 'NatSubnetPrefixLengthInvalidError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSubnetPrefixLengthInvalidError ` + -f $SwitchName, $NatSubnetPrefixLength) + } + New-LabException @exceptionParameters + } # if + $NatGatewayAddress = $VMSwitch.NatGatewayAddress + + # Create the Internal Switch + $null = New-VMSwitch ` + -Name $SwitchName ` + -SwitchType Internal ` + -ErrorAction Stop + # Set the IP Address on the default adapter connected to the NAT switch + $MacAddress = (Get-VMNetworkAdapter ` + -ManagementOS ` + -SwitchName $SwitchName ` + -Name $SwitchName ` + -ErrorAction Stop).MacAddress + if ([System.String]::IsNullOrEmpty($MacAddress)) + { + $exceptionParameters = @{ + errorId = 'NatSwitchDefaultAdapterMacEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSwitchDefaultAdapterMacEmptyError ` + -f $SwitchName) + } + New-LabException @exceptionParameters + } # if + $Adapter = Get-NetAdapter | + Where-Object { ($_.MacAddress -replace '-', '') -eq $MacAddress } + if (-not $Adapter) + { + $exceptionParameters = @{ + errorId = 'NatSwitchDefaultAdapterNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NatSwitchDefaultAdapterNotFoundError ` + -f $SwitchName) + } + New-LabException @exceptionParameters + } + $null = $Adapter | New-NetIPAddress ` + -IPAddress $NatGatewayAddress ` + -PrefixLength $NatSubnetPrefixLength ` + -ErrorAction Stop + # Does the NAT already exist? + $NetNat = Get-NetNat ` + -Name $SwitchName ` + -ErrorAction SilentlyContinue + if ($NetNat) + { + # If the NAT already exists, remove it so it can be recreated + $null = $NetNat | Remove-NetNat -Confirm:$false + } + # Create the new NAT + $null = New-NetNat ` + -Name $SwitchName ` + -InternalIPInterfaceAddressPrefix $NatSubnet ` + -ErrorAction Stop + Break + } # 'NAT' + Default + { + $exceptionParameters = @{ + errorId = 'UnknownSwitchTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.UnknownSwitchTypeError ` + -f $SwitchType, $SwitchName) + } + New-LabException @exceptionParameters + } + } # switch + + if ($SwitchType -ne 'Private') + { + # Configure the VLan on the default Management Adapter + $setLabSwitchAdapterParameters = @{ + Name = $SwitchName + SwitchName = $SwitchName + } + + if ($VMSwitch.VLan) + { + $setLabSwitchAdapterParameters += @{ + VlanId = $VMSwitch.Vlan + } + } # if + + Set-LabSwitchAdapter @setLabSwitchAdapterParameters + + # Add any management OS adapters to the switch + if ($VMSwitch.Adapters) + { + foreach ($Adapter in $VMSwitch.Adapters) + { + $setLabSwitchAdapterParameters = @{ + Name = $Adapter.Name + SwitchName = $SwitchName + } + + if ($Adapter.MacAddress) + { + $setLabSwitchAdapterParameters += @{ + StaticMacAddress = $Adapter.MacAddress + } + } # if + + if ($VMSwitch.VLan) + { + $setLabSwitchAdapterParameters += @{ + VlanId = $VMSwitch.Vlan + } + } # if + + Set-LabSwitchAdapter @setLabSwitchAdapterParameters + } # foreach + } # if + } # if + } # if + } # foreach +} # Initialize-LabSwitch diff --git a/source/public/Initialize-LabVm.ps1 b/source/public/Initialize-LabVm.ps1 new file mode 100644 index 00000000..f4072bed --- /dev/null +++ b/source/public/Initialize-LabVm.ps1 @@ -0,0 +1,318 @@ +function Initialize-LabVM +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position=1, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position=2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position=3)] + [LabVM[]] $VMs + ) + + # if VMs array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMs')) + { + [LabVM[]] $VMs = Get-LabVM ` + @PSBoundParameters + } # if + + # if there are not VMs just return + if (-not $VMs) + { + return + } # if + + $CurrentVMs = Get-VM + + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + # Figure out the name of the LabBuilder control switch + $ManagementSwitchName = Get-LabManagementSwitchName ` + -Lab $Lab + if ($Lab.labbuilderconfig.switches.ManagementVlan) + { + [Int32] $ManagementVlan = $Lab.labbuilderconfig.switches.ManagementVlan + } + else + { + [Int32] $ManagementVlan = $Script:DefaultManagementVLan + } # if + + foreach ($VM in $VMs) + { + if ($Name -and ($VM.Name -notin $Name)) + { + # A names list was passed but this VM wasn't included + continue + } # if + + # Get the root path of the VM + [System.String] $VMRootPath = $VM.VMRootPath + + # Get the Virtual Machine Path + [System.String] $VMPath = Join-Path ` + -Path $VMRootPath ` + -ChildPath 'Virtual Machines' + + # Get the Virtual Hard Disk Path + [System.String] $VHDPath = Join-Path ` + -Path $VMRootPath ` + -ChildPath 'Virtual Hard Disks' + + # Get Path to LabBuilder files + [System.String] $VMLabBuilderFiles = $VM.LabBuilderFilesPath + + if (($CurrentVMs | Where-Object -Property Name -eq $VM.Name).Count -eq 0) + { + Write-LabMessage -Message $($LocalizedData.CreatingVMMessage ` + -f $VM.Name) + + # Make sure the appropriate folders exist + Initialize-LabVMPath ` + -VMPath $VMRootPath + + # Create the boot disk + $VMBootDiskPath = "$VHDPath\$($VM.Name) Boot Disk.vhdx" + if (-not (Test-Path -Path $VMBootDiskPath)) + { + if ($VM.UseDifferencingDisk) + { + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` + -f $VM.Name,$VMBootDiskPath,'Differencing Boot') + + $null = New-VHD ` + -Differencing ` + -Path $VMBootDiskPath ` + -ParentPath $VM.ParentVHD + } + else + { + Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` + -f $VM.Name,$VMBootDiskPath,'Boot') + + $null = Copy-Item ` + -Path $VM.ParentVHD ` + -Destination $VMBootDiskPath + } + + # Create all the required initialization files for this VM + New-LabVMInitializationFile ` + -Lab $Lab ` + -VM $VM + + # Because this is a new boot disk apply any required initialization + Initialize-LabBootVHD ` + -Lab $Lab ` + -VM $VM ` + -VMBootDiskPath $VMBootDiskPath + } + else + { + Write-LabMessage -Message $($LocalizedData.VMDiskAlreadyExistsMessage ` + -f $VM.Name,$VMBootDiskPath,'Boot') + } # if + + # Create New VM from settings + if ($VM.Version -and ($Script:CurrentBuild -ge 14352)) + { + $null = New-VM ` + -Name $VM.Name ` + -MemoryStartupBytes $VM.MemoryStartupBytes ` + -Generation $VM.Generation ` + -Path $LabPath ` + -VHDPath $VMBootDiskPath ` + -Version $VM.Version + } + + else + { + $null = New-VM ` + -Name $VM.Name ` + -MemoryStartupBytes $VM.MemoryStartupBytes ` + -Generation $VM.Generation ` + -Path $LabPath ` + -VHDPath $VMBootDiskPath ` + + + } + + # Remove the default network adapter created with the VM because we don't need it + Remove-VMNetworkAdapter ` + -VMName $VM.Name ` + -Name 'Network Adapter' + } + + # Set the processor count if different to default and if specified in config file + if ($VM.ProcessorCount) + { + if ($VM.ProcessorCount -ne (Get-VM -Name $VM.Name).ProcessorCount) + { + Set-VM ` + -Name $VM.Name ` + -ProcessorCount $VM.ProcessorCount + } # if + } # if + + # Enable/Disable Dynamic Memory + Write-Verbose -Message "Checking Dynamic Memory: $($VM.DynamicMemoryEnabled) = $((Get-VMMemory -VMName $VM.Name).DynamicMemoryEnabled)" -Verbose + if ($VM.DynamicMemoryEnabled -ne (Get-VMMemory -VMName $VM.Name).DynamicMemoryEnabled) + { + Write-Verbose -Message "Checking Dynamic Memory: $($VM.DynamicMemoryEnabled)" -Verbose + Set-VMMemory ` + -VMName $VM.Name ` + -DynamicMemoryEnabled:$($VM.DynamicMemoryEnabled) + } # if + + # Is ExposeVirtualizationExtensions supported? + if ($Script:CurrentBuild -lt 10565) + { + # No, it is not supported - is it required by VM? + if ($VM.ExposeVirtualizationExtensions) + { + # ExposeVirtualizationExtensions is required for this VM + $exceptionParameters = @{ + errorId = 'VMVirtualizationExtError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMVirtualizationExtError ` + -f $VM.Name) + } + New-LabException @exceptionParameters + } # if + } + else + { + # Yes, it is - is the setting different? + if ($VM.ExposeVirtualizationExtensions ` + -ne (Get-VMProcessor -VMName $VM.Name).ExposeVirtualizationExtensions) + { + if ($Script:CurrentBuild -ge 14352 -and ($VM.Version -eq "8.0")) + { + Set-VMSecurity ` + -VMName $VM.Name ` + -VirtualizationBasedSecurityOptOut $true + } # if + # Try and update it + Set-VMProcessor ` + -VMName $VM.Name ` + -ExposeVirtualizationExtensions:$VM.ExposeVirtualizationExtensions ` + -ErrorAction Stop + } # if + } # if + + # Enable/Disable the Integration Services + Update-LabVMIntegrationService ` + -VM $VM + + # Update the data disks for the VM + Update-LabVMDataDisk ` + -Lab $Lab ` + -VM $VM + + # Update the DVD Drives for the VM + Update-LabVMDvdDrive ` + -Lab $Lab ` + -VM $VM + + # Create/Update the Management Network Adapter + if ((Get-VMNetworkAdapter -VMName $VM.Name | Where-Object -Property Name -EQ $ManagementSwitchName).Count -eq 0) + { + Write-LabMessage -Message $($LocalizedData.AddingVMNetworkAdapterMessage ` + -f $VM.Name,$ManagementSwitchName,'Management') + + Add-VMNetworkAdapter ` + -VMName $VM.Name ` + -SwitchName $ManagementSwitchName ` + -Name $ManagementSwitchName + } + $VMNetworkAdapter = Get-VMNetworkAdapter ` + -VMName $VM.Name ` + -Name $ManagementSwitchName + $null = $VMNetworkAdapter | + Set-VMNetworkAdapterVlan ` + -Access ` + -VlanId $ManagementVlan + + Write-LabMessage -Message $($LocalizedData.SettingVMNetworkAdapterVlanMessage ` + -f $VM.Name,$ManagementSwitchName,'Management',$ManagementVlan) + + # Create any network adapters + foreach ($VMAdapter in $VM.Adapters) + { + if ((Get-VMNetworkAdapter -VMName $VM.Name | Where-Object -Property Name -EQ $VMAdapter.Name).Count -eq 0) + { + Write-LabMessage -Message $($LocalizedData.AddingVMNetworkAdapterMessage ` + -f $VM.Name,$VMAdapter.SwitchName,$VMAdapter.Name) + + Add-VMNetworkAdapter ` + -VMName $VM.Name ` + -SwitchName $VMAdapter.SwitchName ` + -Name $VMAdapter.Name + } # if + + $VMNetworkAdapter = Get-VMNetworkAdapter ` + -VMName $VM.Name ` + -Name $VMAdapter.Name + if ($VMAdapter.VLan) + { + $null = $VMNetworkAdapter | + Set-VMNetworkAdapterVlan ` + -Access ` + -VlanId $VMAdapter.VLan + + Write-LabMessage -Message $($LocalizedData.SettingVMNetworkAdapterVlanMessage ` + -f $VM.Name,$VMAdapter.Name,'',$VMAdapter.VLan) + } + else + { + $null = $VMNetworkAdapter | + Set-VMNetworkAdapterVlan ` + -Untagged + + Write-LabMessage -Message $($LocalizedData.ClearingVMNetworkAdapterVlanMessage ` + -f $VM.Name,$VMAdapter.Name,'') + } # if + + if ([System.String]::IsNullOrWhitespace($VMAdapter.MACAddress)) + { + $null = $VMNetworkAdapter | + Set-VMNetworkAdapter ` + -DynamicMacAddress + } + else + { + $null = $VMNetworkAdapter | + Set-VMNetworkAdapter ` + -StaticMacAddress $VMAdapter.MACAddress + } # if + + # Enable Device Naming if supported by VM version and generation + if (((Get-Command -Name Set-VMNetworkAdapter).Parameters.ContainsKey('DeviceNaming')) -and (($VM.Version -ge "6.2") -and ($VM.Generation -eq 2))) + { + $null = $VMNetworkAdapter | + Set-VMNetworkAdapter ` + -DeviceNaming On + } # if + if ($VMAdapter.MACAddressSpoofing -ne $VMNetworkAdapter.MACAddressSpoofing) + { + $MACAddressSpoofing = if ($VMAdapter.MACAddressSpoofing) {'On'} else {'Off'} + $null = $VMNetworkAdapter | + Set-VMNetworkAdapter ` + -MacAddressSpoofing $MACAddressSpoofing + } # if + } # foreach + + Install-LabVM ` + -Lab $Lab ` + -VM $VM + } # foreach +} # Initialize-LabVM diff --git a/source/public/Initialize-LabVmTemplate.ps1 b/source/public/Initialize-LabVmTemplate.ps1 new file mode 100644 index 00000000..6051ee6e --- /dev/null +++ b/source/public/Initialize-LabVmTemplate.ps1 @@ -0,0 +1,228 @@ +function Initialize-LabVMTemplate +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabVMTemplate[]] $VMTemplates, + + [Parameter( + Position = 4)] + [LabVMTemplateVHD[]] $VMTemplateVHDs + ) + + # if VMTeplates array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMTemplates')) + { + [LabVMTemplate[]] $VMTemplates = Get-LabVMTemplate ` + @PSBoundParameters + } + + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + # Check each Parent VHD exists in the Parent VHDs folder for the + # Lab. If it isn't, try and copy it from the SourceVHD + # Location. + foreach ($VMTemplate in $VMTemplates) + { + if ($Name -and ($VMTemplate.Name -notin $Name)) + { + # A names list was passed but this VM Template wasn't included + continue + } # if + + if (-not (Test-Path $VMTemplate.ParentVhd)) + { + # The Parent VHD isn't in the VHD Parent folder + # so copy it there, optimize it and mark it read-only. + if (-not (Test-Path $VMTemplate.SourceVhd)) + { + # The source VHD could not be found. + $exceptionParameters = @{ + errorId = 'TemplateSourceVHDNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.TemplateSourceVHDNotFoundError ` + -f $VMTemplate.Name, $VMTemplate.sourcevhd) + } + New-LabException @exceptionParameters + } + + Write-LabMessage -Message $($LocalizedData.CopyingTemplateSourceVHDMessage ` + -f $VMTemplate.SourceVhd, $VMTemplate.ParentVhd) + Copy-Item ` + -Path $VMTemplate.SourceVhd ` + -Destination $VMTemplate.ParentVhd + + # Add any packages to the template if required + if (-not [System.String]::IsNullOrWhitespace($VMTemplate.Packages)) + { + if ($VMTemplate.OSType -ne [LabOStype]::Nano) + { + # Mount the Template Boot VHD so that files can be loaded into it + Write-LabMessage -Message $($LocalizedData.MountingTemplateBootDiskMessage ` + -f $VMTemplate.Name, $VMTemplate.ParentVhd) + + # Create a mount point for mounting the Boot VHD + [System.String] $MountPoint = Join-Path ` + -Path (Split-Path -Path $VMTemplate.ParentVHD) ` + -ChildPath 'Mount' + + if (-not (Test-Path -Path $MountPoint -PathType Container)) + { + $null = New-Item ` + -Path $MountPoint ` + -ItemType Directory + } + + # Mount the VHD to the Mount point + $null = Mount-WindowsImage ` + -ImagePath $VMTemplate.parentvhd ` + -Path $MountPoint ` + -Index 1 + + # Get the list of Packages to apply + $ApplyPackages = @($VMTemplate.Packages -split ',') + + # Get the list of Lab Resource MSUs + $ResourceMSUs = Get-LabResourceMSU ` + -Lab $Lab + + foreach ($Package in $ApplyPackages) + { + # Find the package in the Resources + [System.Boolean] $Found = $false + foreach ($ResourceMSU in $ResourceMSUs) + { + if ($ResourceMSU.Name -eq $Package) + { + # Found the package + $Found = $true + break + } # if + } # foreach + if (-not $Found) + { + # Dismount before throwing the error + Write-LabMessage -Message $($LocalizedData.DismountingTemplateBootDiskMessage ` + -f $VMTemplate.Name, $VMTemplate.parentvhd) + $null = Dismount-WindowsImage ` + -Path $MountPoint ` + -Save + $null = Remove-Item ` + -Path $MountPoint ` + -Recurse ` + -Force + + $exceptionParameters = @{ + errorId = 'PackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageNotFoundError ` + -f $Package) + } + New-LabException @exceptionParameters + } # if + + $PackagePath = $ResourceMSU.Filename + if (-not (Test-Path -Path $PackagePath)) + { + # Dismount before throwing the error + Write-LabMessage -Message $($LocalizedData.DismountingTemplateBootDiskMessage ` + -f $VMTemplate.Name, $VMTemplate.ParentVhd) + $null = Dismount-WindowsImage ` + -Path $MountPoint ` + -Save + $null = Remove-Item ` + -Path $MountPoint ` + -Recurse ` + -Force + + $exceptionParameters = @{ + errorId = 'PackageMSUNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageMSUNotFoundError ` + -f $Package, $PackagePath) + } + New-LabException @exceptionParameters + } # if + + # Apply a Pacakge + Write-LabMessage -Message $($LocalizedData.ApplyingTemplateBootDiskFileMessage ` + -f $VMTemplate.Name, $Package, $PackagePath) + + $null = Add-WindowsPackage ` + -PackagePath $PackagePath ` + -Path $MountPoint + } # foreach + + # Dismount the VHD + Write-LabMessage -Message $($LocalizedData.DismountingTemplateBootDiskMessage ` + -f $VMTemplate.Name, $VMTemplate.parentvhd) + $null = Dismount-WindowsImage ` + -Path $MountPoint ` + -Save + $null = Remove-Item ` + -Path $MountPoint ` + -Recurse ` + -Force + } # if + } # if + + Write-LabMessage -Message $($LocalizedData.OptimizingParentVHDMessage ` + -f $VMTemplate.parentvhd) + Set-ItemProperty ` + -Path $VMTemplate.parentvhd ` + -Name IsReadOnly ` + -Value $false + Optimize-VHD ` + -Path $VMTemplate.parentvhd ` + -Mode Full + Write-LabMessage -Message $($LocalizedData.SettingParentVHDReadonlyMessage ` + -f $VMTemplate.parentvhd) + Set-ItemProperty ` + -Path $VMTemplate.parentvhd ` + -Name IsReadOnly ` + -Value $true + } + Else + { + Write-LabMessage -Message $($LocalizedData.SkipParentVHDFileMessage ` + -f $VMTemplate.Name, $VMTemplate.parentvhd) + } + + # if this is a Nano Server template, we need to ensure that the + # NanoServerPackages folder is copied to our Lab folder + if ($VMTemplate.OSType -eq [LabOStype]::Nano) + { + [System.String] $VHDPackagesFolder = Join-Path ` + -Path (Split-Path -Path $VMTemplate.SourceVhd -Parent)` + -ChildPath 'NanoServerPackages' + + [System.String] $NanoPackagesFolder = Join-Path ` + -Path $LabPath ` + -ChildPath 'NanoServerPackages' + + if (-not (Test-Path -Path $NanoPackagesFolder -Type Container)) + { + Write-LabMessage -Message $($LocalizedData.CachingNanoServerPackagesMessage ` + -f $VHDPackagesFolder, $NanoPackagesFolder) + Copy-Item ` + -Path $VHDPackagesFolder ` + -Destination $LabPath ` + -Recurse ` + -Force + } + } + } +} diff --git a/source/public/Initialize-LabVmTemplateVhd.ps1 b/source/public/Initialize-LabVmTemplateVhd.ps1 new file mode 100644 index 00000000..c8eacfdc --- /dev/null +++ b/source/public/Initialize-LabVmTemplateVhd.ps1 @@ -0,0 +1,364 @@ +function Initialize-LabVMTemplateVHD +{ + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabVMTemplateVHD[]] $VMTemplateVHDs + ) + + # if VMTeplateVHDs array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMTemplateVHDs')) + { + [LabVMTemplateVHD[]] $VMTemplateVHDs = Get-LabVMTemplateVHD ` + @PSBoundParameters + } # if + + # if there are no VMTemplateVHDs just return + if ($null -eq $VMTemplateVHDs) + { + return + } # if + + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + # Is an alternate path to DISM specified? + if ($Lab.labbuilderconfig.settings.DismPath) + { + $DismPath = Join-Path ` + -Path $Lab.labbuilderconfig.settings.DismPath ` + -ChildPath 'dism.exe' + if (-not (Test-Path -Path $DismPath)) + { + $exceptionParameters = @{ + errorId = 'FileNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.FileNotFoundError ` + -f 'alternate DISM.EXE', $DismPath) + } + New-LabException @exceptionParameters + } + } + + foreach ($VMTemplateVHD in $VMTemplateVHDs) + { + [System.String] $TemplateVHDName = $VMTemplateVHD.Name + if ($Name -and ($TemplateVHDName -notin $Name)) + { + # A names list was passed but this VM Template VHD wasn't included + continue + } # if + + [System.String] $VHDPath = $VMTemplateVHD.VHDPath + + if (Test-Path -Path ($VHDPath)) + { + # The SourceVHD already exists + Write-LabMessage -Message $($LocalizedData.SkipVMTemplateVHDFileMessage ` + -f $TemplateVHDName, $VHDPath) + + continue + } # if + + # Create the VHD + Write-LabMessage -Message $($LocalizedData.CreatingVMTemplateVHDMessage ` + -f $TemplateVHDName, $VHDPath) + + # Check the ISO exists. + [System.String] $ISOPath = $VMTemplateVHD.ISOPath + if (-not (Test-Path -Path $ISOPath)) + { + $exceptionParameters = @{ + errorId = 'VMTemplateVHDISOPathNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMTemplateVHDISOPathNotFoundError ` + -f $TemplateVHDName, $ISOPath) + } + New-LabException @exceptionParameters + } # if + + # Mount the ISO so we can read the files. + Write-LabMessage -Message $($LocalizedData.MountingVMTemplateVHDISOMessage ` + -f $TemplateVHDName, $ISOPath) + + $null = Mount-DiskImage ` + -ImagePath $ISOPath ` + -StorageType ISO ` + -Access Readonly + + # Refresh the PS Drive list to make sure the new drive can be detected + Get-PSDrive ` + -PSProvider FileSystem + + $DiskImage = Get-DiskImage -ImagePath $ISOPath + $Volume = Get-Volume -DiskImage $DiskImage + if (-not $Volume) + { + $exceptionParameters = @{ + errorId = 'VolumeNotAvailableAfterMountError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VolumeNotAvailableAfterMountError ` + -f $ISOPath) + } + New-LabException @exceptionParameters + } + [System.String] $DriveLetter = $Volume.DriveLetter + if (-not $DriveLetter) + { + $exceptionParameters = @{ + errorId = 'DriveLetterNotAssignedError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.DriveLetterNotAssignedError ` + -f $ISOPath) + } + New-LabException @exceptionParameters + } + [System.String] $ISODrive = "$([System.String]$DriveLetter):" + + # Determine the path to the WIM + [System.String] $SourcePath = "$ISODrive\Sources\Install.WIM" + if ($VMTemplateVHD.OSType -eq [LabOStype]::Nano) + { + $SourcePath = "$ISODrive\Nanoserver\NanoServer.WIM" + } # if + + # This will have to change depending on the version + # of Convert-WindowsImage being used. + [System.String] $VHDFormat = $VMTemplateVHD.VHDFormat + [System.String] $VHDType = $VMTemplateVHD.VHDType + [System.String] $VHDDiskLayout = 'UEFI' + if ($VMTemplateVHD.Generation -eq 1) + { + $VHDDiskLayout = 'BIOS' + } # if + + [System.String] $Edition = $VMTemplateVHD.Edition + # if edition is not set then use Get-WindowsImage to get the name + # of the first image in the WIM. + if ([System.String]::IsNullOrWhiteSpace($Edition)) + { + $Edition = (Get-WindowsImage ` + -ImagePath $SourcePath ` + -Index 1).ImageName + } # if + + $ConvertParams = @{ + sourcepath = $SourcePath + vhdpath = $VHDpath + vhdformat = $VHDFormat + # Convert-WindowsImage doesn't support creating different VHDTypes + # vhdtype = $VHDType + edition = $Edition + disklayout = $VHDDiskLayout + erroraction = 'Stop' + } + + # Set the size + if ($null -ne $VMTemplateVHD.VHDSize) + { + $ConvertParams += @{ + sizebytes = $VMTemplateVHD.VHDSize + } + } # if + + # Are any features specified? + if (-not [System.String]::IsNullOrWhitespace($VMTemplateVHD.Features)) + { + $Features = @($VMTemplateVHD.Features -split ',') + $ConvertParams += @{ + feature = $Features + } + } # if + + # Is an alternate path to DISM specified? + if ($DismPath) + { + $ConvertParams += @{ + DismPath = $DismPath + } + } + + # Perform Nano Server package prep + if ($VMTemplateVHD.OSType -eq [LabOStype]::Nano) + { + # Make a copy of the all the Nano packages in the VHD root folder + # So that if any VMs need to add more packages they are accessible + # once the ISO has been dismounted. + [System.String] $VHDFolder = Split-Path ` + -Path $VHDPath ` + -Parent + + [System.String] $NanoPackagesFolder = Join-Path ` + -Path $VHDFolder ` + -ChildPath 'NanoServerPackages' + + if (-not (Test-Path -Path $NanoPackagesFolder -Type Container)) + { + Write-LabMessage -Message $($LocalizedData.CachingNanoServerPackagesMessage ` + -f "$ISODrive\Nanoserver\Packages", $NanoPackagesFolder) + Copy-Item ` + -Path "$ISODrive\Nanoserver\Packages" ` + -Destination $VHDFolder ` + -Recurse ` + -Force + Rename-Item ` + -Path "$VHDFolder\Packages" ` + -NewName 'NanoServerPackages' + } # if + } # if + + # Do we need to add any packages? + if (-not [System.String]::IsNullOrWhitespace($VMTemplateVHD.Packages)) + { + $Packages = @() + + # Get the list of Lab Resource MSUs + $ResourceMSUs = Get-LabResourceMSU ` + -Lab $Lab + + try + { + foreach ($Package in @($VMTemplateVHD.Packages -split ',')) + { + if (([System.IO.Path]::GetExtension($Package) -eq '.cab') ` + -and ($VMTemplateVHD.OSType -eq [LabOSType]::Nano)) + { + # This is a Nano Server .CAB pacakge + # Generate the path to the Nano Package + $PackagePath = Join-Path ` + -Path $NanoPackagesFolder ` + -ChildPath $Package + # Does it exist? + if (-not (Test-Path -Path $PackagePath)) + { + $exceptionParameters = @{ + errorId = 'NanoPackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NanoPackageNotFoundError ` + -f $PackagePath) + } + New-LabException @exceptionParameters + } + $Packages += @( $PackagePath ) + + # Generate the path to the Nano Language Package + $PackageLangFile = $Package -replace '.cab', "_$($Script:NanoPackageCulture).cab" + $PackageLangPath = Join-Path ` + -Path $NanoPackagesFolder ` + -ChildPath "$($Script:NanoPackageCulture)\$PackageLangFile" + # Does it exist? + if (-not (Test-Path -Path $PackageLangPath)) + { + $exceptionParameters = @{ + errorId = 'NanoPackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.NanoPackageNotFoundError ` + -f $PackageLangPath) + } + New-LabException @exceptionParameters + } + $Packages += @( $PackageLangPath ) + } + else + { + # Tihs is a ResourceMSU type package + [System.Boolean] $Found = $false + foreach ($ResourceMSU in $ResourceMSUs) + { + if ($ResourceMSU.Name -eq $Package) + { + # Found the package + $Found = $true + break + } # if + } # foreach + if (-not $Found) + { + $exceptionParameters = @{ + errorId = 'PackageNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageNotFoundError ` + -f $Package) + } + New-LabException @exceptionParameters + } # if + + $PackagePath = $ResourceMSU.Filename + if (-not (Test-Path -Path $PackagePath)) + { + $exceptionParameters = @{ + errorId = 'PackageMSUNotFoundError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.PackageMSUNotFoundError ` + -f $Package, $PackagePath) + } + New-LabException @exceptionParameters + } # if + $Packages += @( $PackagePath ) + } + } # foreach + $ConvertParams += @{ + Package = $Packages + } + } + catch + { + # Dismount Disk Image before throwing exception + $null = Dismount-DiskImage ` + -ImagePath $ISOPath + + Throw $_ + } # try + } # if + + Write-LabMessage -Message ($LocalizedData.ConvertingWIMtoVHDMessage ` + -f $SourcePath, $VHDPath, $VHDFormat, $Edition, $VHDPartitionStyle, $VHDType) + + # Work around an issue with Convert-WindowsImage not seeing the drive + Get-PSDrive ` + -PSProvider FileSystem + + # Dot source the Convert-WindowsImage script + # Should only be done once + if (-not (Test-Path -Path Function:Convert-WindowsImage)) + { + . $Script:SupportConvertWindowsImagePath + } # if + + try + { + # Call the Convert-WindowsImage script + Convert-WindowsImage @ConvertParams + } # try + catch + { + $exceptionParameters = @{ + errorId = 'ConvertWindowsImageError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.ConvertWindowsImageError ` + -f $ISOPath, $SourcePath, $Edition, $VHDFormat, $_.Exception.Message) + } + New-LabException @exceptionParameters + } # catch + finally + { + # Dismount the ISO. + Write-LabMessage -Message $($LocalizedData.DismountingVMTemplateVHDISOMessage ` + -f $TemplateVHDName, $ISOPath) + + $null = Dismount-DiskImage ` + -ImagePath $ISOPath + } # finally + } # endfor +} # Initialize-LabVMTemplateVHD diff --git a/source/public/Install-Lab.ps1 b/source/public/Install-Lab.ps1 new file mode 100644 index 00000000..6d9c8bc4 --- /dev/null +++ b/source/public/Install-Lab.ps1 @@ -0,0 +1,196 @@ +function Install-Lab +{ + [CmdLetBinding(DefaultParameterSetName="Lab")] + param + ( + [parameter( + Position=1, + ParameterSetName="File", + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $ConfigPath, + + [parameter( + Position=2, + ParameterSetName="File")] + [ValidateNotNullOrEmpty()] + [System.String] $LabPath, + + [Parameter( + Position=3, + ParameterSetName="Lab", + Mandatory=$true, + ValueFromPipeline=$true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position=4)] + [Switch] $CheckEnvironment, + + [Parameter( + Position=5)] + [Switch] $Force, + + [Parameter( + Position=6)] + [Switch] $OffLine + + ) # Param + + begin + { + # Create a splat array containing force if it is set + $ForceSplat = @{} + + if ($PSBoundParameters.ContainsKey('Force')) + { + $ForceSplat = @{ Force = $true } + } # if + + # Remove some PSBoundParameters so we can Splat + $null = $PSBoundParameters.Remove('CheckEnvironment') + $null = $PSBoundParameters.Remove('Force') + + if ($CheckEnvironment) + { + # Check Hyper-V + Install-LabHyperV ` + -ErrorAction Stop + } # if + + # Ensure WS-Man is enabled + Enable-LabWSMan ` + @ForceSplat ` + -ErrorAction Stop + + if (!($PSBoundParameters.ContainsKey('OffLine'))) + { + # Install Package Providers + Install-LabPackageProvider ` + @ForceSplat ` + -ErrorAction Stop + + # Register Package Sources + Register-LabPackageSource ` + @ForceSplat ` + -ErrorAction Stop + } + + $null = $PSBoundParameters.Remove('Offline') + + if ($PSCmdlet.ParameterSetName -eq 'File') + { + # Read the configuration + $Lab = Get-Lab ` + @PSBoundParameters ` + -ErrorAction Stop + } # if + } # begin + + process + { + # Initialize the core Lab components + # Check Lab Folder structure + Write-LabMessage -Message $($LocalizedData.InitializingLabFoldersMesage) + + # Check folders are defined + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + if (-not (Test-Path -Path $LabPath)) + { + Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` + -f 'LabPath',$LabPath) + + $null = New-Item ` + -Path $LabPath ` + -Type Directory + } + + [System.String] $VHDParentPath = $Lab.labbuilderconfig.settings.vhdparentpathfull + + if (-not (Test-Path -Path $VHDParentPath)) + { + Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` + -f 'VHDParentPath',$VHDParentPath) + + $null = New-Item ` + -Path $VHDParentPath ` + -Type Directory + } + + [System.String] $ResourcePath = $Lab.labbuilderconfig.settings.resourcepathfull + + if (-not (Test-Path -Path $ResourcePath)) + { + Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` + -f 'ResourcePath',$ResourcePath) + + $null = New-Item ` + -Path $ResourcePath ` + -Type Directory + } + + # Initialize the Lab Management Switch + Initialize-LabManagementSwitch ` + -Lab $Lab ` + -ErrorAction Stop + + # Download any Resource Modules required by this Lab + $ResourceModules = Get-LabResourceModule ` + -Lab $Lab + Initialize-LabResourceModule ` + -Lab $Lab ` + -ResourceModules $ResourceModules ` + -ErrorAction Stop + + # Download any Resource MSUs required by this Lab + $ResourceMSUs = Get-LabResourceMSU ` + -Lab $Lab + Initialize-LabResourceMSU ` + -Lab $Lab ` + -ResourceMSUs $ResourceMSUs ` + -ErrorAction Stop + + # Initialize the Switches + $Switches = Get-LabSwitch ` + -Lab $Lab + Initialize-LabSwitch ` + -Lab $Lab ` + -Switches $Switches ` + -ErrorAction Stop + + # Initialize the VM Template VHDs + $VMTemplateVHDs = Get-LabVMTemplateVHD ` + -Lab $Lab + Initialize-LabVMTemplateVHD ` + -Lab $Lab ` + -VMTemplateVHDs $VMTemplateVHDs ` + -ErrorAction Stop + + # Initialize the VM Templates + $VMTemplates = Get-LabVMTemplate ` + -Lab $Lab + Initialize-LabVMTemplate ` + -Lab $Lab ` + -VMTemplates $VMTemplates ` + -ErrorAction Stop + + # Initialize the VMs + $VMs = Get-LabVM ` + -Lab $Lab ` + -VMTemplates $VMTemplates ` + -Switches $Switches + Initialize-LabVM ` + -Lab $Lab ` + -VMs $VMs ` + -ErrorAction Stop + + Write-LabMessage -Message $($LocalizedData.LabInstallCompleteMessage ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath) + } # process + + end + { + } # end +} # Install-Lab diff --git a/source/public/Install-LabVm.ps1 b/source/public/Install-LabVm.ps1 new file mode 100644 index 00000000..46173164 --- /dev/null +++ b/source/public/Install-LabVm.ps1 @@ -0,0 +1,90 @@ +function Install-LabVM +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position=1, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position=2)] + [ValidateNotNullOrEmpty()] + [LabVM] $VM + ) + + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + # The VM is now ready to be started + if ((Get-VM -Name $VM.Name).State -eq 'Off') + { + Write-LabMessage -Message $($LocalizedData.StartingVMMessage ` + -f $VM.Name) + + Start-VM -VMName $VM.Name + } # if + + # We only perform this section of VM Initialization (DSC, Cert, etc) with Server OS + if ($VM.DSC.ConfigFile) + { + # Has this VM been initialized before (do we have a cert for it) + if (-not (Test-Path "$LabPath\$($VM.Name)\LabBuilder Files\$Script:DSCEncryptionCert")) + { + # No, so check it is initialized and download the cert if required + if (Wait-LabVMInitializationComplete -VM $VM -ErrorAction Continue) + { + Write-LabMessage -Message $($LocalizedData.CertificateDownloadStartedMessage ` + -f $VM.Name) + + if ($VM.CertificateSource -eq [LabCertificateSource]::Guest) + { + if (Recieve-LabSelfSignedCertificate -Lab $Lab -VM $VM) + { + Write-LabMessage -Message $($LocalizedData.CertificateDownloadCompleteMessage ` + -f $VM.Name) + } + else + { + $exceptionParameters = @{ + errorId = 'CertificateDownloadError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.CertificateDownloadError ` + -f $VM.name) + } + New-LabException @exceptionParameters + } # if + } # if + } + else + { + $exceptionParameters = @{ + errorId = 'InitializationDidNotCompleteError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.InitializationDidNotCompleteError ` + -f $VM.name) + } + New-LabException @exceptionParameters + } # if + } # if + + if ($VM.OSType -in ([LabOStype]::Nano)) + { + # Copy ODJ Files if it Exists + Copy-LabOdjFile ` + -Lab $Lab ` + -VM $VM + } # if + + # Create any DSC Files for the VM + Initialize-LabDSC ` + -Lab $Lab ` + -VM $VM + + # Attempt to start DSC on the VM + Start-LabDSC ` + -Lab $Lab ` + -VM $VM + } # if +} # Install-LabVM diff --git a/source/public/New-Lab.ps1 b/source/public/New-Lab.ps1 new file mode 100644 index 00000000..64990739 --- /dev/null +++ b/source/public/New-Lab.ps1 @@ -0,0 +1,157 @@ +function New-Lab +{ + [CmdLetBinding( + SupportsShouldProcess = $true)] + [OutputType([XML])] + param + ( + [Parameter( + Position=1, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $ConfigPath, + + [Parameter( + Position=2, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $LabPath, + + [Parameter( + Position=3, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $Name, + + [Parameter( + Position=4)] + [ValidateNotNullOrEmpty()] + [System.String] $Version = '1.0', + + [Parameter( + Position=5)] + [ValidateNotNullOrEmpty()] + [System.String] $Id, + + [Parameter( + Position=6)] + [ValidateNotNullOrEmpty()] + [System.String] $Description, + + [Parameter( + Position=7)] + [ValidateNotNullOrEmpty()] + [System.String] $DomainName, + + [Parameter( + Position=8)] + [ValidateNotNullOrEmpty()] + [System.String] $Email + ) # Param + + # Determine the full Lab Path + if (-not [System.IO.Path]::IsPathRooted($LabPath)) + { + $LabPath = Join-Path ` + -Path Get-Location ` + -ChildPath $LabPath + } # if + + # Does the Lab Path exist? + if (Test-Path -Path $LabPath -Type Container) + { + # It does - exit if the user declines + if (-not $PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldOverwriteLab ` + -f $LabPath ))) + { + return + } + } + else + { + Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` + -f 'LabPath',$LabPath) + + $null = New-Item ` + -Path $LabPath ` + -Type Directory + } # if + + # Determine the full Lab configuration Path + if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) + { + $ConfigPath = Join-Path ` + -Path $LabPath ` + -ChildPath $ConfigPath + } # if + + # Does the lab configuration path already exist? + if (Test-Path -Path $ConfigPath) + { + # It does - exit if the user declines + if (-not $PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldOverwriteLabConfig ` + -f $ConfigPath ))) + { + return + } + } # if + + # Get the Config Template into a variable + $Content = Get-Content ` + -Path $Script:ConfigurationXMLTemplate + + # The XML passes the Schema check so load it. + [XML] $Lab = New-Object System.Xml.XmlDocument + $Lab.PreserveWhitespace = $true + $Lab.LoadXML($Content) + + # Populate the Lab Entries + $Lab.labbuilderconfig.name = $Name + $Lab.labbuilderconfig.version = $Version + $Lab.labbuilderconfig.settings.labpath = $LabPath + if ($PSBoundParameters.ContainsKey('Id')) + { + $Lab.labbuilderconfig.settings.SetAttribute('Id',$Id) + } # if + if ($PSBoundParameters.ContainsKey('Description')) + { + $Lab.labbuilderconfig.description = $Description + } # if + if ($PSBoundParameters.ContainsKey('DomainName')) + { + $Lab.labbuilderconfig.settings.SetAttribute('DomainName',$DomainName) + } # if + if ($PSBoundParameters.ContainsKey('Email')) + { + $Lab.labbuilderconfig.settings.SetAttribute('Email',$Email) + } # if + + # Save Configiration XML + $Lab.Save($ConfigPath) + + # Create ISOFiles folder + $null = New-Item ` + -Path (Join-Path -Path $LabPath -ChildPath 'ISOFiles')` + -Type Directory ` + -ErrorAction SilentlyContinue + + # Create VDFFiles folder + $null = New-Item ` + -Path (Join-Path -Path $LabPath -ChildPath 'VHDFiles')` + -Type Directory ` + -ErrorAction SilentlyContinue + + # Copy the DSCLibrary + $null = Copy-Item ` + -Path $Script:DSCLibraryPath ` + -Destination $LabPath ` + -Recurse ` + -Force ` + -ErrorAction SilentlyContinue + + Return (Get-Lab ` + -ConfigPath $ConfigPath ` + -LabPath $LabPath) +} # New-Lab diff --git a/source/public/Remove-LabSwitch.ps1 b/source/public/Remove-LabSwitch.ps1 new file mode 100644 index 00000000..38cc5366 --- /dev/null +++ b/source/public/Remove-LabSwitch.ps1 @@ -0,0 +1,138 @@ +function Remove-LabSwitch +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] + $Name, + + [Parameter( + Position = 3)] + [LabSwitch[]] + $Switches, + + [Parameter( + Position = 4)] + [Switch] + $RemoveExternal + ) + + $PSBoundParameters.Remove('RemoveExternal') + + # if switches were not passed so pull them + if (-not $PSBoundParameters.ContainsKey('switches')) + { + [LabSwitch[]] $Switches = Get-LabSwitch ` + @PSBoundParameters + } + + # Delete Hyper-V Switches + foreach ($VMSwitch in $Switches) + { + if ($Name -and ($VMSwitch.name -notin $Name)) + { + # A names list was passed but this swtich wasn't included + continue + } # if + + $existingVMSwitch = Get-VMSwitch | Where-Object -Property Name -eq $VMSwitch.Name + + if ($existingVMSwitch.Count -ne 0) + { + $SwitchName = $VMSwitch.Name + + if (-not $SwitchName) + { + $exceptionParameters = @{ + errorId = 'SwitchNameIsEmptyError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.SwitchNameIsEmptyError) + } + New-LabException @exceptionParameters + } + + [LabSwitchType] $SwitchType = $VMSwitch.Type + + Write-LabMessage -Message $($LocalizedData.DeleteingVirtualSwitchMessage ` + -f $SwitchType, $SwitchName) + + Switch ($SwitchType) + { + 'External' + { + if ($VMSwitch.Adapters) + { + $VMSwitch.Adapters.foreach( { + $null = Remove-VMNetworkAdapter ` + -SwitchName $SwitchName ` + -Name $_.Name ` + -ManagementOS ` + -ErrorAction SilentlyContinue + } ) + } # if + + if ($RemoveExternal) + { + Remove-VMSwitch ` + -Name $SwitchName + } + break + } # 'External' + + 'Private' + { + Remove-VMSwitch ` + -Name $SwitchName + break + } # 'Private' + + 'Internal' + { + if ($VMSwitch.Adapters) + { + $VMSwitch.Adapters.foreach( { + $null = Remove-VMNetworkAdapter ` + -SwitchName $SwitchName ` + -Name $_.Name ` + -ManagementOS ` + -ErrorAction SilentlyContinue + } ) + } # if + + Remove-VMSwitch ` + -Name $SwitchName + break + } # 'Internal' + + 'NAT' + { + Remove-NetNat ` + -Name $SwitchName + Remove-VMSwitch ` + -Name $SwitchName + break + } # 'Internal' + + Default + { + $exceptionParameters = @{ + errorId = 'UnknownSwitchTypeError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.UnknownSwitchTypeError ` + -f $SwitchType, $SwitchName) + } + New-LabException @exceptionParameters + } + } # Switch + } # if + } # foreach +} diff --git a/source/public/Remove-LabVMTemplate.ps1 b/source/public/Remove-LabVMTemplate.ps1 new file mode 100644 index 00000000..9046bbc7 --- /dev/null +++ b/source/public/Remove-LabVMTemplate.ps1 @@ -0,0 +1,50 @@ +function Remove-LabVMTemplate +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabVMTemplate[]] $VMTemplates + ) + + # if VMTeplates array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMTemplates')) + { + $VMTemplates = Get-LabVMTemplate ` + @PSBoundParameters + } # if + foreach ($VMTemplate in $VMTemplates) + { + if ($Name -and ($VMTemplate.Name -notin $Name)) + { + # A names list was passed but this VM Template wasn't included + continue + } # if + + if (Test-Path $VMTemplate.ParentVhd) + { + Set-ItemProperty ` + -Path $VMTemplate.parentvhd ` + -Name IsReadOnly ` + -Value $false + Write-LabMessage -Message $($LocalizedData.DeletingParentVHDMessage ` + -f $VMTemplate.ParentVhd) + Remove-Item ` + -Path $VMTemplate.ParentVhd ` + -Confirm:$false ` + -Force + } # if + } # foreach +} diff --git a/source/public/Remove-LabVm.ps1 b/source/public/Remove-LabVm.ps1 new file mode 100644 index 00000000..3ad7328b --- /dev/null +++ b/source/public/Remove-LabVm.ps1 @@ -0,0 +1,92 @@ +function Remove-LabVM +{ + [CmdLetBinding()] + param + ( + [Parameter( + Position=1, + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position=2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position=3)] + [LabVM[]] $VMs, + + [Parameter( + Position=4)] + [Switch] $RemoveVMFolder + ) + + # if VMs array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMs')) + { + $null = $PSBoundParameters.Remove('RemoveVMFolder') + [LabVM[]] $VMs = Get-LabVM ` + @PSBoundParameters + } # if + + $CurrentVMs = Get-VM + + # Get the LabPath + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + foreach ($VM in $VMs) + { + if ($Name -and ($VM.Name -notin $Name)) + { + # A names list was passed but this VM wasn't included + continue + } # if + + if (($CurrentVMs | Where-Object -Property Name -eq $VM.Name).Count -ne 0) + { + # if the VM is running we need to shut it down. + if ((Get-VM -Name $VM.Name).State -eq 'Running') + { + Write-LabMessage -Message $($LocalizedData.StoppingVMMessage ` + -f $VM.Name) + + Stop-VM ` + -Name $VM.Name + # Wait for it to completely shut down and report that it is off. + Wait-LabVMOff ` + -VM $VM + } + + Write-LabMessage -Message $($LocalizedData.RemovingVMMessage ` + -f $VM.Name) + + # Now delete the actual VM + Get-VM ` + -Name $VM.Name | Remove-VM -Force -Confirm:$false + + Write-LabMessage -Message $($LocalizedData.RemovedVMMessage ` + -f $VM.Name) + } + else + { + Write-LabMessage -Message $($LocalizedData.VMNotFoundMessage ` + -f $VM.Name) + } + } + # Should we remove the VM Folder? + if ($RemoveVMFolder) + { + if (Test-Path -Path $VM.VMRootPath) + { + Write-LabMessage -Message $($LocalizedData.DeletingVMFolderMessage ` + -f $VM.Name) + + Remove-Item ` + -Path $VM.VMRootPath ` + -Recurse ` + -Force + } + } +} # Remove-LabVM diff --git a/source/public/Remove-LabVmTemplateVhd.ps1 b/source/public/Remove-LabVmTemplateVhd.ps1 new file mode 100644 index 00000000..8722cbd2 --- /dev/null +++ b/source/public/Remove-LabVmTemplateVhd.ps1 @@ -0,0 +1,56 @@ +function Remove-LabVMTemplateVHD +{ + param + ( + [Parameter( + Position = 1, + Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String[]] $Name, + + [Parameter( + Position = 3)] + [LabVMTemplateVHD[]] $VMTemplateVHDs + ) + + # if VMTeplateVHDs array not passed, pull it from config. + if (-not $PSBoundParameters.ContainsKey('VMTemplateVHDs')) + { + [LabVMTemplateVHD[]] $VMTemplateVHDs = Get-LabVMTemplateVHD ` + @PSBoundParameters + } # if + + # if there are no VMTemplateVHDs just return + if ($null -eq $VMTemplateVHDs) + { + return + } # if + + [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath + + foreach ($VMTemplateVHD in $VMTemplateVHDs) + { + [System.String] $TemplateVHDName = $VMTemplateVHD.Name + if ($Name -and ($TemplateVHDName -notin $Name)) + { + # A names list was passed but this VM Template VHD wasn't included + continue + } # if + + [System.String] $VHDPath = $VMTemplateVHD.VHDPath + + if (Test-Path -Path ($VHDPath)) + { + Remove-Item ` + -Path $VHDPath ` + -Force + Write-LabMessage -Message $($LocalizedData.DeletingVMTemplateVHDFileMessage ` + -f $TemplateVHDName, $VHDPath) + } # if + } # endfor +} # Remove-LabVMTemplateVHD diff --git a/source/public/Start-Lab.ps1 b/source/public/Start-Lab.ps1 new file mode 100644 index 00000000..60e07b59 --- /dev/null +++ b/source/public/Start-Lab.ps1 @@ -0,0 +1,174 @@ +function Start-Lab +{ + [CmdLetBinding(DefaultParameterSetName="Lab")] + param + ( + [parameter( + Position=1, + ParameterSetName="File", + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $ConfigPath, + + [parameter( + Position=2, + ParameterSetName="File")] + [ValidateNotNullOrEmpty()] + [System.String] $labPath, + + [Parameter( + Position=3, + ParameterSetName="Lab", + Mandatory=$true, + ValueFromPipeline=$true)] + [ValidateNotNullOrEmpty()] + $lab, + + [Parameter( + Position=4)] + [System.Int32] $StartupTimeout = $Script:StartupTimeout + ) # Param + + begin + { + # Remove some PSBoundParameters so we can Splat + $null = $PSBoundParameters.Remove('StartupTimeout') + + if ($PSCmdlet.ParameterSetName -eq 'File') + { + # Read the configuration + $lab = Get-Lab @PSBoundParameters + } # if + } # begin + + process + { + # Get the VMs + $vms = Get-LabVM ` + -Lab $lab + + # Get the bootorders by lowest first and ignoring 0 and call + $bootOrders = @( ($vms | + Where-Object -FilterScript { ($_.Bootorder -gt 0) } ).Bootorder ) + $bootPhases = @( ($bootOrders | Sort-Object -Unique) ) + + # Step through each of these "Bootphases" waiting for them to complete + foreach ($bootPhase in $bootPhases) + { + # Process this "Bootphase" + Write-LabMessage -Message $($LocalizedData.StartingBootPhaseVMsMessage ` + -f $bootPhase) + + # Get all VMs in this "Bootphase" + $bootVMs = @( $vms | Where-Object -FilterScript { ($_.BootOrder -eq $bootPhase) } ) + + $startPhase = Get-Date + $phaseComplete = $false + $phaseAllBooted = $true + $vmCount = $bootVMs.Count + $vmNumber = 0 + + <# + Loop through all the VMs in this "Bootphase" repeatedly + until timeout occurs or PhaseComplete is marked as complete + #> + while (-not $phaseComplete ` + -and ((Get-Date) -lt $startPhase.AddSeconds($StartupTimeout))) + { + # Get the VM to boot/check + $vm = $bootVMs[$vmNumber] + $vmName = $vm.Name + + # Get the actual Hyper-V VM object + $vmObject = Get-VM ` + -Name $vmName ` + -ErrorAction SilentlyContinue + + if ($vmObject) + { + # Start the VM if it is off + if ($vmObject.State -eq 'Off') + { + Write-LabMessage -Message $($LocalizedData.StartingVMMessage -f $vmName) + Start-VM -VM $vmObject + } # if + + <# + Use the allocation of a Management IP Address as an indicator + the machine has booted + #> + $managementIP = Get-LabVMManagementIPAddress ` + -Lab $lab ` + -VM $vm ` + -ErrorAction SilentlyContinue + + if (-not ($managementIP)) + { + # It has not booted + $phaseAllBooted = $false + } # if + } + else + { + # if the VM does not exist then throw a non-terminating exception + $exceptionParameters = @{ + errorId = 'VMDoesNotExistError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDoesNotExistError ` + -f $vmName) + + } + New-LabException @exceptionParameters + } # if + + $vmNumber++ + + if ($vmNumber -eq $vmCount) + { + <# + We have stepped through all VMs in this Phase so check + if all have booted, otherwise reset the loop. + #> + if ($phaseAllBooted) + { + <# + If we have gone through all VMs in this "Bootphase" + and they're all marked as booted then we can mark + this phase as complete and allow moving on to the next one + #> + Write-LabMessage -Message $($LocalizedData.AllBootPhaseVMsStartedMessage -f $bootPhase) + $phaseComplete = $true + } + else + { + $phaseAllBooted = $true + } # if + + # Reset the VM Loop + $vmNumber = 0 + } # if + } # while + + # Did we timeout? + if (-not ($phaseComplete)) + { + # Yes, throw an exception + $exceptionParameters = @{ + errorId = 'BootPhaseVMsTimeoutError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.BootPhaseStartVMsTimeoutError ` + -f $bootPhase) + + } + New-LabException @exceptionParameters + } # if + } # foreach + + Write-LabMessage -Message $($LocalizedData.LabStartCompleteMessage ` + -f $lab.labbuilderconfig.name,$lab.labbuilderconfig.settings.fullconfigpath) + } # process + + end + { + } # end +} # Start-Lab diff --git a/source/public/Stop-Lab.ps1 b/source/public/Stop-Lab.ps1 new file mode 100644 index 00000000..cb3f75d2 --- /dev/null +++ b/source/public/Stop-Lab.ps1 @@ -0,0 +1,146 @@ +function Stop-Lab +{ + [CmdLetBinding(DefaultParameterSetName="Lab")] + param + ( + [parameter( + Position=1, + ParameterSetName="File", + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $ConfigPath, + + [parameter( + Position=2, + ParameterSetName="File")] + [ValidateNotNullOrEmpty()] + [System.String] $LabPath, + + [Parameter( + Position=3, + ParameterSetName="Lab", + Mandatory=$true, + ValueFromPipeline=$true)] + [ValidateNotNullOrEmpty()] + $Lab + ) # Param + + begin + { + # Remove some PSBoundParameters so we can Splat + if ($PSCmdlet.ParameterSetName -eq 'File') + { + # Read the configuration + $Lab = Get-Lab ` + @PSBoundParameters + } # if + } # begin + + process + { + # Get the VMs + $vms = Get-LabVM ` + -Lab $Lab + + # Get the bootorders by highest first and ignoring 0 + $bootOrders = @( ($vms | + Where-Object -FilterScript { ($_.Bootorder -gt 0) } ).Bootorder ) + $bootPhases = @( ($bootOrders | Sort-Object -Unique -Descending) ) + + # Step through each of these "Bootphases" waiting for them to complete + foreach ($bootPhase in $bootPhases) + { + # Process this "Bootphase" + Write-LabMessage -Message $($LocalizedData.StoppingBootPhaseVMsMessage ` + -f $bootPhase) + + # Get all VMs in this "Bootphase" + $bootVMs = @( $vms | + Where-Object -FilterScript { ($_.BootOrder -eq $bootPhase) } ) + + $phaseComplete = $false + $phaseAllStopped = $true + $vmCount = $bootVMs.Count + $vmNumber = 0 + + # Loop through all the VMs in this "Bootphase" repeatedly + while (-not $phaseComplete) + { + # Get the VM to boot/check + $VM = $bootVMs[$vmNumber] + $vmName = $VM.Name + + # Get the actual Hyper-V VM object + $vmObject = Get-VM ` + -Name $vmName ` + -ErrorAction SilentlyContinue + + if (-not $vmObject) + { + # if the VM does not exist then throw a non-terminating exception + $exceptionParameters = @{ + errorId = 'VMDoesNotExistError' + errorCategory = 'InvalidArgument' + errorMessage = $($LocalizedData.VMDoesNotExistError ` + -f $vmName) + + } + New-LabException @exceptionParameters + } # if + + # Shutodwn the VM if it is off + if ($vmObject.State -eq 'Running') + { + Write-LabMessage -Message $($LocalizedData.StoppingVMMessage ` + -f $VMName) + $null = Stop-VM ` + -VM $vmObject ` + -Force ` + -ErrorAction Continue + } # if + + # Determine if the VM has stopped. + if ($vmObject -and (Get-VM -VMName $vmName).State -ne 'Off') + { + # It has not stopped + $phaseAllStopped = $false + } # if + + $vmNumber++ + + if ($vmNumber -eq $vmCount) + { + <# + We have stepped through all VMs in this Phase so check + if all have stopped, otherwise reset the loop. + #> + if ($phaseAllStopped) + { + <# + if we have gone through all VMs in this "Bootphase" + and they're all marked as stopped then we can mark + this phase as complete and allow moving on to the next one + #> + Write-LabMessage -Message $($LocalizedData.AllBootPhaseVMsStoppedMessage ` + -f $bootPhase) + $phaseComplete = $true + } + else + { + $phaseAllStopped = $true + } # if + + # Reset the VM Loop + $vmNumber = 0 + } # if + } # while + } # foreach + + Write-LabMessage -Message $($LocalizedData.LabStopCompleteMessage ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.fullconfigpath) + } # process + + end + { + } # end +} # Stop-Lab diff --git a/source/public/Uninstall-Lab.ps1 b/source/public/Uninstall-Lab.ps1 new file mode 100644 index 00000000..f90b279b --- /dev/null +++ b/source/public/Uninstall-Lab.ps1 @@ -0,0 +1,156 @@ +function Uninstall-Lab +{ + [CmdLetBinding(DefaultParameterSetName="Lab", + SupportsShouldProcess = $true, + ConfirmImpact = 'High')] + param + ( + [parameter( + Position=1, + ParameterSetName="File", + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $ConfigPath, + + [parameter( + Position=2, + ParameterSetName="File")] + [ValidateNotNullOrEmpty()] + [System.String] $LabPath, + + [Parameter( + Position=3, + ParameterSetName="Lab", + Mandatory=$true, + ValueFromPipeline=$true)] + [ValidateNotNullOrEmpty()] + $Lab, + + [Parameter( + Position=4)] + [Switch] $RemoveSwitch, + + [Parameter( + Position=5)] + [Switch] $RemoveVMTemplate, + + [Parameter( + Position=6)] + [Switch] $RemoveVMFolder, + + [Parameter( + Position=7)] + [Switch] $RemoveVMTemplateVHD, + + [Parameter( + Position=8)] + [Switch] $RemoveLabFolder + ) # Param + + begin + { + # Remove some PSBoundParameters so we can Splat + $null = $PSBoundParameters.Remove('RemoveSwitch') + $null = $PSBoundParameters.Remove('RemoveVMTemplate') + $null = $PSBoundParameters.Remove('RemoveVMFolder') + $null = $PSBoundParameters.Remove('RemoveVMTemplateVHD') + $null = $PSBoundParameters.Remove('RemoveLabFolder') + + if ($PSCmdlet.ParameterSetName -eq 'File') + { + # Read the configuration + $Lab = Get-Lab ` + @PSBoundParameters + } # if + } # begin + + process + { + if ($PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldUninstallLab ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) + { + # Remove the VMs + $VMSplat = @{} + if ($RemoveVMFolder) + { + $VMSplat += @{ RemoveVMFolder = $true } + } # if + $null = Remove-LabVM ` + -Lab $Lab ` + @VMSplat + + # Remove the VM Templates + if ($RemoveVMTemplate) + { + if ($PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldRemoveVMTemplate ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) + { + $null = Remove-LabVMTemplate ` + -Lab $Lab + } # if + } # if + + # Remove the VM Switches + if ($RemoveSwitch) + { + if ($PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldRemoveSwitch ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) + { + $null = Remove-LabSwitch ` + -Lab $Lab + } # if + } # if + + # Remove the VM Template VHDs + if ($RemoveVMTemplateVHD) + { + if ($PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldRemoveVMTemplateVHD ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) + { + $null = Remove-LabVMTemplateVHD ` + -Lab $Lab + } # if + } # if + + # Remove the Lab Folder + if ($RemoveLabFolder) + { + if (Test-Path -Path $Lab.labbuilderconfig.settings.labpath) + { + if ($PSCmdlet.ShouldProcess( 'LocalHost', ` + ($LocalizedData.ShouldRemoveLabFolder ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) + { + Remove-Item ` + -Path $Lab.labbuilderconfig.settings.labpath ` + -Recurse ` + -Force + } # if + } # if + } # if + + # Remove the LabBuilder Management Network switch + [System.String] $ManagementSwitchName = Get-LabManagementSwitchName ` + -Lab $Lab + if ((Get-VMSwitch | Where-Object -Property Name -eq $ManagementSwitchName).Count -ne 0) + { + $null = Remove-VMSwitch ` + -Name $ManagementSwitchName + + Write-LabMessage -Message $($LocalizedData.RemovingLabManagementSwitchMessage ` + -f $ManagementSwitchName) + } + + Write-LabMessage -Message $($LocalizedData.LabUninstallCompleteMessage ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ) + } # if + } # process + + end + { + } # end +} # Uninstall-Lab diff --git a/source/public/Update-Lab.ps1 b/source/public/Update-Lab.ps1 new file mode 100644 index 00000000..16a8c1a4 --- /dev/null +++ b/source/public/Update-Lab.ps1 @@ -0,0 +1,50 @@ +function Update-Lab +{ + [CmdLetBinding(DefaultParameterSetName="Lab")] + param + ( + [parameter( + Position=1, + ParameterSetName="File", + Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.String] $ConfigPath, + + [parameter( + Position=2, + ParameterSetName="File")] + [ValidateNotNullOrEmpty()] + [System.String] $LabPath, + + [Parameter( + Position=3, + ParameterSetName="Lab", + Mandatory=$true, + ValueFromPipeline=$true)] + [ValidateNotNullOrEmpty()] + $Lab + ) # Param + + begin + { + if ($PSCmdlet.ParameterSetName -eq 'File') + { + # Read the configuration + $Lab = Get-Lab ` + @PSBoundParameters + } # if + } # begin + + process + { + Install-Lab ` + @PSBoundParameters + + Write-LabMessage -Message $($LocalizedData.LabUpdateCompleteMessage ` + -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.fullconfigpath) + } # process + + end + { + } # end +} # Update-Lab diff --git a/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml b/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml new file mode 100644 index 00000000..348180b5 --- /dev/null +++ b/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_DCandDHCPOnly" + version="1.0"> + <description>Sample Windows Server 2012 R2 Lab Configuration DC and DHCP Only</description> + + <settings labid="LABBUILDER-DCANDDHCPONLY.COM " + domainname="LABBUILDER-DCANDDHCPONLY.COM" + email="admina@LABBUILDER-DCANDDHCPONLY.COM" + labpath="c:\vm\LABBUILDER-DCANDDHCPONLY.COM /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = ''; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml b/source/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml new file mode 100644 index 00000000..b17816e7 --- /dev/null +++ b/source/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_DCandDHCPOnly_NAT" + version="1.0"> + <description>Sample Windows Server 2012 R2 Lab Configuration DC and DHCP Only and using a NAT virtual switch.</description> + + <settings labid="LABBUILDER-NAT.COM " + domainname="LABBUILDER-NAT.COM" + email="admina@LABBUILDER-NAT.COM" + labpath="c:\vm\LABBUILDER-NAT.COM" + requiredwindowsbuild="14295" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="Domain NAT" + type="NAT" + vlan="5" + natsubnet="192.168.140.0/24" + natgatewayaddress="192.168.140.1" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Nat" + switchname="Domain Nat"> + <ipv4 address="192.168.140.10" + defaultgateway="192.168.140.1" + subnetmask="24" + dnsserver="192.168.140.10"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.140.50'; + End = '192.168.140.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.140.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.140.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.140.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.140.16'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.140.0'; + DNServerIPAddress = @('192.168.140.10'); + Router = '192.168.140.1'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Nat" + switchname="Domain Nat"> + <ipv4 address="192.168.140.16" + defaultgateway="192.168.140.1" + subnetmask="24" + dnsserver="192.168.140.10"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_DCandDHCPandEdge.xml b/source/samples/Sample_WS2012R2_DCandDHCPandEdge.xml new file mode 100644 index 00000000..34812101 --- /dev/null +++ b/source/samples/Sample_WS2012R2_DCandDHCPandEdge.xml @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_DCandDHCPandEdge" + version="1.0"> + <description>Sample Windows Server 2012 R2 Lab Configuration DC, DHCP and Edge.</description> + + <settings labid="LABBUILDER-DCDHCPEDGE.COM " + domainname="LABBUILDER-DCDHCPEDGE.COM" + email="admina@LABBUILDER-DCDHCPEDGE.COM" + labpath="c:\vm\LABBUILDER-DCDHCPEDGE.COM" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_DomainClustering.xml b/source/samples/Sample_WS2012R2_DomainClustering.xml new file mode 100644 index 00000000..fb1479bb --- /dev/null +++ b/source/samples/Sample_WS2012R2_DomainClustering.xml @@ -0,0 +1,719 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_DomainClustering" + version="1.0"> + <description>Sample Windows Server 2012 R2 Lab Configuration Domain with Cluster Server and NLB nodes</description> + + <settings labid="LABBUILDER-DOMAINCLUSTERING.COM " + domainname="LABBUILDER-DOMAINCLUSTERING.COM" + email="admin@LABBUILDER-DOMAINCLUSTERING.COM" + labpath="c:\vm\LABBUILDER-DOMAINCLUSTERING.COM" + requiredwindowsbuild="10586" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site A iSCSI" type="Private" vlan="3" /> + <switch name="Domain Private Site A LM" type="Private" vlan="4" /> + <switch name="Domain Private Site A SMB" type="Private" vlan="5" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DC2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC2" + bootorder="1"> + <dsc configname="DC_SECONDARY" + configfile="DC_SECONDARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCName = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.11" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::b" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.24'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000B'; + IPAddress = '192.168.128.25'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB1-CLS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000D'; + IPAddress = '192.168.128.35'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000C'; + IPAddress = '192.168.128.26'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB2-CLS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000E'; + IPAddress = '192.168.128.36'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000F'; + IPAddress = '192.168.128.28'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000010'; + IPAddress = '192.168.128.29'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOC3'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000011'; + IPAddress = '192.168.128.30'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOHV1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000F'; + IPAddress = '192.168.128.31'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOHV2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000010'; + IPAddress = '192.168.128.32'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LABBUILDER.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt\n32:http://pki.labbuilder.com/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FS1" + bootorder="4"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="40GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER_ISCSI" + configfile="MEMBER_FILESERVER_ISCSI.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + TargetName = 'sa-foc-target' + VirtualDisks = @( + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-witness.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 500MB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk1.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk2.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk3.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + } + ) + ClusterInitiatorIds = @( + 'Iqn:iqn.1991-05.com.microsoft:sa-foc1.labbuilder.com' + 'Iqn:iqn.1991-05.com.microsoft:sa-foc2.labbuilder.com' + 'Iqn:iqn.1991-05.com.microsoft:sa-foc3.labbuilder.com' + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.24" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::18" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.24" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::18" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-NLB1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-NLB1" + bootorder="5"> + <dsc configname="MEMBER_NLB" + configfile="MEMBER_NLB.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DCName = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.25" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::19" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A Cluster" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.35" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::35" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-NLB2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-NLB2" + bootorder="5"> + <dsc configname="MEMBER_NLB" + configfile="MEMBER_NLB.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DCName = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.26" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::1a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A Cluster" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.36" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::36" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FOC1" + bootorder="6"> + <dsc configname="MEMBER_FAILOVERCLUSTER_FS" + configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + ServerName = 'sa-fs1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.28' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.28" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::28" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.28" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::28" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.28" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::28" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOC2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FOC2" + bootorder="6"> + <dsc configname="MEMBER_FAILOVERCLUSTER_FS" + configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + ServerName = 'sa-fs1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.29' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.29" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::29" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.29" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::29" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.29" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::29" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOC3" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FOC3" + bootorder="6"> + <dsc configname="MEMBER_FAILOVERCLUSTER_FS" + configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + ServerName = 'sa-fs1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.30' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.30" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::30" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.30" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::30" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.30" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::30" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOHV1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FOHV1" + memorystartupbytes="4GB" + dynamicmemoryenabled="N" + exposevirtualizationextensions="Y" + bootorder="0"> + <dsc configname="MEMBER_FAILOVERCLUSTER_HV" + configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.31" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::31" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A LM" + switchname="Domain Private Site A LM"> + <ipv4 address="192.168.130.31" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc02::31" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.31" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::31" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOHV2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FOHV2" + memorystartupbytes="4GB" + dynamicmemoryenabled="N" + exposevirtualizationextensions="Y" + bootorder="0"> + <dsc configname="MEMBER_FAILOVERCLUSTER_HV" + configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.32" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::32" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A LM" + switchname="Domain Private Site A LM"> + <ipv4 address="192.168.130.32" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc02::32" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.32" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::32" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_DomainComplete.xml b/source/samples/Sample_WS2012R2_DomainComplete.xml new file mode 100644 index 00000000..8dc258c8 --- /dev/null +++ b/source/samples/Sample_WS2012R2_DomainComplete.xml @@ -0,0 +1,691 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_DomainComplete" + version="1.0"> + <description> + Sample Windows Server 2012 R2 Lab Configuration Domain with multiple DCs, DHCP Servers, WSUS, WDS, Edge, File Servers and two-tier PKI with offline CA. + + Useful for general experimentation and testing of Windows Server 2012 R2 features. + </description> + + <settings labid="LABBUILDER-DOMAINCOMPLETE.COM " + domainname="LABBUILDER-DOMAINCOMPLETE.COM" + email="admin@LABBUILDER-DOMAINCOMPLETE.COM" + labpath="c:\vm\LABBUILDER-DOMAINCOMPLETE.COM" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + <msu name="RSAT-W10" + url="http://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-KB2693643-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + <switch name="Internet" type="Private" vlan="9" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + <templatevhd name="Windows 10 Enterprise" + iso="10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise" + vhd="Windows 10 Enterprise.vhdx" + edition="Windows 10 Enterprise" + ostype="Client" + packages="RSAT-W10" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter Core" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows 10 Enterprise" + templatevhd="Windows 10 Enterprise" + memorystartupbytes="2GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Client" + packages="RSAT-W10" /> + </templates> + + <vms> + <vm name="SS-ROOTCA" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SS-ROOTCA"> + <dsc configname="STANDALONE_ROOTCA" + configfile="STANDALONE_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + CACommonName = "LABBUILDER.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + SubCAs = @('SA-SUBCA') + </parameters> + </dsc> + </vm> + + <vm name="SS-INTERNET" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="SS-INTERNET"> + <dsc configname="STANDALONE_INTERNET" + configfile="STANDALONE_INTERNET.DSC.ps1" + logging="Y"> + <parameters> + Scopes = @( + @{ Name = 'Internet'; + Start = '131.107.0.50'; + End = '131.107.0.250'; + SubnetMask = '255.255.0.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-EDGE1'; + ScopeID = '131.107.0.0'; + ClientMACAddress = '000000000000'; + IPAddress = '131.107.0.50'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE2'; + ScopeID = '131.107.0.0'; + ClientMACAddress = '000000000001'; + IPAddress = '131.107.0.51'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '131.107.0.0'; + DNServerIPAddress = @('131.107.0.1'); + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Internet" + switchname="Internet"> + <ipv4 address="131.107.0.1" + defaultgateway="" + subnetmask="8" + dnsserver="131.107.0.1"/> + <ipv6 address="2001:54b9:f782:ae41::10" + defaultgateway="" + subnetmask="64" + dnsserver="2001:54b9:f782:ae41::10"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DC2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC2"> + <dsc configname="DC_SECONDARY" + configfile="DC_SECONDARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + Forwarders = @('8.8.8.8','8.8.4.4') + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.11" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::b" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-RODC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-RODC1"> + <dsc configname="RODC_SECONDARY" + configfile="RODC_SECONDARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + Forwarders = @('8.8.8.8','8.8.4.4') + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.30" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.30,192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::20" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::20,fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-RODC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.30'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000003'; + IPAddress = '192.168.128.17'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NPS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000004'; + IPAddress = '192.168.128.18'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000006'; + IPAddress = '192.168.128.20'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-WDS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.21'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-WSUS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000008'; + IPAddress = '192.168.128.22'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-SUBCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000009'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.24'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000B'; + IPAddress = '192.168.128.25'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Remediation Site A'; + Start = '192.168.129.50'; + End = '192.168.129.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.129.0'; + DNServerIPAddress = ''; + Router = ''; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.17" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::11" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-NPS" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="SA-NPS"> + <dsc configname="MEMBER_NPS_DFSTEST" + configfile="MEMBER_NPS_DFSTEST.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.18" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::12" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-EDGE1"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Internet" + switchname="Internet" > + <ipv4 address="131.107.0.50" + defaultgateway="" + subnetmask="16" + dnsserver="131.107.0.1"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-EDGE2"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.20" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::14" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Internet" + switchname="Internet" > + <ipv4 address="131.107.0.51" + defaultgateway="" + subnetmask="16" + dnsserver="131.107.0.1"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-WDS" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="SA-WDS"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-WDS Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_WDS" + configfile="MEMBER_WDS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.21" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::15" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-WSUS" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-WSUS"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-WSUS Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_WSUS" + configfile="MEMBER_WSUS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.22" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::16" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-SUBCA" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-SUBCA"> + <dsc configname="MEMBER_SUBCA" + configfile="MEMBER_SUBCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LABBUILDER.COM Issuing CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + RootCAName = "SS_ROOTCA" + RootCACommonName = "LABBUILDER.COM Root CA" + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FS1"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER" + configfile="MEMBER_FILESERVER.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.24" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::18" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS2" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-FS2"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS2 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER" + configfile="MEMBER_FILESERVER.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.25" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::19" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="DA-IT01" + template="Template Windows 10 Enterprise" + computername="DA-IT01"> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A" /> + <adapter name="Internet" + switchname="Internet" > + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_DomainSQL2014.xml b/source/samples/Sample_WS2012R2_DomainSQL2014.xml new file mode 100644 index 00000000..05a51920 --- /dev/null +++ b/source/samples/Sample_WS2012R2_DomainSQL2014.xml @@ -0,0 +1,263 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_DomainSQL2014" + version="1.0"> + <description> + Sample Windows Server 2012 R2 Lab Configuration DC, DHCP and Edge. + + A SQL Server 2014 node will also be installed from a SQL Server 2014 ISO with data stored on a local data disk. + + Download the SQL Server 2014 ISO from here: + https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2014 + + **Update the Resource ISO path below with the location of the SQL Server 2014 ISO.** + + Please Note: This sample could be updated to Windows Server 2016 TP5, but there is a problen with installing the .NET 3.5 Framework on that version. + This problem can be worked around by manually installing this feature using the Source set to E:\Sources\Sxs. + </description> + + <settings labid="DOMAINSQL2014.COM " + domainname="DOMAINSQL2014.COM" + email="admina@DOMAINSQL2014.COM" + labpath="c:\vm\DOMAINSQL2014.COM" /> + + <resources isopath="ISOFiles"> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + <iso name="SQL2014SP1_FULL_ENU" + path="SQLServer2014SP1-FullSlipstream-x64-ENU.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2014" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2014.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2014.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + @{ Name = 'SA-SQL1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.20'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2012 R2 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2014.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="SA-SQL1" + template="Template Windows Server 2012 R2 Datacenter FULL" + computername="SA-SQL1" + bootorder="4"> + <dsc configname="MEMBER_SQLSERVER2014" + configfile="MEMBER_SQLSERVER2014.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2014.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallerUsername = 'Administrator' + InstallerPassword = 'P@ssword!1' + SQLAdminAccount = 'Administrator' + SQLDataDrive = 'E' + SourcePath = 'D:\' + Instances = @( + @{ + Name = 'MSSQLSERVER' + Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } + ) + InstallManagementTools = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.21" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::15" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + <datavhds> + <datavhd vhd="SQLData.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="SQLData" /> + </datavhds> + <dvddrives> + <dvddrive iso="SQL2014SP1_FULL_ENU" /> + </dvddrives> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_MultiForest.xml b/source/samples/Sample_WS2012R2_MultiForest.xml new file mode 100644 index 00000000..8e62d37b --- /dev/null +++ b/source/samples/Sample_WS2012R2_MultiForest.xml @@ -0,0 +1,416 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_MultiForest" + version="1.0"> + <description> + Sample Windows Server 2012 R2 Lab Configuration containing two forests: + - ALPHA.LOCAL + - BRAVO.LOCAL + Each forest contains a child domain DEV.*.LOCAL. + Each forest is on an isolated subnet and contains a DC server, DHCP server and an Edge Server. + The edge servers are also on a shared subnet (Domain Internal) enabling routing between the isolated subnets (Domain Private Alpha/Bravo). + A forest trust is not established between the forests. + </description> + + <settings labid="LABBUILDER-TWOFORESTS.COM " + domainname="LABBUILDER-TWOFORESTS.COM" + email="admina@LABBUILDER-TWOFORESTS.COM" + labpath="c:\vm\LABBUILDER-TWOFORESTS.COM" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Alpha" type="Private" vlan="2" /> + <switch name="Domain Private Bravo" type="Private" vlan="3" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="ALPHA-DC1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-DC2" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-DC2"> + <dsc configname="DC_FORESTCHILDDOMAIN" + configfile="DC_FORESTCHILDDOMAIN.DSC.ps1"> + <parameters> + ParentDomainName = "ALPHA.LOCAL" + DomainName = "DEV" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.11" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.11,192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::b" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::b,fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Alpha Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'ALPHA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-EDGE1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="Domain Internal" + switchname="Domain Internal"> + <ipv4 address="192.168.131.10" + defaultgateway="" + subnetmask="24" + dnsserver=""/> + <ipv6 address="fd53:ccc5:895d:bc00::10" + defaultgateway="" + subnetmask="64" + dnsserver=""/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="BRAVO-DC1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.10" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::a" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-DC2" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-DC2"> + <dsc configname="DC_FORESTCHILDDOMAIN" + configfile="DC_FORESTCHILDDOMAIN.DSC.ps1"> + <parameters> + ParentDomainName = "BRAVO.LOCAL" + DomainName = "SALES" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.11" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.11,192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::b" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::b,fd53:ccc5:895c:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-DHCP1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Bravo Primary'; + Start = '192.168.130.50'; + End = '192.168.130.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'BRAVO-DC1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.130.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-DC2'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.130.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-DHCP1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.130.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-EDGE1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.130.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.130.0'; + DNServerIPAddress = @('192.168.130.10','192.168.130.11'); + Router = '192.168.130.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.16" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::10" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-EDGE1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a"/> + </adapter> + <adapter name="Domain Internal" + switchname="Domain Internal"> + <ipv4 address="192.168.131.20" + defaultgateway="" + subnetmask="24" + dnsserver=""/> + <ipv6 address="fd53:ccc5:895d:bc00::20" + defaultgateway="" + subnetmask="64" + dnsserver=""/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_MultiForest_ADFS.xml b/source/samples/Sample_WS2012R2_MultiForest_ADFS.xml new file mode 100644 index 00000000..a25ffbc4 --- /dev/null +++ b/source/samples/Sample_WS2012R2_MultiForest_ADFS.xml @@ -0,0 +1,586 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_MultiForest_ADFS" + version="1.0"> + <description> + Sample Windows Server 2012 R2 Lab Configuration containing two forests: + - ALPHA.LOCAL + - BRAVO.LOCAL + Each forest is on an isolated subnet and contains a DC server, DHCP server, ADFS Server, ADCS Server and an Edge Server. + The edge servers are also on a shared subnet (Domain Internal) enabling routing between the isolated subnets (Domain Private Alpha/Bravo). + ADFS Trusts are not established between the two forests/domains. + The two forests are not configured to trust each others Root certificates. + </description> + + <settings labid="LABBUILDER-ADFS.COM " + domainname="LABBUILDER-ADFS.COM" + email="admina@LABBUILDER-ADFS.COM" + labpath="c:\vm\LABBUILDER-ADFS.COM" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Alpha" type="Private" vlan="2" /> + <switch name="Domain Private Bravo" type="Private" vlan="3" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + <template name="Template Windows Server 2012 R2 Datacenter Core" + templatevhd="Windows Server 2012 R2 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="ALPHA-DC1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-DHCP1" + template="Template Windows Server 2012 R2 Datacenter Core" + computername="ALPHA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Alpha Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'ALPHA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-ROOTCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000003'; + IPAddress = '192.168.128.17'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-ADFS1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000004'; + IPAddress = '192.168.128.18'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'ALPHA-WEBAPP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000006'; + IPAddress = '192.168.128.20'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-ROOTCA" + template="Template Windows Server 2012 R2 Datacenter Core" + computername="ALPHA-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "ALPHA.LOCAL Root CA" + CADistinguishedNameSuffix = "DC=ALPHA,DC=LOCAL" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.alpha.local/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.alpha.local/CertEnroll/%1_%3%4.crt\n32:http://pki.alpha.local/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.17" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-ADFS1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-ADFS1" + bootorder="3"> + <dsc configname="MEMBER_ADFS" + configfile="MEMBER_ADFS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.18" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::18" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="ALPHA-EDGE1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS_WAP" + configfile="MEMBER_REMOTEACCESS_WAP.DSC.ps1"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="Domain Internal" + switchname="Domain Internal"> + <ipv4 address="192.168.131.10" + defaultgateway="" + subnetmask="24" + dnsserver=""/> + <ipv6 address="fd53:ccc5:895d:bc00::10" + defaultgateway="" + subnetmask="64" + dnsserver=""/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="ALPHA-WEBAPP1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="ALPHA-WEBAPP1" + bootorder="3"> + <dsc configname="MEMBER_WEBSERVER" + configfile="MEMBER_WEBSERVER.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "ALPHA.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "ALPHA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Alpha" + switchname="Domain Private Alpha"> + <ipv4 address="192.168.128.20" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::20" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-DC1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.10" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::a" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-DHCP1" + template="Template Windows Server 2012 R2 Datacenter Core" + computername="BRAVO-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Bravo Primary'; + Start = '192.168.130.50'; + End = '192.168.130.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'BRAVO-DC1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.130.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-DHCP1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.130.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-ROOTCA'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000003'; + IPAddress = '192.168.130.17'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-ADFS1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000004'; + IPAddress = '192.168.130.18'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-EDGE1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.130.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'BRAVO-WEBAP1'; + ScopeID = '192.168.130.0'; + ClientMACAddress = '000000000006'; + IPAddress = '192.168.130.20'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.130.0'; + DNServerIPAddress = @('192.168.130.10','192.168.130.11'); + Router = '192.168.130.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.16" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::10" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-ROOTCA" + template="Template Windows Server 2012 R2 Datacenter Core" + computername="BRAVO-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "BRAVO.LOCAL Root CA" + CADistinguishedNameSuffix = "DC=BRAVO,DC=LOCAL" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.bravo.local/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.bravo.local/CertEnroll/%1_%3%4.crt\n32:http://pki.bravo.local/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.17" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10,192.168.130.11"/> + <ipv6 address="fd53:ccc5:895c:bc00::17" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a,fd53:ccc5:895c:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-ADFS1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-ADFS1" + bootorder="3"> + <dsc configname="MEMBER_ADFS" + configfile="MEMBER_ADFS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.18" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10,192.168.130.11"/> + <ipv6 address="fd53:ccc5:895c:bc00::18" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a,fd53:ccc5:895c:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="BRAVO-EDGE1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS_WAP" + configfile="MEMBER_REMOTEACCESS_WAP.DSC.ps1"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.130.10"/> + <ipv6 address="fd53:ccc5:895c:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a"/> + </adapter> + <adapter name="Domain Internal" + switchname="Domain Internal"> + <ipv4 address="192.168.131.20" + defaultgateway="" + subnetmask="24" + dnsserver=""/> + <ipv6 address="fd53:ccc5:895d:bc00::20" + defaultgateway="" + subnetmask="64" + dnsserver=""/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="BRAVO-WEBAPP1" + template="Template Windows Server 2012 R2 Datacenter Full" + computername="BRAVO-WEBAPP1" + bootorder="3"> + <dsc configname="MEMBER_WEBSERVER" + configfile="MEMBER_WEBSERVER.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "BRAVO.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "BRAVO-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Bravo" + switchname="Domain Private Bravo"> + <ipv4 address="192.168.130.20" + defaultgateway="192.168.130.19" + subnetmask="24" + dnsserver="192.168.130.10,192.168.130.11"/> + <ipv6 address="fd53:ccc5:895c:bc00::20" + defaultgateway="fd53:ccc5:895c:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895c:bc00::a,fd53:ccc5:895c:bc00::b"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2012R2_Simple.xml b/source/samples/Sample_WS2012R2_Simple.xml new file mode 100644 index 00000000..753076cb --- /dev/null +++ b/source/samples/Sample_WS2012R2_Simple.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2012R2_Simple" + version="1.0"> + <description>Sample Windows Server 2012 R2 Lab Configuration Simple</description> + + <settings labid="LABBUILDER-SIMPLE.COM " + domainname="LABBUILDER-SIMPLE.COM" + email="admin@LABBUILDER-SIMPLE.COM" + labpath="c:\vm\LABBUILDER-SIMPLE.COM" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="General Purpose Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2012 R2 Datacenter Full" + templatevhd="Windows Server 2012 R2 Datacenter FULL" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" /> + </templates> + + <vms> + <vm name="SS-DEFAULT" + template="Template Windows Server 2012 R2 Datacenter FULL" + computername="SS-DEFAULT"> + <dsc configname="STANDALONE_DEFAULT" + configfile="STANDALONE_DEFAULT.DSC.ps1"> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.10.2" + defaultgateway="192.168.10.1" + subnetmask="24" + dnsserver="192.168.10.2"/> + <ipv6 address="fd53:ccc5:895a:ba00::2" + defaultgateway="fd53:ccc5:895a:ba00::1" + subnetmask="64" + dnsserver="fd53:ccc5:895a:ba00::2"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DCandDHCPOnly.xml b/source/samples/Sample_WS2016_DCandDHCPOnly.xml new file mode 100644 index 00000000..3ed4a5c3 --- /dev/null +++ b/source/samples/Sample_WS2016_DCandDHCPOnly.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DCandDHCPOnly" + version="1.0"> + <description>Sample Windows Server 2016 Lab Configuration DC and DHCP Only</description> + + <settings labid="LABBUILDER-WS2016.COM " + domainname="LABBUILDER-WS2016.COM" + email="admin@LABBUILDER-WS2016.COM" + labpath="c:\vm\LABBUILDER-WS2016.COM" /> + + <resources> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="General Purpose Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1" > + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DCandDHCPandCA.xml b/source/samples/Sample_WS2016_DCandDHCPandCA.xml new file mode 100644 index 00000000..a32581ef --- /dev/null +++ b/source/samples/Sample_WS2016_DCandDHCPandCA.xml @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DcandDHCPandCA" + version="1.0"> + <description> + Sample Windows Server 2016 Lab Configuration Domain with multiple DCs, DHCP Servers, PKI with enterprise CA. + + Useful for general experimentation and testing of Windows Server 2016 features. + </description> + + <settings labid="LABBUILDER-DCANDDHCPANDCA.COM " + domainname="LABBUILDER-DCANDDHCPANDCA.COM" + email="admin@LABBUILDER-DCANDDHCPANDCA.COM" + labpath="c:\vm\LABBUILDER-DCANDDHCPANDCA.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-ROOTCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000009'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-ROOTCA"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LABBUILDER.COM Issuing CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DCandDHCPandEdge.xml b/source/samples/Sample_WS2016_DCandDHCPandEdge.xml new file mode 100644 index 00000000..6bfeba08 --- /dev/null +++ b/source/samples/Sample_WS2016_DCandDHCPandEdge.xml @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DCandDHCPandEdge" + version="1.0"> + <description>Sample Windows Server 2016 Lab Configuration DC, DHCP and Edge.</description> + + <settings labid="LABBUILDER-DCDHCPEDGE.COM " + domainname="LABBUILDER-DCDHCPEDGE.COM" + email="admina@LABBUILDER-DCDHCPEDGE.COM" + labpath="c:\vm\LABBUILDER-DCDHCPEDGE.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DFSHubAndSpoke.xml b/source/samples/Sample_WS2016_DFSHubAndSpoke.xml new file mode 100644 index 00000000..2393561f --- /dev/null +++ b/source/samples/Sample_WS2016_DFSHubAndSpoke.xml @@ -0,0 +1,311 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DFSHubAndSpoke" + version="1.0"> + <description>Sample Windows Server 2016 Lab Configuration DC, DHCP, EDGE and Hub and Spoke DFS replication group.</description> + + <settings labid="LABBUILDER-DFSHUBANDSPOKE.COM " + domainname="LABBUILDER-DFSHUBANDSPOKE.COM" + email="admina@LABBUILDER-DFSHUBANDSPOKE.COM" + labpath="c:\vm\LABBUILDER-DFSHUBANDSPOKE.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-SPOKE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000006'; + IPAddress = '192.168.128.20'; + AddressFamily = 'IPv4' + } + @{ Name = 'SA-SPOKE2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.21'; + AddressFamily = 'IPv4' + } + @{ Name = 'SA-HUB'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000008'; + IPAddress = '192.168.128.22'; + AddressFamily = 'IPv4' + } + + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="SA-SPOKE1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-SPOKE1" + bootorder="4"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_DFSSPOKE" + configfile="MEMBER_DFSSPOKE.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.20" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::14" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-SPOKE2" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-SPOKE2" + bootorder="4"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_DFSSPOKE" + configfile="MEMBER_DFSSPOKE.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.21" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::15" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-HUB" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-HUB" + bootorder="5"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_DFSHUB" + configfile="MEMBER_DFSHUB.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + SpokeComputerName = @('SA-SPOKE1','SA-SPOKE2') + ResourceGroupName = 'WebSite' + ResourceGroupDescription = 'Files for web server' + ResourceGroupFolderName = 'WebSiteFiles' + ResourceGroupContentPath = 'd:\inetpub\wwwroot\WebSiteFiles' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.22" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::16" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DomainClustering.xml b/source/samples/Sample_WS2016_DomainClustering.xml new file mode 100644 index 00000000..f9729683 --- /dev/null +++ b/source/samples/Sample_WS2016_DomainClustering.xml @@ -0,0 +1,710 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DomainClustering" + version="1.0"> + <description>Sample Windows Server 2016 Lab Configuration Domain with Cluster Server and NLB nodes</description> + + <settings labid="LABBUILDER-DOMAINCLUSTERING.COM " + domainname="LABBUILDER-DOMAINCLUSTERING.COM" + email="admin@LABBUILDER-DOMAINCLUSTERING.COM" + labpath="c:\vm\LABBUILDER-DOMAINCLUSTERING.COM" + requiredwindowsbuild="10586" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site A iSCSI" type="Private" vlan="3" /> + <switch name="Domain Private Site A LM" type="Private" vlan="4" /> + <switch name="Domain Private Site A SMB" type="Private" vlan="5" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DC2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC2" + bootorder="1"> + <dsc configname="DC_SECONDARY" + configfile="DC_SECONDARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCName = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.11" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::b" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.24'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000B'; + IPAddress = '192.168.128.25'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB1-CLS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000D'; + IPAddress = '192.168.128.35'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000C'; + IPAddress = '192.168.128.26'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NLB2-CLS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000E'; + IPAddress = '192.168.128.36'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000F'; + IPAddress = '192.168.128.28'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000010'; + IPAddress = '192.168.128.29'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOC3'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000011'; + IPAddress = '192.168.128.30'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOHV1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000F'; + IPAddress = '192.168.128.31'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FOHV2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000010'; + IPAddress = '192.168.128.32'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LABBUILDER.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt\n32:http://pki.labbuilder.com/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FS1" + bootorder="4"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="40GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER_ISCSI" + configfile="MEMBER_FILESERVER_ISCSI.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + TargetName = 'sa-foc-target' + VirtualDisks = @( + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-witness.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 500MB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk1.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk2.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + }, + @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk3.vhdx'; + DiskType = 'Dynamic'; + SizeBytes = 10GB; + } + ) + ClusterInitiatorIds = @( + 'Iqn:iqn.1991-05.com.microsoft:sa-foc1.labbuilder.com' + 'Iqn:iqn.1991-05.com.microsoft:sa-foc2.labbuilder.com' + 'Iqn:iqn.1991-05.com.microsoft:sa-foc3.labbuilder.com' + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.24" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::18" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.24" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::18" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-NLB1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-NLB1" + bootorder="5"> + <dsc configname="MEMBER_NLB" + configfile="MEMBER_NLB.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DCName = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.25" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::19" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A Cluster" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.35" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::35" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-NLB2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-NLB2" + bootorder="5"> + <dsc configname="MEMBER_NLB" + configfile="MEMBER_NLB.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DCName = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.26" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::1a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A Cluster" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.36" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::36" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FOC1" + bootorder="6"> + <dsc configname="MEMBER_FAILOVERCLUSTER_FS" + configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + ServerName = 'sa-fs1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.28' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.28" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::28" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.28" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::28" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.28" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::28" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOC2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FOC2" + bootorder="6"> + <dsc configname="MEMBER_FAILOVERCLUSTER_FS" + configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + ServerName = 'sa-fs1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.29' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.29" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::29" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.29" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::29" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.29" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::29" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOC3" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FOC3" + bootorder="6"> + <dsc configname="MEMBER_FAILOVERCLUSTER_FS" + configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + ServerName = 'sa-fs1' + ServerTargetName = 'sa-fs1-sa-foc-target-target' + TargetPortalAddress = '192.168.129.24' + InitiatorPortalAddress = '192.168.129.30' + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.30" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::30" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A iSCSI" + switchname="Domain Private Site A iSCSI"> + <ipv4 address="192.168.129.30" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc01::30" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.30" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::30" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOHV1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FOHV1" + memorystartupbytes="4GB" + dynamicmemoryenabled="N" + exposevirtualizationextensions="Y" + bootorder="0"> + <dsc configname="MEMBER_FAILOVERCLUSTER_HV" + configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.31" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::31" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A LM" + switchname="Domain Private Site A LM"> + <ipv4 address="192.168.130.31" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc02::31" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.31" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::31" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + + <vm name="SA-FOHV2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FOHV2" + memorystartupbytes="4GB" + dynamicmemoryenabled="N" + exposevirtualizationextensions="Y" + bootorder="0"> + <dsc configname="MEMBER_FAILOVERCLUSTER_HV" + configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A" + macaddressspoofing="On"> + <ipv4 address="192.168.128.32" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::32" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Domain Private Site A LM" + switchname="Domain Private Site A LM"> + <ipv4 address="192.168.130.32" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc02::32" + subnetmask="64" /> + </adapter> + <adapter name="Domain Private Site A SMB" + switchname="Domain Private Site A SMB"> + <ipv4 address="192.168.131.32" + subnetmask="24" /> + <ipv6 address="fd53:ccc5:895a:bc03::32" + subnetmask="64" /> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DomainComplete.xml b/source/samples/Sample_WS2016_DomainComplete.xml new file mode 100644 index 00000000..a8702fee --- /dev/null +++ b/source/samples/Sample_WS2016_DomainComplete.xml @@ -0,0 +1,687 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DomainComplete" + version="1.0"> + <description> + Sample Windows Server 2016 Lab Configuration Domain with multiple DCs, DHCP Servers, WSUS, WDS, Edge, File Servers and two-tier PKI with offline CA. + + Useful for general experimentation and testing of Windows Server 2016 features. + </description> + + <settings labid="LABBUILDER-DOMAINCOMPLETE.COM " + domainname="LABBUILDER-DOMAINCOMPLETE.COM" + email="admin@LABBUILDER-DOMAINCOMPLETE.COM" + labpath="c:\vm\LABBUILDER-DOMAINCOMPLETE.COM" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + <msu name="RSAT-W10" + url="http://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-KB2693643-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + <switch name="Internet" type="Private" vlan="9" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + <templatevhd name="Windows 10 Enterprise" + iso="10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise" + vhd="Windows 10 Enterprise.vhdx" + edition="Windows 10 Enterprise" + ostype="Client" + packages="RSAT-W10" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows 10 Enterprise" + templatevhd="Windows 10 Enterprise" + memorystartupbytes="2GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Client" + packages="RSAT-W10" /> + </templates> + + <vms> + <vm name="SS-ROOTCA" + template="Template Windows Server 2016 Datacenter CORE" + computername="SS-ROOTCA"> + <dsc configname="STANDALONE_ROOTCA" + configfile="STANDALONE_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + CACommonName = "LABBUILDER.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + SubCAs = @('SA-SUBCA') + </parameters> + </dsc> + </vm> + + <vm name="SS-INTERNET" + template="Template Windows Server 2016 Datacenter Full" + computername="SS-INTERNET"> + <dsc configname="STANDALONE_INTERNET" + configfile="STANDALONE_INTERNET.DSC.ps1" + logging="Y"> + <parameters> + Scopes = @( + @{ Name = 'Internet'; + Start = '131.107.0.50'; + End = '131.107.0.250'; + SubnetMask = '255.255.0.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-EDGE1'; + ScopeID = '131.107.0.0'; + ClientMACAddress = '000000000000'; + IPAddress = '131.107.0.50'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE2'; + ScopeID = '131.107.0.0'; + ClientMACAddress = '000000000001'; + IPAddress = '131.107.0.51'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '131.107.0.0'; + DNServerIPAddress = @('131.107.0.1'); + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Internet" + switchname="Internet"> + <ipv4 address="131.107.0.1" + defaultgateway="" + subnetmask="8" + dnsserver="131.107.0.1"/> + <ipv6 address="2001:54b9:f782:ae41::10" + defaultgateway="" + subnetmask="64" + dnsserver="2001:54b9:f782:ae41::10"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DC2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC2"> + <dsc configname="DC_SECONDARY" + configfile="DC_SECONDARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + Forwarders = @('8.8.8.8','8.8.4.4') + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.11" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::b" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-RODC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-RODC1"> + <dsc configname="RODC_SECONDARY" + configfile="RODC_SECONDARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + Forwarders = @('8.8.8.8','8.8.4.4') + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.30" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.30,192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::20" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::20,fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DC2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.11'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-RODC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000001'; + IPAddress = '192.168.128.30'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000003'; + IPAddress = '192.168.128.17'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NPS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000004'; + IPAddress = '192.168.128.18'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000006'; + IPAddress = '192.168.128.20'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-WDS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.21'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-WSUS'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000008'; + IPAddress = '192.168.128.22'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-SUBCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000009'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.24'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000B'; + IPAddress = '192.168.128.25'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10','192.168.128.11'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Remediation Site A'; + Start = '192.168.129.50'; + End = '192.168.129.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.129.0'; + DNServerIPAddress = ''; + Router = ''; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.17" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::11" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-NPS" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-NPS"> + <dsc configname="MEMBER_NPS_DFSTEST" + configfile="MEMBER_NPS_DFSTEST.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.18" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::12" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-EDGE1"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Internet" + switchname="Internet" > + <ipv4 address="131.107.0.50" + defaultgateway="" + subnetmask="16" + dnsserver="131.107.0.1"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-EDGE2"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.20" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::14" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + <adapter name="Internet" + switchname="Internet" > + <ipv4 address="131.107.0.51" + defaultgateway="" + subnetmask="16" + dnsserver="131.107.0.1"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-WDS" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-WDS"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-WDS Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_WDS" + configfile="MEMBER_WDS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.21" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::15" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-WSUS" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-WSUS"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-WSUS Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_WSUS" + configfile="MEMBER_WSUS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.22" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::16" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-SUBCA" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-SUBCA"> + <dsc configname="MEMBER_SUBCA" + configfile="MEMBER_SUBCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LABBUILDER.COM Issuing CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + RootCAName = "SS_ROOTCA" + RootCACommonName = "LABBUILDER.COM Root CA" + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FS1"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER" + configfile="MEMBER_FILESERVER.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.24" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::18" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS2" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FS2"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS2 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER" + configfile="MEMBER_FILESERVER.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.25" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::19" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="DA-IT01" + template="Template Windows 10 Enterprise" + computername="DA-IT01"> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A" /> + <adapter name="Internet" + switchname="Internet" > + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DomainFunctions.xml b/source/samples/Sample_WS2016_DomainFunctions.xml new file mode 100644 index 00000000..b0afd0c3 --- /dev/null +++ b/source/samples/Sample_WS2016_DomainFunctions.xml @@ -0,0 +1,330 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="LBFUNCTIONS.LOCAL" + version="1.0" > + <description>Simple Windows Server 2016 Lab Configuration creating an AD DC, DHCP Server, Edge Server, Root CA, SQL Server 2016 and File Server.</description> + + <settings labid="LBFUNCTIONS.LOCAL " + domainname="LBFUNCTIONS.LOCAL" + email="daniel@LBFUNCTIONS.LOCAL" + labpath="c:\vm\LBFUNCTIONS.LOCAL" /> + + <resources isopath="ISOFiles"> + <iso name="SQL2016_Full_ENU" + path="SQLServer2016-x64-ENU.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016" /> + </resources> + + <switches managementvlan="97"> + <switch name="General Purpose External" type="External" /> + <switch name="Domain Private" type="Private" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LBFUNCTIONS.LOCAL" + DomainAdminPassword = "P@ssword!1" + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LBFUNCTIONS.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'LBFUNCTIONS.LOCAL Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-ROOTCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-FS1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.24'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-SQL1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.50'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LBFUNCTIONS.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="General Purpose External" + switchname="General Purpose External" /> + </adapters> + <datavhds> + <datavhd vhd="ToolsDisk.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="ToolsDisk" /> + </datavhds> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LBFUNCTIONS.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LBFUNCTIONS.LOCAL Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n74:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.LBFUNCTIONS.LOCAL/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.LBFUNCTIONS.LOCAL/CertEnroll/%1_%3%4.crt\n32:http://pki.LBFUNCTIONS.LOCAL/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-FS1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-FS1"> + <datavhds> + <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> + </datavhds> + <dsc configname="MEMBER_FILESERVER" + configfile="MEMBER_FILESERVER.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LBFUNCTIONS.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.24" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::18" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-SQL1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-SQL1" + bootorder="4"> + <dsc configname="MEMBER_SQLSERVER2016" + configfile="MEMBER_SQLSERVER2016.DSC.ps1"> + <parameters> + DomainName = "LBFUNCTIONS.LOCAL" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallerUsername = 'Administrator' + InstallerPassword = 'P@ssword!1' + SQLAdminAccount = 'Administrator' + SQLDataDrive = 'E' + SourcePath = 'D:\' + SourceFolder = '' + Instances = @( + @{ + Name = 'MSSQLSERVER' + Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.50" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::50" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + <datavhds> + <datavhd vhd="SQLData.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="SQLData" /> + </datavhds> + <dvddrives> + <dvddrive iso="SQL2016_Full_ENU" /> + </dvddrives> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_DomainSQL2016.xml b/source/samples/Sample_WS2016_DomainSQL2016.xml new file mode 100644 index 00000000..fd078e1f --- /dev/null +++ b/source/samples/Sample_WS2016_DomainSQL2016.xml @@ -0,0 +1,257 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_DomainSQL2016" + version="1.0"> + <description> + Sample Windows Server 2016 Lab Configuration DC, DHCP and Edge. + + A SQL Server 2016 node will also be installed from a SQL Server 2016 ISO with data stored on a local data disk. + + Download the SQL Server 2016 ISO from here: + https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016 + + Use the evaluation installer to download media (as an ISO). + + **Update the Resource ISO path below with the location of the SQL Server 2016 ISO.** + + Management Tools will not be automatically installed as they do not appear on the SQL Server 2016 ISO and must be downloaded and installed separately. + </description> + + <settings labid="DOMAINSQL2016.COM " + domainname="DOMAINSQL2016.COM" + email="admina@DOMAINSQL2016.COM" + labpath="c:\vm\DOMAINSQL2016.COM" /> + + <resources isopath="ISOFiles"> + <iso name="SQL2016_Full_ENU" + path="SQLServer2016-x64-ENU.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016" /> + </resources> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Core.vhdx" + edition="Windows Server 2016 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2016 Datacenter CORE" + templatevhd="Windows Server 2016 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2016.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2016.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + @{ Name = 'SA-SQL1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.20'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2016.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + + <vm name="SA-SQL1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-SQL1" + bootorder="4"> + <dsc configname="MEMBER_SQLSERVER2016" + configfile="MEMBER_SQLSERVER2016.DSC.ps1"> + <parameters> + DomainName = "DOMAINSQL2016.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallerUsername = 'Administrator' + InstallerPassword = 'P@ssword!1' + SQLAdminAccount = 'Administrator' + SQLDataDrive = 'E' + SourcePath = 'D:\' + Instances = @( + @{ + Name = 'MSSQLSERVER' + Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.21" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::15" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + <datavhds> + <datavhd vhd="SQLData.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="SQLData" /> + </datavhds> + <dvddrives> + <dvddrive iso="SQL2016_Full_ENU" /> + </dvddrives> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_NanoDomain.xml b/source/samples/Sample_WS2016_NanoDomain.xml new file mode 100644 index 00000000..955bcca5 --- /dev/null +++ b/source/samples/Sample_WS2016_NanoDomain.xml @@ -0,0 +1,328 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="NANOTEST.COM" + version="1.0" > + <description>Simple Windows Server 2016 Lab Configuration creating an AD DC, DHCP Server, Edge Server, Root CA and eight Nano Servers</description> + + <settings labid="NANOTEST.COM " + domainname="NANOTEST.COM" + email="daniel@NANOTEST.COM" + labpath="c:\vm\NANOTEST.COM" /> + + <switches managementvlan="97"> + <switch name="General Purpose External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private" type="Private" vlan="30" /> + <switch name="Domain Private iSCSI" type="Private" vlan="31" /> + <switch name="Domain Private LM" type="Private" vlan="32" /> + <switch name="Domain Private SMB" type="Private" vlan="33" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Nano Server 2016 Datacenter" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Nano Server 2016 Datacenter.vhdx" + edition="Windows Server 2016 SERVERDATACENTERNANO" + ostype="Nano" + packages="Microsoft-NanoServer-Guest-Package.cab" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Nano Server 2016 Datacenter" + templatevhd="Nano Server 2016 Datacenter" + memorystartupbytes="500MB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Nano" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'NANOTEST.COM Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-ROOTCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000008'; + IPAddress = '192.168.128.30'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000009'; + IPAddress = '192.168.128.31'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO3'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.32'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO4'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000B'; + IPAddress = '192.168.128.33'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO5'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000C'; + IPAddress = '192.168.128.34'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO6'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000D'; + IPAddress = '192.168.128.35'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO7'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000E'; + IPAddress = '192.168.128.36'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO8'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000F'; + IPAddress = '192.168.128.37'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="General Purpose External" + switchname="General Purpose External" /> + </adapters> + <datavhds> + <datavhd vhd="ToolsDisk.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="ToolsDisk" /> + </datavhds> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2016 Datacenter Full" + computername="SA-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "NANOTEST.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n74:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.NANOTEST.COM/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.NANOTEST.COM/CertEnroll/%1_%3%4.crt\n32:http://pki.NANOTEST.COM/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-NANO" + template="Template Nano Server 2016 Datacenter" + computername="SA-NANO" + bootorder="3" + instancecount="8" + packages="Microsoft-NanoServer-DSC-Package.cab,Microsoft-NanoServer-Containers-Package.cab,Microsoft-NanoServer-Guest-Package.cab" > + <dsc configname="MEMBER_NANO" + configfile="MEMBER_NANO.DSC.ps1" + logging="Y"> + <parameters> + ODJRequestFile = "c:\ODJRequest.txt" + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.30" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::30" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="General Purpose External" + switchname="General Purpose External" /> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2016_Simple.xml b/source/samples/Sample_WS2016_Simple.xml new file mode 100644 index 00000000..5c19481c --- /dev/null +++ b/source/samples/Sample_WS2016_Simple.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2016_Simple" + version="1.0"> + <description>Sample Windows Server 2016 Lab Configuration Simple</description> + + <settings labid="LABBUILDER-SIMPLE.COM " + domainname="LABBUILDER-SIMPLE.COM" + email="admin@LABBUILDER-SIMPLE.COM" + labpath="c:\vm\LABBUILDER-SIMPLE.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="General Purpose Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2016 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" + vhd="Windows Server 2016 Datacenter Full.vhdx" + edition="Windows Server 2016 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2016 Datacenter Full" + templatevhd="Windows Server 2016 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SS-DEFAULT" + template="Template Windows Server 2016 Datacenter Full" + computername="SS-DEFAULT"> + <dsc configname="STANDALONE_DEFAULT" + configfile="STANDALONE_DEFAULT.DSC.ps1"> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.10.2" + defaultgateway="192.168.10.1" + subnetmask="24" + dnsserver="192.168.10.2"/> + <ipv6 address="fd53:ccc5:895a:ba00::2" + defaultgateway="fd53:ccc5:895a:ba00::1" + subnetmask="64" + dnsserver="fd53:ccc5:895a:ba00::2"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2019_AzureADConnect.xml b/source/samples/Sample_WS2019_AzureADConnect.xml new file mode 100644 index 00000000..ac91c3e0 --- /dev/null +++ b/source/samples/Sample_WS2019_AzureADConnect.xml @@ -0,0 +1,225 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2019_AzureADConnect" + version="1.0"> + <description>Sample Windows Server 2019 Lab Configuration DC, DHCP, Edge and a Member for testing Azure AD Connect.</description> + + <settings labid="LABBUILDER-AZUREADCONNECT.COM " + domainname="LABBUILDER-AZUREADCONNECT.COM" + email="admina@LABBUILDER-AZUREADCONNECT.COM" + labpath="c:\vm\LABBUILDER-AZUREADCONNECT.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2019 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Full.vhdx" + edition="Windows Server 2019 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2019 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Core.vhdx" + edition="Windows Server 2019 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2019 Datacenter Full" + templatevhd="Windows Server 2019 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2019 Datacenter CORE" + templatevhd="Windows Server 2019 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-AADC'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.17'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-AADC" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-AADC" + bootorder="3"> + <dsc configname="MEMBER_DEFAULT" + configfile="MEMBER_DEFAULT.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.17" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::b" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + </vms> +</labbuilderconfig> diff --git a/source/samples/Sample_WS2019_DCandDHCPandCA.xml b/source/samples/Sample_WS2019_DCandDHCPandCA.xml new file mode 100644 index 00000000..00dab3dd --- /dev/null +++ b/source/samples/Sample_WS2019_DCandDHCPandCA.xml @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2019_DcandDHCPandCA" + version="1.0"> + <description> + Sample Windows Server 2019 Lab Configuration Domain with multiple DCs, DHCP Servers, PKI with enterprise CA. + + Useful for general experimentation and testing of Windows Server 2019 features. + </description> + + <settings labid="LABBUILDER-DCANDDHCPANDCA.COM " + domainname="LABBUILDER-DCANDDHCPANDCA.COM" + email="admin@LABBUILDER-DCANDDHCPANDCA.COM" + labpath="c:\vm\LABBUILDER-DCANDDHCPANDCA.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2019 Datacenter Full" + iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Full.vhdx" + edition="Windows Server 2019 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2019 Datacenter CORE" + iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Core.vhdx" + edition="Windows Server 2019 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2019 Datacenter Full" + templatevhd="Windows Server 2019 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2019 Datacenter CORE" + templatevhd="Windows Server 2019 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-ROOTCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000009'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-ROOTCA"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "LABBUILDER.COM Issuing CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10,192.168.128.11"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2019_DCandDHCPandEdge.xml b/source/samples/Sample_WS2019_DCandDHCPandEdge.xml new file mode 100644 index 00000000..7ece5182 --- /dev/null +++ b/source/samples/Sample_WS2019_DCandDHCPandEdge.xml @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2019_DCandDHCPandEdge" + version="1.0"> + <description>Sample Windows Server 2019 Lab Configuration DC, DHCP and Edge.</description> + + <settings labid="LABBUILDER-DCDHCPEDGE.COM " + domainname="LABBUILDER-DCDHCPEDGE.COM" + email="admina@LABBUILDER-DCDHCPEDGE.COM" + labpath="c:\vm\LABBUILDER-DCDHCPEDGE.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2019 Datacenter Full" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Full.vhdx" + edition="Windows Server 2019 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2019 Datacenter CORE" + iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Core.vhdx" + edition="Windows Server 2019 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2019 Datacenter Full" + templatevhd="Windows Server 2019 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Windows Server 2019 Datacenter CORE" + templatevhd="Windows Server 2019 Datacenter CORE" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-DC1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-DHCP1"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DCname = "SA-DC1" + DomainAdminPassword = "P@ssword!1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'Site A Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2019 Datacenter CORE" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "LABBUILDER.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="External" + switchname="External" /> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2019_NanoDomain.xml b/source/samples/Sample_WS2019_NanoDomain.xml new file mode 100644 index 00000000..23939124 --- /dev/null +++ b/source/samples/Sample_WS2019_NanoDomain.xml @@ -0,0 +1,328 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="NANOTEST.COM" + version="1.0" > + <description>Simple Windows Server 2019 Lab Configuration creating an AD DC, DHCP Server, Edge Server, Root CA and eight Nano Servers</description> + + <settings labid="NANOTEST.COM " + domainname="NANOTEST.COM" + email="daniel@NANOTEST.COM" + labpath="c:\vm\NANOTEST.COM" /> + + <switches managementvlan="97"> + <switch name="General Purpose External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="Domain Private" type="Private" vlan="30" /> + <switch name="Domain Private iSCSI" type="Private" vlan="31" /> + <switch name="Domain Private LM" type="Private" vlan="32" /> + <switch name="Domain Private SMB" type="Private" vlan="33" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2019 Datacenter Full" + iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Full.vhdx" + edition="Windows Server 2019 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Nano Server 2019 Datacenter" + iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" + url="https://software-download.microsoft.com/download/sg/" + vhd="Nano Server 2019 Datacenter.vhdx" + edition="Windows Server 2019 SERVERDATACENTERNANO" + ostype="Nano" + packages="Microsoft-NanoServer-Guest-Package.cab" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2019 Datacenter Full" + templatevhd="Windows Server 2019 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + <template name="Template Nano Server 2019 Datacenter" + templatevhd="Nano Server 2019 Datacenter" + memorystartupbytes="500MB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Nano" /> + </templates> + + <vms> + <vm name="SA-DC1" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-DC1" + bootorder="1"> + <dsc configname="DC_FORESTPRIMARY" + configfile="DC_FORESTPRIMARY.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + InstallRSATTools = $true + Forwarders = @('8.8.8.8','8.8.4.4') + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.10" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::a" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-DHCP1" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-DHCP1" + bootorder="2"> + <dsc configname="MEMBER_DHCP" + configfile="MEMBER_DHCP.DSC.ps1"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + Scopes = @( + @{ Name = 'NANOTEST.COM Primary'; + Start = '192.168.128.50'; + End = '192.168.128.254'; + SubnetMask = '255.255.255.0'; + AddressFamily = 'IPv4' + } + ) + Reservations = @( + @{ Name = 'SA-DC1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000000'; + IPAddress = '192.168.128.10'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-DHCP1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000002'; + IPAddress = '192.168.128.16'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-EDGE1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000005'; + IPAddress = '192.168.128.19'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-ROOTCA'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000007'; + IPAddress = '192.168.128.23'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO1'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000008'; + IPAddress = '192.168.128.30'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO2'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '000000000009'; + IPAddress = '192.168.128.31'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO3'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000A'; + IPAddress = '192.168.128.32'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO4'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000B'; + IPAddress = '192.168.128.33'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO5'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000C'; + IPAddress = '192.168.128.34'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO6'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000D'; + IPAddress = '192.168.128.35'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO7'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000E'; + IPAddress = '192.168.128.36'; + AddressFamily = 'IPv4' + }, + @{ Name = 'SA-NANO8'; + ScopeID = '192.168.128.0'; + ClientMACAddress = '00000000000F'; + IPAddress = '192.168.128.37'; + AddressFamily = 'IPv4' + } + ) + ScopeOptions = @( + @{ ScopeID = '192.168.128.0'; + DNServerIPAddress = @('192.168.128.10'); + Router = '192.168.128.19'; + AddressFamily = 'IPv4' + } + ) + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.16" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::10" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-EDGE1" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-EDGE1" + bootorder="3"> + <dsc configname="MEMBER_REMOTEACCESS" + configfile="MEMBER_REMOTEACCESS.DSC.ps1"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.19" + defaultgateway="" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::13" + defaultgateway="" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="General Purpose External" + switchname="General Purpose External" /> + </adapters> + <datavhds> + <datavhd vhd="ToolsDisk.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="ToolsDisk" /> + </datavhds> + </vm> + + <vm name="SA-ROOTCA" + template="Template Windows Server 2019 Datacenter Full" + computername="SA-ROOTCA" + bootorder="3"> + <dsc configname="MEMBER_ROOTCA" + configfile="MEMBER_ROOTCA.DSC.ps1" + logging="Y"> + <parameters> + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + InstallOnlineResponder = $true + InstallEnrollmentWebService = $true + CACommonName = "NANOTEST.COM Root CA" + CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" + CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n74:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.NANOTEST.COM/CertEnroll/%3%8%9.crl" + CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.NANOTEST.COM/CertEnroll/%1_%3%4.crt\n32:http://pki.NANOTEST.COM/ocsp" + CRLPeriodUnits = 52 + CRLPeriod = 'Weeks' + CRLOverlapUnits = 12 + CRLOverlapPeriod = 'Hours' + ValidityPeriodUnits = 10 + ValidityPeriod = 'Years' + AuditFilter = 127 + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.23" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::17" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + </adapters> + </vm> + + <vm name="SA-NANO" + template="Template Nano Server 2019 Datacenter" + computername="SA-NANO" + bootorder="3" + instancecount="8" + packages="Microsoft-NanoServer-DSC-Package.cab,Microsoft-NanoServer-Containers-Package.cab,Microsoft-NanoServer-Guest-Package.cab" > + <dsc configname="MEMBER_NANO" + configfile="MEMBER_NANO.DSC.ps1" + logging="Y"> + <parameters> + ODJRequestFile = "c:\ODJRequest.txt" + DomainName = "NANOTEST.COM" + DomainAdminPassword = "P@ssword!1" + DCName = "SA-DC1" + PSDscAllowDomainUser = $true + </parameters> + </dsc> + <adapters> + <adapter name="Domain Private" + switchname="Domain Private"> + <ipv4 address="192.168.128.30" + defaultgateway="192.168.128.19" + subnetmask="24" + dnsserver="192.168.128.10"/> + <ipv6 address="fd53:ccc5:895a:bc00::30" + defaultgateway="fd53:ccc5:895a:bc00::13" + subnetmask="64" + dnsserver="fd53:ccc5:895a:bc00::a"/> + </adapter> + <adapter name="General Purpose External" + switchname="General Purpose External" /> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/Sample_WS2019_Simple.xml b/source/samples/Sample_WS2019_Simple.xml new file mode 100644 index 00000000..999ed64e --- /dev/null +++ b/source/samples/Sample_WS2019_Simple.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="Sample_WS2019_Simple" + version="1.0"> + <description>Sample Windows Server 2019 Lab Configuration Simple</description> + + <settings labid="LABBUILDER-SIMPLE.COM " + domainname="LABBUILDER-SIMPLE.COM" + email="admin@LABBUILDER-SIMPLE.COM" + labpath="c:\vm\LABBUILDER-SIMPLE.COM" /> + + <switches> + <switch name="External" type="External"> + <adapters> + <adapter name="Cluster" macaddress="00155D010701" /> + <adapter name="Management" macaddress="00155D010702" /> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + <switch name="General Purpose Internal" type="Internal" /> + <switch name="Domain Private Site A" type="Private" vlan="2" /> + <switch name="Domain Private Site B" type="Private" vlan="3" /> + <switch name="Domain Private Site C" type="Private" vlan="4" /> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" + prefix="" > + <templatevhd name="Windows Server 2019 Datacenter Full" + iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" + url="https://software-download.microsoft.com/download/sg/" + vhd="Windows Server 2019 Datacenter Full.vhdx" + edition="Windows Server 2019 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + </templatevhds> + + <templates> + <template name="Template Windows Server 2019 Datacenter Full" + templatevhd="Windows Server 2019 Datacenter Full" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="P@ssword!1" + timezone="New Zealand Standard Time" + ostype="Server" /> + </templates> + + <vms> + <vm name="SS-DEFAULT" + template="Template Windows Server 2019 Datacenter Full" + computername="SS-DEFAULT"> + <dsc configname="STANDALONE_DEFAULT" + configfile="STANDALONE_DEFAULT.DSC.ps1"> + </dsc> + <adapters> + <adapter name="External" + switchname="External" /> + <adapter name="Domain Private Site A" + switchname="Domain Private Site A"> + <ipv4 address="192.168.10.2" + defaultgateway="192.168.10.1" + subnetmask="24" + dnsserver="192.168.10.2"/> + <ipv6 address="fd53:ccc5:895a:ba00::2" + defaultgateway="fd53:ccc5:895a:ba00::1" + subnetmask="64" + dnsserver="fd53:ccc5:895a:ba00::2"/> + </adapter> + </adapters> + </vm> + </vms> + +</labbuilderconfig> diff --git a/source/samples/isofiles/Put Windows installation ISO files here.txt b/source/samples/isofiles/Put Windows installation ISO files here.txt new file mode 100644 index 00000000..351f4e66 --- /dev/null +++ b/source/samples/isofiles/Put Windows installation ISO files here.txt @@ -0,0 +1 @@ +Place any Windows Installation ISO Files required for this Lab in this folder. \ No newline at end of file diff --git a/source/schema/labbuilderconfig-schema.xsd b/source/schema/labbuilderconfig-schema.xsd new file mode 100644 index 00000000..4338ee60 --- /dev/null +++ b/source/schema/labbuilderconfig-schema.xsd @@ -0,0 +1,1569 @@ +<?xml version="1.0" encoding="utf-8"?> +<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="labbuilderconfig"> + <xs:complexType> + <xs:all> + <xs:element minOccurs="0" maxOccurs="1" name="description" type="xs:string"> + <xs:annotation> + <xs:documentation> +This optional element should contain a brief description of this Lab. + </xs:documentation> + <xs:appinfo>&lt;description&gt;This Lab builds two Domain Controllers and two DHCP Servers.&lt;/description&gt;</xs:appinfo> + </xs:annotation> + </xs:element> + <xs:element minOccurs="1" maxOccurs="1" name="settings"> + <xs:annotation> + <xs:documentation> +This required element contains settings attributes controlling general settings of this Lab. + </xs:documentation> + <xs:appinfo>&lt;settings /&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="labid" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains a Lab Identifier for the Lab. +This identifier will be pre-pended to the names of any Virtual Machines, Switches and Network Adapter names created for this Lab. + </xs:documentation> + <xs:appinfo>labid="WS2012R2-CLUSTER-TEST"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="domainname" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the Domain Name identifier used by Virtual Machines created in this Lab. It may be used by DSC to configure the Virtual Machines in the Lab. + </xs:documentation> + <xs:appinfo>domainname="CONTOSO.COM"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="email" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains an E-mail address of the Administrator of this Lab. It may be used by DSC to configure the Virtual Machines in the Lab. + </xs:documentation> + <xs:appinfo>email="dev@contoso.com"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="labpath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the full path to the folder that this Lab should be created in. It can be overridden when the Lab is installed. +The folder will be created when the Lab is installed if it doesn't already exist. +The Virtual Machines, Virtual Hard Disk drives and other Lab related files will be created in this folder. + </xs:documentation> + <xs:appinfo>labpath="f:\Labs\WS2012R2-CLUSTER-TEST-01"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhdparentpath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the path to the folder that will contain the Parent VHD files used by the Virtual Machines in this Lab. +If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. +The Parent VHD files are used as Parent VHD's to any Lab VM boot disks or cloned to each Virtual Machine folder depending on the 'usedifferencingdisk' setting for each Lab VM. + +- Default Value: ParentVHDs + </xs:documentation> + <xs:appinfo>vhdparentpath="f:\Labs\WS2012R2-CLUSTER-TEST-01\ParentVHDs"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="dsclibrarypath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the path to the folder that will contain the DSC Library files used by the Virtual Machines in this Lab. +If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. +If this setting is not set it will default to the 'dsclibrary' folder within this module and will not be a subfolder of the 'labpath'. +Usually the content of this folder will either be provided with the Lab or created by copying the DSCLibrary folder provided with the LabBuilder module. + +Each Virtual Machine that is set to be configured by DSC requires a DSC configuration file that must be found in this folder. + +- Default Value: DSCLibrary + </xs:documentation> + <xs:appinfo>dsclibrarypath="C:\DSC\MyLibrary"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="resourcepath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the path to the folder that will contain any Resource files required by this Lab. +This includes the MSU packages that may need to be installed into the TemplateVHD or VM BootVHD files by specifing the MSU package names in the Packages attribute of the VMTemplateVHD, TemplateVHD or VM elements. +If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. + +- Default Value: Resource + </xs:documentation> + <xs:appinfo>resourcepath="f:\SharedResources\"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="modulepath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be used to add a path to the PowerShell Module Search path. +It can be used to specify an alternate path for DSC Resource Modules for the use in this Lab. +If specified, LabBuilder will search for DSC Resource Modules in this path before searching all other default PowerShell Module Paths. +If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. + </xs:documentation> + <xs:appinfo>modulepath="f:\SharedModules\"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="dismpath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the path to the copy of DISM.EXE that should be used to convert any Windows Install Media ISOs to VHD files. +This is usually only required if the Lab Host is running Windows Server 2012 R2 or earlier or Windows 8.1 or earlier and the Windows Install Media ISO being converted is Windows Server 2016. +The latest version of DISM can be found in the Windows ADK here https://msdn.microsoft.com/en-us/library/hh825494.aspx. +Once the ADK is installed this setting can be configured to tell LabBuilder where to find the appropriate version (x86 or amd64) of DISM. +You should not include the DISM.EXE application name in the path. + </xs:documentation> + <xs:appinfo>resourcepath="C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="requiredwindowsbuild" type="xs:integer" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the minimum build required on the Lab host to install or use this Lab. +If the Lab Host does not meet this build number an error will be thrown when loading the Lab configuration. +This ensures that all features required to install a Lab are available on the Lab Host before installation will proceed. +If this attribute is not set then the Lab Configuration will be able to installed on any Windows build version Lab Host. + </xs:documentation> + <xs:appinfo>requiredwindowsbuild="14295"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="resources"> + <xs:annotation> + <xs:documentation> +This optional element can contain one or more resources that will be required for this Lab to be installed. +These resources may be downloaded from the Internet automatically depending on the resource type. + +There can be different types of Resources that can be contained in the Resources element. + +Currently the Resource types that are supported are: + - Module: A PowerShell (DSC) Module that is downloaded via URL or using PowerShell Get. This can be a DSC or non-DSC PowerShell module. + - MSU: A Microsoft Update package that will be downloaded to the lab Resources folder and can be installed into the Boot VHD when it is created from an ISO, when it is copied to the Parent VHD folder or when the VM is prepared for first boot. + </xs:documentation> + <xs:appinfo>&lt;resources&gt;...&lt;/resources&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="module"> + <xs:annotation> + <xs:documentation> +A PowerShell (DSC) Module that will be downloaded and installed to the Lab Host when this Lab is installed. + +Note: This is not required for any PowerShell DSC Modules that are referenced in a DSC configuration used by a Virtual Machine if the version required is available in the PowerShell Gallery and is just the latest version. +This is usually only required if the Lab requires the use of development resources or versions that are either not available on PowerShell Gallery or a specific version is requred. + </xs:documentation> + <xs:appinfo>&lt;module /&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +The Name of the PowerShell (DSC) Module that this Lab requires. +If a URL attribute is not specified, the PowerShell Gallery will be searched for a module with this name and downloaded. + </xs:documentation> + <xs:appinfo>name="NetworkingDsc"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +An optional URL that will be used to download the PowerShell (DSC) Module from. Setting this attribute prevent LabBuilder from using PowerShell Get to download the Module if it is missing. +This is commonly used to download PowerShell (DSC) Modules directly from GitHub or other repositories. + </xs:documentation> + <xs:appinfo>url="https://github.com/PowerShell/NetworkingDsc/archive/dev.zip"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="folder" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute only needs to be set if the zip file downloaded by the URL in the URL attribute contains a folder that the PowerShell (DSC) Module is in. +This is usually used when the URL specifies a GitHub repository branch, which will cause the downloaded zip file to contain a folder named 'name-branch' (e.g. NetworkingDsc-dev). + </xs:documentation> + <xs:appinfo>folder="NetworkingDsc-dev"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="minimumversion" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the minimum PowerShell module version that is required by this Lab. +If a version of the Module is not found that is at least this version then a newer version will be downloaded using PowerShell Get. +This attribute should only be used if URL is not set. + </xs:documentation> + <xs:appinfo>minimumversion="2.0.0.0"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="requiredversion" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the specific PowerShell module version that is required by this Lab. +If a version of the Module is not found that is exactly this version then this version will be downloaded using PowerShell Get. +This attribute should only be used if URL is not set. + </xs:documentation> + <xs:appinfo>requiredversion="2.1.0.0"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="unbounded" name="msu"> + <xs:annotation> + <xs:documentation> +An Microsoft Update (MSU) package file to be installed into a Boot VM. + </xs:documentation> + <xs:appinfo>&lt;msu /&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +A descriptive name for this MSU that will be used to identify this package. +Any Lab build process that installs MSU packages will need to refer to this name, not the file name of the package. + </xs:documentation> + <xs:appinfo>name="WMF5.1-WS2012R2-W81"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +The URL to download this MSU file from. +If this file already exists in the Resources folder for this Lab when the Lab is installed, it will not be downloaded again. + +Note: If the Lab contains Windows Server 2012 R2, Windows Server 2012 or Windows Server 2008 R2 machines, the WMF 5.0 MSU packages MUST be installed on these machines before first boot or they will not be able to be configured. + +To download these packages: + - Windows Server 2012 R2 - https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu + - Windows Server 2012 - https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/W2K12-KB3134759-x64.msu + - Windows Server 2008 R2 - https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win7AndW2K8R2-KB3134760-x64.msu' + </xs:documentation> + <xs:appinfo>url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="path" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be used to set an optional path this package will be stored and/or downloaded to. + </xs:documentation> + <xs:appinfo>path="f:\LabBuilder\sharedpackages\"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="unbounded" name="iso"> + <xs:annotation> + <xs:documentation> +An ISO file that can be mounted into one or more Lab Virtual Machines. + </xs:documentation> + <xs:appinfo>&lt;iso /&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +A descriptive name for this ISO that will be used to identify this disk. +Any Lab build process that mounts ISO files will need to refer to this name, not the file name of the ISO. + </xs:documentation> + <xs:appinfo>name="SQL2012_FULL_ENU"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +The optional URL to download this ISO file from. +If this file already exists in the Resources folder for this Lab when the Lab is installed, it will not be downloaded again. +This attribute should not be used if the path attribute is also set. + </xs:documentation> + <xs:appinfo>url="https://download.microsoft.com/download/4/C/7/4C7D40B9-BCF8-4F8A-9E76-06E9B92FE5AE/ENU/SQLFULL_ENU.iso"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="path" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to set the filename (and optionally path) of the source ISO. +The ISO will be used from that location and not copied into the Resources folder of the Lab. +If this path does not contain a root it will be appended onto the _ISOFiles_ attribute on the _Resources_ node or the path set in the _ResourcePath_ attribute on the _Settings_ node. +If the ISO file does not exist but a URL is provided that contains a filename with an extension of ISO or ZIP it will be downloaded to this location and optionally unzipped. +If the ISO file does not exist but a URL is provided that does not contain an ISO or ZIP filename the user will be requested to manually download the file from the URL and Lab installation will terminate. + </xs:documentation> + <xs:appinfo>path="f:\isos\SQLFULL_ENU.iso"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="isopath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be used to set the path to the folder that LabBuilder will look for the Resource ISO files. +If not set this will default to the ResourcePath specified in the Lab configuration file. +If a ResourcePath is not set then this will be the Resource folder within the Lab folder. +This can be a relative or full path. +If a relative path is set, it will be relative to the full path of the Lab Resource folder. + </xs:documentation> + <xs:appinfo>isopath="d:\LabShared\ISOs"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="switches"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more Switch nodes representing the Hyper-V Virtual Switches that are required for this Lab. +Any missing switches in this list will be created on the Lab Host when this Lab is installed. + +Note: A Private Management Virtual Switch will always be created for each installed Lab for LabBuilder to install and configure the Virtual Machines in a Lab. This Management Virtual Switch will not appear in this list but will always be created. + </xs:documentation> + <xs:appinfo>&lt;switches&gt;...&lt;/switches&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="switch"> + <xs:annotation> + <xs:documentation> +This optional element represents a Hyper-V Virtual Switch that is required for this Lab. +A Lab may contain one or more Internal, Private, External or NAT Virtual Switches. + </xs:documentation> + <xs:appinfo>&lt;switch&gt;...&lt;/switch&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:all> + <xs:element minOccurs="0" maxOccurs="1" name="adapters"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more Adapter nodes representing Hyper-V Virtual Network Adapters that are used by the Host Operating System to connect to this Hyper-V Virtual Switch. +This should element should only be added for External or Private switches. + </xs:documentation> + <xs:appinfo>&lt;adapters&gt;...&lt;/adapters&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="adapter"> + <xs:annotation> + <xs:documentation> +This optional element represents a Hyper-V Virtual Network Adapter that will be used as a Management Adapter for the Host Operating system to connect to this Virtual Switch. +These Management Adapters are usually used to allow access to the Internet by the Virtual Machines in a Lab. + </xs:documentation> + <xs:appinfo>&lt;switch&gt;...&lt;/switch&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to set the Name of the Management Virtual Adapter connected to this Hyper-V Virtual Switch. + +Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Switch is created if the Switch type is a Private, Internal or NAT. + </xs:documentation> + <xs:appinfo>name="Cluster Network"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="macaddress" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to set the MAC Address of the Management Virtual Adapter connected to this Hyper-V Virtual Switch. + </xs:documentation> + <xs:appinfo>macaddress="00155D010703"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vlan" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to set a VLAN ID of the Management Virtual Adapter connected to this Hyper-V Virtual Switch. + </xs:documentation> + <xs:appinfo>vlan="10"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to configure the Name of the Hyper-V Virtual Switch to be created for this Lab. + +Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Switch is created if the Switch type is a Private, Internal or NAT. + </xs:documentation> + <xs:appinfo>name="Domain Cluster"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to set the Type of Hyper-V Virtual Switch to create. +It can be set to: + - Internal + - Private + - External + - NAT (only available on Windows 10 and Windows Server 2016 build 14295 and above) + </xs:documentation> + <xs:appinfo>type="Internal"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vlan" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to configure the VLAN ID of all Virtual Network Adapters that will connect to this Virtual Switch. + </xs:documentation> + <xs:appinfo>vlan="43"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="bindingadaptername" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to configure which physical network adapter an External switch will be bound to. +This attribute should only be set if the switch type is External. +If the bindingadaptermac attribute is set then this attribute should not be set. + </xs:documentation> + <xs:appinfo>bindingadaptername="Ethernet 1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="bindingadaptermac" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to configure which physical network adapter an External switch will be bound to. +This attribute should only be set if the switch type is External. +If the bindingadaptername attribute is set then this attribute should not be set. + </xs:documentation> + <xs:appinfo>bindingadaptermac="C86000A1A895"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="natsubnet" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to configure the subnet that will be assigned to this NAT switch. +It must contain an IP address (192.168.10.0) followed by a slash (/) then the subnet prefix length (e.g. 24). +This attribute should only be set if the switch type is NAT. +If this attribute is set the natgatewayaddress attribute must also be set. + </xs:documentation> + <xs:appinfo>natsubnet="192.168.10.0/24"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="natgatewayaddress" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to configure the IP address that will be used as a gateway address on this NAT switch. +This IP Address must be within the defined NAT subnet set in the natsubnet attribute. +This attribute should only be set if the switch type is NAT. +If this attribute is set the natsubnet attribute must also be set. + </xs:documentation> + <xs:appinfo>natgatewayaddress="192.168.10.1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="managementvlan" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to change the VLAN ID used by the Private Hyper-V Management Switch created to manage this Lab. +If not set the default VLAN ID value of 99 will be used. +All Virtual Network Adapters automatically created and attached to the Management Switch for this Lab will be set to use this VLAN ID. + </xs:documentation> + <xs:appinfo>managementvlan="55"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="templatevhds"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more TemplateVHD nodes representing the Template Virtual Hard Disk files that are required by the Templates and/or Virtual Machines in this Lab. +These Template VHD files will be created from Windows Install Media ISO files if they can't be found in the specified VHDPath folder during the Lab Install process. + </xs:documentation> + <xs:appinfo>&lt;templatevhds&gt;...&lt;/templatevhds&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="templatevhd"> + <xs:annotation> + <xs:documentation> +This optional element represents a Template VHD (Virtual Hard Disk) that will be created and used by a Virtual Machine Template to create boot disks for Virtual Machines. +If the VHD/VHDx file for this Template VHD can not be found it will be created using the specified Windows Install Media ISO file. +If the Windows Media ISO file can not be found the Lab Install will not be able to proceed. + +Note: It is common for more than one Lab to use the same Template VHD files, therefore it is common to set the VHDPath and ISOPath attributes of this element to be a folder that can be accessed by multiple Labs. + </xs:documentation> + <xs:appinfo>&lt;templatevhd&gt;...&lt;/templatevhd&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute will be the name of this Template VHD. +It will be used by Template elements to refer to this Template VHD. + </xs:documentation> + <xs:appinfo>name="Windows Server 2012 R2 Datacenter Full"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="iso" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute should contain the relative or full path to the Windows Install Media ISO file required to build this Template VHD. +If this filename is not a rooted path it will be appended to the path found in the ISOFiles attribute. + </xs:documentation> + <xs:appinfo>iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be set to a URL that will be reported to a user if the ISO file required to build this Template VHD is not found. + +Note: This URL will not be automatically downloaded, a user will need to open this URL in a browser. + </xs:documentation> + <xs:appinfo>url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhd" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute should contain the relative or full path to the VHD file that this Template VHD will use and/or create. +If this filename is not a rooted path it will be appended to the path found in the VHDFiles attribute. + </xs:documentation> + <xs:appinfo>vhd="Windows Server 2012 R2 Datacenter Full.vhdx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="edition" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute will be the Edition that is installed from the Windows Install Media ISO if the Template VHD needs to be created. +If this is not provided and the VHD file needs to be created from the Windows Install Media ISO then the first edition in this Install Media will be created. + </xs:documentation> + <xs:appinfo>edition="Windows Server 2012 R2 SERVERDATACENTER"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="ostype" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute sets defines the type of Operating System that this Template VHD contains. +It is used by LabBuilder to determine how to configure Virtual Machines based on this template VHD. +It is also used to determine if Nano Server packages should be applied. + + - Default Value: Server. + - Valid Values: Server | Client | Nano + </xs:documentation> + <xs:appinfo>ostype="Server"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="features" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can contain a comma delimited list of Windows Server Features that should be installed into this Virtual Machine Template VHD. +Normally, additional Windows Server Features are installed via DSC Library Configurations, so this attribute should not normally be used. + </xs:documentation> + <xs:appinfo>features="Web-Application-Proxy,Routing"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhdformat" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the type of VHD format to create if the VHD does not already exist. + + - Default Value: VHDX + - Valid Values: VHDX | VHD + </xs:documentation> + <xs:appinfo>vhdformat="VHDx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhdtype" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the type of VHD file to create if the VHD does not already exist. + + - Default Value: Dynamic + - Valid Values: Fixed | Dynamic + </xs:documentation> + <xs:appinfo>vhdtype="Fixed"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="generation" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the Virtual Machine generation of VHD file to create if the VHD does not already exist. + + - Default Value: 2 + - Valid Values: 1 | 2 + </xs:documentation> + <xs:appinfo>generation="1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhdsize" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the size of Boot VHD file to create if the VHD does not already exist. + +Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). + </xs:documentation> + <xs:appinfo>size="25GB"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="packages" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can contain a comma delimited list of packages that should be installed into this Virtual Machine Template VHD. + +If the Template VHD is a Nano Server then the packages can be .cab files, which will install the Nano Server package from the ISO or a Resource MSU file. + +If the Template VHD is not a Nano Server then the packages must be Resource MSU files. + +Valid Values: + - Resource MSU names that are can be found in the ResourceMSU list. + +Valid Values for Nano Server: + - Filename including the .cab extension of a valid Nano Server package found on the Windows Install Media ISO. + - Resource MSU names that are can be found in the ResourceMSU list. + </xs:documentation> + <xs:appinfo>packages="Microsoft-NanoServer-DNS-Package.cab,SomePackage.msu"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="isopath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be used to set the path to the folder that LabBuilder will look for the Windows Install Media ISO files for any missing Template VHD files. +If not set this will default to the same path as the Lab configuration file. +This can be a relative or full path. +If a relative path is set, it will be relative to the full path of the Lab configuration file. + </xs:documentation> + <xs:appinfo>isopath="d:\LabShared\ISOs"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhdpath" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be used to set the path to the folder that LabBuilder will create or look for any Template VHD files in. +If not set this will default to the same path as the Lab configuration file. +This can be a relative or full path. +If a relative path is set, it will be relative to the full path of the Lab configuration file. + </xs:documentation> + <xs:appinfo>isopath="d:\LabShared\VHDs"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="prefix" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can be used to pre-pend a string to the VHD Template file that is created. + </xs:documentation> + <xs:appinfo>prefix="Templates "</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="templates"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more Template nodes representing the Virtual Machine Templates used to build the Virtual Machines in this Lab. +The Virtual Machine Templates in this list may refer to a TemplateVHD or define a direct path to a Source VHD file. +If a TemplateVHD is specified, this TemplateVHD must be found in the TemplateVHDs collection. +Every Virtual Machine defined in this Lab must refer to a Template in this collection. + </xs:documentation> + <xs:appinfo>&lt;templates&gt;...&lt;/templates&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="template"> + <xs:annotation> + <xs:documentation> +This optional element represents a Virtual Machine Template that will be created when this Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;template&gt;...&lt;/template&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute contains the name of this Virtual Machine Template. +Virtual Machines will refer to this value if they are going to use this Virtual Machine Template. + </xs:documentation> + <xs:appinfo>name="Windows Server 2012 R2 Datacenter CORE"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vhd" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the file name of the VHD Boot file that will be used for any Virtual Machines using this Template. +If this attribute is not set it will default to the filename specified in the SourceVHD attribute or the filename of the Template VHD linked to by the TemplateVHD attribute. + </xs:documentation> + <xs:appinfo>vhd="Windows Server 2012 R2 Datacenter CORE.vhdx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="sourcevhd" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the relative or full path to the VHD file that will be cloned for use as the Boot VHD for any Virtual Machines using this template. + +Note: This attribute should not be set if the TemplateVHD attribute is set. + </xs:documentation> + <xs:appinfo>sourcevhd="VhdFiles\Windows Server 2012 R2 Datacenter Full.vhdx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="memorystartupbytes" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the amount of startup memory to assign to Virtual Machines based on this Template. + + - Default Value: 1GB. + - Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). + </xs:documentation> + <xs:appinfo>memorystartupbytes="8GB"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="dynamicmemoryenabled" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains a flag to enable or disable Dynamic Memory for Virtual Machines based on this Template. +Note: Disabling this value is usually only used when Nested Virtualization is required. + + - Default Value: Y. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>dynamicmemoryenabled="N"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="generation" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the Virtual Machine generation to create. + + - Default Value: 2 + - Valid Values: 1 | 2 + </xs:documentation> + <xs:appinfo>generation="1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="Version" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the Virtual Machine Version to create, this is only applicable to Windows 10 build 14352 or higher/Server 2016 post TP5. + + - Default Value: 8.0 + - Valid Values: 5.0, 6.2, 7.0, 7.1, 8.0, 254.0, and 255.0 + </xs:documentation> + <xs:appinfo>version="5.0"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="processorcount" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute determines the number of virtual processors assigned to Virtual Machines based on this Template. + + - Default Value: 1. + - Valid Values: 1-n. + </xs:documentation> + <xs:appinfo>processorcount="2"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="administratorpassword" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute specifies the local Administrator password to assign when Virtual Machines based on this Template are installed. +If this is not defined for the Template it should be defined in the Virtual Machine definition. + </xs:documentation> + <xs:appinfo>administratorpassword="MyP@ssw0rd!1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="productkey" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute specifies the Windows product key to set on any Virtual Machines based on this Template. + </xs:documentation> + <xs:appinfo>productkey="AAAAA-AAAAA-AAAAA-AAAAA-AAAAA"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="timezone" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute sets the timezone assigned to Virtual Machines based on this Template. + + - Default Value: PST. + </xs:documentation> + <xs:appinfo>timezone="CST"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="ostype" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute sets defines the type of Operating System that this Template contains. +It is used by LabBuilder to determine how to configure Virtual Machines based on this template. + + - Default Value: Server. + - Valid Values: Server | Client | Nano + </xs:documentation> + <xs:appinfo>ostype="Server"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="integrationservices" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls which Integration Services are enabled on any Virtual Machines created using this Template. +It should contain a comma delimited list of Integration Service names that should be enabled. +If this attribute is defined but left empty, all Integration Services will be disabled. +If this attribute is not defined all Integration Services will be enabled. + + - Default Value: Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS + - Valid Values: Guest Service Interface | Heartbeat | Key-Value Pair Exchange | Shutdown | Time Synchronization | VSS + </xs:documentation> + <xs:appinfo>ostype="Server"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="templatevhd" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute defines a Template VHD that this Virtual Machine Template will use to determine the Boot VHD file. +If a TemplateVHD with a name matching the value of this attribute can not be found then an error will occur when this Lab is installed. +If this attribute is defined, the SourceVHD attribute should not be defined. + </xs:documentation> + <xs:appinfo>templatevhd="Windows Server 2012 R2 Datacenter Core"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="exposevirtualizationextensions" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls whether or not Virtualization Extensions are exposed for Virtual Machines based on this Template. +This attribute should only be enabled if the Host system this Lab is to be installed on is able to support Nested Virtualization. +Currently this is only supported on Windows 10 built 10586 or above and Windows Server 2016 TP4 or above. + + - Default Value: N. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>exposevirtualizationextensions="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="packages" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can contain a comma delimited list of packages that should be installed onto any Virtual Machines using this Template. + +If the Template is a Nano Server then the packages can be .cab files, which will install the Nano Server package from the ISO or a Resource MSU file. + +If the Template is not a Nano Server then the packages must be Resource MSU files. + +Valid Values: + - Resource MSU names that are can be found in the ResourceMSU list. + +Valid Values for Nano Server: + - Filename including the .cab extension of a valid Nano Server package found on the Windows Install Media ISO. + - Resource MSU names that are can be found in the ResourceMSU list. + </xs:documentation> + <xs:appinfo>packages="Microsoft-NanoServer-DNS-Package.cab,SomePackage.msu"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="fromvm" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute enables the list of Template Virtual Machines to be pulled from the Virtual Machines defined in Hyper-V. +The list of Hyper-V Virtual Machines to use as templates can be specified using a wild card value. E.g. 'Template *' +If specified, when the Lab is installed the list of available Virtual Machine Templates will be pulled from Hyper-V by matching the names against the FromVM attribute. +After the list of Hyper-V Virtual Machines to use a templates is pulled in, any Templates defined in this container will be merged into this list. + </xs:documentation> + <xs:appinfo>fromvm="Template *"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="vms"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more VM nodes, each representing a Virtual Machine that will be created when this Lab is installed. +Each Virtual Machine will refer back to a Template that is found in the Templates collection. +If the Template used by this Virtual Machine can not be found, an error will occur when this Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;vms&gt;...&lt;/vms&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="vm"> + <xs:annotation> + <xs:documentation> +This optional element represents a Virtual Machine that will be created when this Lab is installed. + +All Lab configurations should include at least one Virtual Machine, although an error will not be thrown if no Virtual Machines are defined in a Lab. + </xs:documentation> + <xs:appinfo>&lt;vm&gt;...&lt;/vm&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:all> + <xs:element minOccurs="0" maxOccurs="1" name="datavhds"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more DataVHD nodes, each representing a Data Virtual Hard Drive that will be created and attached to this Virtual Machine when this Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;datavhds&gt;...&lt;/datavhds&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="datavhd"> + <xs:annotation> + <xs:documentation> +This optional element represents a Data Virtual Hard Drive that will be created and attached to the Virtual Machine when the Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;datavhd&gt;...&lt;/datavhd&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="vhd" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to specify the path and filename of the Virtual Machine Data VHD to create and attach to the Virtual Machine. +If a relative path or just a filename is provided to the VHD file then it will be set as relative to the 'Virtual Hard Disks' folder in the Virtual Machine folder. +If this VHD does not exist then it may be created using the additional attributes provided. + </xs:documentation> + <xs:appinfo>vhd="DataDisks/DataDisk1.vhdx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="sourcevhd" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the file path to the VHD file that will be cloned to create the new Data VHD if it does not exist. +This attribute is only used when the Data VHD does not exist and need to be created. +This attribute should not be defined if the Size or Type attributes are defined. +If the MoveSourceVHD attribute is set to 'Y' then this file will be moved to Data VHD location instead of being copied. + </xs:documentation> + <xs:appinfo>vhd="DataDisks/DataDisk1.vhdx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="copyfolders" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute allows specified folders to be copied onto a new Data VHD when it is first created. +This attribute is only used when the Data VHD does not exist and need to be created. +When a new DataVHD is created the folders specified in this attribute are copied recursively to the first formatted partition on the new Data VHD. +This attribute should only be set if both the partitionstyle and filesystem attributes are defined, or if the new Data VHD is cloned from a VHD that contains a formatted volume. + </xs:documentation> + <xs:appinfo>copyfolders="f:\data\tools"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the type of Data VHD file to create if the VHD does not already exist. +This attribute is only used when the Data VHD does not exist and need to be created. +This attribute should not be defined if the SourceVHD attribute is defined. +If the value of this attribute is Differencing then the ParentVHD attribute must also be set. + + - Valid Values: Fixed | Dynamic | Differencing + </xs:documentation> + <xs:appinfo>type="Dynamic"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="size" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the size of Data VHD file to create if the VHD does not already exist. +If the VHD already exists and this value is larger than the current size of the VHD, then it will be expanded, otherwise an error will occur. +This attribute should not be defined if the SourceVHD attribute is defined. + + - Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). + </xs:documentation> + <xs:appinfo>size="100GB"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="supportpr" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute enables support persistent reservation on this Data VHD. +This attribute is only used when the Data VHD does not exist and need to be created. +This attribute is only used when the Data VHD has the Shared attribute set to 'Y'. + + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>supportpr="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="partitionstyle" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute causes a newly created Data VHD to be partitioned and formatted so that files and folders can be copied to it. +This attribute is only used when the Data VHD does not exist and need to be created. +Normally, this attribute would be set if the CopyFolders attribute was also set, enabling the folders to be copied onto the Data VHD before the Virtual Machine has been provisioned. +If this is not defined for then the Data VHD will need to be partitioned and allocated within the host operating system via DSC or some other mechanism. +If this attribute is defined then the FileSystem attribute must also be defined. + + - Valid Values: MBR | GPT + </xs:documentation> + <xs:appinfo>partitionstyle="GPT"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="filesystem" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute causes a newly created Data VHD to be partitioned and formatted so that files and folders can be copied to it. +This attribute is only used when the Data VHD does not exist and need to be created. +Normally, this attribute would be set if the CopyFolders attribute was also set, enabling the folders to be copied onto the Data VHD before the Virtual Machine has been provisioned. +If this is not defined for then the Data VHD will need to be partitioned and allocated within the host operating system via DSC or some other mechanism. +If this attribute is defined then the PartitionStyle attribute must also be defined. + + - Valid Values: FAT32 | EXFAT | NTFS | REFS +Note: REFS can only be used if the host is Windows Server 2012 or above. However, REFS can still be formatted within the guest operating system even if the Host is Windows 10. + </xs:documentation> + <xs:appinfo>filesystem="NTFS"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="filesystemlabel" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute causes a newly created Data VHD to be set with a label if it has been partitioned and formatted. +This attribute is only used when the Data VHD does not exist and need to be created. +Normally, this attribute would be set if the CopyFolders attribute was also set, enabling the folders to be copied onto the Data VHD before the Virtual Machine has been provisioned. +If this attribute is defined then both the PartitionStyle and FileSystem attributes must also be defined. + </xs:documentation> + <xs:appinfo>filesystemlabel="ToolsDisk"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="parentvhd" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute specifies the Parent VHD to use for a Differencing VHD. +It should be a full path or a path relative to the Virtual Hard Disk folder in the Virtual Machine. +This attribute is only used when the Data VHD does not exist and need to be created. +If this attribute is defined then the Type attribute must be set to Differencing. + </xs:documentation> + <xs:appinfo>parentvhd="..\..\ToolsDiskParent.vhdx"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="movesourcevhd" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute causes the Source VHD that is used to create the new Data VHD to be moved instead of copied. +This attribute should only be set to 'Y' if this SourceVHD attribute is defined. + + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>movesourcevhd="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="shared" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute enables the Data VHD to be attached as a Shared VHD to the Virtual Machine. +This attribute should only be set to 'Y' if this DataVHD is being stored on a Cluster Shared Volume and the share supports SMB 3.02. + + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>shared="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="dvddrives"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more DVDDrive nodes, each representing a Virtual DVD Drive that will be created and attached to this Virtual Machine when this Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;dvddrives&gt;...&lt;/dvedrives&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="dvddrive"> + <xs:annotation> + <xs:documentation> +This optional element represents a Virtual DVD Drive that will be created and attached to the Virtual Machine when the Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;dvddrive&gt;...&lt;/dvddrive&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="iso" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to specify the name of the ISO Resource to mount to this Virtual DVD Drive. + </xs:documentation> + <xs:appinfo>iso=""</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="adapters"> + <xs:annotation> + <xs:documentation> +This optional element contains a collection of zero or more Adapter nodes, each representing a Virtual Network Adapter that will be created, attached to the Virtual Machine and connected to a Virtual Switch when this Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;adapters&gt;...&lt;/adapters&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="adapter"> + <xs:annotation> + <xs:documentation> +This optional element represents a Virtual Network Adapter that will be created, attached to the Virtual Machine and connected to a Virtual Switch when the Lab is installed. + </xs:documentation> + <xs:appinfo>&lt;adapter&gt;...&lt;/adapter&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:all> + <xs:element minOccurs="0" maxOccurs="1" name="ipv4"> + <xs:annotation> + <xs:documentation> +This optional element represents the IPv4 settings that will be assigned to this Virtual Network Adapter from within the guest operating system. +If this element is not defined, then the IPv4 settings for the adapter will not be set or changed. + </xs:documentation> + <xs:appinfo>&lt;ipv4&gt;...&lt;/ipv4&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="address" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute configures this Virtual Adapter to use a static IPv4 address. +If this attribute is defined then the IPv4 subnetmask attribute should also be defined. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>address="10.0.1.19"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="defaultgateway" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute configures the IPv4 default gateway of the Virtual Adapter. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>defaultgateway="10.0.1.0"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="subnetmask" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This required attribute configures the subnet mask of the IPv4 Address assigned to this Virtual Network Adapter. +If this attribute is defined then the IPv4 address attribute should also be defined. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>subnetmask="8"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="dnsserver" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This required attribute configures the IPv4 DNS Server addresses assigned to this Virtual Network Adapter. +This can be a comma delimited list of addresses. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>dnsserver="10.0.1.10,10.0.1.11"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="ipv6"> + <xs:annotation> + <xs:documentation> +This optional element represents the IPv6 settings that will be assigned to this Virtual Network Adapter from within the guest operating system. +If this element is not defined, then the IPv6 settings for the adapter will not be set or changed. + </xs:documentation> + <xs:appinfo>&lt;ipv4&gt;...&lt;/ipv4&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:attribute name="address" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute configures this Virtual Adapter to use a static IPv6 address. +If this attribute is defined then the IPv6 subnetmask attribute should also be defined. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>address="fd53:ccc5:895a:0000::1a"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="defaultgateway" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute configures the IPv6 default gateway of the Virtual Adapter. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>defaultgateway="fd53:ccc5:895a:0000::0"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="subnetmask" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This required attribute configures the subnet mask of the IPv6 Address assigned to this Virtual Network Adapter. +If this attribute is defined then the IPv6 address attribute should also be defined. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>subnetmask="64"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="dnsserver" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This required attribute configures the IPv6 DNS Server addresses assigned to this Virtual Network Adapter. +This can be a comma delimited list of addresses. +This property of the Virtual Adapter is configured using Desired State Configuration. + </xs:documentation> + <xs:appinfo>dnsserver="fd53:ccc5:895a:0000::a,fd53:ccc5:895a:0000::b"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute controls the name of this Virtual Network Adapter within the Host Operating System and the Guest Operating System of this Virtual Machine. +The Virtual Network Adapter name within the Host will be changed immediately, but the Adapter name within the Guest Operating System will only be configured when the Guest is first installed. + +Changing the Name of the Adapter after the Guest Operating System has been installed is not possible, by changing this Name value. + +Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Virtual Machine is created. + </xs:documentation> + <xs:appinfo>name="Cluster Comms"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="switchname" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute controls the which Virtual Switch this Virtual Network Adapter should connect to. + +Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Virtual Machine is created as long as the switch being connected to is an Internal, Private or NAT switch. + </xs:documentation> + <xs:appinfo>switchname="Cluster"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="macaddress" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to set a static MAC Address on the Virtual Network Adapter. +Care should be taken to ensure that this MAC Address is unique on the Virtual Switch that is being connected to. + </xs:documentation> + <xs:appinfo>macaddress="00155D010801"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="vlan" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute is used to configure a VLAN ID on the Virtual Network Adapter. +If this attribute is set it will override any VLAN ID that is set on the Virtual Switch that this Virtual Network Adapter connects to. +If this attribute is not set but the VLAN ID is set on the Virtual Switch that this Virtual Network Adapter connects to is set, then the Virtual Network Adapter VLAN ID will be set to the Virtual Switch VLAN ID. + </xs:documentation> + <xs:appinfo>vlan="80"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="macaddressspoofing" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute enables MAC Address spoofing by the Guest Operating System. +This is usually required when Network Virtualization is being implemented. + + - Default Value: N. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>macaddressspoofing="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element minOccurs="0" maxOccurs="1" name="dsc"> + <xs:annotation> + <xs:documentation> +This optional element contains the settings related to configuring Desired State Configuration on the Lab Virtual Machine. + </xs:documentation> + <xs:appinfo>&lt;dsc&gt;...&lt;/dsc&gt;</xs:appinfo> + </xs:annotation> + <xs:complexType> + <xs:all> + <xs:element minOccurs="0" maxOccurs="1" name="parameters" type="xs:string"> + <xs:annotation> + <xs:documentation> +This optional element contains any parameters that should be passed to the DSC Library Configuration script being used to configure this Virtual Machine. +These parameters get loaded into the ConfigData object and can be then used by the DSC Library Configuration script. +The parameters that are available to be set depends on the DSC Library Configuration script that is assigned to this Virtual Machine. +Review the documentation within the DSC Library Configuration script to see what parameters are available. + </xs:documentation> + <xs:appinfo>&lt;parameters&gt;...&lt;/parameters&gt;</xs:appinfo> + </xs:annotation> + </xs:element> + </xs:all> + <xs:attribute name="configname" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute contains the configuration name that is set in the DSC Library Configuration file that is used to configure this Virtual Machine. + </xs:documentation> + <xs:appinfo>configname="DC_FORESTPRIMARY"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="configfile" type="xs:string" use="required" > + <xs:annotation> + <xs:documentation> +This required attribute contains the filename for the DSC Library Configuration file to use to configure this Virtual Machine. +If a relative path is used for this attribute then it will be appended onto the DSCLibrary path specified in the Lab settings, otherwise the full rooted path to the file will be used. + </xs:documentation> + <xs:appinfo>configfile="DC_FORESTPRIMARY.DSC.ps1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="logging" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute enables DSC Logging on the Virtual Machine in the DSC Event Logs. + + - Default Value: N. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>logging="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute contains the name of the Lab Virtual Machine. +This is the name that will appear in the Hyper-V manager for this Virtual Machine when this Lab is installed. + +Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Virtual Machines is created. + </xs:documentation> + <xs:appinfo>name="SA-DC1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="template" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute is used to specify the template from the templates collection that will be used to create this Virtual Machine from. +The Template must match the name of one of the Templates in the Template collection, otherwise an error will occur when the Lab is installed. + +Note: many of the attributes defined in for the Virtual Machine may also be defined in the Template. +If they are defined in the template but not in the Virtual Machine then the template setting will be used. +If the setting is defined in the Virtual Machine and also in the Template, the Virtual Machine value will be used. + </xs:documentation> + <xs:appinfo>template="Windows Server 2012 R2 Datacenter Core"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="computername" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute contains the Computer Name of the Virtual Machine. +This is the name that will be set on the Virtual Machine once it has been first booted. + </xs:documentation> + <xs:appinfo>computername="SA-DC1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="memorystartupbytes" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains the amount of startup memory to assign to Virtual Machine. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. + + - Default Value: 1GB. + - Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). + </xs:documentation> + <xs:appinfo>memorystartupbytes="8GB"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="generation" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the Virtual Machine generation to create. + + - Default Value: 2 + - Valid Values: 1 | 2 + </xs:documentation> + <xs:appinfo>generation="1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="Version" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the Virtual Machine Version to create, this is only applicable to Windows 10 build 14352 or higher/Server 2016 post TP5. + + - Default Value: 8.0 + - Valid Values: 5.0, 6.2, 7.0, 7.1, 8.0, 254.0, and 255.0 + </xs:documentation> + <xs:appinfo>version="5.0"</xs:appinfo> + </xs:annotation> + </xs:attribute> <xs:attribute name="dynamicmemoryenabled" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute contains a flag to enable or disable Dynamic Memory for Virtual Machine. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. +Note: Disabling this value is usually only used when Nested Virtualization is required. + + - Default Value: Y. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>dynamicmemoryenabled="N"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="exposevirtualizationextensions" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls whether or not Virtualization Extensions are exposed for Virtual Machine. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. +This attribute should only be enabled if the Host system this Lab is to be installed on is able to support Nested Virtualziation. +Currently this is only supported on Windows 10 built 10586 or above and Windows Server 2016 TP4 or above. + + - Default Value: N. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>exposevirtualizationextensions="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="usedifferencingdisk" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls whether or not the Boot VHD created for this Virtual Machine will be a Differencing disk or a copy of the Template VHD. +Using a Differencing Disk for the Boot VHD will conserve disk space. + + - Default Value: Y. + - Valid Values: Y | N + </xs:documentation> + <xs:appinfo>usedifferencingdisk="Y"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="administratorpassword" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute specifies the local Administrator password to assign when this Virtual Machine is installed. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. + </xs:documentation> + <xs:appinfo>administratorpassword="MyP@ssw0rd!1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="productkey" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute specifies the Windows product key to set on this Virtual Machine. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. + </xs:documentation> + <xs:appinfo>productkey="AAAAA-AAAAA-AAAAA-AAAAA-AAAAA"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="timezone" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute sets the timezone assigned to this Virtual Machine. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. + + - Default Value: PST. + </xs:documentation> + <xs:appinfo>timezone="CST"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="unattendfile" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute allows a specific unattend XML file to be used instead of the default XML file. +If a relative path is used for this attribute then it will be appended onto the path of this the Lab config file, otherwise the full rooted path to the file will be used. + </xs:documentation> + <xs:appinfo>unattendfile="Unattend\SpecialUnattend.xml"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="setupcomplete" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute allows a specific Setup Complete script file to be used instead of the default Setup Complete script. +If a relative path is used for this attribute then it will be appended onto the path of this the Lab config file, otherwise the full rooted path to the file will be used. + </xs:documentation> + <xs:appinfo>setupcomplete="Scripts\SetupScompleteDebug.cmd"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="integrationservices" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls which Integration Services are enabled on this Virtual Machine. +It should contain a comma delimited list of Integration Service names that should be enabled. +If this attribute is defined but left empty, all Integration Services will be disabled. +If this attribute is not defined all Integration Services will be enabled. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. + + - Default Value: Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS + - Valid Values: Guest Service Interface | Heartbeat | Key-Value Pair Exchange | Shutdown | Time Synchronization | VSS + </xs:documentation> + <xs:appinfo>integrationservices="Guest Service Interface,Heartbeat"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="packages" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute can contain a comma delimited list of packages that should be installed onto this Virtual Machine. +If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. + +If the Virtual Machine is a Nano Server then the packages can be .cab files, which will install the Nano Server package from the ISO or a Resource MSU file. + +If the Virtual Machine is not a Nano Server then the packages must be Resource MSU files. + +Valid Values: + - Resource MSU names that are can be found in the ResourceMSU list. + +Valid Values for Nano Server: + - Filename including the .cab extension of a valid Nano Server package found on the Windows Install Media ISO. + - Resource MSU names that are can be found in the ResourceMSU list. + </xs:documentation> + <xs:appinfo>packages="Microsoft-NanoServer-DNS-Package.cab,SomePackage.msu"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="bootorder" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls the boot and shutdown order of the Virtual Machine when Start-Lab or Stop-Lab is called repsectively. +Multiple Lab Virtual Machines in the same Lab can share the same boot order. +Any Lab Virtual Machines without a boot order will be started last or shutdown first. + </xs:documentation> + <xs:appinfo>bootorder="4"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="certificatesource" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute controls where the Certificates for the Lab Virtual Machine is generated from. +This attribute should not need to be changed in most Lab Virtual Machines. +The attribute is ignored for Nano Servers because certificate generation can not be performed by Nano Servers (currently). + + - Default Value: Guest (or Host for Nano Servers). + - Valid Values: Guest | Host + </xs:documentation> + <xs:appinfo>certificatesource="Host"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="instancecount" type="xs:unsignedByte" use="optional"> + <xs:annotation> + <xs:documentation> +This optional attribute causes more than one copy of the Virtual Machine to be generated. +If set to a value more than one, it will cause this Virtual Machine to be replicated this number of times, with the machine number appended onto the end of the Virtual Machine and folder. +Any IP addresses and MAC addresses statically assigned to the network adapters in this machine will also be adjusted by increasing by one each time. + +**Care should be taken to ensure that IP addresses and MAC addresses do not overlap or stretch outside of subnet boundaries.** +**It is strongly recommended that the adapter MAC addresses on Lab VMs that have an instance count of more than one is not set, but allowed to be managed by the Hyper-V Host.** +**DHCP address assignment is also recommneded on all adapters connected to Lab VMs with an instance count of more than one.** +**If DHCP address assignement is not used then extreme care must be taken to ensure that all adapters are assigned to different subnets and will not overlap any other Lab Virtual Machine IP address assignments.** + + - Default Value: 1 + - Valid Values: 1 - 255 + </xs:documentation> + <xs:appinfo>instancecount="5"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute should be used to set a descriptive name for this Lab configuration. + </xs:documentation> + <xs:appinfo>name="WS2012R2-DOMAIN-CLUSTER"</xs:appinfo> + </xs:annotation> + </xs:attribute> + <xs:attribute name="version" type="xs:decimal" use="required"> + <xs:annotation> + <xs:documentation> +This required attribute should be used to set a version number for this Lab configuration in the format #.#. +It should be updated each time the Lab configuration is changed. + </xs:documentation> + <xs:appinfo>version="2.1"</xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/source/suffix.ps1 b/source/suffix.ps1 new file mode 100644 index 00000000..e69de29b diff --git a/source/support/Convert-LabBuilderConfigSchemaToMD.ps1 b/source/support/Convert-LabBuilderConfigSchemaToMD.ps1 new file mode 100644 index 00000000..241dc11c --- /dev/null +++ b/source/support/Convert-LabBuilderConfigSchemaToMD.ps1 @@ -0,0 +1,14 @@ +<# +.Synopsis + Creates the ..\schema\labbuilderconfig-schema.md from the ..\schema\labbuilderconfig-schema.xsd + using the transform\labbuilderconfig-schema-transformtomd.xsl transformation file. +#> +$XMLFile = Join-Path -Path $PSScriptRoot -ChildPath '..\schema\labbuilderconfig-schema.xsd' +$XSLFile = Join-Path -Path $PSScriptRoot -ChildPath 'transform\labbuilderconfig-schema-transformtomd.xsl' +$OutputFile = Join-Path -Path $PSScriptRoot -ChildPath '..\docs\labbuilderconfig-schema.md' +Write-Verbose -Verbose "Conversion '..\schema\labbuilderconfig-schema.xsd' to '..\docs\labbuilderconfig-schema.md' started" +& "$PSScriptRoot\Convert-XSDToMD.ps1" ` + -XmlFile $XMLFile ` + -XslFile $XSLFile ` + -OutputFile $OutputFile +Write-Verbose -Verbose "'..\schema\labbuilderconfig-schema.xsd' has been converted to '..\docs\labbuilderconfig-schema.md' successfully" \ No newline at end of file diff --git a/source/support/Convert-WindowsImage.ps1 b/source/support/Convert-WindowsImage.ps1 new file mode 100644 index 00000000..1c6c9907 --- /dev/null +++ b/source/support/Convert-WindowsImage.ps1 @@ -0,0 +1,4053 @@ +function +Convert-WindowsImage +{ + <# + .NOTES + Copyright (c) Microsoft Corporation. All rights reserved. + + Use of this sample source code is subject to the terms of the Microsoft + license agreement under which you licensed this sample source code. If + you did not accept the terms of the license agreement, you are not + authorized to use this sample source code. For the terms of the license, + please see the license agreement between you and Microsoft or, if applicable, + see the LICENSE.RTF on your install media or the root of your tools installation. + THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES. + + .SYNOPSIS + Creates a bootable VHD(X) based on Windows 7 or Windows 8 installation media. + + .DESCRIPTION + Creates a bootable VHD(X) based on Windows 7 or Windows 8 installation media. + + .PARAMETER SourcePath + The complete path to the WIM or ISO file that will be converted to a Virtual Hard Disk. + The ISO file must be valid Windows installation media to be recognized successfully. + + .PARAMETER CacheSource + If the source WIM/ISO was copied locally, we delete it by default. + Pass $true to cache the source image from the temp directory. + + .PARAMETER VHDPath + The name and path of the Virtual Hard Disk to create. + Omitting this parameter will create the Virtual Hard Disk is the current directory, (or, + if specified by the -WorkingDirectory parameter, the working directory) and will automatically + name the file in the following format: + + <build>.<revision>.<architecture>.<branch>.<timestamp>_<skufamily>_<sku>_<language>.<extension> + i.e.: + 9200.0.amd64fre.winmain_win8rtm.120725-1247_client_professional_en-us.vhd(x) + + .PARAMETER WorkingDirectory + Specifies the directory where the VHD(X) file should be generated. + If specified along with -VHDPath, the -WorkingDirectory value is ignored. + The default value is the current directory ($pwd). + + .PARAMETER TempDirectory + Specifies the directory where the logs and ISO files should be placed. + The default value is the temp directory ($env:Temp). + + .PARAMETER SizeBytes + The size of the Virtual Hard Disk to create. + For fixed disks, the VHD(X) file will be allocated all of this space immediately. + For dynamic disks, this will be the maximum size that the VHD(X) can grow to. + The default value is 40GB. + + .PARAMETER VHDFormat + Specifies whether to create a VHD or VHDX formatted Virtual Hard Disk. + The default is AUTO, which will create a VHD if using the BIOS disk layout or + VHDX if using UEFI or WindowsToGo layouts. + + .PARAMETER DiskLayout + Specifies whether to build the image for BIOS (MBR), UEFI (GPT), or WindowsToGo (MBR). + Generation 1 VMs require BIOS (MBR) images. Generation 2 VMs require UEFI (GPT) images. + Windows To Go images will boot in UEFI or BIOS but are not technically supported (upgrade + doesn't work) + + .PARAMETER UnattendPath + The complete path to an unattend.xml file that can be injected into the VHD(X). + + .PARAMETER Edition + The name or image index of the image to apply from the WIM. + + .PARAMETER Passthru + Specifies that the full path to the VHD(X) that is created should be + returned on the pipeline. + + .PARAMETER BCDBoot + By default, the version of BCDBOOT.EXE that is present in \Windows\System32 + is used by Convert-WindowsImage. If you need to specify an alternate version, + use this parameter to do so. + + .PARAMETER MergeFolder + Specifies additional MergeFolder path to be added to the root of the VHD(X) + + .PARAMETER BCDinVHD + Specifies the purpose of the VHD(x). Use NativeBoot to skip cration of BCD store + inside the VHD(x). Use VirtualMachine (or do not specify this option) to ensure + the BCD store is created inside the VHD(x). + + .PARAMETER Driver + Full path to driver(s) (.inf files) to inject to the OS inside the VHD(x). + + .PARAMETER ExpandOnNativeBoot + Specifies whether to expand the VHD(x) to its maximum suze upon native boot. + The default is True. Set to False to disable expansion. + + .PARAMETER RemoteDesktopEnable + Enable Remote Desktop to connect to the OS inside the VHD(x) upon provisioning. + Does not include Windows Firewall rules (firewall exceptions). The default is False. + + .PARAMETER Feature + Enables specified Windows Feature(s). Note that you need to specify the Internal names + understood by DISM and DISM CMDLets (e.g. NetFx3) instead of the "Friendly" names + from Server Manager CMDLets (e.g. NET-Framework-Core). + + .PARAMETER Package + Injects specified Windows Package(s). Accepts path to either a directory or individual + CAB or MSU file. + + .PARAMETER ShowUI + Specifies that the Graphical User Interface should be displayed. + + .PARAMETER EnableDebugger + Configures kernel debugging for the VHD(X) being created. + EnableDebugger takes a single argument which specifies the debugging transport to use. + Valid transports are: None, Serial, 1394, USB, Network, Local. + + Depending on the type of transport selected, additional configuration parameters will become + available. + + Serial: + -ComPort - The COM port number to use while communicating with the debugger. + The default value is 1 (indicating COM1). + -BaudRate - The baud rate (in bps) to use while communicating with the debugger. + The default value is 115200, valid values are: + 9600, 19200, 38400, 56700, 115200 + + 1394: + -Channel - The 1394 channel used to communicate with the debugger. + The default value is 10. + + USB: + -Target - The target name used for USB debugging. + The default value is "debugging". + + Network: + -IPAddress - The IP address of the debugging host computer. + -Port - The port on which to connect to the debugging host. + The default value is 50000, with a minimum value of 49152. + -Key - The key used to encrypt the connection. Only [0-9] and [a-z] are allowed. + -nodhcp - Prevents the use of DHCP to obtain the target IP address. + -newkey - Specifies that a new encryption key should be generated for the connection. + + .PARAMETER DismPath + Full Path to an alternative version of the Dism.exe tool. The default is the current OS version. + + .PARAMETER ApplyEA + Specifies that any EAs captured in the WIM should be applied to the VHD. + The default is False. + + .EXAMPLE + .\Convert-WindowsImage.ps1 -SourcePath D:\foo\install.wim -Edition Professional -WorkingDirectory D:\foo + + This command will create a 40GB dynamically expanding VHD in the D:\foo folder. + The VHD will be based on the Professional edition from D:\foo\install.wim, + and will be named automatically. + + .EXAMPLE + .\Convert-WindowsImage.ps1 -SourcePath D:\foo\Win7SP1.iso -Edition Ultimate -VHDPath D:\foo\Win7_Ultimate_SP1.vhd + + This command will parse the ISO file D:\foo\Win7SP1.iso and try to locate + \sources\install.wim. If that file is found, it will be used to create a + dynamically-expanding 40GB VHD containing the Ultimate SKU, and will be + named D:\foo\Win7_Ultimate_SP1.vhd + + .EXAMPLE + .\Convert-WindowsImage.ps1 -SourcePath D:\foo\install.wim -Edition Professional -EnableDebugger Serial -ComPort 2 -BaudRate 38400 + + This command will create a VHD from D:\foo\install.wim of the Professional SKU. + Serial debugging will be enabled in the VHD via COM2 at a baud rate of 38400bps. + + .OUTPUTS + System.IO.FileInfo + #> + #Requires -Version 3.0 + [CmdletBinding(DefaultParameterSetName="SRC", + HelpURI="https://github.com/Microsoft/Virtualization-Documentation/tree/master/hyperv-tools/Convert-WindowsImage")] + + param( + [Parameter(ParameterSetName="SRC", Mandatory=$true, ValueFromPipeline=$true)] + [Alias("WIM")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $(Resolve-Path $_) })] + $SourcePath, + + [Parameter(ParameterSetName="SRC")] + [switch] + $CacheSource = $false, + + [Parameter(ParameterSetName="SRC")] + [Alias("SKU")] + [System.String[]] + [ValidateNotNullOrEmpty()] + $Edition, + + [Parameter(ParameterSetName="SRC")] + [Alias("WorkDir")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $_ })] + $WorkingDirectory = $pwd, + + [Parameter(ParameterSetName="SRC")] + [Alias("TempDir")] + [System.String] + [ValidateNotNullOrEmpty()] + $TempDirectory = $env:Temp, + + [Parameter(ParameterSetName="SRC")] + [Alias("VHD")] + [System.String] + [ValidateNotNullOrEmpty()] + $VHDPath, + + [Parameter(ParameterSetName="SRC")] + [Alias("Size")] + [UInt64] + [ValidateNotNullOrEmpty()] + [ValidateRange(512MB, 64TB)] + $SizeBytes = 25GB, + + [Parameter(ParameterSetName="SRC")] + [Alias("Format")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateSet("VHD", "VHDX", "AUTO")] + $VHDFormat = "AUTO", + + [Parameter(ParameterSetName="SRC")] + [Alias("MergeFolder")] + [System.String] + [ValidateNotNullOrEmpty()] + $MergeFolderPath = "", + + [Parameter(ParameterSetName="SRC", Mandatory=$true)] + [Alias("Layout")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateSet("BIOS", "UEFI", "WindowsToGo")] + $DiskLayout, + + [Parameter(ParameterSetName="SRC")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateSet("NativeBoot", "VirtualMachine")] + $BCDinVHD = "VirtualMachine", + + [Parameter(ParameterSetName="SRC")] + [Parameter(ParameterSetName="UI")] + [System.String] + $BCDBoot = "bcdboot.exe", + + [Parameter(ParameterSetName="SRC")] + [Parameter(ParameterSetName="UI")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateSet("None", "Serial", "1394", "USB", "Local", "Network")] + $EnableDebugger = "None", + + [Parameter(ParameterSetName="SRC")] + [System.String[]] + [ValidateNotNullOrEmpty()] + $Feature, + + [Parameter(ParameterSetName="SRC")] + [System.String[]] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $(Resolve-Path $_) })] + $Driver, + + [Parameter(ParameterSetName="SRC")] + [System.String[]] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $(Resolve-Path $_) })] + $Package, + + [Parameter(ParameterSetName="SRC")] + [switch] + $ExpandOnNativeBoot = $true, + + [Parameter(ParameterSetName="SRC")] + [switch] + $RemoteDesktopEnable = $false, + + [Parameter(ParameterSetName="SRC")] + [Alias("Unattend")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $(Resolve-Path $_) })] + $UnattendPath, + + [Parameter(ParameterSetName="SRC")] + [Parameter(ParameterSetName="UI")] + [switch] + $Passthru, + + [Parameter(ParameterSetName="SRC")] + [System.String] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path $(Resolve-Path $_) })] + $DismPath, + + [Parameter(ParameterSetName="SRC")] + [switch] + $ApplyEA = $false, + + [Parameter(ParameterSetName="UI")] + [switch] + $ShowUI + ) + #region Code + + # Begin Dynamic Parameters + # Create the parameters for the various types of debugging. + DynamicParam + { + Set-StrictMode -version 3 + + # Set up the dynamic parameters. + # Dynamic parameters are only available if certain conditions are met, so they'll only show up + # as valid parameters when those conditions apply. Here, the conditions are based on the value of + # the EnableDebugger parameter. Depending on which of a set of values is the specified argument + # for EnableDebugger, different parameters will light up, as outlined below. + + $parameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + + if (!(Test-Path Variable:Private:EnableDebugger)) + { + return $parameterDictionary + } + + switch ($EnableDebugger) + { + "Serial" + { + #region ComPort + + $ComPortAttr = New-Object System.Management.Automation.ParameterAttribute + $ComPortAttr.ParameterSetName = "__AllParameterSets" + $ComPortAttr.Mandatory = $false + + $ComPortValidator = New-Object System.Management.Automation.ValidateRangeAttribute( + 1, + 10 # Is that a good maximum? + ) + + $ComPortNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $ComPortAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $ComPortAttrCollection.Add($ComPortAttr) + $ComPortAttrCollection.Add($ComPortValidator) + $ComPortAttrCollection.Add($ComPortNotNull) + + $ComPort = New-Object System.Management.Automation.RuntimeDefinedParameter( + "ComPort", + [UInt16], + $ComPortAttrCollection + ) + + # By default, use COM1 + $ComPort.Value = 1 + $parameterDictionary.Add("ComPort", $ComPort) + #endregion ComPort + + #region BaudRate + $BaudRateAttr = New-Object System.Management.Automation.ParameterAttribute + $BaudRateAttr.ParameterSetName = "__AllParameterSets" + $BaudRateAttr.Mandatory = $false + + $BaudRateValidator = New-Object System.Management.Automation.ValidateSetAttribute( + 9600, 19200,38400, 57600, 115200 + ) + + $BaudRateNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $BaudRateAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $BaudRateAttrCollection.Add($BaudRateAttr) + $BaudRateAttrCollection.Add($BaudRateValidator) + $BaudRateAttrCollection.Add($BaudRateNotNull) + + $BaudRate = New-Object System.Management.Automation.RuntimeDefinedParameter( + "BaudRate", + [UInt32], + $BaudRateAttrCollection + ) + + # By default, use 115,200. + $BaudRate.Value = 115200 + $parameterDictionary.Add("BaudRate", $BaudRate) + #endregion BaudRate + + break + } + + "1394" + { + $ChannelAttr = New-Object System.Management.Automation.ParameterAttribute + $ChannelAttr.ParameterSetName = "__AllParameterSets" + $ChannelAttr.Mandatory = $false + + $ChannelValidator = New-Object System.Management.Automation.ValidateRangeAttribute( + 0, + 62 + ) + + $ChannelNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $ChannelAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $ChannelAttrCollection.Add($ChannelAttr) + $ChannelAttrCollection.Add($ChannelValidator) + $ChannelAttrCollection.Add($ChannelNotNull) + + $Channel = New-Object System.Management.Automation.RuntimeDefinedParameter( + "Channel", + [UInt16], + $ChannelAttrCollection + ) + + # By default, use channel 10 + $Channel.Value = 10 + $parameterDictionary.Add("Channel", $Channel) + break + } + + "USB" + { + $TargetAttr = New-Object System.Management.Automation.ParameterAttribute + $TargetAttr.ParameterSetName = "__AllParameterSets" + $TargetAttr.Mandatory = $false + + $TargetNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $TargetAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $TargetAttrCollection.Add($TargetAttr) + $TargetAttrCollection.Add($TargetNotNull) + + $Target = New-Object System.Management.Automation.RuntimeDefinedParameter( + "Target", + [System.String], + $TargetAttrCollection + ) + + # By default, use target = "debugging" + $Target.Value = "Debugging" + $parameterDictionary.Add("Target", $Target) + break + } + + "Network" + { + #region IP + $IpAttr = New-Object System.Management.Automation.ParameterAttribute + $IpAttr.ParameterSetName = "__AllParameterSets" + $IpAttr.Mandatory = $true + + $IpValidator = New-Object System.Management.Automation.ValidatePatternAttribute( + "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b" + ) + $IpNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $IpAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $IpAttrCollection.Add($IpAttr) + $IpAttrCollection.Add($IpValidator) + $IpAttrCollection.Add($IpNotNull) + + $IP = New-Object System.Management.Automation.RuntimeDefinedParameter( + "IPAddress", + [System.String], + $IpAttrCollection + ) + + # There's no good way to set a default value for this. + $parameterDictionary.Add("IPAddress", $IP) + #endregion IP + + #region Port + $PortAttr = New-Object System.Management.Automation.ParameterAttribute + $PortAttr.ParameterSetName = "__AllParameterSets" + $PortAttr.Mandatory = $false + + $PortValidator = New-Object System.Management.Automation.ValidateRangeAttribute( + 49152, + 50039 + ) + + $PortNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $PortAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $PortAttrCollection.Add($PortAttr) + $PortAttrCollection.Add($PortValidator) + $PortAttrCollection.Add($PortNotNull) + + + $Port = New-Object System.Management.Automation.RuntimeDefinedParameter( + "Port", + [UInt16], + $PortAttrCollection + ) + + # By default, use port 50000 + $Port.Value = 50000 + $parameterDictionary.Add("Port", $Port) + #endregion Port + + #region Key + $KeyAttr = New-Object System.Management.Automation.ParameterAttribute + $KeyAttr.ParameterSetName = "__AllParameterSets" + $KeyAttr.Mandatory = $true + + $KeyValidator = New-Object System.Management.Automation.ValidatePatternAttribute( + "\b([A-Z0-9]+).([A-Z0-9]+).([A-Z0-9]+).([A-Z0-9]+)\b" + ) + + $KeyNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute + + $KeyAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $KeyAttrCollection.Add($KeyAttr) + $KeyAttrCollection.Add($KeyValidator) + $KeyAttrCollection.Add($KeyNotNull) + + $Key = New-Object System.Management.Automation.RuntimeDefinedParameter( + "Key", + [System.String], + $KeyAttrCollection + ) + + # Don't set a default key. + $parameterDictionary.Add("Key", $Key) + #endregion Key + + #region NoDHCP + $NoDHCPAttr = New-Object System.Management.Automation.ParameterAttribute + $NoDHCPAttr.ParameterSetName = "__AllParameterSets" + $NoDHCPAttr.Mandatory = $false + + $NoDHCPAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $NoDHCPAttrCollection.Add($NoDHCPAttr) + + $NoDHCP = New-Object System.Management.Automation.RuntimeDefinedParameter( + "NoDHCP", + [switch], + $NoDHCPAttrCollection + ) + + $parameterDictionary.Add("NoDHCP", $NoDHCP) + #endregion NoDHCP + + #region NewKey + $NewKeyAttr = New-Object System.Management.Automation.ParameterAttribute + $NewKeyAttr.ParameterSetName = "__AllParameterSets" + $NewKeyAttr.Mandatory = $false + + $NewKeyAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] + $NewKeyAttrCollection.Add($NewKeyAttr) + + $NewKey = New-Object System.Management.Automation.RuntimeDefinedParameter( + "NewKey", + [switch], + $NewKeyAttrCollection + ) + + # Don't set a default key. + $parameterDictionary.Add("NewKey", $NewKey) + #endregion NewKey + + break + } + + # There's nothing to do for local debugging. + # Synthetic debugging is not yet implemented. + + default + { + break + } + } + + return $parameterDictionary + } + + Begin + { + ########################################################################################## + # Constants and Pseudo-Constants + ########################################################################################## + $PARTITION_STYLE_MBR = 0x00000000 # The default value + $PARTITION_STYLE_GPT = 0x00000001 # Just in case... + + # Version information that can be populated by timebuild. + $ScriptVersion = DATA + { + ConvertFrom-StringData -StringData @" + Major = 10 + Minor = 0 + Build = 14278 + Qfe = 1000 + Branch = rs1_es_media + Timestamp = 160201-1707 + Flavor = amd64fre +"@ +} + + $myVersion = "$($ScriptVersion.Major).$($ScriptVersion.Minor).$($ScriptVersion.Build).$($ScriptVersion.QFE).$($ScriptVersion.Flavor).$($ScriptVersion.Branch).$($ScriptVersion.Timestamp)" + $scriptName = "Convert-WindowsImage" # Name of the script, obviously. + $sessionKey = [Guid]::NewGuid().ToString() # Session key, used for keeping records unique between multiple runs. + $logFolder = "$($TempDirectory)\$($scriptName)\$($sessionKey)" # Log folder path. + $vhdMaxSize = 2040GB # Maximum size for VHD is ~2040GB. + $vhdxMaxSize = 64TB # Maximum size for VHDX is ~64TB. + $lowestSupportedVersion = New-Object Version "6.1" # The lowest supported *image* version; making sure we don't run against Vista/2k8. + $lowestSupportedBuild = 9200 # The lowest supported *host* build. Set to Win8 CP. + $transcripting = $false + + # Since we use the VHDFormat in output, make it uppercase. + # We'll make it lowercase again when we use it as a file extension. + $VHDFormat = $VHDFormat.ToUpper() + ########################################################################################## + # Here Strings + ########################################################################################## + + # Banner text displayed during each run. + $header = @" + +Windows(R) Image to Virtual Hard Disk Converter for Windows(R) 10 +Copyright (C) Microsoft Corporation. All rights reserved. +Version $myVersion + +"@ + + # Text used as the banner in the UI. + $uiHeader = @" +You can use the fields below to configure the VHD or VHDX that you want to create! +"@ + + #region Helper Functions + + ########################################################################################## + # Helper Functions + ########################################################################################## + + <# + Functions to mount and dismount registry hives. + These hives will automatically be accessible via the HKLM:\ registry PSDrive. + + It should be noted that I have more confidence in using the RegLoadKey and + RegUnloadKey Win32 APIs than I do using REG.EXE - it just seems like we should + do things ourselves if we can, instead of using yet another binary. + + Consider this a TODO for future versions. + #> + Function Mount-RegistryHive + { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [System.IO.FileInfo] + [ValidateNotNullOrEmpty()] + [ValidateScript({ $_.Exists })] + $Hive + ) + + $mountKey = [System.Guid]::NewGuid().ToString() + $regPath = "REG.EXE" + + if (Test-Path HKLM:\$mountKey) + { + throw "The registry path already exists. I should just regenerate it, but I'm lazy." + } + + $regArgs = ( + "LOAD", + "HKLM\$mountKey", + $Hive.Fullname + ) + try + { + + Run-Executable -Executable $regPath -Arguments $regArgs + + } + catch + { + throw + } + + # Set a global variable containing the name of the mounted registry key + # so we can unmount it if there's an error. + $global:mountedHive = $mountKey + + return $mountKey + } + + ########################################################################################## + + Function Dismount-RegistryHive + { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] + [System.String] + [ValidateNotNullOrEmpty()] + $HiveMountPoint + ) + + $regPath = "REG.EXE" + + $regArgs = ( + "UNLOAD", + "HKLM\$($HiveMountPoint)" + ) + + Run-Executable -Executable $regPath -Arguments $regArgs + + $global:mountedHive = $null + } + + ########################################################################################## + + function + Test-Admin + { + <# + .SYNOPSIS + Short function to determine whether the logged-on user is an administrator. + + .EXAMPLE + Do you honestly need one? There are no parameters! + + .OUTPUTS + $true if user is admin. + $false if user is not an admin. + #> + [CmdletBinding()] + param() + + $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) + $isAdmin = $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) + Write-W2VTrace "isUserAdmin? $isAdmin" + + return $isAdmin + } + + ########################################################################################## + + function + Get-WindowsBuildNumber + { + $os = Get-WmiObject -Class Win32_OperatingSystem + return [System.Int32]($os.BuildNumber) + } + + ########################################################################################## + + function + Test-WindowsVersion + { + $isWin8 = ((Get-WindowsBuildNumber) -ge [System.Int32]$lowestSupportedBuild) + + Write-W2VTrace "is Windows 8 or Higher? $isWin8" + return $isWin8 + } + + ########################################################################################## + + function + Write-W2VInfo + { + # Function to make the Write-Host output a bit prettier. + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [System.String] + [ValidateNotNullOrEmpty()] + $text + ) + Write-Host "INFO : $($text)" + } + + ########################################################################################## + + function + Write-W2VTrace + { + # Function to make the Write-Verbose output... well... exactly the same as it was before. + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [System.String] + [ValidateNotNullOrEmpty()] + $text + ) + Write-Verbose $text + } + + ########################################################################################## + + function + Write-W2VError + { + # Function to make the Write-Host (NOT Write-Error) output prettier in the case of an error. + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [System.String] + [ValidateNotNullOrEmpty()] + $text + ) + Write-Host "ERROR : $($text)" -ForegroundColor (Get-Host).PrivateData.ErrorForegroundColor + } + + ########################################################################################## + + function + Write-W2VWarn + { + # Function to make the Write-Host (NOT Write-Warning) output prettier. + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [System.String] + [ValidateNotNullOrEmpty()] + $text + ) + Write-Host "WARN : $($text)" -ForegroundColor (Get-Host).PrivateData.WarningForegroundColor + } + + ########################################################################################## + + function + Run-Executable + { + <# + .SYNOPSIS + Runs an external executable file, and validates the error level. + + .PARAMETER Executable + The path to the executable to run and monitor. + + .PARAMETER Arguments + An array of arguments to pass to the executable when it's executed. + + .PARAMETER SuccessfulErrorCode + The error code that means the executable ran successfully. + The default value is 0. + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [System.String] + [ValidateNotNullOrEmpty()] + $Executable, + + [Parameter(Mandatory=$true)] + [System.String[]] + [ValidateNotNullOrEmpty()] + $Arguments, + + [Parameter()] + [System.Int32] + [ValidateNotNullOrEmpty()] + $SuccessfulErrorCode = 0 + + ) + + Write-W2VTrace "Running $Executable $Arguments" + $ret = Start-Process ` + -FilePath $Executable ` + -ArgumentList $Arguments ` + -NoNewWindow ` + -Wait ` + -RedirectStandardOutput "$($TempDirectory)\$($scriptName)\$($sessionKey)\$($Executable)-StandardOutput.txt" ` + -RedirectStandardError "$($TempDirectory)\$($scriptName)\$($sessionKey)\$($Executable)-StandardError.txt" ` + -Passthru + + Write-W2VTrace "Return code was $($ret.ExitCode)." + + if ($ret.ExitCode -ne $SuccessfulErrorCode) + { + throw "$Executable failed with code $($ret.ExitCode)!" + } + } + + ########################################################################################## + Function Test-IsNetworkLocation + { + <# + .SYNOPSIS + Determines whether or not a given path is a network location or a local drive. + + .DESCRIPTION + Function to determine whether or not a specified path is a local path, a UNC path, + or a mapped network drive. + + .PARAMETER Path + The path that we need to figure stuff out about, + #> + + [CmdletBinding()] + param( + [Parameter(ValueFromPipeLine = $true)] + [System.String] + [ValidateNotNullOrEmpty()] + $Path + ) + + $result = $false + + if ([bool]([URI]$Path).IsUNC) + { + $result = $true + } + else + { + $driveInfo = [IO.DriveInfo]((Resolve-Path $Path).Path) + + if ($driveInfo.DriveType -eq "Network") + { + $result = $true + } + } + + return $result + } + ########################################################################################## + + #endregion Helper Functions + } + + Process + { + Write-Host $header + + $disk = $null + $openWim = $null + $openIso = $null + $openImage = $null + $vhdFinalName = $null + $vhdFinalPath = $null + $mountedHive = $null + $isoPath = $null + $tempSource = $null + + if (Get-Command Get-WindowsOptionalFeature -ErrorAction SilentlyContinue) + { + try + { + $hyperVEnabled = $((Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V).State -eq "Enabled") + } + catch + { + # WinPE DISM does not support online queries. This will throw on non-WinPE machines + $winpeVersion = (Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\WinPE').Version + + Write-W2VInfo "Running WinPE version $winpeVersion" + + $hyperVEnabled = $false + } + } + else + { + $hyperVEnabled = $false + } + + $vhd = @() + + try + { + # Create log folder + if (Test-Path $logFolder) + { + $null = rd $logFolder -Force -Recurse + } + + $null = md $logFolder -Force + + # Try to start transcripting. If it's already running, we'll get an exception and swallow it. + try + { + $null = Start-Transcript -Path (Join-Path $logFolder "Convert-WindowsImageTranscript.txt") -Force -ErrorAction SilentlyContinue + $transcripting = $true + } + catch + { + Write-W2VWarn "Transcription is already running. No Convert-WindowsImage-specific transcript will be created." + $transcripting = $false + } + + # + # Add types + # + Add-WindowsImageTypes + + # Check to make sure we're running as Admin. + if (!(Test-Admin)) + { + throw "Images can only be applied by an administrator. Please launch PowerShell elevated and run this script again." + } + + # Check to make sure we're running on Win8. + if (!(Test-WindowsVersion)) + { + throw "$scriptName requires Windows 8 Consumer Preview or higher. Please use WIM2VHD.WSF (http://code.msdn.microsoft.com/wim2vhd) if you need to create VHDs from Windows 7." + } + + # Resolve the path for the unattend file. + if (![System.String]::IsNullOrEmpty($UnattendPath)) + { + $UnattendPath = (Resolve-Path $UnattendPath).Path + } + + if ($ShowUI) + { + + Write-W2VInfo "Launching UI..." + Add-Type -AssemblyName System.Drawing,System.Windows.Forms + + #region Form Objects + $frmMain = New-Object System.Windows.Forms.Form + $groupBox4 = New-Object System.Windows.Forms.GroupBox + $btnGo = New-Object System.Windows.Forms.Button + $groupBox3 = New-Object System.Windows.Forms.GroupBox + $txtVhdName = New-Object System.Windows.Forms.TextBox + $label6 = New-Object System.Windows.Forms.Label + $btnWrkBrowse = New-Object System.Windows.Forms.Button + $cmbVhdSizeUnit = New-Object System.Windows.Forms.ComboBox + $numVhdSize = New-Object System.Windows.Forms.NumericUpDown + $cmbVhdFormat = New-Object System.Windows.Forms.ComboBox + $label5 = New-Object System.Windows.Forms.Label + $txtWorkingDirectory = New-Object System.Windows.Forms.TextBox + $label4 = New-Object System.Windows.Forms.Label + $label3 = New-Object System.Windows.Forms.Label + $label2 = New-Object System.Windows.Forms.Label + $label7 = New-Object System.Windows.Forms.Label + $txtUnattendFile = New-Object System.Windows.Forms.TextBox + $btnUnattendBrowse = New-Object System.Windows.Forms.Button + $groupBox2 = New-Object System.Windows.Forms.GroupBox + $cmbSkuList = New-Object System.Windows.Forms.ComboBox + $label1 = New-Object System.Windows.Forms.Label + $groupBox1 = New-Object System.Windows.Forms.GroupBox + $txtSourcePath = New-Object System.Windows.Forms.TextBox + $btnBrowseWim = New-Object System.Windows.Forms.Button + $openFileDialog1 = New-Object System.Windows.Forms.OpenFileDialog + $openFolderDialog1 = New-Object System.Windows.Forms.FolderBrowserDialog + $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState + + #endregion Form Objects + + #region Event scriptblocks. + + $btnGo_OnClick = { + $frmMain.Close() + } + + $btnWrkBrowse_OnClick = { + $openFolderDialog1.RootFolder = "Desktop" + $openFolderDialog1.Description = "Select the folder you'd like your VHD(X) to be created in." + $openFolderDialog1.SelectedPath = $WorkingDirectory + + $ret = $openFolderDialog1.ShowDialog() + + if ($ret -ilike "ok") + { + $WorkingDirectory = $txtWorkingDirectory = $openFolderDialog1.SelectedPath + Write-W2VInfo "Selected Working Directory is $WorkingDirectory..." + } + } + + $btnUnattendBrowse_OnClick = { + $openFileDialog1.InitialDirectory = $pwd + $openFileDialog1.Filter = "XML files (*.xml)|*.XML|All files (*.*)|*.*" + $openFileDialog1.FilterIndex = 1 + $openFileDialog1.CheckFileExists = $true + $openFileDialog1.CheckPathExists = $true + $openFileDialog1.FileName = $null + $openFileDialog1.ShowHelp = $false + $openFileDialog1.Title = "Select an unattend file..." + + $ret = $openFileDialog1.ShowDialog() + + if ($ret -ilike "ok") + { + $UnattendPath = $txtUnattendFile.Text = $openFileDialog1.FileName + } + } + + $btnBrowseWim_OnClick = { + $openFileDialog1.InitialDirectory = $pwd + $openFileDialog1.Filter = "All compatible files (*.ISO, *.WIM)|*.ISO;*.WIM|All files (*.*)|*.*" + $openFileDialog1.FilterIndex = 1 + $openFileDialog1.CheckFileExists = $true + $openFileDialog1.CheckPathExists = $true + $openFileDialog1.FileName = $null + $openFileDialog1.ShowHelp = $false + $openFileDialog1.Title = "Select a source file..." + + $ret = $openFileDialog1.ShowDialog() + + if ($ret -ilike "ok") + { + + if (([IO.FileInfo]$openFileDialog1.FileName).Extension -ilike ".iso") + { + + if (Test-IsNetworkLocation $openFileDialog1.FileName) + { + Write-W2VInfo "Copying ISO $(Split-Path $openFileDialog1.FileName -Leaf) to temp folder..." + Write-W2VWarn "The UI may become non-responsive while this copy takes place..." + Copy-Item -Path $openFileDialog1.FileName -Destination $TempDirectory -Force + $openFileDialog1.FileName = "$($TempDirectory)\$(Split-Path $openFileDialog1.FileName -Leaf)" + } + + $txtSourcePath.Text = $isoPath = (Resolve-Path $openFileDialog1.FileName).Path + Write-W2VInfo "Opening ISO $(Split-Path $isoPath -Leaf)..." + + $openIso = Mount-DiskImage -ImagePath $isoPath -StorageType ISO -PassThru + + # Refresh the DiskImage object so we can get the real information about it. I assume this is a bug. + $openIso = Get-DiskImage -ImagePath $isoPath + $driveLetter = ($openIso | Get-Volume).DriveLetter + + $script:SourcePath = "$($driveLetter):\sources\install.wim" + + # Check to see if there's a WIM file we can muck about with. + Write-W2VInfo "Looking for $($SourcePath)..." + if (!(Test-Path $SourcePath)) + { + throw "The specified ISO does not appear to be valid Windows installation media." + } + } + else + { + $txtSourcePath.Text = $script:SourcePath = $openFileDialog1.FileName + } + + # Check to see if the WIM is local, or on a network location. If the latter, copy it locally. + if (Test-IsNetworkLocation $SourcePath) + { + Write-W2VInfo "Copying WIM $(Split-Path $SourcePath -Leaf) to temp folder..." + Write-W2VWarn "The UI may become non-responsive while this copy takes place..." + Copy-Item -Path $SourcePath -Destination $TempDirectory -Force + $txtSourcePath.Text = $script:SourcePath = "$($TempDirectory)\$(Split-Path $SourcePath -Leaf)" + } + + $script:SourcePath = (Resolve-Path $SourcePath).Path + + Write-W2VInfo "Scanning WIM metadata..." + + $tempOpenWim = $null + + try + { + $tempOpenWim = New-Object WIM2VHD.WimFile $SourcePath + + # Let's see if we're running against an unstaged build. If we are, we need to blow up. + if ($tempOpenWim.ImageNames.Contains("Windows Longhorn Client") -or + $tempOpenWim.ImageNames.Contains("Windows Longhorn Server") -or + $tempOpenWim.ImageNames.Contains("Windows Longhorn Server Core")) + { + [Windows.Forms.MessageBox]::Show( + "Convert-WindowsImage cannot run against unstaged builds. Please try again with a staged build.", + "WIM is incompatible!", + "OK", + "Error" + ) + + return + } + else + { + $tempOpenWim.Images | %{ $cmbSkuList.Items.Add($_.ImageFlags) } + $cmbSkuList.SelectedIndex = 0 + } + + } + catch + { + throw "Unable to load WIM metadata!" + } + finally + { + $tempOpenWim.Close() + Write-W2VTrace "Closing WIM metadata..." + } + } + } + + $OnLoadForm_StateCorrection = { + + # Correct the initial state of the form to prevent the .Net maximized form issue + $frmMain.WindowState = $InitialFormWindowState + } + + #endregion Event scriptblocks + + # Figure out VHD size and size unit. + $unit = $null + switch ([Math]::Round($SizeBytes.ToString().Length / 3)) + { + 3 { $unit = "MB"; break } + 4 { $unit = "GB"; break } + 5 { $unit = "TB"; break } + default { $unit = ""; break } + } + + $quantity = Invoke-Expression -Command "$($SizeBytes) / 1$($unit)" + + #region Form Code + #region frmMain + $frmMain.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 579 + $System_Drawing_Size.Width = 512 + $frmMain.ClientSize = $System_Drawing_Size + $frmMain.Font = New-Object System.Drawing.Font("Segoe UI",10,0,3,1) + $frmMain.FormBorderStyle = 1 + $frmMain.MaximizeBox = $false + $frmMain.MinimizeBox = $false + $frmMain.Name = "frmMain" + $frmMain.StartPosition = 1 + $frmMain.Text = "Convert-WindowsImage UI" + #endregion frmMain + + #region groupBox4 + $groupBox4.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 10 + $System_Drawing_Point.Y = 498 + $groupBox4.Location = $System_Drawing_Point + $groupBox4.Name = "groupBox4" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 69 + $System_Drawing_Size.Width = 489 + $groupBox4.Size = $System_Drawing_Size + $groupBox4.TabIndex = 8 + $groupBox4.TabStop = $false + $groupBox4.Text = "4. Make the VHD!" + + $frmMain.Controls.Add($groupBox4) + #endregion groupBox4 + + #region btnGo + $btnGo.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 39 + $System_Drawing_Point.Y = 24 + $btnGo.Location = $System_Drawing_Point + $btnGo.Name = "btnGo" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 33 + $System_Drawing_Size.Width = 415 + $btnGo.Size = $System_Drawing_Size + $btnGo.TabIndex = 0 + $btnGo.Text = "&Make my VHD" + $btnGo.UseVisualStyleBackColor = $true + $btnGo.DialogResult = "OK" + $btnGo.add_Click($btnGo_OnClick) + + $groupBox4.Controls.Add($btnGo) + $frmMain.AcceptButton = $btnGo + #endregion btnGo + + #region groupBox3 + $groupBox3.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 10 + $System_Drawing_Point.Y = 243 + $groupBox3.Location = $System_Drawing_Point + $groupBox3.Name = "groupBox3" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 245 + $System_Drawing_Size.Width = 489 + $groupBox3.Size = $System_Drawing_Size + $groupBox3.TabIndex = 7 + $groupBox3.TabStop = $false + $groupBox3.Text = "3. Choose configuration options" + + $frmMain.Controls.Add($groupBox3) + #endregion groupBox3 + + #region txtVhdName + $txtVhdName.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 150 + $txtVhdName.Location = $System_Drawing_Point + $txtVhdName.Name = "txtVhdName" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 418 + $txtVhdName.Size = $System_Drawing_Size + $txtVhdName.TabIndex = 10 + + $groupBox3.Controls.Add($txtVhdName) + #endregion txtVhdName + + #region txtUnattendFile + $txtUnattendFile.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 198 + $txtUnattendFile.Location = $System_Drawing_Point + $txtUnattendFile.Name = "txtUnattendFile" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 418 + $txtUnattendFile.Size = $System_Drawing_Size + $txtUnattendFile.TabIndex = 11 + + $groupBox3.Controls.Add($txtUnattendFile) + #endregion txtUnattendFile + + #region label7 + $label7.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 23 + $System_Drawing_Point.Y = 180 + $label7.Location = $System_Drawing_Point + $label7.Name = "label7" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 23 + $System_Drawing_Size.Width = 175 + $label7.Size = $System_Drawing_Size + $label7.Text = "Unattend File (Optional)" + + $groupBox3.Controls.Add($label7) + #endregion label7 + + #region label6 + $label6.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 23 + $System_Drawing_Point.Y = 132 + $label6.Location = $System_Drawing_Point + $label6.Name = "label6" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 23 + $System_Drawing_Size.Width = 175 + $label6.Size = $System_Drawing_Size + $label6.Text = "VHD Name (Optional)" + + $groupBox3.Controls.Add($label6) + #endregion label6 + + #region btnUnattendBrowse + $btnUnattendBrowse.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 449 + $System_Drawing_Point.Y = 199 + $btnUnattendBrowse.Location = $System_Drawing_Point + $btnUnattendBrowse.Name = "btnUnattendBrowse" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 27 + $btnUnattendBrowse.Size = $System_Drawing_Size + $btnUnattendBrowse.TabIndex = 9 + $btnUnattendBrowse.Text = "..." + $btnUnattendBrowse.UseVisualStyleBackColor = $true + $btnUnattendBrowse.add_Click($btnUnattendBrowse_OnClick) + + $groupBox3.Controls.Add($btnUnattendBrowse) + #endregion btnUnattendBrowse + + #region btnWrkBrowse + $btnWrkBrowse.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 449 + $System_Drawing_Point.Y = 98 + $btnWrkBrowse.Location = $System_Drawing_Point + $btnWrkBrowse.Name = "btnWrkBrowse" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 27 + $btnWrkBrowse.Size = $System_Drawing_Size + $btnWrkBrowse.TabIndex = 9 + $btnWrkBrowse.Text = "..." + $btnWrkBrowse.UseVisualStyleBackColor = $true + $btnWrkBrowse.add_Click($btnWrkBrowse_OnClick) + + $groupBox3.Controls.Add($btnWrkBrowse) + #endregion btnWrkBrowse + + #region cmbVhdSizeUnit + $cmbVhdSizeUnit.DataBindings.DefaultDataSourceUpdateMode = 0 + $cmbVhdSizeUnit.FormattingEnabled = $true + $cmbVhdSizeUnit.Items.Add("MB") | Out-Null + $cmbVhdSizeUnit.Items.Add("GB") | Out-Null + $cmbVhdSizeUnit.Items.Add("TB") | Out-Null + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 409 + $System_Drawing_Point.Y = 42 + $cmbVhdSizeUnit.Location = $System_Drawing_Point + $cmbVhdSizeUnit.Name = "cmbVhdSizeUnit" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 67 + $cmbVhdSizeUnit.Size = $System_Drawing_Size + $cmbVhdSizeUnit.TabIndex = 5 + $cmbVhdSizeUnit.Text = $unit + + $groupBox3.Controls.Add($cmbVhdSizeUnit) + #endregion cmbVhdSizeUnit + + #region numVhdSize + $numVhdSize.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 340 + $System_Drawing_Point.Y = 42 + $numVhdSize.Location = $System_Drawing_Point + $numVhdSize.Name = "numVhdSize" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 63 + $numVhdSize.Size = $System_Drawing_Size + $numVhdSize.TabIndex = 4 + $numVhdSize.Value = $quantity + + $groupBox3.Controls.Add($numVhdSize) + #endregion numVhdSize + + #region cmbVhdFormat + $cmbVhdFormat.DataBindings.DefaultDataSourceUpdateMode = 0 + $cmbVhdFormat.FormattingEnabled = $true + $cmbVhdFormat.Items.Add("VHD") | Out-Null + $cmbVhdFormat.Items.Add("VHDX") | Out-Null + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 42 + $cmbVhdFormat.Location = $System_Drawing_Point + $cmbVhdFormat.Name = "cmbVhdFormat" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 136 + $cmbVhdFormat.Size = $System_Drawing_Size + $cmbVhdFormat.TabIndex = 0 + $cmbVhdFormat.Text = $VHDFormat + + $groupBox3.Controls.Add($cmbVhdFormat) + #endregion cmbVhdFormat + + #region label5 + $label5.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 23 + $System_Drawing_Point.Y = 76 + $label5.Location = $System_Drawing_Point + $label5.Name = "label5" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 23 + $System_Drawing_Size.Width = 264 + $label5.Size = $System_Drawing_Size + $label5.TabIndex = 8 + $label5.Text = "Working Directory" + + $groupBox3.Controls.Add($label5) + #endregion label5 + + #region txtWorkingDirectory + $txtWorkingDirectory.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 99 + $txtWorkingDirectory.Location = $System_Drawing_Point + $txtWorkingDirectory.Name = "txtWorkingDirectory" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 418 + $txtWorkingDirectory.Size = $System_Drawing_Size + $txtWorkingDirectory.TabIndex = 7 + $txtWorkingDirectory.Text = $WorkingDirectory + + $groupBox3.Controls.Add($txtWorkingDirectory) + #endregion txtWorkingDirectory + + #region label4 + $label4.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 340 + $System_Drawing_Point.Y = 21 + $label4.Location = $System_Drawing_Point + $label4.Name = "label4" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 27 + $System_Drawing_Size.Width = 86 + $label4.Size = $System_Drawing_Size + $label4.TabIndex = 6 + $label4.Text = "VHD Size" + + $groupBox3.Controls.Add($label4) + #endregion label4 + + #region label3 + $label3.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 176 + $System_Drawing_Point.Y = 21 + $label3.Location = $System_Drawing_Point + $label3.Name = "label3" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 27 + $System_Drawing_Size.Width = 92 + $label3.Size = $System_Drawing_Size + $label3.TabIndex = 3 + $label3.Text = "VHD Type" + + $groupBox3.Controls.Add($label3) + #endregion label3 + + #region label2 + $label2.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 21 + $label2.Location = $System_Drawing_Point + $label2.Name = "label2" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 30 + $System_Drawing_Size.Width = 118 + $label2.Size = $System_Drawing_Size + $label2.TabIndex = 1 + $label2.Text = "VHD Format" + + $groupBox3.Controls.Add($label2) + #endregion label2 + + #region groupBox2 + $groupBox2.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 10 + $System_Drawing_Point.Y = 169 + $groupBox2.Location = $System_Drawing_Point + $groupBox2.Name = "groupBox2" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 68 + $System_Drawing_Size.Width = 490 + $groupBox2.Size = $System_Drawing_Size + $groupBox2.TabIndex = 6 + $groupBox2.TabStop = $false + $groupBox2.Text = "2. Choose a SKU from the list" + + $frmMain.Controls.Add($groupBox2) + #endregion groupBox2 + + #region cmbSkuList + $cmbSkuList.DataBindings.DefaultDataSourceUpdateMode = 0 + $cmbSkuList.FormattingEnabled = $true + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 24 + $cmbSkuList.Location = $System_Drawing_Point + $cmbSkuList.Name = "cmbSkuList" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 452 + $cmbSkuList.Size = $System_Drawing_Size + $cmbSkuList.TabIndex = 2 + + $groupBox2.Controls.Add($cmbSkuList) + #endregion cmbSkuList + + #region label1 + $label1.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 23 + $System_Drawing_Point.Y = 21 + $label1.Location = $System_Drawing_Point + $label1.Name = "label1" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 71 + $System_Drawing_Size.Width = 464 + $label1.Size = $System_Drawing_Size + $label1.TabIndex = 5 + $label1.Text = $uiHeader + + $frmMain.Controls.Add($label1) + #endregion label1 + + #region groupBox1 + $groupBox1.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 10 + $System_Drawing_Point.Y = 95 + $groupBox1.Location = $System_Drawing_Point + $groupBox1.Name = "groupBox1" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 68 + $System_Drawing_Size.Width = 490 + $groupBox1.Size = $System_Drawing_Size + $groupBox1.TabIndex = 4 + $groupBox1.TabStop = $false + $groupBox1.Text = "1. Choose a source" + + $frmMain.Controls.Add($groupBox1) + #endregion groupBox1 + + #region txtSourcePath + $txtSourcePath.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 25 + $System_Drawing_Point.Y = 24 + $txtSourcePath.Location = $System_Drawing_Point + $txtSourcePath.Name = "txtSourcePath" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 418 + $txtSourcePath.Size = $System_Drawing_Size + $txtSourcePath.TabIndex = 0 + + $groupBox1.Controls.Add($txtSourcePath) + #endregion txtSourcePath + + #region btnBrowseWim + $btnBrowseWim.DataBindings.DefaultDataSourceUpdateMode = 0 + $System_Drawing_Point = New-Object System.Drawing.Point + $System_Drawing_Point.X = 449 + $System_Drawing_Point.Y = 24 + $btnBrowseWim.Location = $System_Drawing_Point + $btnBrowseWim.Name = "btnBrowseWim" + $System_Drawing_Size = New-Object System.Drawing.Size + $System_Drawing_Size.Height = 25 + $System_Drawing_Size.Width = 28 + $btnBrowseWim.Size = $System_Drawing_Size + $btnBrowseWim.TabIndex = 1 + $btnBrowseWim.Text = "..." + $btnBrowseWim.UseVisualStyleBackColor = $true + $btnBrowseWim.add_Click($btnBrowseWim_OnClick) + + $groupBox1.Controls.Add($btnBrowseWim) + #endregion btnBrowseWim + + $openFileDialog1.FileName = "openFileDialog1" + $openFileDialog1.ShowHelp = $true + + #endregion Form Code + + # Save the initial state of the form + $InitialFormWindowState = $frmMain.WindowState + + # Init the OnLoad event to correct the initial state of the form + $frmMain.add_Load($OnLoadForm_StateCorrection) + + # Return the constructed form. + $ret = $frmMain.ShowDialog() + + if (!($ret -ilike "OK")) + { + throw "Form session has been cancelled." + } + + if ([System.String]::IsNullOrEmpty($SourcePath)) + { + throw "No source path specified." + } + + # VHD Format + $VHDFormat = $cmbVhdFormat.SelectedItem + + # VHD Size + $SizeBytes = Invoke-Expression "$($numVhdSize.Value)$($cmbVhdSizeUnit.SelectedItem)" + + # Working Directory + $WorkingDirectory = $txtWorkingDirectory.Text + + # VHDPath + if (![System.String]::IsNullOrEmpty($txtVhdName.Text)) + { + $VHDPath = "$($WorkingDirectory)\$($txtVhdName.Text)" + } + + # Edition + if (![System.String]::IsNullOrEmpty($cmbSkuList.SelectedItem)) + { + $Edition = $cmbSkuList.SelectedItem + } + + # Because we used ShowDialog, we need to manually dispose of the form. + # This probably won't make much of a difference, but let's free up all of the resources we can + # before we start the conversion process. + + $frmMain.Dispose() + } + + if ($VHDFormat -ilike "AUTO") + { + if ($DiskLayout -eq "BIOS") + { + $VHDFormat = "VHD" + } + else + { + $VHDFormat = "VHDX" + } + } + + # + # Choose smallest supported block size for dynamic VHD(X) + # + $BlockSizeBytes = 1MB + + # There's a difference between the maximum sizes for VHDs and VHDXs. Make sure we follow it. + if ("VHD" -ilike $VHDFormat) + { + if ($SizeBytes -gt $vhdMaxSize) + { + Write-W2VWarn "For the VHD file format, the maximum file size is ~2040GB. We're automatically setting the size to 2040GB for you." + $SizeBytes = 2040GB + } + + $BlockSizeBytes = 512KB + } + + # Check if -VHDPath and -WorkingDirectory were both specified. + if ((![System.String]::IsNullOrEmpty($VHDPath)) -and (![System.String]::IsNullOrEmpty($WorkingDirectory))) + { + if ($WorkingDirectory -ne $pwd) + { + # If the WorkingDirectory is anything besides $pwd, tell people that the WorkingDirectory is being ignored. + Write-W2VWarn "Specifying -VHDPath and -WorkingDirectory at the same time is contradictory." + Write-W2VWarn "Ignoring the WorkingDirectory specification." + $WorkingDirectory = Split-Path $VHDPath -Parent + } + } + + if ($VHDPath) + { + # Check to see if there's a conflict between the specified file extension and the VHDFormat being used. + $ext = ([IO.FileInfo]$VHDPath).Extension + + if (!($ext -ilike ".$($VHDFormat)")) + { + throw "There is a mismatch between the VHDPath file extension ($($ext.ToUpper())), and the VHDFormat (.$($VHDFormat)). Please ensure that these match and try again." + } + } + + # Create a temporary name for the VHD(x). We'll name it properly at the end of the script. + if ([System.String]::IsNullOrEmpty($VHDPath)) + { + $VHDPath = Join-Path $WorkingDirectory "$($sessionKey).$($VHDFormat.ToLower())" + } + else + { + # Since we can't do Resolve-Path against a file that doesn't exist, we need to get creative in determining + # the full path that the user specified (or meant to specify if they gave us a relative path). + # Check to see if the path has a root specified. If it doesn't, use the working directory. + if (![IO.Path]::IsPathRooted($VHDPath)) + { + $VHDPath = Join-Path $WorkingDirectory $VHDPath + } + + $vhdFinalName = Split-Path $VHDPath -Leaf + $VHDPath = Join-Path (Split-Path $VHDPath -Parent) "$($sessionKey).$($VHDFormat.ToLower())" + } + + Write-W2VTrace "Temporary $VHDFormat path is : $VHDPath" + + # If we're using an ISO, mount it and get the path to the WIM file. + if (([IO.FileInfo]$SourcePath).Extension -ilike ".ISO") + { + # If the ISO isn't local, copy it down so we don't have to worry about resource contention + # or about network latency. + if (Test-IsNetworkLocation $SourcePath) + { + Write-W2VInfo "Copying ISO $(Split-Path $SourcePath -Leaf) to temp folder..." + robocopy $(Split-Path $SourcePath -Parent) $TempDirectory $(Split-Path $SourcePath -Leaf) | Out-Null + $SourcePath = "$($TempDirectory)\$(Split-Path $SourcePath -Leaf)" + + $tempSource = $SourcePath + } + + $isoPath = (Resolve-Path $SourcePath).Path + + Write-W2VInfo "Opening ISO $(Split-Path $isoPath -Leaf)..." + $openIso = Mount-DiskImage -ImagePath $isoPath -StorageType ISO -PassThru + # Refresh the DiskImage object so we can get the real information about it. I assume this is a bug. + $openIso = Get-DiskImage -ImagePath $isoPath + $driveLetter = ($openIso | Get-Volume).DriveLetter + + $SourcePath = "$($driveLetter):\sources\install.wim" + + # Check to see if there's a WIM file we can muck about with. + Write-W2VInfo "Looking for $($SourcePath)..." + if (!(Test-Path $SourcePath)) + { + throw "The specified ISO does not appear to be valid Windows installation media." + } + } + + # Check to see if the WIM is local, or on a network location. If the latter, copy it locally. + if (Test-IsNetworkLocation $SourcePath) + { + Write-W2VInfo "Copying WIM $(Split-Path $SourcePath -Leaf) to temp folder..." + robocopy $(Split-Path $SourcePath -Parent) $TempDirectory $(Split-Path $SourcePath -Leaf) | Out-Null + $SourcePath = "$($TempDirectory)\$(Split-Path $SourcePath -Leaf)" + + $tempSource = $SourcePath + } + + $SourcePath = (Resolve-Path $SourcePath).Path + + #################################################################################################### + # QUERY WIM INFORMATION AND EXTRACT THE INDEX OF TARGETED IMAGE + #################################################################################################### + + Write-W2VInfo "Looking for the requested Windows image in the WIM file" + $WindowsImage = Get-WindowsImage -ImagePath $SourcePath + + if (-not $WindowsImage -or ($WindowsImage -is [System.Array])) + { + # + # WIM may have multiple images. Filter on Edition (can be index or name) and try to find a unique image + # + $EditionIndex = 0; + if ([Int32]::TryParse($Edition, [ref]$EditionIndex)) + { + $WindowsImage = Get-WindowsImage -ImagePath $SourcePath -Index $EditionIndex + } + else + { + $WindowsImage = Get-WindowsImage -ImagePath $SourcePath | Where-Object {$_.ImageName -ilike "*$($Edition)"} + } + + if (-not $WindowsImage) + { + throw "Requested windows Image was not found on the WIM file!" + } + if ($WindowsImage -is [System.Array]) + { + Write-W2VInfo "WIM file has the following $($WindowsImage.Count) images that match filter *$($Edition)" + Get-WindowsImage -ImagePath $SourcePath + + Write-W2VError "You must specify an Edition or SKU index, since the WIM has more than one image." + throw "There are more than one images that match ImageName filter *$($Edition)" + } + } + + $ImageIndex = $WindowsImage[0].ImageIndex + + # We're good. Open the WIM container. + # NOTE: this is only required because we want to get the XML-based meta-data at the end. Is there a better way? + # If we can get this information from DISM cmdlets, we can remove the openWim constructs + $openWim = New-Object WIM2VHD.WimFile $SourcePath + + $openImage = $openWim[[Int32]$ImageIndex] + + if ($null -eq $openImage) + { + Write-W2VError "The specified edition does not appear to exist in the specified WIM." + Write-W2VError "Valid edition names are:" + $openWim.Images | %{ Write-W2VError " $($_.ImageFlags)" } + throw + } + + Write-W2VInfo "Image $($openImage.ImageIndex) selected ($($openImage.ImageFlags))..." + + # Check to make sure that the image we're applying is Windows 7 or greater. + if ($openImage.ImageVersion -lt $lowestSupportedVersion) + { + if ($openImage.ImageVersion -eq "0.0.0.0") + { + Write-W2VWarn "The specified WIM does not encode the Windows version." + } + else + { + throw "Convert-WindowsImage only supports Windows 7 and Windows 8 WIM files. The specified image (version $($openImage.ImageVersion)) does not appear to contain one of those operating systems." + } + } + + if ($hyperVEnabled) + { + Write-W2VInfo "Creating sparse disk..." + $newVhd = New-VHD -Path $VHDPath -SizeBytes $SizeBytes -BlockSizeBytes $BlockSizeBytes -Dynamic + + Write-W2VInfo "Mounting $VHDFormat..." + $disk = $newVhd | Mount-VHD -PassThru | Get-Disk + } + else + { + <# + Create the VHD using the VirtDisk Win32 API. + So, why not use the New-VHD cmdlet here? + + New-VHD depends on the Hyper-V Cmdlets, which aren't installed by default. + Installing those cmdlets isn't a big deal, but they depend on the Hyper-V WMI + APIs, which in turn depend on Hyper-V. In order to prevent Convert-WindowsImage + from being dependent on Hyper-V (and thus, x64 systems only), we're using the + VirtDisk APIs directly. + #> + + Write-W2VInfo "Creating sparse disk..." + [WIM2VHD.VirtualHardDisk]::CreateSparseDisk( + $VHDFormat, + $VHDPath, + $SizeBytes, + $true + ) + + # Attach the VHD.\ + Write-W2VInfo "Attaching $VHDFormat..." + $disk = Mount-DiskImage -ImagePath $VHDPath -PassThru | Get-DiskImage | Get-Disk + } + + switch ($DiskLayout) + { + "BIOS" + { + Write-W2VInfo "Initializing disk..." + Initialize-Disk -Number $disk.Number -PartitionStyle MBR + + # + # Create the Windows/system partition + # + Write-W2VInfo "Creating single partition..." + $systemPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -MbrType IFS -IsActive + $windowsPartition = $systemPartition + + Write-W2VInfo "Formatting windows volume..." + $systemVolume = Format-Volume -Partition $systemPartition -FileSystem NTFS -Force -Confirm:$false + $windowsVolume = $systemVolume + } + + "UEFI" + { + Write-W2VInfo "Initializing disk..." + Initialize-Disk -Number $disk.Number -PartitionStyle GPT + + if ((Get-WindowsBuildNumber) -ge 10240) + { + # + # Create the system partition. Create a data partition so we can format it, then change to ESP + # + Write-W2VInfo "Creating EFI system partition..." + $systemPartition = New-Partition -DiskNumber $disk.Number -Size 200MB -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' + + Write-W2VInfo "Formatting system volume..." + $systemVolume = Format-Volume -Partition $systemPartition -FileSystem FAT32 -Force -Confirm:$false + + Write-W2VInfo "Setting system partition as ESP..." + $systemPartition | Set-Partition -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' + $systemPartition | Add-PartitionAccessPath -AssignDriveLetter + } + else + { + # + # Create the system partition + # + Write-W2VInfo "Creating EFI system partition (ESP)..." + $systemPartition = New-Partition -DiskNumber $disk.Number -Size 200MB -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' -AssignDriveLetter + + Write-W2VInfo "Formatting ESP..." + $formatArgs = @( + "$($systemPartition.DriveLetter):", # Partition drive letter + "/FS:FAT32", # File system + "/Q", # Quick format + "/Y" # Suppress prompt + ) + + Run-Executable -Executable format -Arguments $formatArgs + } + + # + # Create the reserved partition + # + Write-W2VInfo "Creating MSR partition..." + $reservedPartition = New-Partition -DiskNumber $disk.Number -Size 128MB -GptType '{e3c9e316-0b5c-4db8-817d-f92df00215ae}' + + # + # Create the Windows partition + # + Write-W2VInfo "Creating windows partition..." + $windowsPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' + + Write-W2VInfo "Formatting windows volume..." + $windowsVolume = Format-Volume -Partition $windowsPartition -FileSystem NTFS -Force -Confirm:$false + } + + "WindowsToGo" + { + Write-W2VInfo "Initializing disk..." + Initialize-Disk -Number $disk.Number -PartitionStyle MBR + + # + # Create the system partition + # + Write-W2VInfo "Creating system partition..." + $systemPartition = New-Partition -DiskNumber $disk.Number -Size 350MB -MbrType FAT32 -IsActive + + Write-W2VInfo "Formatting system volume..." + $systemVolume = Format-Volume -Partition $systemPartition -FileSystem FAT32 -Force -Confirm:$false + + # + # Create the Windows partition + # + Write-W2VInfo "Creating windows partition..." + $windowsPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -MbrType IFS + + Write-W2VInfo "Formatting windows volume..." + $windowsVolume = Format-Volume -Partition $windowsPartition -FileSystem NTFS -Force -Confirm:$false + } + } + + # + # Assign drive letter to Windows partition. This is required for bcdboot + # + $windowsPartition | Add-PartitionAccessPath -AssignDriveLetter + $windowsDrive = $(Get-Partition -Volume $windowsVolume).AccessPaths[0].substring(0,2) + Write-W2VInfo "Windows path ($windowsDrive) has been assigned." + + # + # Refresh access paths (we have now formatted the volume) + # + $systemPartition = $systemPartition | Get-Partition + $systemDrive = $systemPartition.AccessPaths[0].trimend("\").replace("\?", "??") + Write-W2VInfo "System volume location: $systemDrive" + + #################################################################################################### + # APPLY IMAGE FROM WIM TO THE NEW VHD + #################################################################################################### + + Write-W2VInfo "Applying image to $VHDFormat. This could take a while..." + if ((Get-Command Expand-WindowsImage -ErrorAction SilentlyContinue) -and ((-not $ApplyEA) -and ([System.String]::IsNullOrEmpty($DismPath)))) + { + Expand-WindowsImage -ApplyPath $windowsDrive -ImagePath $SourcePath -Index $ImageIndex -LogPath "$($logFolder)\DismLogs.log" | Out-Null + } + else + { + if (![System.String]::IsNullOrEmpty($DismPath)) + { + $dismPath = $DismPath + } + else + { + $dismPath = $(Join-Path (get-item env:\windir).value "system32\dism.exe") + } + + $applyImage = "/Apply-Image" + if ($ApplyEA) + { + $applyImage = $applyImage + " /EA" + } + + $dismArgs = @("$applyImage /ImageFile:`"$SourcePath`" /Index:$ImageIndex /ApplyDir:$windowsDrive /LogPath:`"$($logFolder)\DismLogs.log`"") + Write-W2VInfo "Applying image: $dismPath $dismArgs" + $process = Start-Process -Passthru -Wait -NoNewWindow -FilePath $dismPath ` + -ArgumentList $dismArgs ` + + if ($process.ExitCode -ne 0) + { + throw "Image Apply failed! See DismImageApply logs for details" + } + } + Write-W2VInfo "Image was applied successfully. " + + # + # Here we copy in the unattend file (if specified by the command line) + # + if (![System.String]::IsNullOrEmpty($UnattendPath)) + { + Write-W2VInfo "Applying unattend file ($(Split-Path $UnattendPath -Leaf))..." + Copy-Item -Path $UnattendPath -Destination (Join-Path $windowsDrive "unattend.xml") -Force + } + + if (![System.String]::IsNullOrEmpty($MergeFolderPath)) + { + Write-W2VInfo "Applying merge folder ($MergeFolderPath)..." + Copy-Item -Recurse -Path (Join-Path $MergeFolderPath "*") -Destination $windowsDrive -Force #added to handle merge folders + } + + if (($openImage.ImageArchitecture -ne "ARM") -and # No virtualization platform for ARM images + ($openImage.ImageArchitecture -ne "ARM64") -and # No virtualization platform for ARM64 images + ($BCDinVHD -ne "NativeBoot")) # User asked for a non-bootable image + { + if (Test-Path "$($systemDrive)\boot\bcd") + { + Write-W2VInfo "Image already has BIOS BCD store..." + } + elseif (Test-Path "$($systemDrive)\efi\microsoft\boot\bcd") + { + Write-W2VInfo "Image already has EFI BCD store..." + } + else + { + Write-W2VInfo "Making image bootable..." + $bcdBootArgs = @( + "$($windowsDrive)\Windows", # Path to the \Windows on the VHD + "/s $systemDrive", # Specifies the volume letter of the drive to create the \BOOT folder on. + "/v" # Enabled verbose logging. + ) + + switch ($DiskLayout) + { + "BIOS" + { + $bcdBootArgs += "/f BIOS" # Specifies the firmware type of the target system partition + } + + "UEFI" + { + $bcdBootArgs += "/f UEFI" # Specifies the firmware type of the target system partition + } + + "WindowsToGo" + { + # Create entries for both UEFI and BIOS if possible + if (Test-Path "$($windowsDrive)\Windows\boot\EFI\bootmgfw.efi") + { + $bcdBootArgs += "/f ALL" + } + } + } + + Run-Executable -Executable $BCDBoot -Arguments $bcdBootArgs + + # The following is added to mitigate the VMM diff disk handling + # We're going to change from MBRBootOption to LocateBootOption. + + if ($DiskLayout -eq "BIOS") + { + Write-W2VInfo "Fixing the Device ID in the BCD store on $($VHDFormat)..." + Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( + "/store $($systemDrive)\boot\bcd", + "/set `{bootmgr`} device locate" + ) + Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( + "/store $($systemDrive)\boot\bcd", + "/set `{default`} device locate" + ) + Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( + "/store $($systemDrive)\boot\bcd", + "/set `{default`} osdevice locate" + ) + } + } + + Write-W2VInfo "Drive is bootable. Cleaning up..." + + # Are we turning the debugger on? + if ($EnableDebugger -inotlike "None") + { + $bcdEditArgs = $null; + + # Configure the specified debugging transport and other settings. + switch ($EnableDebugger) + { + "Serial" + { + $bcdEditArgs = @( + "/dbgsettings SERIAL", + "DEBUGPORT:$($ComPort.Value)", + "BAUDRATE:$($BaudRate.Value)" + ) + } + + "1394" + { + $bcdEditArgs = @( + "/dbgsettings 1394", + "CHANNEL:$($Channel.Value)" + ) + } + + "USB" + { + $bcdEditArgs = @( + "/dbgsettings USB", + "TARGETNAME:$($Target.Value)" + ) + } + + "Local" + { + $bcdEditArgs = @( + "/dbgsettings LOCAL" + ) + } + + "Network" + { + $bcdEditArgs = @( + "/dbgsettings NET", + "HOSTIP:$($IP.Value)", + "PORT:$($Port.Value)", + "KEY:$($Key.Value)" + ) + } + } + + $bcdStores = @( + "$($systemDrive)\boot\bcd", + "$($systemDrive)\efi\microsoft\boot\bcd" + ) + + foreach ($bcdStore in $bcdStores) + { + if (Test-Path $bcdStore) + { + Write-W2VInfo "Turning kernel debugging on in the $($VHDFormat) for $($bcdStore)..." + Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( + "/store $($bcdStore)", + "/set `{default`} debug on" + ) + + $bcdEditArguments = @("/store $($bcdStore)") + $bcdEditArgs + + Run-Executable -Executable "BCDEDIT.EXE" -Arguments $bcdEditArguments + } + } + } + } + else + { + # Don't bother to check on debugging. We can't boot WoA VHDs in VMs, and + # if we're native booting, the changes need to be made to the BCD store on the + # physical computer's boot volume. + + Write-W2VInfo "Image applied. It is not bootable." + } + + if ($RemoteDesktopEnable -or (-not $ExpandOnNativeBoot)) + { + $hive = Mount-RegistryHive -Hive (Join-Path $windowsDrive "Windows\System32\Config\System") + + if ($RemoteDesktopEnable) + { + Write-W2VInfo -text "Enabling Remote Desktop" + Set-ItemProperty -Path "HKLM:\$($hive)\ControlSet001\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 + } + + if (-not $ExpandOnNativeBoot) + { + Write-W2VInfo -text "Disabling automatic $VHDFormat expansion for Native Boot" + Set-ItemProperty -Path "HKLM:\$($hive)\ControlSet001\Services\FsDepends\Parameters" -Name "VirtualDiskExpandOnMount" -Value 4 + } + + Dismount-RegistryHive -HiveMountPoint $hive + } + + if ($Driver) + { + Write-W2VInfo -text "Adding Windows Drivers to the Image" + $Driver | ForEach-Object -Process { + Write-W2VInfo -text "Driver path: $PSItem" + Add-WindowsDriver -Path $windowsDrive -Recurse -Driver $PSItem -Verbose | Out-Null + } + } + + if ($Feature) + { + Write-W2VInfo -text "Installing Windows Feature(s) $Feature to the Image" + $FeatureSourcePath = Join-Path -Path "$($driveLetter):" -ChildPath "sources\sxs" + Write-W2VInfo -text "From $FeatureSourcePath" + Enable-WindowsOptionalFeature -FeatureName $Feature -Source $FeatureSourcePath -Path $windowsDrive -All | Out-Null + } + + if ($Package) + { + Write-W2VInfo -text "Adding Windows Packages to the Image" + + $Package | ForEach-Object -Process { + Write-W2VInfo -text "Package path: $PSItem" + Add-WindowsPackage -Path $windowsDrive -PackagePath $PSItem | Out-Null + } + } + + # + # Remove system partition access path, if necessary + # + if ($DiskLayout -eq "UEFI") + { + $systemPartition | Remove-PartitionAccessPath -AccessPath $systemPartition.AccessPaths[0] + } + + if ([System.String]::IsNullOrEmpty($vhdFinalName)) + { + # We need to generate a file name. + Write-W2VInfo "Generating name for $($VHDFormat)..." + $hive = Mount-RegistryHive -Hive (Join-Path $windowsDrive "Windows\System32\Config\Software") + + $buildLabEx = (Get-ItemProperty "HKLM:\$($hive)\Microsoft\Windows NT\CurrentVersion").BuildLabEx + $installType = (Get-ItemProperty "HKLM:\$($hive)\Microsoft\Windows NT\CurrentVersion").InstallationType + $editionId = (Get-ItemProperty "HKLM:\$($hive)\Microsoft\Windows NT\CurrentVersion").EditionID + $skuFamily = $null + + Dismount-RegistryHive -HiveMountPoint $hive + + # Is this ServerCore? + # Since we're only doing this string comparison against the InstallType key, we won't get + # false positives with the Core SKU. + if ($installType.ToUpper().Contains("CORE")) + { + $editionId += "Core" + } + + # What type of SKU are we? + if ($installType.ToUpper().Contains("SERVER")) + { + $skuFamily = "Server" + } + elseif ($installType.ToUpper().Contains("CLIENT")) + { + $skuFamily = "Client" + } + else + { + $skuFamily = "Unknown" + } + + # + # ISSUE - do we want VL here? + # + $vhdFinalName = "$($buildLabEx)_$($skuFamily)_$($editionId)_$($openImage.ImageDefaultLanguage).$($VHDFormat.ToLower())" + Write-W2VTrace "$VHDFormat final name is : $vhdFinalName" + } + + if ($hyperVEnabled) + { + Write-W2VInfo "Dismounting $VHDFormat..." + Dismount-VHD -Path $VHDPath + } + else + { + Write-W2VInfo "Closing $VHDFormat..." + Dismount-DiskImage -ImagePath $VHDPath + } + + $vhdFinalPath = Join-Path (Split-Path $VHDPath -Parent) $vhdFinalName + Write-W2VTrace "$VHDFormat final path is : $vhdFinalPath" + + if (Test-Path $vhdFinalPath) + { + Write-W2VInfo "Deleting pre-existing $VHDFormat : $(Split-Path $vhdFinalPath -Leaf)..." + Remove-Item -Path $vhdFinalPath -Force + } + + Write-W2VTrace -Text "Renaming $VHDFormat at $VHDPath to $vhdFinalName" + Rename-Item -Path (Resolve-Path $VHDPath).Path -NewName $vhdFinalName -Force + $vhd += Get-DiskImage -ImagePath $vhdFinalPath + + $vhdFinalName = $null + } + catch + { + Write-W2VError $_ + Write-W2VInfo "Log folder is $logFolder" + } + finally + { + # If we still have a WIM image open, close it. + if ($openWim -ne $null) + { + Write-W2VInfo "Closing Windows image..." + $openWim.Close() + } + + # If we still have a registry hive mounted, dismount it. + if ($mountedHive -ne $null) + { + Write-W2VInfo "Closing registry hive..." + Dismount-RegistryHive -HiveMountPoint $mountedHive + } + + # If VHD is mounted, unmount it + if (Test-Path $VHDPath) + { + if ($hyperVEnabled) + { + if ((Get-VHD -Path $VHDPath).Attached) + { + Dismount-VHD -Path $VHDPath + } + } + else + { + Dismount-DiskImage -ImagePath $VHDPath + } + } + + # If we still have an ISO open, close it. + if ($openIso -ne $null) + { + Write-W2VInfo "Closing ISO..." + Dismount-DiskImage $ISOPath + } + + if (-not $CacheSource) + { + if ($tempSource -and (Test-Path $tempSource)) + { + Remove-Item -Path $tempSource -Force + } + } + + # Close out the transcript and tell the user we're done. + Write-W2VInfo "Done." + if ($transcripting) + { + $null = Stop-Transcript + } + } + } + + End + { + if ($Passthru) + { + return $vhd + } + } + #endregion Code + +} + + +function +Add-WindowsImageTypes +{ + $code = @" +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml.Linq; +using System.Xml.XPath; +using Microsoft.Win32.SafeHandles; + +namespace WIM2VHD +{ + +/// <summary> +/// P/Invoke methods and associated enums, flags, and structs. +/// </summary> +public class +NativeMethods +{ + + #region Delegates and Callbacks + #region WIMGAPI + + ///<summary> + ///User-defined function used with the RegisterMessageCallback or UnregisterMessageCallback function. + ///</summary> + ///<param name="MessageId">Specifies the message being sent.</param> + ///<param name="wParam">Specifies additional message information. The contents of this parameter depend on the value of the + ///MessageId parameter.</param> + ///<param name="lParam">Specifies additional message information. The contents of this parameter depend on the value of the + ///MessageId parameter.</param> + ///<param name="UserData">Specifies the user-defined value passed to RegisterCallback.</param> + ///<returns> + ///To indicate success and to enable other subscribers to process the message return WIM_MSG_SUCCESS. + ///To prevent other subscribers from receiving the message, return WIM_MSG_DONE. + ///To cancel an image apply or capture, return WIM_MSG_ABORT_IMAGE when handling the WIM_MSG_PROCESS message. + ///</returns> + public delegate uint + WimMessageCallback( + uint MessageId, + IntPtr wParam, + IntPtr lParam, + IntPtr UserData + ); + + public static void + RegisterMessageCallback( + WimFileHandle hWim, + WimMessageCallback callback) + { + + uint _callback = NativeMethods.WimRegisterMessageCallback(hWim, callback, IntPtr.Zero); + int rc = Marshal.GetLastWin32Error(); + if (0 != rc) + { + // Throw an exception if something bad happened on the Win32 end. + throw + new InvalidOperationException( + string.Format( + CultureInfo.CurrentCulture, + "Unable to register message callback." + )); + } + } + + public static void + UnregisterMessageCallback( + WimFileHandle hWim, + WimMessageCallback registeredCallback) + { + + bool status = NativeMethods.WimUnregisterMessageCallback(hWim, registeredCallback); + int rc = Marshal.GetLastWin32Error(); + if (!status) + { + throw + new InvalidOperationException( + string.Format( + CultureInfo.CurrentCulture, + "Unable to unregister message callback." + )); + } + } + + #endregion WIMGAPI + #endregion Delegates and Callbacks + + #region Constants + + #region VDiskInterop + + /// <summary> + /// The default depth in a VHD parent chain that this library will search through. + /// If you want to go more than one disk deep into the parent chain, provide a different value. + /// </summary> + public const uint OPEN_VIRTUAL_DISK_RW_DEFAULT_DEPTH = 0x00000001; + + public const uint DEFAULT_BLOCK_SIZE = 0x00080000; + public const uint DISK_SECTOR_SIZE = 0x00000200; + + internal const uint ERROR_VIRTDISK_NOT_VIRTUAL_DISK = 0xC03A0015; + internal const uint ERROR_NOT_FOUND = 0x00000490; + internal const uint ERROR_IO_PENDING = 0x000003E5; + internal const uint ERROR_INSUFFICIENT_BUFFER = 0x0000007A; + internal const uint ERROR_ERROR_DEV_NOT_EXIST = 0x00000037; + internal const uint ERROR_BAD_COMMAND = 0x00000016; + internal const uint ERROR_SUCCESS = 0x00000000; + + public const uint GENERIC_READ = 0x80000000; + public const uint GENERIC_WRITE = 0x40000000; + public const short FILE_ATTRIBUTE_NORMAL = 0x00000080; + public const uint CREATE_NEW = 0x00000001; + public const uint CREATE_ALWAYS = 0x00000002; + public const uint OPEN_EXISTING = 0x00000003; + public const short INVALID_HANDLE_VALUE = -1; + + internal static Guid VirtualStorageTypeVendorUnknown = new Guid("00000000-0000-0000-0000-000000000000"); + internal static Guid VirtualStorageTypeVendorMicrosoft = new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B"); + + #endregion VDiskInterop + + #region WIMGAPI + + public const uint WIM_FLAG_VERIFY = 0x00000002; + public const uint WIM_FLAG_INDEX = 0x00000004; + + public const uint WM_APP = 0x00008000; + + #endregion WIMGAPI + + #endregion Constants + + #region Enums and Flags + + #region VDiskInterop + + /// <summary> + /// Indicates the version of the virtual disk to create. + /// </summary> + public enum CreateVirtualDiskVersion : int + { + VersionUnspecified = 0x00000000, + Version1 = 0x00000001, + Version2 = 0x00000002 + } + + public enum OpenVirtualDiskVersion : int + { + VersionUnspecified = 0x00000000, + Version1 = 0x00000001, + Version2 = 0x00000002 + } + + /// <summary> + /// Contains the version of the virtual hard disk (VHD) ATTACH_VIRTUAL_DISK_PARAMETERS structure to use in calls to VHD functions. + /// </summary> + public enum AttachVirtualDiskVersion : int + { + VersionUnspecified = 0x00000000, + Version1 = 0x00000001, + Version2 = 0x00000002 + } + + public enum CompactVirtualDiskVersion : int + { + VersionUnspecified = 0x00000000, + Version1 = 0x00000001 + } + + /// <summary> + /// Contains the type and provider (vendor) of the virtual storage device. + /// </summary> + public enum VirtualStorageDeviceType : int + { + /// <summary> + /// The storage type is unknown or not valid. + /// </summary> + Unknown = 0x00000000, + /// <summary> + /// For internal use only. This type is not supported. + /// </summary> + ISO = 0x00000001, + /// <summary> + /// Virtual Hard Disk device type. + /// </summary> + VHD = 0x00000002, + /// <summary> + /// Virtual Hard Disk v2 device type. + /// </summary> + VHDX = 0x00000003 + } + + /// <summary> + /// Contains virtual hard disk (VHD) open request flags. + /// </summary> + [Flags] + public enum OpenVirtualDiskFlags + { + /// <summary> + /// No flags. Use system defaults. + /// </summary> + None = 0x00000000, + /// <summary> + /// Open the VHD file (backing store) without opening any differencing-chain parents. Used to correct broken parent links. + /// </summary> + NoParents = 0x00000001, + /// <summary> + /// Reserved. + /// </summary> + BlankFile = 0x00000002, + /// <summary> + /// Reserved. + /// </summary> + BootDrive = 0x00000004, + } + + /// <summary> + /// Contains the bit mask for specifying access rights to a virtual hard disk (VHD). + /// </summary> + [Flags] + public enum VirtualDiskAccessMask + { + /// <summary> + /// Only Version2 of OpenVirtualDisk API accepts this parameter + /// </summary> + None = 0x00000000, + /// <summary> + /// Open the virtual disk for read-only attach access. The caller must have READ access to the virtual disk image file. + /// </summary> + /// <remarks> + /// If used in a request to open a virtual disk that is already open, the other handles must be limited to either + /// VIRTUAL_DISK_ACCESS_DETACH or VIRTUAL_DISK_ACCESS_GET_INFO access, otherwise the open request with this flag will fail. + /// </remarks> + AttachReadOnly = 0x00010000, + /// <summary> + /// Open the virtual disk for read-write attaching access. The caller must have (READ | WRITE) access to the virtual disk image file. + /// </summary> + /// <remarks> + /// If used in a request to open a virtual disk that is already open, the other handles must be limited to either + /// VIRTUAL_DISK_ACCESS_DETACH or VIRTUAL_DISK_ACCESS_GET_INFO access, otherwise the open request with this flag will fail. + /// If the virtual disk is part of a differencing chain, the disk for this request cannot be less than the readWriteDepth specified + /// during the prior open request for that differencing chain. + /// </remarks> + AttachReadWrite = 0x00020000, + /// <summary> + /// Open the virtual disk to allow detaching of an attached virtual disk. The caller must have + /// (FILE_READ_ATTRIBUTES | FILE_READ_DATA) access to the virtual disk image file. + /// </summary> + Detach = 0x00040000, + /// <summary> + /// Information retrieval access to the virtual disk. The caller must have READ access to the virtual disk image file. + /// </summary> + GetInfo = 0x00080000, + /// <summary> + /// Virtual disk creation access. + /// </summary> + Create = 0x00100000, + /// <summary> + /// Open the virtual disk to perform offline meta-operations. The caller must have (READ | WRITE) access to the virtual + /// disk image file, up to readWriteDepth if working with a differencing chain. + /// </summary> + /// <remarks> + /// If the virtual disk is part of a differencing chain, the backing store (host volume) is opened in RW exclusive mode up to readWriteDepth. + /// </remarks> + MetaOperations = 0x00200000, + /// <summary> + /// Reserved. + /// </summary> + Read = 0x000D0000, + /// <summary> + /// Allows unrestricted access to the virtual disk. The caller must have unrestricted access rights to the virtual disk image file. + /// </summary> + All = 0x003F0000, + /// <summary> + /// Reserved. + /// </summary> + Writable = 0x00320000 + } + + /// <summary> + /// Contains virtual hard disk (VHD) creation flags. + /// </summary> + [Flags] + public enum CreateVirtualDiskFlags + { + /// <summary> + /// Contains virtual hard disk (VHD) creation flags. + /// </summary> + None = 0x00000000, + /// <summary> + /// Pre-allocate all physical space necessary for the size of the virtual disk. + /// </summary> + /// <remarks> + /// The CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION flag is used for the creation of a fixed VHD. + /// </remarks> + FullPhysicalAllocation = 0x00000001 + } + + /// <summary> + /// Contains virtual disk attach request flags. + /// </summary> + [Flags] + public enum AttachVirtualDiskFlags + { + /// <summary> + /// No flags. Use system defaults. + /// </summary> + None = 0x00000000, + /// <summary> + /// Attach the virtual disk as read-only. + /// </summary> + ReadOnly = 0x00000001, + /// <summary> + /// No drive letters are assigned to the disk's volumes. + /// </summary> + /// <remarks>Oddly enough, this doesn't apply to NTFS mount points.</remarks> + NoDriveLetter = 0x00000002, + /// <summary> + /// Will decouple the virtual disk lifetime from that of the VirtualDiskHandle. + /// The virtual disk will be attached until the Detach() function is called, even if all open handles to the virtual disk are closed. + /// </summary> + PermanentLifetime = 0x00000004, + /// <summary> + /// Reserved. + /// </summary> + NoLocalHost = 0x00000008 + } + + [Flags] + public enum DetachVirtualDiskFlag + { + None = 0x00000000 + } + + [Flags] + public enum CompactVirtualDiskFlags + { + None = 0x00000000, + NoZeroScan = 0x00000001, + NoBlockMoves = 0x00000002 + } + + #endregion VDiskInterop + + #region WIMGAPI + + [FlagsAttribute] + internal enum + WimCreateFileDesiredAccess : uint + { + WimQuery = 0x00000000, + WimGenericRead = 0x80000000 + } + + public enum WimMessage : uint + { + WIM_MSG = WM_APP + 0x1476, + WIM_MSG_TEXT, + ///<summary> + ///Indicates an update in the progress of an image application. + ///</summary> + WIM_MSG_PROGRESS, + ///<summary> + ///Enables the caller to prevent a file or a directory from being captured or applied. + ///</summary> + WIM_MSG_PROCESS, + ///<summary> + ///Indicates that volume information is being gathered during an image capture. + ///</summary> + WIM_MSG_SCANNING, + ///<summary> + ///Indicates the number of files that will be captured or applied. + ///</summary> + WIM_MSG_SETRANGE, + ///<summary> + ///Indicates the number of files that have been captured or applied. + ///</summary> + WIM_MSG_SETPOS, + ///<summary> + ///Indicates that a file has been either captured or applied. + ///</summary> + WIM_MSG_STEPIT, + ///<summary> + ///Enables the caller to prevent a file resource from being compressed during a capture. + ///</summary> + WIM_MSG_COMPRESS, + ///<summary> + ///Alerts the caller that an error has occurred while capturing or applying an image. + ///</summary> + WIM_MSG_ERROR, + ///<summary> + ///Enables the caller to align a file resource on a particular alignment boundary. + ///</summary> + WIM_MSG_ALIGNMENT, + WIM_MSG_RETRY, + ///<summary> + ///Enables the caller to align a file resource on a particular alignment boundary. + ///</summary> + WIM_MSG_SPLIT, + WIM_MSG_SUCCESS = 0x00000000, + WIM_MSG_ABORT_IMAGE = 0xFFFFFFFF + } + + internal enum + WimCreationDisposition : uint + { + WimOpenExisting = 0x00000003, + } + + internal enum + WimActionFlags : uint + { + WimIgnored = 0x00000000 + } + + internal enum + WimCompressionType : uint + { + WimIgnored = 0x00000000 + } + + internal enum + WimCreationResult : uint + { + WimCreatedNew = 0x00000000, + WimOpenedExisting = 0x00000001 + } + + #endregion WIMGAPI + + #endregion Enums and Flags + + #region Structs + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct CreateVirtualDiskParameters + { + /// <summary> + /// A CREATE_VIRTUAL_DISK_VERSION enumeration that specifies the version of the CREATE_VIRTUAL_DISK_PARAMETERS structure being passed to or from the virtual hard disk (VHD) functions. + /// </summary> + public CreateVirtualDiskVersion Version; + + /// <summary> + /// Unique identifier to assign to the virtual disk object. If this member is set to zero, a unique identifier is created by the system. + /// </summary> + public Guid UniqueId; + + /// <summary> + /// The maximum virtual size of the virtual disk object. Must be a multiple of 512. + /// If a ParentPath is specified, this value must be zero. + /// If a SourcePath is specified, this value can be zero to specify the size of the source VHD to be used, otherwise the size specified must be greater than or equal to the size of the source disk. + /// </summary> + public ulong MaximumSize; + + /// <summary> + /// Internal size of the virtual disk object blocks. + /// The following are predefined block sizes and their behaviors. For a fixed VHD type, this parameter must be zero. + /// </summary> + public uint BlockSizeInBytes; + + /// <summary> + /// Internal size of the virtual disk object sectors. Must be set to 512. + /// </summary> + public uint SectorSizeInBytes; + + /// <summary> + /// Optional path to a parent virtual disk object. Associates the new virtual disk with an existing virtual disk. + /// If this parameter is not NULL, SourcePath must be NULL. + /// </summary> + public string ParentPath; + + /// <summary> + /// Optional path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a VHD or a physical disk. + /// If this parameter is not NULL, ParentPath must be NULL. + /// </summary> + public string SourcePath; + + /// <summary> + /// Flags for opening the VHD + /// </summary> + public OpenVirtualDiskFlags OpenFlags; + + /// <summary> + /// GetInfoOnly flag for V2 handles + /// </summary> + public bool GetInfoOnly; + + /// <summary> + /// Virtual Storage Type of the parent disk + /// </summary> + public VirtualStorageType ParentVirtualStorageType; + + /// <summary> + /// Virtual Storage Type of the source disk + /// </summary> + public VirtualStorageType SourceVirtualStorageType; + + /// <summary> + /// A GUID to use for fallback resiliency over SMB. + /// </summary> + public Guid ResiliencyGuid; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct VirtualStorageType + { + public VirtualStorageDeviceType DeviceId; + public Guid VendorId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SecurityDescriptor + { + public byte revision; + public byte size; + public short control; + public IntPtr owner; + public IntPtr group; + public IntPtr sacl; + public IntPtr dacl; + } + + #endregion Structs + + #region VirtDisk.DLL P/Invoke + + [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)] + public static extern uint + CreateVirtualDisk( + [In, Out] ref VirtualStorageType VirtualStorageType, + [In] string Path, + [In] VirtualDiskAccessMask VirtualDiskAccessMask, + [In, Out] ref SecurityDescriptor SecurityDescriptor, + [In] CreateVirtualDiskFlags Flags, + [In] uint ProviderSpecificFlags, + [In, Out] ref CreateVirtualDiskParameters Parameters, + [In] IntPtr Overlapped, + [Out] out SafeFileHandle Handle); + + #endregion VirtDisk.DLL P/Invoke + + #region Win32 P/Invoke + + [DllImport("advapi32", SetLastError = true)] + public static extern bool InitializeSecurityDescriptor( + [Out] out SecurityDescriptor pSecurityDescriptor, + [In] uint dwRevision); + + #endregion Win32 P/Invoke + + #region WIMGAPI P/Invoke + + #region SafeHandle wrappers for WimFileHandle and WimImageHandle + + public sealed class WimFileHandle : SafeHandle + { + + public WimFileHandle( + string wimPath) + : base(IntPtr.Zero, true) + { + + if (String.IsNullOrEmpty(wimPath)) + { + throw new ArgumentNullException("wimPath"); + } + + if (!File.Exists(Path.GetFullPath(wimPath))) + { + throw new FileNotFoundException((new FileNotFoundException()).Message, wimPath); + } + + NativeMethods.WimCreationResult creationResult; + + this.handle = NativeMethods.WimCreateFile( + wimPath, + NativeMethods.WimCreateFileDesiredAccess.WimGenericRead, + NativeMethods.WimCreationDisposition.WimOpenExisting, + NativeMethods.WimActionFlags.WimIgnored, + NativeMethods.WimCompressionType.WimIgnored, + out creationResult + ); + + // Check results. + if (creationResult != NativeMethods.WimCreationResult.WimOpenedExisting) + { + throw new Win32Exception(); + } + + if (this.handle == IntPtr.Zero) + { + throw new Win32Exception(); + } + + // Set the temporary path. + NativeMethods.WimSetTemporaryPath( + this, + Environment.ExpandEnvironmentVariables("%TEMP%") + ); + } + + protected override bool ReleaseHandle() + { + return NativeMethods.WimCloseHandle(this.handle); + } + + public override bool IsInvalid + { + get { return this.handle == IntPtr.Zero; } + } + } + + public sealed class WimImageHandle : SafeHandle + { + public WimImageHandle( + WimFile Container, + uint ImageIndex) + : base(IntPtr.Zero, true) + { + + if (null == Container) + { + throw new ArgumentNullException("Container"); + } + + if ((Container.Handle.IsClosed) || (Container.Handle.IsInvalid)) + { + throw new ArgumentNullException("The handle to the WIM file has already been closed, or is invalid.", "Container"); + } + + if (ImageIndex > Container.ImageCount) + { + throw new ArgumentOutOfRangeException("ImageIndex", "The index does not exist in the specified WIM file."); + } + + this.handle = NativeMethods.WimLoadImage( + Container.Handle.DangerousGetHandle(), + ImageIndex); + } + + protected override bool ReleaseHandle() + { + return NativeMethods.WimCloseHandle(this.handle); + } + + public override bool IsInvalid + { + get { return this.handle == IntPtr.Zero; } + } + } + + #endregion SafeHandle wrappers for WimFileHandle and WimImageHandle + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMCreateFile")] + internal static extern IntPtr + WimCreateFile( + [In, MarshalAs(UnmanagedType.LPWStr)] string WimPath, + [In] WimCreateFileDesiredAccess DesiredAccess, + [In] WimCreationDisposition CreationDisposition, + [In] WimActionFlags FlagsAndAttributes, + [In] WimCompressionType CompressionType, + [Out, Optional] out WimCreationResult CreationResult + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMCloseHandle")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool + WimCloseHandle( + [In] IntPtr Handle + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMLoadImage")] + internal static extern IntPtr + WimLoadImage( + [In] IntPtr Handle, + [In] uint ImageIndex + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMGetImageCount")] + internal static extern uint + WimGetImageCount( + [In] WimFileHandle Handle + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMGetImageInformation")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool + WimGetImageInformation( + [In] SafeHandle Handle, + [Out] out StringBuilder ImageInfo, + [Out] out uint SizeOfImageInfo + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMSetTemporaryPath")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool + WimSetTemporaryPath( + [In] WimFileHandle Handle, + [In] string TempPath + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMRegisterMessageCallback", CallingConvention = CallingConvention.StdCall)] + internal static extern uint + WimRegisterMessageCallback( + [In, Optional] WimFileHandle hWim, + [In] WimMessageCallback MessageProc, + [In, Optional] IntPtr ImageInfo + ); + + [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMUnregisterMessageCallback", CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool + WimUnregisterMessageCallback( + [In, Optional] WimFileHandle hWim, + [In] WimMessageCallback MessageProc + ); + + + #endregion WIMGAPI P/Invoke +} + +#region WIM Interop + +public class WimFile +{ + + internal XDocument m_xmlInfo; + internal List<WimImage> m_imageList; + + private static NativeMethods.WimMessageCallback wimMessageCallback; + + #region Events + + /// <summary> + /// DefaultImageEvent handler + /// </summary> + public delegate void DefaultImageEventHandler(object sender, DefaultImageEventArgs e); + + ///<summary> + ///ProcessFileEvent handler + ///</summary> + public delegate void ProcessFileEventHandler(object sender, ProcessFileEventArgs e); + + ///<summary> + ///Enable the caller to prevent a file resource from being compressed during a capture. + ///</summary> + public event ProcessFileEventHandler ProcessFileEvent; + + ///<summary> + ///Indicate an update in the progress of an image application. + ///</summary> + public event DefaultImageEventHandler ProgressEvent; + + ///<summary> + ///Alert the caller that an error has occurred while capturing or applying an image. + ///</summary> + public event DefaultImageEventHandler ErrorEvent; + + ///<summary> + ///Indicate that a file has been either captured or applied. + ///</summary> + public event DefaultImageEventHandler StepItEvent; + + ///<summary> + ///Indicate the number of files that will be captured or applied. + ///</summary> + public event DefaultImageEventHandler SetRangeEvent; + + ///<summary> + ///Indicate the number of files that have been captured or applied. + ///</summary> + public event DefaultImageEventHandler SetPosEvent; + + #endregion Events + + private + enum + ImageEventMessage : uint + { + ///<summary> + ///Enables the caller to prevent a file or a directory from being captured or applied. + ///</summary> + Progress = NativeMethods.WimMessage.WIM_MSG_PROGRESS, + ///<summary> + ///Notification sent to enable the caller to prevent a file or a directory from being captured or applied. + ///To prevent a file or a directory from being captured or applied, call WindowsImageContainer.SkipFile(). + ///</summary> + Process = NativeMethods.WimMessage.WIM_MSG_PROCESS, + ///<summary> + ///Enables the caller to prevent a file resource from being compressed during a capture. + ///</summary> + Compress = NativeMethods.WimMessage.WIM_MSG_COMPRESS, + ///<summary> + ///Alerts the caller that an error has occurred while capturing or applying an image. + ///</summary> + Error = NativeMethods.WimMessage.WIM_MSG_ERROR, + ///<summary> + ///Enables the caller to align a file resource on a particular alignment boundary. + ///</summary> + Alignment = NativeMethods.WimMessage.WIM_MSG_ALIGNMENT, + ///<summary> + ///Enables the caller to align a file resource on a particular alignment boundary. + ///</summary> + Split = NativeMethods.WimMessage.WIM_MSG_SPLIT, + ///<summary> + ///Indicates that volume information is being gathered during an image capture. + ///</summary> + Scanning = NativeMethods.WimMessage.WIM_MSG_SCANNING, + ///<summary> + ///Indicates the number of files that will be captured or applied. + ///</summary> + SetRange = NativeMethods.WimMessage.WIM_MSG_SETRANGE, + ///<summary> + ///Indicates the number of files that have been captured or applied. + /// </summary> + SetPos = NativeMethods.WimMessage.WIM_MSG_SETPOS, + ///<summary> + ///Indicates that a file has been either captured or applied. + ///</summary> + StepIt = NativeMethods.WimMessage.WIM_MSG_STEPIT, + ///<summary> + ///Success. + ///</summary> + Success = NativeMethods.WimMessage.WIM_MSG_SUCCESS, + ///<summary> + ///Abort. + ///</summary> + Abort = NativeMethods.WimMessage.WIM_MSG_ABORT_IMAGE + } + + ///<summary> + ///Event callback to the Wimgapi events + ///</summary> + private + uint + ImageEventMessagePump( + uint MessageId, + IntPtr wParam, + IntPtr lParam, + IntPtr UserData) + { + + uint status = (uint) NativeMethods.WimMessage.WIM_MSG_SUCCESS; + + DefaultImageEventArgs eventArgs = new DefaultImageEventArgs(wParam, lParam, UserData); + + switch ((ImageEventMessage)MessageId) + { + + case ImageEventMessage.Progress: + ProgressEvent(this, eventArgs); + break; + + case ImageEventMessage.Process: + if (null != ProcessFileEvent) + { + string fileToImage = Marshal.PtrToStringUni(wParam); + ProcessFileEventArgs fileToProcess = new ProcessFileEventArgs(fileToImage, lParam); + ProcessFileEvent(this, fileToProcess); + + if (fileToProcess.Abort == true) + { + status = (uint)ImageEventMessage.Abort; + } + } + break; + + case ImageEventMessage.Error: + if (null != ErrorEvent) + { + ErrorEvent(this, eventArgs); + } + break; + + case ImageEventMessage.SetRange: + if (null != SetRangeEvent) + { + SetRangeEvent(this, eventArgs); + } + break; + + case ImageEventMessage.SetPos: + if (null != SetPosEvent) + { + SetPosEvent(this, eventArgs); + } + break; + + case ImageEventMessage.StepIt: + if (null != StepItEvent) + { + StepItEvent(this, eventArgs); + } + break; + + default: + break; + } + return status; + + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="wimPath">Path to the WIM container.</param> + public + WimFile(string wimPath) + { + if (string.IsNullOrEmpty(wimPath)) + { + throw new ArgumentNullException("wimPath"); + } + + if (!File.Exists(Path.GetFullPath(wimPath))) + { + throw new FileNotFoundException((new FileNotFoundException()).Message, wimPath); + } + + Handle = new NativeMethods.WimFileHandle(wimPath); + + // Hook up the events before we return. + //wimMessageCallback = new NativeMethods.WimMessageCallback(ImageEventMessagePump); + //NativeMethods.RegisterMessageCallback(this.Handle, wimMessageCallback); + } + + /// <summary> + /// Closes the WIM file. + /// </summary> + public void + Close() + { + foreach (WimImage image in Images) + { + image.Close(); + } + + if (null != wimMessageCallback) + { + NativeMethods.UnregisterMessageCallback(this.Handle, wimMessageCallback); + wimMessageCallback = null; + } + + if ((!Handle.IsClosed) && (!Handle.IsInvalid)) + { + Handle.Close(); + } + } + + /// <summary> + /// Provides a list of WimImage objects, representing the images in the WIM container file. + /// </summary> + public List<WimImage> + Images + { + get + { + if (null == m_imageList) + { + + int imageCount = (int)ImageCount; + m_imageList = new List<WimImage>(imageCount); + for (int i = 0; i < imageCount; i++) + { + + // Load up each image so it's ready for us. + m_imageList.Add( + new WimImage(this, (uint)i + 1)); + } + } + + return m_imageList; + } + } + + /// <summary> + /// Provides a list of names of the images in the specified WIM container file. + /// </summary> + public List<string> + ImageNames + { + get + { + List<string> nameList = new List<string>(); + foreach (WimImage image in Images) + { + nameList.Add(image.ImageName); + } + return nameList; + } + } + + /// <summary> + /// Indexer for WIM images inside the WIM container, indexed by the image number. + /// The list of Images is 0-based, but the WIM container is 1-based, so we automatically compensate for that. + /// this[1] returns the 0th image in the WIM container. + /// </summary> + /// <param name="ImageIndex">The 1-based index of the image to retrieve.</param> + /// <returns>WinImage object.</returns> + public WimImage + this[int ImageIndex] + { + get { return Images[ImageIndex - 1]; } + } + + /// <summary> + /// Indexer for WIM images inside the WIM container, indexed by the image name. + /// WIMs created by different processes sometimes contain different information - including the name. + /// Some images have their name stored in the Name field, some in the Flags field, and some in the EditionID field. + /// We take all of those into account in while searching the WIM. + /// </summary> + /// <param name="ImageName"></param> + /// <returns></returns> + public WimImage + this[string ImageName] + { + get + { + return + Images.Where(i => ( + i.ImageName.ToUpper() == ImageName.ToUpper() || + i.ImageFlags.ToUpper() == ImageName.ToUpper() )) + .DefaultIfEmpty(null) + .FirstOrDefault<WimImage>(); + } + } + + /// <summary> + /// Returns the number of images in the WIM container. + /// </summary> + internal uint + ImageCount + { + get { return NativeMethods.WimGetImageCount(Handle); } + } + + /// <summary> + /// Returns an XDocument representation of the XML metadata for the WIM container and associated images. + /// </summary> + internal XDocument + XmlInfo + { + get + { + + if (null == m_xmlInfo) + { + StringBuilder builder; + uint bytes; + if (!NativeMethods.WimGetImageInformation(Handle, out builder, out bytes)) + { + throw new Win32Exception(); + } + + // Ensure the length of the returned bytes to avoid garbage characters at the end. + int charCount = (int)bytes / sizeof(char); + if (null != builder) + { + // Get rid of the unicode file marker at the beginning of the XML. + builder.Remove(0, 1); + builder.EnsureCapacity(charCount - 1); + builder.Length = charCount - 1; + + // This isn't likely to change while we have the image open, so cache it. + m_xmlInfo = XDocument.Parse(builder.ToString().Trim()); + } + else + { + m_xmlInfo = null; + } + } + + return m_xmlInfo; + } + } + + public NativeMethods.WimFileHandle Handle + { + get; + private set; + } +} + +public class +WimImage +{ + + internal XDocument m_xmlInfo; + + public + WimImage( + WimFile Container, + uint ImageIndex) + { + + if (null == Container) + { + throw new ArgumentNullException("Container"); + } + + if ((Container.Handle.IsClosed) || (Container.Handle.IsInvalid)) + { + throw new ArgumentNullException("The handle to the WIM file has already been closed, or is invalid.", "Container"); + } + + if (ImageIndex > Container.ImageCount) + { + throw new ArgumentOutOfRangeException("ImageIndex", "The index does not exist in the specified WIM file."); + } + + Handle = new NativeMethods.WimImageHandle(Container, ImageIndex); + } + + public enum + Architectures : uint + { + x86 = 0x0, + ARM = 0x5, + IA64 = 0x6, + AMD64 = 0x9, + ARM64 = 0xC + } + + public void + Close() + { + if ((!Handle.IsClosed) && (!Handle.IsInvalid)) + { + Handle.Close(); + } + } + + public NativeMethods.WimImageHandle + Handle + { + get; + private set; + } + + internal XDocument + XmlInfo + { + get + { + + if (null == m_xmlInfo) + { + StringBuilder builder; + uint bytes; + if (!NativeMethods.WimGetImageInformation(Handle, out builder, out bytes)) + { + throw new Win32Exception(); + } + + // Ensure the length of the returned bytes to avoid garbage characters at the end. + int charCount = (int)bytes / sizeof(char); + if (null != builder) + { + // Get rid of the unicode file marker at the beginning of the XML. + builder.Remove(0, 1); + builder.EnsureCapacity(charCount - 1); + builder.Length = charCount - 1; + + // This isn't likely to change while we have the image open, so cache it. + m_xmlInfo = XDocument.Parse(builder.ToString().Trim()); + } + else + { + m_xmlInfo = null; + } + } + + return m_xmlInfo; + } + } + + public string + ImageIndex + { + get { return XmlInfo.Element("IMAGE").Attribute("INDEX").Value; } + } + + public string + ImageName + { + get { return XmlInfo.XPathSelectElement("/IMAGE/NAME").Value; } + } + + public string + ImageEditionId + { + get { return XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/EDITIONID").Value; } + } + + public string + ImageFlags + { + get + { + string flagValue = String.Empty; + + try + { + flagValue = XmlInfo.XPathSelectElement("/IMAGE/FLAGS").Value; + } + catch + { + + // Some WIM files don't contain a FLAGS element in the metadata. + // In an effort to support those WIMs too, inherit the EditionId if there + // are no Flags. + + if (String.IsNullOrEmpty(flagValue)) + { + flagValue = this.ImageEditionId; + + // Check to see if the EditionId is "ServerHyper". If so, + // tweak it to be "ServerHyperCore" instead. + + if (0 == String.Compare("serverhyper", flagValue, true)) + { + flagValue = "ServerHyperCore"; + } + } + + } + + return flagValue; + } + } + + public string + ImageProductType + { + get { return XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/PRODUCTTYPE").Value; } + } + + public string + ImageInstallationType + { + get { return XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/INSTALLATIONTYPE").Value; } + } + + public string + ImageDescription + { + get { return XmlInfo.XPathSelectElement("/IMAGE/DESCRIPTION").Value; } + } + + public ulong + ImageSize + { + get { return ulong.Parse(XmlInfo.XPathSelectElement("/IMAGE/TOTALBYTES").Value); } + } + + public Architectures + ImageArchitecture + { + get + { + int arch = -1; + try + { + arch = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/ARCH").Value); + } + catch { } + + return (Architectures)arch; + } + } + + public string + ImageDefaultLanguage + { + get + { + string lang = null; + try + { + lang = XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/LANGUAGES/DEFAULT").Value; + } + catch { } + + return lang; + } + } + + public Version + ImageVersion + { + get + { + int major = 0; + int minor = 0; + int build = 0; + int revision = 0; + + try + { + major = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/MAJOR").Value); + minor = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/MINOR").Value); + build = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/BUILD").Value); + revision = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/SPBUILD").Value); + } + catch { } + + return (new Version(major, minor, build, revision)); + } + } + + public string + ImageDisplayName + { + get { return XmlInfo.XPathSelectElement("/IMAGE/DISPLAYNAME").Value; } + } + + public string + ImageDisplayDescription + { + get { return XmlInfo.XPathSelectElement("/IMAGE/DISPLAYDESCRIPTION").Value; } + } +} + +///<summary> +///Describes the file that is being processed for the ProcessFileEvent. +///</summary> +public class +DefaultImageEventArgs : EventArgs +{ + ///<summary> + ///Default constructor. + ///</summary> + public + DefaultImageEventArgs( + IntPtr wideParameter, + IntPtr leftParameter, + IntPtr userData) + { + + WideParameter = wideParameter; + LeftParameter = leftParameter; + UserData = userData; + } + + ///<summary> + ///wParam + ///</summary> + public IntPtr WideParameter + { + get; + private set; + } + + ///<summary> + ///lParam + ///</summary> + public IntPtr LeftParameter + { + get; + private set; + } + + ///<summary> + ///UserData + ///</summary> + public IntPtr UserData + { + get; + private set; + } +} + +///<summary> +///Describes the file that is being processed for the ProcessFileEvent. +///</summary> +public class +ProcessFileEventArgs : EventArgs +{ + ///<summary> + ///Default constructor. + ///</summary> + ///<param name="file">Fully qualified path and file name. For example: c:\file.sys.</param> + ///<param name="skipFileFlag">Default is false - skip file and continue. + ///Set to true to abort the entire image capture.</param> + public + ProcessFileEventArgs( + string file, + IntPtr skipFileFlag) + { + + m_FilePath = file; + m_SkipFileFlag = skipFileFlag; + } + + ///<summary> + ///Skip file from being imaged. + ///</summary> + public void + SkipFile() + { + byte[] byteBuffer = + { + 0 + }; + int byteBufferSize = byteBuffer.Length; + Marshal.Copy(byteBuffer, 0, m_SkipFileFlag, byteBufferSize); + } + + ///<summary> + ///Fully qualified path and file name. + ///</summary> + public string + FilePath + { + get + { + string stringToReturn = ""; + if (m_FilePath != null) + { + stringToReturn = m_FilePath; + } + return stringToReturn; + } + } + + ///<summary> + ///Flag to indicate if the entire image capture should be aborted. + ///Default is false - skip file and continue. Setting to true will + ///abort the entire image capture. + ///</summary> + public bool Abort + { + set { m_Abort = value; } + get { return m_Abort; } + } + + private string m_FilePath; + private bool m_Abort; + private IntPtr m_SkipFileFlag; + +} + +#endregion WIM Interop + +#region VHD Interop +// Based on code written by the Hyper-V Test team. +/// <summary> +/// The Virtual Hard Disk class provides methods for creating and manipulating Virtual Hard Disk files. +/// </summary> +public class +VirtualHardDisk +{ + #region Static Methods + + #region Sparse Disks + + /// <summary> + /// Abbreviated signature of CreateSparseDisk so it's easier to use from WIM2VHD. + /// </summary> + /// <param name="virtualStorageDeviceType">The type of disk to create, VHD or VHDX.</param> + /// <param name="path">The path of the disk to create.</param> + /// <param name="size">The maximum size of the disk to create.</param> + /// <param name="overwrite">Overwrite the VHD if it already exists.</param> + public static void + CreateSparseDisk( + NativeMethods.VirtualStorageDeviceType virtualStorageDeviceType, + string path, + ulong size, + bool overwrite) + { + + CreateSparseDisk( + path, + size, + overwrite, + null, + IntPtr.Zero, + (virtualStorageDeviceType == NativeMethods.VirtualStorageDeviceType.VHD) + ? NativeMethods.DEFAULT_BLOCK_SIZE + : 0, + virtualStorageDeviceType, + NativeMethods.DISK_SECTOR_SIZE); + } + + /// <summary> + /// Creates a new sparse (dynamically expanding) virtual hard disk (.vhd). Supports both sync and async modes. + /// The VHD image file uses only as much space on the backing store as needed to store the actual data the VHD currently contains. + /// </summary> + /// <param name="path">The path and name of the VHD to create.</param> + /// <param name="size">The size of the VHD to create in bytes. + /// When creating this type of VHD, the VHD API does not test for free space on the physical backing store based on the maximum size requested, + /// therefore it is possible to successfully create a dynamic VHD with a maximum size larger than the available physical disk free space. + /// The maximum size of a dynamic VHD is 2,040 GB. The minimum size is 3 MB.</param> + /// <param name="source">Optional path to pre-populate the new virtual disk object with block data from an existing disk + /// This path may refer to a VHD or a physical disk. Use NULL if you don't want a source.</param> + /// <param name="overwrite">If the VHD exists, setting this parameter to 'True' will delete it and create a new one.</param> + /// <param name="overlapped">If not null, the operation runs in async mode</param> + /// <param name="blockSizeInBytes">Block size for the VHD.</param> + /// <param name="virtualStorageDeviceType">VHD format version (VHD1 or VHD2)</param> + /// <param name="sectorSizeInBytes">Sector size for the VHD.</param> + /// <exception cref="ArgumentOutOfRangeException">Thrown when an invalid size is specified</exception> + /// <exception cref="FileNotFoundException">Thrown when source VHD is not found.</exception> + /// <exception cref="SecurityException">Thrown when there was an error while creating the default security descriptor.</exception> + /// <exception cref="Win32Exception">Thrown when an error occurred while creating the VHD.</exception> + public static void + CreateSparseDisk( + string path, + ulong size, + bool overwrite, + string source, + IntPtr overlapped, + uint blockSizeInBytes, + NativeMethods.VirtualStorageDeviceType virtualStorageDeviceType, + uint sectorSizeInBytes) + { + + // Validate the virtualStorageDeviceType + if (virtualStorageDeviceType != NativeMethods.VirtualStorageDeviceType.VHD && virtualStorageDeviceType != NativeMethods.VirtualStorageDeviceType.VHDX) + { + + throw ( + new ArgumentOutOfRangeException( + "virtualStorageDeviceType", + virtualStorageDeviceType, + "VirtualStorageDeviceType must be VHD or VHDX." + )); + } + + // Validate size. It needs to be a multiple of DISK_SECTOR_SIZE (512)... + if ((size % NativeMethods.DISK_SECTOR_SIZE) != 0) + { + + throw ( + new ArgumentOutOfRangeException( + "size", + size, + "The size of the virtual disk must be a multiple of 512." + )); + } + + if ((!String.IsNullOrEmpty(source)) && (!System.IO.File.Exists(source))) + { + + throw ( + new System.IO.FileNotFoundException( + "Unable to find the source file.", + source + )); + } + + if ((overwrite) && (System.IO.File.Exists(path))) + { + + System.IO.File.Delete(path); + } + + NativeMethods.CreateVirtualDiskParameters createParams = new NativeMethods.CreateVirtualDiskParameters(); + + // Select the correct version. + createParams.Version = (virtualStorageDeviceType == NativeMethods.VirtualStorageDeviceType.VHD) + ? NativeMethods.CreateVirtualDiskVersion.Version1 + : NativeMethods.CreateVirtualDiskVersion.Version2; + + createParams.UniqueId = Guid.NewGuid(); + createParams.MaximumSize = size; + createParams.BlockSizeInBytes = blockSizeInBytes; + createParams.SectorSizeInBytes = sectorSizeInBytes; + createParams.ParentPath = null; + createParams.SourcePath = source; + createParams.OpenFlags = NativeMethods.OpenVirtualDiskFlags.None; + createParams.GetInfoOnly = false; + createParams.ParentVirtualStorageType = new NativeMethods.VirtualStorageType(); + createParams.SourceVirtualStorageType = new NativeMethods.VirtualStorageType(); + + // + // Create and init a security descriptor. + // Since we're creating an essentially blank SD to use with CreateVirtualDisk + // the VHD will take on the security values from the parent directory. + // + + NativeMethods.SecurityDescriptor securityDescriptor; + if (!NativeMethods.InitializeSecurityDescriptor(out securityDescriptor, 1)) + { + + throw ( + new SecurityException( + "Unable to initialize the security descriptor for the virtual disk." + )); + } + + NativeMethods.VirtualStorageType virtualStorageType = new NativeMethods.VirtualStorageType(); + virtualStorageType.DeviceId = virtualStorageDeviceType; + virtualStorageType.VendorId = NativeMethods.VirtualStorageTypeVendorMicrosoft; + + SafeFileHandle vhdHandle; + + uint returnCode = NativeMethods.CreateVirtualDisk( + ref virtualStorageType, + path, + (virtualStorageDeviceType == NativeMethods.VirtualStorageDeviceType.VHD) + ? NativeMethods.VirtualDiskAccessMask.All + : NativeMethods.VirtualDiskAccessMask.None, + ref securityDescriptor, + NativeMethods.CreateVirtualDiskFlags.None, + 0, + ref createParams, + overlapped, + out vhdHandle); + + vhdHandle.Close(); + + if (NativeMethods.ERROR_SUCCESS != returnCode && NativeMethods.ERROR_IO_PENDING != returnCode) + { + + throw ( + new Win32Exception( + (int)returnCode + )); + } + } + + #endregion Sparse Disks + + #endregion Static Methods + +} +#endregion VHD Interop +} +"@ + + Add-Type -TypeDefinition $code -ReferencedAssemblies "System.Xml","System.Linq","System.Xml.Linq" -ErrorAction SilentlyContinue +} diff --git a/source/support/Convert-XSDToMD.ps1 b/source/support/Convert-XSDToMD.ps1 new file mode 100644 index 00000000..e5ba68d3 --- /dev/null +++ b/source/support/Convert-XSDToMD.ps1 @@ -0,0 +1,24 @@ +<# +.Synopsis + Uses the MSXSL.EXE to apply an XML Stylesheet Transformation to an XML file. +.Parameter XmlFile + The full path to the XML file to transform. +.Parameter XslFile + The full path to the XSLT file to use as the transformation. +.Parameter OutputFile + The full path to the output file to create. +#> +param +( + [System.String] + $XmlFile, + + [System.String] + $XslFile, + + [System.String] + $OutputFile +) +& "$PSScriptRoot\tools\msxsl.exe" @($XmlFile,$XslFile,'-o',$OutputFile) +$content = Get-Content -Raw -Encoding Unicode -Path $OutputFile +[System.IO.File]::WriteAllText($OutputFile, $content, [System.Text.Encoding]::UTF8) diff --git a/source/support/New-SelfSignedCertificateEx.ps1 b/source/support/New-SelfSignedCertificateEx.ps1 new file mode 100644 index 00000000..458be9ca --- /dev/null +++ b/source/support/New-SelfSignedCertificateEx.ps1 @@ -0,0 +1,501 @@ +##################################################################### +# New-SelfSignedCertificateEx.ps1 +# Version 1.0 +# +# Creates self-signed certificate. This tool is a base replacement +# for deprecated makecert.exe +# +# Vadims Podans (c) 2013 +# http://en-us.sysadmins.lv/ +##################################################################### +#requires -Version 2.0 + +function New-SelfSignedCertificateEx { +<# +.Synopsis + This cmdlet generates a self-signed certificate. +.Description + This cmdlet generates a self-signed certificate with the required data. +.Parameter Subject + Specifies the certificate subject in a X500 distinguished name format. + Example: CN=Test Cert, OU=Sandbox +.Parameter NotBefore + Specifies the date and time when the certificate become valid. By default previous day + date is used. +.Parameter NotAfter + Specifies the date and time when the certificate expires. By default, the certificate is + valid for 1 year. +.Parameter SerialNumber + Specifies the desired serial number in a hex format. + Example: 01a4ff2 +.Parameter ProviderName + Specifies the Cryptography Service Provider (CSP) name. You can use either legacy CSP + and Key Storage Providers (KSP). By default "Microsoft Enhanced Cryptographic Provider v1.0" + CSP is used. +.Parameter AlgorithmName + Specifies the public key algorithm. By default RSA algorithm is used. RSA is the only + algorithm supported by legacy CSPs. With key storage providers (KSP) you can use CNG + algorithms, like ECDH. For CNG algorithms you must use full name: + ECDH_P256 + ECDH_P384 + ECDH_P521 + + In addition, KeyLength parameter must be specified explicitly when non-RSA algorithm is used. +.Parameter KeyLength + Specifies the key length to generate. By default 2048-bit key is generated. +.Parameter KeySpec + Specifies the public key operations type. The possible values are: Exchange and Signature. + Default value is Exchange. +.Parameter EnhancedKeyUsage + Specifies the intended uses of the public key contained in a certificate. You can + specify either, EKU friendly name (for example 'Server Authentication') or + object identifier (OID) value (for example '1.3.6.1.5.5.7.3.1'). +.Parameter KeyUsages + Specifies restrictions on the operations that can be performed by the public key contained in the certificate. + Possible values (and their respective integer values to make bitwise operations) are: + EncipherOnly + CrlSign + KeyCertSign + KeyAgreement + DataEncipherment + KeyEncipherment + NonRepudiation + DigitalSignature + DecipherOnly + + you can combine key usages values by using bitwise OR operation. when combining multiple + flags, they must be enclosed in quotes and separated by a comma character. For example, + to combine KeyEncipherment and DigitalSignature flags you should type: + "KeyEncipherment, DigitalSignature". + + If the certificate is CA certificate (see IsCA parameter), key usages extension is generated + automatically with the following key usages: Certificate Signing, Off-line CRL Signing, CRL Signing. +.Parameter SubjectAlternativeName + Specifies alternative names for the subject. Unlike Subject field, this extension + allows to specify more than one name. Also, multiple types of alternative names + are supported. The cmdlet supports the following SAN types: + RFC822 Name + IP address (both, IPv4 and IPv6) + Guid + Directory name + DNS name +.Parameter IsCA + Specifies whether the certificate is CA (IsCA = $true) or end entity (IsCA = $false) + certificate. If this parameter is set to $false, PathLength parameter is ignored. + Basic Constraints extension is marked as critical. +.PathLength + Specifies the number of additional CA certificates in the chain under this certificate. If + PathLength parameter is set to zero, then no additional (subordinate) CA certificates are + permitted under this CA. +.CustomExtension + Specifies the custom extension to include to a self-signed certificate. This parameter + must not be used to specify the extension that is supported via other parameters. In order + to use this parameter, the extension must be formed in a collection of initialized + System.Security.Cryptography.X509Certificates.X509Extension objects. +.Parameter SignatureAlgorithm + Specifies signature algorithm used to sign the certificate. By default 'SHA1' + algorithm is used. +.Parameter FriendlyName + Specifies friendly name for the certificate. +.Parameter StoreLocation + Specifies the store location to store self-signed certificate. Possible values are: + 'CurrentUser' and 'LocalMachine'. 'CurrentUser' store is intended for user certificates + and computer (as well as CA) certificates must be stored in 'LocalMachine' store. +.Parameter StoreName + Specifies the container name in the certificate store. Possible container names are: + AddressBook + AuthRoot + CertificateAuthority + Disallowed + My + Root + TrustedPeople + TrustedPublisher +.Parameter Path + Specifies the path to a PFX file to export a self-signed certificate. +.Parameter Password + Specifies the password for PFX file. +.Parameter AllowSMIME + Enables Secure/Multipurpose Internet Mail Extensions for the certificate. +.Parameter Exportable + Marks private key as exportable. Smart card providers usually do not allow + exportable keys. +.Example + New-SelfsignedCertificateEx -Subject "CN=Test Code Signing" -EKU "Code Signing" -KeySpec "Signature" ` + -KeyUsage "DigitalSignature" -FriendlyName "Test code signing" -NotAfter [datetime]::now.AddYears(5) + + Creates a self-signed certificate intended for code signing and which is valid for 5 years. Certificate + is saved in the Personal store of the current user account. +.Example + New-SelfsignedCertificateEx -Subject "CN=www.domain.com" -EKU "Server Authentication", "Client authentication" ` + -KeyUsage "KeyEcipherment, DigitalSignature" -SAN "sub.domain.com","www.domain.com","192.168.1.1" ` + -AllowSMIME -Path C:\test\ssl.pfx -Password (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force) -Exportable ` + -StoreLocation "LocalMachine" + + Creates a self-signed SSL certificate with multiple subject names and saves it to a file. Additionally, the + certificate is saved in the Personal store of the Local Machine store. Private key is marked as exportable, + so you can export the certificate with a associated private key to a file at any time. The certificate + includes SMIME capabilities. +.Example + New-SelfsignedCertificateEx -Subject "CN=www.domain.com" -EKU "Server Authentication", "Client authentication" ` + -KeyUsage "KeyEcipherment, DigitalSignature" -SAN "sub.domain.com","www.domain.com","192.168.1.1" ` + -StoreLocation "LocalMachine" -ProviderName "Microsoft Software Key Storae Provider" -AlgorithmName ecdh_256 ` + -KeyLength 256 -SignatureAlgorithm sha256 + + Creates a self-signed SSL certificate with multiple subject names and saves it to a file. Additionally, the + certificate is saved in the Personal store of the Local Machine store. Private key is marked as exportable, + so you can export the certificate with a associated private key to a file at any time. Certificate uses + Ellyptic Curve Cryptography (ECC) key algorithm ECDH with 256-bit key. The certificate is signed by using + SHA256 algorithm. +.Example + New-SelfsignedCertificateEx -Subject "CN=Test Root CA, OU=Sandbox" -IsCA $true -ProviderName ` + "Microsoft Software Key Storage Provider" -Exportable + + Creates self-signed root CA certificate. +#> +[CmdletBinding(DefaultParameterSetName = '__store')] + param ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String]$Subject, + [Parameter(Position = 1)] + [datetime]$NotBefore = [DateTime]::Now.AddDays(-1), + [Parameter(Position = 2)] + [datetime]$NotAfter = $NotBefore.AddDays(365), + [System.String]$SerialNumber, + [Alias('CSP')] + [System.String]$ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0", + [System.String]$AlgorithmName = "RSA", + [System.Int32]$KeyLength = 2048, + [validateSet("Exchange","Signature")] + [System.String]$KeySpec = "Exchange", + [Alias('EKU')] + [Security.Cryptography.Oid[]]$EnhancedKeyUsage, + [Alias('KU')] + [Security.Cryptography.X509Certificates.X509KeyUsageFlags]$KeyUsage, + [Alias('SAN')] + [System.String[]]$SubjectAlternativeName, + [bool]$IsCA, + [System.Int32]$PathLength = -1, + [Security.Cryptography.X509Certificates.X509ExtensionCollection]$CustomExtension, + [ValidateSet('MD5','SHA1','SHA256','SHA384','SHA512')] + [System.String]$SignatureAlgorithm = "SHA1", + [System.String]$FriendlyName, + [Parameter(ParameterSetName = '__store')] + [Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation = "CurrentUser", + [Parameter(ParameterSetName = '__store')] + [Security.Cryptography.X509Certificates.StoreName]$StoreName = "My", + [Parameter(Mandatory = $true, ParameterSetName = '__file')] + [Alias('OutFile','OutPath','Out')] + [IO.FileInfo]$Path, + [Parameter(Mandatory = $true, ParameterSetName = '__file')] + [Security.SecureString]$Password, + [switch]$AllowSMIME, + [switch]$Exportable + ) + $ErrorActionPreference = "Stop" + if ([Environment]::OSVersion.Version.Major -lt 6) { + $NotSupported = New-Object NotSupportedException -ArgumentList "Windows XP and Windows Server 2003 are not supported!" + throw $NotSupported + } + $ExtensionsToAdd = @() + +#region constants + # contexts + New-Variable -Name UserContext -Value 0x1 -Option Constant + New-Variable -Name MachineContext -Value 0x2 -Option Constant + # encoding + New-Variable -Name Base64Header -Value 0x0 -Option Constant + New-Variable -Name Base64 -Value 0x1 -Option Constant + New-Variable -Name Binary -Value 0x3 -Option Constant + New-Variable -Name Base64RequestHeader -Value 0x4 -Option Constant + # SANs + New-Variable -Name OtherName -Value 0x1 -Option Constant + New-Variable -Name RFC822Name -Value 0x2 -Option Constant + New-Variable -Name DNSName -Value 0x3 -Option Constant + New-Variable -Name DirectoryName -Value 0x5 -Option Constant + New-Variable -Name URL -Value 0x7 -Option Constant + New-Variable -Name IPAddress -Value 0x8 -Option Constant + New-Variable -Name RegisteredID -Value 0x9 -Option Constant + New-Variable -Name Guid -Value 0xa -Option Constant + New-Variable -Name UPN -Value 0xb -Option Constant + # installation options + New-Variable -Name AllowNone -Value 0x0 -Option Constant + New-Variable -Name AllowNoOutstandingRequest -Value 0x1 -Option Constant + New-Variable -Name AllowUntrustedCertificate -Value 0x2 -Option Constant + New-Variable -Name AllowUntrustedRoot -Value 0x4 -Option Constant + # PFX export options + New-Variable -Name PFXExportEEOnly -Value 0x0 -Option Constant + New-Variable -Name PFXExportChainNoRoot -Value 0x1 -Option Constant + New-Variable -Name PFXExportChainWithRoot -Value 0x2 -Option Constant +#endregion + +#region Subject processing + # http://msdn.microsoft.com/en-us/library/aa377051(VS.85).aspx + $SubjectDN = New-Object -ComObject X509Enrollment.CX500DistinguishedName + $SubjectDN.Encode($Subject, 0x0) +#endregion + +#region Extensions + +#region Enhanced Key Usages processing + if ($EnhancedKeyUsage) { + $OIDs = New-Object -ComObject X509Enrollment.CObjectIDs + $EnhancedKeyUsage | %{ + $OID = New-Object -ComObject X509Enrollment.CObjectID + $OID.InitializeFromValue($_.Value) + # http://msdn.microsoft.com/en-us/library/aa376785(VS.85).aspx + $OIDs.Add($OID) + } + # http://msdn.microsoft.com/en-us/library/aa378132(VS.85).aspx + $EKU = New-Object -ComObject X509Enrollment.CX509ExtensionEnhancedKeyUsage + $EKU.InitializeEncode($OIDs) + $ExtensionsToAdd += "EKU" + } +#endregion + +#region Key Usages processing + if ($KeyUsage -ne $null) { + $KU = New-Object -ComObject X509Enrollment.CX509ExtensionKeyUsage + $KU.InitializeEncode([System.Int32]$KeyUsage) + $KU.Critical = $true + $ExtensionsToAdd += "KU" + } +#endregion + +#region Basic Constraints processing + if ($PSBoundParameters.Keys.Contains("IsCA")) { + # http://msdn.microsoft.com/en-us/library/aa378108(v=vs.85).aspx + $BasicConstraints = New-Object -ComObject X509Enrollment.CX509ExtensionBasicConstraints + if (!$IsCA) {$PathLength = -1} + $BasicConstraints.InitializeEncode($IsCA,$PathLength) + $BasicConstraints.Critical = $IsCA + $ExtensionsToAdd += "BasicConstraints" + } +#endregion + +#region SAN processing + if ($SubjectAlternativeName) { + $SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames + $Names = New-Object -ComObject X509Enrollment.CAlternativeNames + foreach ($altname in $SubjectAlternativeName) { + $Name = New-Object -ComObject X509Enrollment.CAlternativeName + if ($altname.Contains("@")) { + $Name.InitializeFromString($RFC822Name,$altname) + } else { + try { + $Bytes = [Net.IPAddress]::Parse($altname).GetAddressBytes() + $Name.InitializeFromRawData($IPAddress,$Base64,[Convert]::ToBase64String($Bytes)) + } catch { + try { + $Bytes = [Guid]::Parse($altname).ToByteArray() + $Name.InitializeFromRawData($Guid,$Base64,[Convert]::ToBase64String($Bytes)) + } catch { + try { + $Bytes = ([Security.Cryptography.X509Certificates.X500DistinguishedName]$altname).RawData + $Name.InitializeFromRawData($DirectoryName,$Base64,[Convert]::ToBase64String($Bytes)) + } catch {$Name.InitializeFromString($DNSName,$altname)} + } + } + } + $Names.Add($Name) + } + $SAN.InitializeEncode($Names) + $ExtensionsToAdd += "SAN" + } +#endregion + +#region Custom Extensions + if ($CustomExtension) { + $count = 0 + foreach ($ext in $CustomExtension) { + # http://msdn.microsoft.com/en-us/library/aa378077(v=vs.85).aspx + $Extension = New-Object -ComObject X509Enrollment.CX509Extension + $EOID = New-Object -ComObject X509Enrollment.CObjectId + $EOID.InitializeFromValue($ext.Oid.Value) + $EValue = [Convert]::ToBase64String($ext.RawData) + $Extension.Initialize($EOID,$Base64,$EValue) + $Extension.Critical = $ext.Critical + New-Variable -Name ("ext" + $count) -Value $Extension + $ExtensionsToAdd += ("ext" + $count) + $count++ + } + } +#endregion + +#endregion + +#region Private Key + # http://msdn.microsoft.com/en-us/library/aa378921(VS.85).aspx + $PrivateKey = New-Object -ComObject X509Enrollment.CX509PrivateKey + $PrivateKey.ProviderName = $ProviderName + $AlgID = New-Object -ComObject X509Enrollment.CObjectId + $AlgID.InitializeFromValue(([Security.Cryptography.Oid]$AlgorithmName).Value) + $PrivateKey.Algorithm = $AlgID + # http://msdn.microsoft.com/en-us/library/aa379409(VS.85).aspx + $PrivateKey.KeySpec = switch ($KeySpec) {"Exchange" {1}; "Signature" {2}} + $PrivateKey.Length = $KeyLength + # key will be stored in current user certificate store + switch ($PSCmdlet.ParameterSetName) { + '__store' { + $PrivateKey.MachineContext = if ($StoreLocation -eq "LocalMachine") {$true} else {$false} + } + '__file' { + $PrivateKey.MachineContext = $false + } + } + $PrivateKey.ExportPolicy = if ($Exportable) {1} else {0} + $PrivateKey.Create() +#endregion + + # http://msdn.microsoft.com/en-us/library/aa377124(VS.85).aspx + $Cert = New-Object -ComObject X509Enrollment.CX509CertificateRequestCertificate + if ($PrivateKey.MachineContext) { + $Cert.InitializeFromPrivateKey($MachineContext,$PrivateKey,"") + } else { + $Cert.InitializeFromPrivateKey($UserContext,$PrivateKey,"") + } + $Cert.Subject = $SubjectDN + $Cert.Issuer = $Cert.Subject + $Cert.NotBefore = $NotBefore + $Cert.NotAfter = $NotAfter + foreach ($item in $ExtensionsToAdd) {$Cert.X509Extensions.Add((Get-Variable -Name $item -ValueOnly))} + if (![System.String]::IsNullOrEmpty($SerialNumber)) { + if ($SerialNumber -match "[^0-9a-fA-F]") {throw "Invalid serial number specified."} + if ($SerialNumber.Length % 2) {$SerialNumber = "0" + $SerialNumber} + $Bytes = $SerialNumber -split "(.{2})" | ?{$_} | %{[Convert]::ToByte($_,16)} + $ByteString = [Convert]::ToBase64String($Bytes) + $Cert.SerialNumber.InvokeSet($ByteString,1) + } + if ($AllowSMIME) {$Cert.SmimeCapabilities = $true} + $SigOID = New-Object -ComObject X509Enrollment.CObjectId + $SigOID.InitializeFromValue(([Security.Cryptography.Oid]$SignatureAlgorithm).Value) + $Cert.SignatureInformation.HashAlgorithm = $SigOID + # completing certificate request template building + $Cert.Encode() + + # interface: http://msdn.microsoft.com/en-us/library/aa377809(VS.85).aspx + $Request = New-Object -ComObject X509Enrollment.CX509enrollment + $Request.InitializeFromRequest($Cert) + $Request.CertificateFriendlyName = $FriendlyName + $endCert = $Request.CreateRequest($Base64) + $Request.InstallResponse($AllowUntrustedCertificate,$endCert,$Base64,"") + switch ($PSCmdlet.ParameterSetName) { + '__file' { + $PFXString = $Request.CreatePFX( + [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)), + $PFXExportEEOnly, + $Base64 + ) + Set-Content -Path $Path -Value ([Convert]::FromBase64String($PFXString)) -Encoding Byte + } + } +} +# SIG # Begin signature block +# MIIT9wYJKoZIhvcNAQcCoIIT6DCCE+QCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB +# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR +# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUAeqLu7+6JFZ1eT+FMVIQ0Tgq +# Y0Gggg8tMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B +# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG +# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh +# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg +# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV +# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu +# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN +# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q +# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC +# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4 +# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3 +# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI +# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd +# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG +# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB +# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro +# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV +# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y +# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf +# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y +# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq +# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3 +# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh +# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD +# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE +# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT +# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN +# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow +# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0 +# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu +# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh +# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz +# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB +# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO +# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw +# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90 +# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx +# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy +# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV +# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa +# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH +# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73 +# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR +# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW +# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu +# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw +# ggaQMIIFeKADAgECAhAGnC2gXFmy7q5ox0B+K5/xMA0GCSqGSIb3DQEBBQUAMG8x +# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +# dy5kaWdpY2VydC5jb20xLjAsBgNVBAMTJURpZ2lDZXJ0IEFzc3VyZWQgSUQgQ29k +# ZSBTaWduaW5nIENBLTEwHhcNMTMwMTI4MDAwMDAwWhcNMTQwMjA1MTIwMDAwWjBc +# MQswCQYDVQQGEwJMVjEKMAgGA1UECBMBLTENMAsGA1UEBxMEUmlnYTEYMBYGA1UE +# ChMPU3lzYWRtaW5zIExWIElLMRgwFgYDVQQDEw9TeXNhZG1pbnMgTFYgSUswggEi +# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChzYQDqMuYBs/jnfLsMvbbuZTV +# wwY1yHJ92TvD7bNwVx1OFEENNGrXkLNz9Ro6XtJ8zcB/80FmxE9jL2ARLd2TmEJt +# aYBvvmGMsS17zCGYDZZU7aVjaKZX2R665V+LWJUEIaHCcY5XjfmeZvCk1tHOtTAX +# qKjUd6fGIWXpxrSP9WKxW7FpTDGzQ2BpkZ+snmPS9yWDgeu709zPeoSTbdEIva6J +# ckzFj0uK7k2BqlLG3dsxBIzUqr+yTbdAuWfhR731iyWHk5GT6XCtBjBmuouKOCT1 +# Jn0xmYNAwgdtSiBlTL4A/Rm3YuP57VP+EBrrgA5g7Pekdo9APU+7QqWF51YhAgMB +# AAGjggM5MIIDNTAfBgNVHSMEGDAWgBR7aM4pqsAXvkl64eU/1qf3RY81MjAdBgNV +# HQ4EFgQUgiIRdHkZ2SctPGaLDBBOX67N7NQwDgYDVR0PAQH/BAQDAgeAMBMGA1Ud +# JQQMMAoGCCsGAQUFBwMDMHMGA1UdHwRsMGowM6AxoC+GLWh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9hc3N1cmVkLWNzLTIwMTFhLmNybDAzoDGgL4YtaHR0cDovL2Ny +# bDQuZGlnaWNlcnQuY29tL2Fzc3VyZWQtY3MtMjAxMWEuY3JsMIIBxAYDVR0gBIIB +# uzCCAbcwggGzBglghkgBhv1sAwEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 +# LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH +# AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy +# AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj +# AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg +# AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ +# AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt +# AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj +# AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl +# AHIAZQBuAGMAZQAuMIGCBggrBgEFBQcBAQR2MHQwJAYIKwYBBQUHMAGGGGh0dHA6 +# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBMBggrBgEFBQcwAoZAaHR0cDovL2NhY2VydHMu +# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ29kZVNpZ25pbmdDQS0xLmNy +# dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQAh3nRpJ8WxlJZ8NI1y +# B4iM7RzjL7D57lVj/shWkbCp2znzBLVMGnYVK+Z0QL2PSxpxX52Khhc2MHXTM+Yf +# 74sO5XZm5IMMAnlpK2FeyQBGIKcFmrzkvj3LUcCc7RU0duioVHQ+C+hOQmpmSYiA +# 0zOoJgO4zFy5SKT1mzPEElup1B2aiE+WQZpcEWUv4I+/lYvYIBhyz+WZ2xm4kLbG +# QYR/08cei9X70x02wpgMSK9yKhSzcpwbq+ccnOtFUlTLNyRr9OuRnTi3ZCM8w5Is +# a2+UsnxsF5F5CGsw+GMRT/Jrm2mHMcKIW+qp8reUXattRTjobnbARJSQS3NBt4wp +# wTIZMYIENDCCBDACAQEwgYMwbzELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD +# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEuMCwGA1UEAxMlRGln +# aUNlcnQgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EtMQIQBpwtoFxZsu6uaMdA +# fiuf8TAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkq +# hkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGC +# NwIBFTAjBgkqhkiG9w0BCQQxFgQU/ix9ZAr39xxjbsYVBZ5BgjBjq+MwDQYJKoZI +# hvcNAQEBBQAEggEAI0yNayiE8E3Ttsmvd5tvmAUN9ngGpez7qvGtjcUEFXyjRHjW +# d3XieT98UHYL1/+IoX6QiEXP/t5SreMqQmql9wvlhyDt2Qw2+E80MXFCGvKuAJAp +# LvTY3F4i9fY+wCT33B2dDUSAI+mFGbJxl1GgulqSxLOUiGnlpWvtJ07lgSki6xN0 +# MTlNCt4xem5P43iRLFtlkEJjYpznClPx0Ipu1IG3gND4hos5jVofxJ5ZcVbMQ07c +# +9Rq4iTccmNE9oV4H0ZRRCrJbNfIart3je0/aSeOpo1WGwZr6hyJEvETGruyDbSW +# uK9kIfAZbVSvpx4clfeP9SIxcyhom1e8xp6daKGCAgswggIHBgkqhkiG9w0BCQYx +# ggH4MIIB9AIBATByMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBD +# b3Jwb3JhdGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2 +# aWNlcyBDQSAtIEcyAhAOz/Q4yP6/NW4E2GqYGxpQMAkGBSsOAwIaBQCgXTAYBgkq +# hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xMzA0MTQxMDU2 +# NTFaMCMGCSqGSIb3DQEJBDEWBBR3BvjwJvcH3OSIjiXabjSSjC3hizANBgkqhkiG +# 9w0BAQEFAASCAQBa1lxJa2qVZmZKDY2Rm7XNJEIoxOzdw+WZViQC9qsi9FrFVPwj +# y3UjlfVm5AynoSXs0+mXgyU4xW3c/xDgFKEyLN+mocD8mx+ufc/bG1l0SWVYITgV +# WiQe5G0oOW/mRQNLhaiJGE3fZuxvTbN0rGYIOeZ5Nsl6yyJpsx96em0skNk2XxVA +# rYoniI/OCP7a9VyqXZr/ScgG18ZreKZikyrjBz6zQh9ibrApLprm/nJ+7go+yeHA +# hqUEwDtiRAyCJfH3DKLPCm6YqWOaT07+LWZSm2DM+mcRydj7gKGoOILfgLhjnUdz +# IJUMgUNRqvIxWuUtU1wAquBSObIOtRD4hkBG +# SIG # End signature block diff --git a/source/support/tools/msxsl.exe b/source/support/tools/msxsl.exe new file mode 100644 index 0000000000000000000000000000000000000000..865a7b2bd3354d58b2d858ef9085117e3725c5d3 GIT binary patch literal 24896 zcmeHvdt6l2+W#JAfDuLq6$%w~s~FzEfk6ck1%VM0bdZ~9UJzyk<krj{@d5-6nB#WR z%C09%EzLZg>V&3g30@K{t+bn$E>>2X;1q@_CN{tCv-b?3W#{+)^LyX(`Mn$0ti9HH z*0Y}V+}5+!nlX9qMn=vsi~`BwV3>W#>4_0P|N7%BntKi2-;3GZ<J6FSvZPZ(((>}n zY^kYai7C5?&B-n<F5%e42G(RLX7h{L_*toJQAw^Lq-RgBKqu<Hd9&AL_Ly|em2yY- zJC}~KV$|?+3&i^9a~9<NM-4f*5cxPpaV|q_mx=l8{G2@Mdpo821cphHDVQG2`Y2c1 zMMf?2mU%J^4bDon{=Ue4kYcEe4|;c#_(3(q7xAgWCkkcRjL0t$kYK5Un0IDW$wXlM z<@B<MVdnIu7w40RQTn+G|Mz(*XeuS}7>0S~FS><rhUFaE?;nN_qKoF*ISIVuVL?@h zDL0$LTox%&5rgD~Gy<vH6N8oz2~29zqY-p4Vcmf68pALRAttja2k+us7vY8Y8jjTM zi2<mdZJVe;9p%x;Q;^^5%+sh|Q8y7}bv`o^X3tDW(uRfP78WwG@forD>CQq)p~3k! zD=8s1J<VCjoIZ2<%!D)%z7xy;zTAle8T@63-=dfpM$f-5{N~Ou3y!4oEh#B{f|8F> zSPdPF)zHoqFnW7JhdwVC5)_L38OD0lr?#Oy&SB|rjPudQFv5?67$$EE@HoroQ7&NY z3GMnk19gfJJE_pAm&4NT1lj{2pb+$TQQj^jBG1Y?aah4@Wr4J|!Q#GeCvYCw8hA@F zlP?cc);cU|K0Z)o9J1Wa*vM#@=2hATt0Omq=K_7qzA;vZm$A`9!<c&%pk3#tVdfNg z*%dl{rjUkltVdND4!=3TUhVJ+Vi<dp?l+U4wV}h>E-#lC$mbNu=Z^l+XbnsOK+IG% zWA0~<RW^Iu^}ieWiAGxle>mz;*=1v+EQH~YEm(M1$$)XyEo;GPiFp~BT1RFV-)t2f zwu*LP?A>T@1^=S1sn^#6IJJw3+Xvpt#K%zP6I6U+JYO2aFHhp<Boa^0>QjX4zdIbj z{iHra_(^)*hlzbFzSb^xpfOeWL~4Ktj3k0R;;aMs*4nG{x=Ud!Mj<J@3zH=&97dj% zwGfj!fk{C@Fc168Ikw6qTV<N9QZGF2$1t`^jjeKwtuk?8qsPl&BA|~DsAE~8R3WSa z8a|psTb5(l7{_OhWg5q4z$4_MyJK0BFqg(z2z|2`2Cl7cc})ghWZ08i1RKb=+spAO z?C--c+6$?+`JBUVBMs9U)mGu1AzU56Fioe4)#H3nU2xU$skPnlyg$^0n^5qpRPv?L z$bu_UiPV%EV6Qz+Txk@uO+euAQvm@~t(U`3gEze}5`1c2#@C(&pgmdtd;mHMBS9Sv zUY<h~9_w&8sKj~O6ZCrFT@;&jk@H7zy&aYj=p(#Kq!D<yJ)s41((9cQn$XADFd$<7 zFs_fimZm8J2yfq%kXEFo3f_3v7x>s~@1y1nVH=hZXsllN7DEA?)8|#d>k40kht{M1 zFsnh`t?xdt$6?`|W;RnQ2~8L~%i*^PvvgA93-3?!nTn}u8=~44s2^*XXMJb+i6erM z*yQl@1@K9jnIbVGlfTaSCURbh7XO1xYz)(^I`}zSo81rYLsrlut8M#2ZNmasFATxj z;**-ek5ix}lU#@PLVZ8Rjmb>Si;0h6^1z-dTXL&ye!FdchiyJY6m`UMzcrcxubBh% z$qlt9T+#FL%mT(ysw5lvw6=kpYHfG3Ot&6USfd!rmjz7TYT`lwCYY6FOK7)NT!BU~ z7?g8&SV-^{9pWo`^9k(>7tA|xgxB&Fts=F}AL@lkU|{n*l9FfGDt&B~N?WDM;pYz= z7*8EpsVG-Jpsbz@IXtk;-8R`)sUXvkyd?QIhhG3-ft^q9I6*cbd<|HePHD?fi8jG| zc<U70Na0;nkJc#%WT<M-JkY<~Gn!DA^>&QUBliM4Ebz#5KD^Fn!4Ye=57YE2Ac*(P zy{*^1D!r`Ndvfm9>#W5qCPuwiX}#W~;s<bBgt*IkU0JCnu)YM=3t(Ii5v=xv<y%0h z_X3f%y+?UpYdhg;CtU5UMe&-f*)6O=l3Gd%d3=&I?+It+@F8cRUVjYYf}2tvpa@YY zpy?$jjl9xWdqQ3FH#8QwEmZG4ZT)d57i9g>of~TX(SuWr{>-A1H#R18z;qaN(DNS( zdZ};oJ@Q8W9RFdnr!ne1uBW`Q6QAX$$che&Cwj@z3u5P!+j*JYqRg1>&?|@>O9b4j zG0NW}msLP7l<=z(U1y-Nop{u>ONn_E?Yt-dfkUqpy<SBxXbeXha|p|k%f~6m$H8w2 zC)5}biw7m)HxyuTwGB31;sWP7B>DK%42MO<gChQ%5Y-!#ch8DUR&k1`Lzc;z^@&h# z?HOa+iwiB9+6McKB+dse+}iM)9%Ne^RQhI55J9V&@HXrbmOHjFE`}ns=Kgvhw$GS@ zNeLeTG1~f~4U00vCo63;0{LW>ZN`WN^X7vPZG$Z~z!s~r#roP}6}DK`7VBe+^|!?; z*TzPyjh(PIcKq7d@ME##WVYBCTWq{7c9N*GB;Zu=K3T=fGBArwzJ2uXrybS`ADOx) z8=_J_v>pSZELU$>O+%-~Sby^@Tj>aV?Ct(g^wfCkj|`V}FE<S^ZkC}@t^Dt1AER|j zH0LpGT2!RzO4RQbVstSW-SjG9Xy+S8A3NQy&*0w^4nRs^YC=c(2=K)g(LBUvR@qkh zG{(id0k*l1&8)DkQfeC_;v(RUVr+5od_o6V?qL;UGyB?B`CD_~b$+y&18l3<=3lT> z^T+E)Om(u|;%<wZ)ciSShFVRWtF}jD+$0hIVW}m8#MA&WS*V3Hw0S3Jt|8?(C59o* zywN&2+7eXj;D%WpvPutP3FnU(<2MBevo?5GkGa|6Vr-#J^GMa?{Cm)|rrCJU%z)g5 zi@mT$k-}4Pj<5sKcvCEj+A~ulQ)9}Z22DJ1r%`)i^dWVP8bl<jYiLbMv~UG(dAT4l zLpamZsfQLN8#k`073@RXePY`ioozji#f8h7K9U;K#KxyO8;A38@rj%|(b5+)M99(5 z)DL7h8GbA-Le}&&I%-d(WRie>rWGb{2{Ga*R%+NlQze?!dp)e}84rYT@m669H_$5d z;?fcmxkRhr&P`0TjDn()#Wu|X>^=kHjiyArS%o4l7+^yPtZyQBE|F8D%x-?-SloD7 zQy>72#Z8bk4Zw>nHKwUAa^N93HF@G4bQ5P<u^56g=f$k{CuAupO(~c{sDBLe3etY0 z!$<-Bq?C?47YQrSWnLGry@18VJ2elhPD4{8kXzejRL-aJUX*!VjI*kMs$LL5+z4bT zl}RLs3aL!`(S#3_-7R;eI+l&#-*+qv6!zaBrWK;Kd6u<Z(Y@(8v8jlf!maI|-J5E} zrV?uEXKnYe4C+FdFE*8;Ntlc!3Q}mE7P&xS@r_)dvZzz@Ow<yHmaMGikW71JQYM8~ zvmmgjBjo}7%mDaRKf6w&x9i5};jx#oDf|b*Tj&5&I<ij|!&DviA;(u$sbsjGj%5mm zUtkPsjY@0*umXsY8~oyUirf_Rp`;HLefZFaFMR|Go4kmB`r3vp&VyI#9Vtr3vPF%$ zIfYO{p$v^;h(}uriU(>iL}oqs$2y||EGsHR0p!Q$W08i#Pj&d^0g9jRlW9-xNFkqS zlcSy1f6D+@kXEB2|A-|YYk=0_M`5OOfrp#0mIo>rZZQ0~QcNPk+6F!;P-T-@Jooit zJgb`Bv<(0iLlu9x=G2T#dwR=k?bW=Epxl-$U@|sKi_Ow%v$Wd~OFX<0Wb^G&zgKu? z3d`W0n>=B$d_oJaw5PYu#>@k$wfq}Cp*8B5x`vJ<sHvTr^2DY_-qMbxGD8SzhjO>_ zUp9LiYa7%xuK-kj&B%KgqoWy1OMxP<3N)k&pY{+Xk9qeu@^MOP<-GJlD0<g4a304J z9I|FbmBGQl6TwqlBcw*Y7f@NR2XP+w9BN6&D4rOFOOi&wDs{Q4!ogTP<=2`_Fe2Kl zfXpi(^WiJv6l)#o_2eP=WWgS*7~Q5n{X6zSW(onW2%)gcW^(UV(!g!tLsk|K`L*-o zNC+fC#9iP4hIA?fc+{=XDEe|SNW?<w5AhbbBgTV({nnEdvBXylqY58~#W&APqfDYz zmdUf=oo^M~Q9{g%AZ$LiDxv_nRBeN}rbQjk$h6JBWJ|bWOAu@ct;8bE+t`?JQVE%U z+&s)Nj`j%lRdZks^RfO)0nSs9BmwngY!x3@t@4NVDJ=oKyugv_*vOKYi7M<nDiIYl zEv1TzOH~i_V=TP^1jcOicLJ4PsCuA(SGA9;`lu7GgE$Oi<#oK=p4^^dZSmnd?3R<; zw{K6~?${`}nl~A(743|=76BJSkXKSLNUO6|y<QUBm2RpBoa*)SM0twUEu?nhW>397 zzN`8oRgaNo9gJ#g#YKi&#wVZ;DUv;1AOKbZP<8eDoT@ca^{?cbEdoL@4?f`%UmEDJ zwAvG1F7Se_elA?|5S3Ps&33|i)Laz4bJk?!RgyG|#mLIHIk38-#e-2*S$rVdYHCSC z3;dZk@Hs3k!V>IGcuR}JB1ql{4hIdNf!Ho}f>^Yq&fH)`6EGB!qL@G&SmZ4ulxMLB zlk+5vEu)G?R7jB@A_{SPi+CG5T%a5Srv@M4!}r#wIA)LF+iM*a{+Yr$ETquKWvaZD zgkX(?k%u0;wmK?4mQBsjUe#WRELCLMlG`gwY=~SDwRKRmF<yR%PgL=GWeWU=B=AxM zg;yZ++0gs#4t=7i5`AbB5yM&&I@q|VIg0Xw%^n$o3Zr9^gQPYdlpOlj-NDHfd8>$; zc4?KTEeYl$hO$+a1DH%sX`eYlc%u!HiVsv#L@PSy0C7^vOkofFXLD}?#U2)rX@Iop zVld<WyiNf|%6Xm0#pN{0Bho11oGJ8-k!FtgQI%Ps$f~~rccpD;dyyN28mhSDs_4ll z_*hwHpBt8zW-szJ$v!rjwqYTR*Xey(3XBnzR^gyY^uispVarN=V?wJU0UiZ@RaEX% z(0<wwyeCW}1r+Pfbk><W>kg8YAhb*n5IFKmFdU|Ru_r@zb`d<+Z+99-c_o}G<UUMq zPi__VK=}x11q)%Dm3b>@;!lB1q&`Jk=`evPglp-<XisQ$jnW-?xG2pbRja^UrG3T- zVeBuUmuRu4dyySY630<y2xGlPDG;DH0lt43QV;{)TQqzS9|w_e3jS=foSz4=q~?_q zbvMu*)PZ^@uY}Q98l1Wyo<~_*RHT^L&_zywN=i!^=N(z5unetwAb_z1WZ0(#>hm5T zbek05z*!)S`cl1K^juJ0oj*TQ!Dsl|=Xa#)Q}RYYBD3K;C;x^iovpo~UjGBwUG;!C z)mbBic+9{t0b4E{C}nrT9C5*T+W{lAVwTeqX?)I?B>I7XBS9y+CrNrbRva|IKEJ*D zpeL?NQ02?I2K)%f9Ayd`v0eDt1&}TRh@-6(M@xd06V7ESaj8qm^T$LoXk=HHp|pkp z%N1cCB4C<ZxmqNSs!|bj@@FB#F1f%UyBE6oq=gIkBQ9ek+j{nAI$g2pTwxKN4d@g$ z51Y={rt`7sR5o4UT3!FOx&do-{x)4a?9ir5!oJg{)7x}u$8`N<bmEYjPDh)*ybgP| z>zGTXo<cewton!{*1s(B4Rci4GZnMNreCj0O%IDrkfX0XQz<o_7n{mgikuFxXR4&? zBT{t^zhDd>yd0~seVUIx)$xfw6(jAC8g={vjeVMLXY~fDdh9w~INXfy+G+mlbmP`e z>$grfe(kjW>vR*=P8+aJ7hyeyC3o$#09zR=<ba!S0Q2B~5vn2n#7V6zn6VymyLJ{K zsrJk@L~hpKdMtCd9?oo*TMuiDw%)O^wGCyLjMgE{^yx<aE+a2P<#eOXW4cZkb=lOi z;0T6Sz;^vcfN-(k@Eb#~sr-F`DtnwvFPw+2P^3DDIH}6y-{VgoYI5V-+qU!XTECXJ zy-*eCF9yhB7x@KyoSR<Q?(8)9PMr`O$;Dn2i&XfKm$P`)HdJ^N$O^og#o)#V`)$Do zh~SvAvgRMrHblZLb2It0K%66Bw%QX2+;lpGY_X;u_?%lNw;p1#L*?D3PBliyGN_H7 z%b>P_bzqGG%W6+>?#8I&+)?%357m1QjXu2K2!BX>!ltw(;-7+!qO6sOgJ(K}c?Z;A zr{H@Xx`_2diIBcq39)^*5`uhJ$sVut!O>J9_(;es`xjzIFjFCHhP%zefUyd`w>W`A z7Zo<nM@bc6E#F%zs~oY41rRfdMmlZR<5ej<L*28E(9sotyjg`ADUH^MiCnLk7*QiE z|3Y-GWJVu4-QIi+Y)!Ez_~OifP7)Cq=HXfoOMwcDr!W{!>%@@)iVqnChNOa&1W(k{ zz~kuZK%h;dV;o&7Scg*uBYch;>u{<;IB-Px80{iuw10n|KtP)^3mAkqQIkhCV*4W~ z;b2BVq|<sUJdFyHpa_y7P-KSoKDAdXRn_0qfm!wY4E7taU}@2m&e-S(CQcC*r_9JK z*W2S6oFphA7`-roJPjg`ES$et)Ooa35mo>vX5V}tudThx1@JQzhrV{_4?%UIxW`4s zb!Y`I^vr@+vdMX~0?Y29@7?8EEB13%BTh~{>^c%;IRq(jPTFSC`Idrv(h-Lqu2UA0 zVUG;VRwb$mY=={};w}Z;6n9eaUv$nZE-<&vCbN8Y=VTOmp@2N;ZIgK!qNmB|GP=!1 z@rT(=LRV=v3&1Gen@<OS6gFR_vjo}{wlt+kwiwBgmSC;BfLJbazKXw${UjHjibt9* za=&m)9viV67GKvkGFl?0X$@RtwSz&lUg2)nshSh`H7XRMj&RDTgR~Tu2kV8GuqU@q zgBy7iFHuKu;w%DLRFafF0{@#|w~63VchF(;;D)EBW<qO8Te}mXt|=qU@rbxD!eNok z;yu;|c;fWgB?gJR(rMj%6*i}nHoJ8a$zOh;JDFIAoMcvlOgB)MDg2DZ7p~4lqf?W{ zf<|Kg%<g2?ULYONWWuFelw?T`zX*|2xMJ(P73oW}I|{d8w3aXbogs?pJ#aa$g1Qk6 zr;`Z=LIxg|VK_6;3zZ-wsu7z+Y4<%yV%82(Gn)Ir0jlgG8+oP8%NAjq><p~C5a|?7 zOiTV0(|r=AzJI_}spyK)t6LHw#8mACYcyTw(8;H-`mqE@R2#O7S8Vf-+U84_5iRPP zr@&^u!k_oVWuOCh3Jr=#f6G-E)}f174W2-}0NG=2>9WoX;CIu{=tQ>}3BD2Y9b7Ly z!I!!w_$q9g<|)pLMtz3ez7Gl~q=1)EM-WM>*Y8I^>~ozB2hr;6=WKr(?N(0(PO(cn zqS`FmfE}BlR4xb#mB+xxkybhU;vp{_F#GgmSca|=Vo<gv_%tt|>!-hfn*=x-$%l*N zD_X245U2d|%C*zeQ?UPX(qwTFTaL>um9uhzXg60P=lfX39IQg{7->;*LsM~Mm2eWe zt`sgKphF}{dY>UgL2$V7#DsD65_N+cj9FOv;xMGyYo;T7ub>BT7H0QLrjS6dwE3Ha z7l-Aj5RM#sC2ZMfgCp+u$>+4=UK%$QLI6<HEet7bDJd-?`0`0oY&Mz#?hXpLeZ}hG z2<IGr6xM*O4#A&>$`XBjvlr3a2e@!5(><;ed(gQ?Efhdm;RY{GyDa#c>K5AaTMbtj zx(;xyZNwmfL|hD|5C;vW8(XYed+6lWv4<Klmc#EpLfY-tv5w-<R*E&JBBIL@2NKs! z@j60U@my=$Xov)-cjWP`(c%(jPXKp2phw~Ysmh~Dn$lYs?Sy#lFF^<`n6wK>T+;yb z1c0z)$uUC-R@$>+86Y0lHsIh6`#f%hxTxFXeOjCQMU^UW^buiKjmHL8d!cOyHwiFS z!6)jl`CF_}fnbVKcmts!-&(+A<lRsE3k$}{0GYxf)YA!0=ZfPfRnaA%>zt0Zh%W2G zEtKf7&S)E&Pt%O~bX6*jXK+MW;bTQxa)+hI>{Q{O;2JG8ocHL@?!_Xs%1z(wHrsia zh_gS7ug$~KOL9IVNvGPI2jKV#TlCCS!4s58J7YvQ(qW=>Oy!F`LTDzxLV?jo*mX*K zq5>z9<7bKIrKCylYW{kZglvh4p4MY5+~&vP;Q+-5p!0H-(U;LWhE)0>oJSaxh<>=o zRBvjCtW>Vm`B?_#k=Y89eidCdmRE~&NvRSi)#b+Mdl^2Y{3Mi2uu@&K8x8{Ou*X3G zaFB=FzT}7*zUo@~k`O-w?i}V1<S7nZtIU9&CP{kdEv<mo_9Bb1YLy=Ymldb1is(-V zC`v1f4YY`iON+!AMumzut4ypIEO&;=yi%5|8kyYDh=CZ!Qqvqv#~l<;cY(Yso1q#h zZtk1adf`Ke9VfQU>SJ-fGE8JNw#3Q4HoS^Av(RXfVcM${d-!*zQn2iHYpE4Ai()F# z460jvVT@pj*3s$W`ODnE%)BblR^XPINjJ_hqT!{10T4qoEp~LrDSiipe3sqbLYBLm zcz!nOs|p-LwF`T2E|S$8dpn{%1nTh90~erxPFPQX2$AXB()mS#i;yR_?22PLrL1#L z2s-{JJ+ajn%90+ufTJ;nIr5TFmU&(%+q9vnY;HkQnanPf9at}vnF@rmJcCfyhLoKz zl+AolC_A%4DBCu%sVr$oQ<>=*p)B&CP&V2uls#A?lwB+p%BEBcWvh%rnet0P#JzFj z<}%G2o6DF!o68nGvbn4QiKQ~0h!MkZ(ZMj4Yj%_!oU*;_`{?arJ=OITznDgE4D;sQ zG0eY^zQ8pk#Ldtho(~d(^kG~K(}1)QsR}6q$qg`c1E@r5pBlp)N7{$97HK(>28r&h zMj&U9T4R9&={2M(q*A02Nc1E#smvUFCgIC(A&ibOF$VmlVbeix*-S1zbD4N%7GUC; z5+;YSFh!^<W;noyBWXlDhw{&@edO}Ty>~2RVjd4m`R-P{|Gt02U-q8^Dw9B!0b`dk zg`kVWd-w5=+}3|pL_}`n_{bb>urV|;EI2eYH#|6d{5WH<MiZ`y9Ip)<7it)XubQ}K za4Wt4RX%7t`Jmn4`D@BMAN97q$uRAMzZr5X-aGN3tAE&E_P-M!y84g5t^X?HIL(9z zqjr37&bV=*#0PC~MC9T~@Ij+7MuZv-VPT=*gA2p0^!``*Ksq#D)Q5oE=(rW{o%qnz zf52b%zY`z2`VYCSzmpGPq5p;tE(~!=sng>zljYF3LTEegYoK|!SrzLfGhPlIq-WyL z7$)4A{vH~`Tpd8)1S0z}QnUD!qD<Cf046i=6f;YhU@<pCmrby$9OU_cDaLnFgPh|A z1D0b}fCB25hff2_LCi=OS}|{sN;=I%vnpWJ`@R419kYvxFN^L@7t!=kSPb)My>{gI zRcCs?-iv9N_2lG5)wh-3N<irCeBPMGFsc}ad97RBH>hJ!H%Zq8cTXb2tVf-Hw>k~# z@=zDvtu769HK_Zu=P2-=A>4RYqR%>{r;&Cd9Yy*QNkF=aqySt2@&M#vNQp>ukqVKj zkRC#M8fhoe0>JqoPeM))b{17QWT`ri<bmXgq(q{&$)Jy(KFHmWTG5Z5tIRY57gthL zlwF*glwWMfWY)?H&77&wP@IWfi43ht*=8=mWGXS?TV7PoFqq8wCB+HLW0{AkoXX`U zqIV$_=6NPzc|NB%mE;)AW~SIJ#bPk6&>Kv~5)%f<G3ZQ&`z?mzoE1#bAKKzdEXAC` z#7uQdwiL$a<0fb%6IWPbHaL;r<CavClU*2FSXh$7Fr%ar5E__;@=Q}cXVB$?gxT`U z{9Hp^UbZQ%1bDdosVg`G6Gu3u_p}lh0CSen5nWs|h|OMNh-H{_x7L6I3uuPPCApSD zCmpCuq&gZcZAGbpdBAP9Av;$boJnBfOonWUlvpOQTPc&d<y+Crd$QSvCF1mE7*;Ts zs5Gn8P>cfc^_DM9GmDwHlJw&I;(RVYyD)zx_#A<n>D|gcC`%(xFE(?;X@<GOcdePF z%yi;&u_*S%5P7<(Fq8S;uV72|@E`Gs_~vs&vHOtz_xb;w0}3%7Mx<JWn??8>rV5wc z%p&AI$Z-{1g{<2n6XWoWcc|a_C-rb9Ov9hl%TRyzPwIPMA!_Y5qVy_bs9qfWmNu!L z&TDUdq<R+hnry6(Ze6XKT>NGQm%OgWA7y+=s`FV}$}lIp!H+--ixh(-V~7Y4-K|}$ z==`XXW>i(h;0+0Xcxh;8Ku$Hd9iT6G;Fdtl@JSa22rE$eho@evxuxz#_oBR>srPQv zb^^$6+_+I+*HvcJb(#b6&Sr-3j?&`7yYrb+uc`CC(KSNU4NaT6+lbaFQ?#TLoljJo zroJ0Kd3}BTfi|uDMpWAkErylfU~XVYdHs~SdaZ=E9`v?}?L=o(>kTb4rS1T=V_f-x zI-;rW2L5S5c+?H8x{f*nL<_>|>ol4hZPX6@n))`hGxc?1e`=@xXrX`TPq?DmB;+D~ zj4ZE{kW+s&Ba#>KWBdcoS$hLHxJLNJS=QIp*VolWX<S^;YA_LvoN>XsSn==5uKq6g z+sm%{Tk?*<4oAD%;b@RM991$Yx!y&EAx)urd|?;K6G`3xl|=q$57kq<=S9HdJ4otw zXX=Q}!zeV8E78cLfMnY&>mzD8S&U><r0Tg)e<M-=-j_g~Ymqbx*cQsk`8lQ%bBU2- zN6a3{&Ph#5W1S&fiHUVad+hMgo?d_rfc~UYv^0@TP6a4yF0q($46K=3QD`vd84Mgd zucVYid}yA}&MVEvj|!ugWfxiuAt52CSYA|EY@S$ZG8prhM_Ww!RNK?5r`Ig0%fy~u zES}&g^yzw1^GeECj((+BBn4_tpc2-I7_2zE$N)$&0%S`pT&aa)xe}JDb436rbP-6* zHc==C@KkyQP6NxCvWv|Wp3;vUr9lAU?6bVgMFa9w&Xixu7MBzUoAZ|x=Nt3!TS<;B z%R}U8F3koDjHVI{BC!MUa5t8?&=H}oCmuB7mzE}jxukHJfyFNX5tC&XvbkUlMRs5y zn+pyVFJZw&vw<zm%Qm|RTjs(-c?N<){Nm5R=9c7GiVVfvUv%WUcN7_0SdyK~61kn6 z$fm(rF24w4o1LVW=6CCe=(pSC*!0;+(%d23;BI=LpH4h=GQ>rj6Q6}$ChZ~!Z8~O` z&6-OMImFe@mJ%bWSaQ-tHYhA)e9#y+NE@P|EF75=PdBPqHa^>&$6Cygd$!p%ZnkNO z$P<?4X=Z`WDJL#BzxdxH>C)=k5kd7OflG*B8q9H|R7w7>>59V=FJUkA3=d_C@(T;| z&4!$k;#_bywQGbw>oYv;&hR-UMWrx+ZrFieduMpVazl>AC0iFg;kUulJOYu@i%lRA zdcG3ulnk1MAcBX728EC#U;&eyZ#I*DIz{ZVMzNi6bhdZR@fMiyZZJ;Eb+xnEM%V+! zD+zLrE@G8_p4x?%teAeCESh^~pQ79P#9$mfc_IVh6dgto=D`+O%;Ha-jV0JqmSLWY zSHKe3py(hr2OG%j9DviXh^d&F=q;Ift|6xojUpreOK6xyz>@}_bISmoRKO|b7;+^l z$TNv_!UnR*{++bLA7_gVe;~z0XV*MM+Q?}-rIgKzewdYJOLC)8ZlT4=WBp&x_ZFP# zv#0-CjA_897hOIp64mWT=92fz4UwFHv^oqAjSj-P5JYYq`_o*h6AIaIHPf9mRylc@ zUrY{P^k0}wH}BJFO|H>OxIFQ9?bxh$vs{cE%dv$9oN=%v#Re7(2V06dr9y54G7(Lk z#!qsNUyAW@Vo{hYdhr}_*Do=YG^=EBfgy(j?&R#Hpvq!`mF8u0l0WNoa4>H0GN;4@ z$#aD(;4QI46&8}g(OP6+i?Va_utBFG$yUTe7`7xQ$ATWYwDiFGX>lN7!W+;T4qNE# z0QiVI^%2RXPNIKh<7Op0DK?9<yW?2I4C>a&TN0<l5Q-!Il0H`-C;#qDo9NYOly1WO zzZj=;PUJ#Fl@Uiux)WVy1IN)Q#OJP&g0OH<Y(OodV}aUr4o-gH4s(<oVV9wbdXB!g zV*ht@cY5eM&7H;g6o$pY<I!@WSw1|hr`K@paE%z6iLo3sR9tBYj?VL_Ku~cdg_fe? ziC7@eBV6j?4}6ol7O}}LJn^VcBetwCb1(&`0pY)hScaoD)<8#rEd7@SFnp7<SZpa; z4BrZhLZwNPeE*08M|Tvc5LYA<ajp7>Tqkch^y{)KCww--q|U|26*#!UgWHXTW%Xpv z_#Z3`!A^_1&F&CQu%NsUq=^1~i;LFV*6!RAgIO}~l433!u?#H^w+1;u^gn0NVJ9gJ zjb^gsh%1BWM_}C)T25pqO}Vuj%i-iOI*6D!cDcDQh|Pn0j1KD3!(fZ4FlfqTNv<w= z1O;1ME1Tq>^0E;*y8Jpuyfse36va?U<ShAMl1ET*X?`aXmuoagQ4=l<CBM@}2iqC; zU@p!Gty5Mf-N;j9;QneMJ`<SPc$<#fw3#SR$EObO^t`S3`FCk=BAq?ZC+$q8h^=&P zt#IyCoE6B%h`X0m+z6PQHwrrBbXPDPw+KeuB`}Oqgwen^YM4l*aGaEq9}36)ql}3I z4Be<@1AYal$wtW_4v-8?IpUp#88AkSL47>X$2CSgAk897J}|iME~s-6>X#!G;$6ci zyY<OH9}`-ndk%sP#f=dqjhJIj;uu6f-SC-^X9G_cW!-TvXGQ^c65y7IP&AuT%!9bG z1h*xko&2GW#r;Yg&dpieXx=_YR^*5YSVGVq2CC?f#Z8T?6bhOriM*TT9Ea|5h(;G* zs3dW*i`Ed(DBTvtGkrj_9(_x&-;{2Bx@HHvMtu?fNFuoyH3>8pqc@9NTImjy<Ul;V z702zIy`8UQA2MiQHb!<)+NC8F&nf_shE^JxWJh<bU2^K7c#Q!5V;=GUqH%L$G^_gs zd-NE!CUMQRo-$9jEvx$tLREh^nJl!IM&+TDTHM^-8O;(8uhAYdg>3Z%H<@BfmL^l9 z?N+1K442E8E$p^IRRPcd`kMt?BI_WFBW;~be;W2yv<jcH7XCBYrHhxdX1DG14nDwd zS?wF4S?%@~(r`DQd;6V<Sid?eVEcUYLcjT+Kd<T8Nv=!*s;Zw29jqDXAx~F$^}P!R z4f(0qS+i*-ipysjxUv${($Kp#egxp%x0efG3_HCzCnR)?W|Y)0%+<iAVV^~Nd+dwg zj#CY$W%<~b&n_w9Lc=v-67=Yqv)H8Ru~VlfO;5{WW8>ly^l1t4W7rWnBO@o!X@^LJ zG=^r--8~~GXvT$x(T%c3GxzSExM2^~ObFACiyRj@_kaEbs-Nr5bD29MueO8lHn-|( z=A#g{HLrY3a7cB)KRsT5(R*+2o|)%U&sx4cp&j-5$5(sI3vX-M_<Il4hZhFSee={0 zSJ(b?bHjtfn=9t{m<yJ_cfa4wV{@*K{QH~*k1K8lFYZ03I^f;=H+?d6&e%^*`nuPQ z-@j?s?&N!$eu^6UkBp}(hdfibrs3XcPZaFlG5(W|9>E{&j{KV&bWYZtJg9^mbY-Xa zn!Kv<kv^5ZeI6Qm^5eR`qvr|nm%Nuh`$A>gi=L)I-(>xKvgTXcrlboqvwyC8;f5|+ zAMX8JX~xxuMm=!%``_i%ugLc_hrIUKh;_gIYu8I>=AZI<*QdvZ_v>CA@%YgdBWoV{ z+_8Vj)WjVdeJ<5!w?C8m-J_??Q`&ECo>lX5`stg!b8<AR6>gf<@}*nkZZbDFALYuT z`$`r+I&1&Yn{i+J`ft9C;{W+N040XbhJ{7ks>I_vm3WKG$h`hwWKQ$+zN2|YX(A=N z3QIGXip;cs!U+tEy-ps-m6|7x9a~mb7P1UxMW<#V*rkj$m1dK*X+lGTG{dR0yzd~F z-Tk=<YgWsKc4s^3@oJeG4p8Rhwpu1*8dhdM*!=p7@|*#q`)7YywbyUp7Jg&&fuKif zd?TA{-yivihcWR5`7FCI>&4br7n~m3ukqo_d!AkO@R0YL9N*4t?RfREIs0;gzklfJ zNdHr%59yEm>+u6I7Y+^=rlx;-we}Z}ANZ#NLfho(kCUDW9QFBt0UK7gz4r5q$=~|E zcyYt0jd_Q&=e%V4xLuPt>)hCq9QK^;-PNlO&v<LZJJoqVe7`#8W!=Or*Qbm>m2@C& z>fDuvRlnC<YMhq#@|)2uRgL|x9o1G>pBVMnjRzJ?d~l=hg_9wcUw&Ttee_F<4o;Qr znzd`*&M8AfpX=jy-Mn?sYF;t=N1a<z(T2hLkjG|>TJBN9ML)b@%(PI^l%B8Nr>Wkf ziSbav)w{cU%47<7J&ndy*2vZbl29O%k{omCtwN!ivDo@>7PF=Ygfg%%jDXR^(<`fp z){vaN7p+N@t!Yn?0kvk7W`ql|o9wQDzmm45FJT$3P-(neUF1rQH&v<?3b~s{LwDgi zy+Cl=teiz-{LlXysGeP?YOZ^0N$ufb|JtDI`9c5WgKgz2SxuOK9{<RJu%}ud-52HO z5wvQun+Nm4kVk6zdcE`HcQKjoxDOXzeYJPNOW#Gk*EH<vrjhew(%ZJDZazMKVx&Q7 zHop*d=GB+$8{H>45?YQ3=L1i_pYuwO7jJws;M<$AUImZC3-rM%_M7Ad)XX7<pEPN0 z@6(g7=Cphja~r{$($LYGk(dzHsiAQtr7KMNOY(4>o->jSjl{Cjb;2H3Vk!*@9i$16 zfc<W5DUr^VLWgJuQ@gyce^)y#V{EL2%PYZ`9acD(rqEE0W`c8R3e$v!X|Xto#s3D1 z{+*G&<o5QV((j{wnK@v@b5AT^p!spzOZMRl+kby7>G{3CZ{EgEE>GF^w{061g)Kcj zHFw2NFE2ZhcK(;<XVwI4c&^4+_s-Ili-&zWaMG7**(1W^N9%))Pd}Y^&r=^x98<5_ zGv}T|x*xnIM?5~}r4f-kTc%s5ep{nH@N{8%_RFiww=N1UOKN&*cW%_v`hZYnpzm`p z{qXQ;|L>!p%<)|`$KCMUzzMS-y!PVHo7|2K_^3WTq0UxSKd~ik)67?HzPPf8oB68$ z$;W$)7{X+3T$DfIz>GeglQJChZoFXhQtmiYoss$T8&M1HsxDKUzk2YMs>gnR?bHLG zzS!S%-=ufnYE?czR8!|sd!mjl>s$Mk(@1w}s<&&Zw~_YC6xB~_s-LLxnfGDo&-tcj zhow~dzMlM$<Grn>|M&7+{cm&}9#_UZCU`g4+n(?r_pg1jz_Vq&+wNNw_T01H?@e}p zc<qK06TctwOKaw%WA<#Bc6{;A9iN?yikkb<__X}r1B<4dIJxUf_wp}7?a|NqlolNL zz0WNF{DzJX<G$@Zm!0+F;#IHi>UVtfgyF#l4O{!H8?MfI{#shV^&uxd^=q5`a&cUk z=grl3w|}>!uxHBEw=d6beEWx^nhrL!$J&99jqIQN$w0U5m#Z$ych9@>&o7Q={$!Zm zI6Li)-SQEA92-AvRc@%<_ryDYpD^aq%1b-TzFoG3`LJNhp)=#xU5xFsb6mlIf(zrm z{y0E!X=j4s_*`v7adJS<#e2QBt^eqgv?;n%0qHwRFZ7xC;G>r3UOck}UV%q!V3xds zSGLC}ySQ!e?(PsM;I7^g>Mc4_g-2hv^wiM4n%-p5%D!HixTQdN%9Rv{`e=GlrDtEy z*@oOATnM-vgIC`>IEFiU846JU;AMtshDctdf0q}bvm=(mY>JTeak1n_5FKd4BEv$% zCWMWr5I0OSj*40;YO1&Xzn)0wA12~-m-0JsMe|ot?n8H#oWJ{{ZxbGS{pF9o`ye!W z;L!&U%uIB@&S$(d`oWyz@#i%seBWwb-hcnmAHOl-t8~V{eYsdy{Oq;({lfka4uA4% z)Z^3kr7SpIwq)q4)KS4d4&6{b)8V#ffOgA|k2U-6Uv@qF4CB3NQFzq!HDl_|J@jZu z@Y0*#rtjLftN3E+m2HbW#!6PdS{8#~Ws)fGfohq|;avCrWM!0#JEDQT6n80nf8H4L z<*bVX>JrX+%2z#g-O|rEuD_=GCGs7^6xCZ1dsg|5yzk8cZ=BNpbl};nifiA!8*;Q~ z_=v6C3;!J+#HYafsaXxom%Dm@d^YCkod>_y+)JbmR>WyyHB+`kZ;4tH>5M>fOobsu zu6t)OEG%7`KNhuPaiErqLp1YPQdQDaXlV!<ZnH4a9VwQa{NRiGlcybi$eQ10>9&QB z-{UE4IQ!AFb1wwt4;*-9Qu>J0FM0<CJ@QIcMoiPu<jpM?{$9E1Z_(_SalZu3Y&agd z*N}61obi0>+p(2d_r5TD@2@wHzrA&p$G#`eKKavW?{JH;=jX8-j=c6rP3EDgD{GHE z-1kD-KfbJt{hWQZX~2oYVWIEXo8J4)I^)osN5hVO_U);afB&jN`OLA}C?U2w;-Aa= zy%llo)Y8N6?@HP@(0c59nJsVE4;ute{iv`nKKtP9lPk|nR6AaeUzd5j|Abu6yDL_i zX6QV&j+i$rb5Oy}hlgroPFrU5krjIksTp6gbCl|V$*+tXeRXezx3cednQb%vv4O4M z2X}M=?&zP+t;!$Rf7^chza>i1AS^+YBJMuOyNnAB#eP~U{^z*K)iSob0!llOp0oo| ziaU^PHGZjQy*0hUA9G9`aJX&ks-g+QlH1O#|L*3iH{Wp!9~{{D-PX_3S}NVRX@9>E zHs>zil#vmaD_`Hee&Vz}ku!_a4u`5H7Tq{?>OK84173OY?74gInX>1d_clEK&GcW2 z&Ne+h`Ahc?T6d&R_{Z2qr>e52Zk?WXuiAg_jB}4ZrI|a;lDqq(17E!Hch%-u`^-`P z6L;-?aQ$m*UrU}fcxLZC+N!U5PRuQdZw!C?-berT){cSK-G|LwG;+hqQNPqY{nS5p zUhpbi^=WwQ#_fBJjrR>0_`J5)-_qp$r#!iF-^m}M6<l1vhHH`E*S(y$a_OjEi)Go7 z%ZhJKe$sP>?`4_prXO>0+WA?R+%HuPcazDVUo9H}egytmxG(?Dh<JQFdN_~C{AAde z$e3=v_|N>Tre9|VUpIwnkQdG|E%bd|CbrxDMyAuK$q)7=zwfS*BkQh9wBe!HDq+qy zPscvcvAI66tn8Kb=f3avPUi0ZZ|{C{q1)Dkg2=4vZw5URocwIZwo4J~B65dL{p!uJ zqdwUCsmHs`qv|jAuPwijqKv*W^rMd(iq=;9#V^dY<{o`%)0lM^Hcq%#y;t}+d&9D2 zU!NQ92&{R`uE<E+v^8Mj<kfHewEe;L0qbY1Sh#2UZwtcmCk{^ghb8%|+#fWPF62&+ zzj5Pez|{M{-x8hh(^BTSm#4nHzjs~6r5m4Y8&!RF@XT%L2ZJ`0?$}m1z>&6Y^;^|5 zc5MBpapg;QzxbX<!?fldbxomusnMep^$v65=X_9HM~?7Q;DhrHjz2Ag{u*{}!B;C* z?$^AWzvkW>>wDJ^u%~5(u2w97Lz|29OWMc&r$kV0-J`%;PF=TDzf20^JfQ6%4^?$L zb5&pq*mWlBEuPqRmHn^*?Chur?W6GdsMp1afzbo@UyN#hT=zxGqZ)m;?yAtKnwTyA zRd?asKNY`anu#;@RGe~4zbG<n4XhgKTz8Aixy83FyoLFTP1&XuxBE9mb=9KdH@}&= zdDt(3iMy5>kG#RJt82-6b{GFH8@qQ_g7=cwl{@D?xNUCHzv2eob85|vPn);gA0F?1 zbzA+xmD_HVuFL(0-;ixyOK-m5x%ruoJ>K2FH{m7kl5e)Qo@*_8-Ep>|&-?BBu8&>w z%z#xj<-g3!a$7iPl*iuJKDfT}=2Jbn3-u1RAY<!lw<PSr6T4X95xUwf5>?|wC8_%# zfB5z%$A`D7NS4OGYj*Nb@6Lj!3<}bz0`8$|F;0&R)rLmm{5fLo9~7iE`F!@h^}D}_ z*s)3R>bxILb$2mXt`w_f%-@eB)IRKav~B<6UoEOpzj@Ddn$kAg$<O|kV$R{$gk||1 zJL<pZ^!cB5hgEp)F8=<PxFPoxRE+xk`Sr1(^WE$>wiG@2%9p(w`^Wod$9{O>o~wPo zj(+}Z;hWlZzm=c==83PK{{EC<Meg?Li)(iH-?!_-y^DLN{&e%6v5ier{@J#$=gOz! z<7f7;md&nH#C~_N|1>@&V*0s<U%%eg^T=wY$CNfrxO~EtKKv_r8=w4c+Mp+6LM-=w zz18dWQ^k|#_bd)y?U=XXsRN(98+Yj&+nUWg6Q2Kh*z4Lo{ZH(g@lA+&6xa4f`Oh;d v_gp*H_otfXH$OPk`_;P*ul;&_Vb832eWt(n%g!~Q=UWFKduOuqhUfnPfVFR9 literal 0 HcmV?d00001 diff --git a/source/support/transform/labbuilderconfig-schema-transformtomd.xsl b/source/support/transform/labbuilderconfig-schema-transformtomd.xsl new file mode 100644 index 00000000..8da86023 --- /dev/null +++ b/source/support/transform/labbuilderconfig-schema-transformtomd.xsl @@ -0,0 +1,95 @@ +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> +<xsl:output method="text" omit-xml-declaration="yes" indent="no"/> +<xsl:variable name="Title1" select="1" /> +<xsl:variable name="Title2" select="1" /> +<xsl:variable name="Title3" select="1" /> +<xsl:variable name="Title4" select="1" /> +<xsl:variable name="Title5" select="1" /> +<xsl:template match="/"> +# LabBuilder Configuration XML File Format +> labbuilderconfig xmlns="labbuilderconfig" + <xsl:for-each select="xs:schema/xs:element/xs:complexType"> + <xsl:for-each select="xs:attribute"> + <xsl:variable name="Title1" select="position()" /> +### <xsl:value-of select="$Title1"/>.0a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute +> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:all/xs:element|xs:sequence/xs:element"> + <xsl:variable name="Title1" select="position()" /> +### <xsl:value-of select="$Title1"/>.0e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element +<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:attribute"> + <xsl:variable name="Title2" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute +> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:all/xs:element|xs:complexType/xs:sequence/xs:element"> + <xsl:variable name="Title2" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element +<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:attribute"> + <xsl:variable name="Title3" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute +> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:all/xs:element|xs:complexType/xs:sequence/xs:element"> + <xsl:variable name="Title3" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element +<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:attribute"> + <xsl:variable name="Title4" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>.<xsl:value-of select="$Title4"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute +> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:all/xs:element|xs:complexType/xs:sequence/xs:element"> + <xsl:variable name="Title4" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>.<xsl:value-of select="$Title4"/>e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element +<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + <xsl:for-each select="xs:complexType/xs:attribute"> + <xsl:variable name="Title5" select="position()" /> +### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>.<xsl:value-of select="$Title4"/>.<xsl:value-of select="$Title5"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute +> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> + <xsl:for-each select="xs:annotation"> + <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> + </xsl:for-each> + </xsl:for-each> + </xsl:for-each> + </xsl:for-each> + </xsl:for-each> + </xsl:for-each> + </xsl:for-each> +</xsl:template> +</xsl:stylesheet> \ No newline at end of file diff --git a/source/template/labbuilderconfig-template.xml b/source/template/labbuilderconfig-template.xml new file mode 100644 index 00000000..b8d60925 --- /dev/null +++ b/source/template/labbuilderconfig-template.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="utf-8"?> + +<labbuilderconfig xmlns="labbuilderconfig" + name="" + version=""> + + <description></description> + + <settings labpath="" /> + + <resources> + <msu name="WMF5.1-WS2012R2-W81" + url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> + <msu name="WMF5.0-WS2012-W8" + url="https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/W2K12-KB3134759-x64.msu" /> + <msu name="WMF5.0-WS2008R2-W7" + url="https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win7AndW2K8R2-KB3134760-x64.msu" /> + </resources> + + <switches> + <switch name="External" type="External" > + <adapters> + <adapter name="Cluster" macaddress="00155D010701" vlan="5"/> + <adapter name="Management" macaddress="00155D010702" vlan="6"/> + <adapter name="SMB" macaddress="00155D010703" /> + <adapter name="LM" macaddress="00155D010704" /> + </adapters> + </switch> + </switches> + + <templatevhds isopath="ISOFiles" + vhdpath="VHDFiles" > + <templatevhd name="Windows Server 2012 R2 Datacenter Full" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Full.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTER" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2012 R2 Datacenter Core" + iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + edition="Windows Server 2012 R2 SERVERDATACENTERCORE" + ostype="Server" + packages="WMF5.1-WS2012R2-W81" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + <templatevhd name="Windows Server 2016 TP5 Datacenter FULL" + iso="14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" + vhd="Windows Server 2016 TP5 Datacenter Full.vhdx" + edition="Windows Server 2016 Technical Preview 4 SERVERDATACENTER" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="40GB" /> + <templatevhd name="Windows Server 2016 TP5 Datacenter CORE" + iso="14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" + vhd="Windows Server 2016 TP5 Datacenter Core.vhdx" + edition="Windows Server 2016 Technical Preview 4 SERVERDATACENTERCORE" + ostype="Server" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + <templatevhd name="Windows 10 Enterprise" + iso="10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise" + vhd="Windows 10 Enterprise.vhdx" + edition="Windows 10 Enterprise" + ostype="Client" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + <templatevhd name="Nano Server 2016 TP5 Basic" + iso="14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO" + url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" + vhd="Nano Server 2016 TP5 Basic.vhdx" + ostype="Nano" + packages="Microsoft-NanoServer-Guest-Package.cab" + vhdformat="vhdx" + vhdtype="dynamic" + generation="2" + vhdsize="25GB" /> + </templatevhds> + + <templates> + <template name="Pester Windows Server 2012 R2 Datacenter Full" + vhd="Server 2012 R2 Datacenter Full.vhdx" + sourcevhd="VhdFiles\Windows Server 2012 R2 Datacenter Full.vhdx" + memorystartupbytes="1GB" + dynamicmemoryenabled="Y" + processorcount="1" + administratorpassword="None" + productkey="AAAAA-AAAAA-AAAAA-AAAAA-AAAAA" + timezone="Pacific Standard Time" + ostype="Server" + integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" /> + <template name="Windows Server 2012 R2 Datacenter Core" + vhd="Windows Server 2012 R2 Datacenter Core.vhdx" + templatevhd="Windows Server 2012 R2 Datacenter Core" + memorystartupbytes="1GB" + dynamicmemoryenabled="N" + processorcount="1" + exposevirtualizationextensions="Y" + administratorpassword="None" + productkey="BBBBB-BBBBB-BBBBB-BBBBB-BBBBB" + timezone="Pacific Standard Time" + ostype="Server" + integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" /> + <template name="Windows 10 Enterprise" + templatevhd="Windows 10 Enterprise" + memorystartupbytes="2GB" + processorcount="1" + administratorpassword="None" + productkey="CCCCC-CCCCC-CCCCC-CCCCC-CCCCC" + timezone="Pacific Standard Time" + ostype="Client" + integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" /> + <template name="Pester Nano Server" + vhd="MyNanoServerVHD.vhdx" + sourcevhd="VhdFiles\Nano Server 2016 TP5 Basic.vhdx" + memorystartupbytes="1GB" + processorcount="1" + administratorpassword="None" + timezone="Pacific Standard Time" + ostype="Nano" + integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" + packages="Storage"/> + </templates> + + <vms> + </vms> + +</labbuilderconfig> From 7f1c50a65e5d80e1996cdb29e08b719deee6648d Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Sun, 19 Apr 2020 18:21:27 +1200 Subject: [PATCH 05/14] Moved all files --- .../Assert-LabValidConfigurationXMLSchema.ps1 | 0 source/{private => Private}/Assert-LabValidIpAddress.ps1 | 0 source/{private => Private}/ConvertTo-LabAbsolutePath.ps1 | 0 source/{private => Private}/Copy-LabOdjFile.ps1 | 0 source/{private => Private}/Enable-LabWSMan.ps1 | 0 source/{private => Private}/Get-LabBuilderModulePath.ps1 | 0 source/{private => Private}/Get-LabCertificatePsFileContent.ps1 | 0 source/{private => Private}/Get-LabDSCNetworkingConfig.ps1 | 0 source/{private => Private}/Get-LabIntegrationServiceName.ps1 | 0 source/{private => Private}/Get-LabManagementSwitchName.ps1 | 0 source/{private => Private}/Get-LabModulesInDSCConfig.ps1 | 0 source/{private => Private}/Get-LabNextIpAddress.ps1 | 0 source/{private => Private}/Get-LabNextMacAddress.ps1 | 0 source/{private => Private}/Get-LabUnattendFileContent.ps1 | 0 source/{private => Private}/Get-LabVMManagementIPAddress.ps1 | 0 source/{private => Private}/Initialize-LabBootVHD.ps1 | 0 source/{private => Private}/Initialize-LabDSC.ps1 | 0 source/{private => Private}/Initialize-LabManagementSwitch.ps1 | 0 source/{private => Private}/Initialize-LabVHD.ps1 | 0 source/{private => Private}/Initialize-LabVMPath.ps1 | 0 source/{private => Private}/Install-LabHyperV.ps1 | 0 source/{private => Private}/Install-LabPackageProvider.ps1 | 0 source/{private => Private}/Invoke-LabDownloadAndUnzipFile.ps1 | 0 source/{private => Private}/Invoke-LabDownloadResourceModule.ps1 | 0 source/{private => Private}/New-LabCredential.ps1 | 0 source/{private => Private}/New-LabException.ps1 | 0 source/{private => Private}/New-LabHostSelfSignedCertificate.ps1 | 0 source/{private => Private}/New-LabVMInitializationFile.ps1 | 0 source/{private => Private}/Recieve-LabSelfSignedCertificate.ps1 | 0 source/{private => Private}/Register-LabPackageSource.ps1 | 0 source/{private => Private}/Request-LabSelfSignedCertificate.ps1 | 0 source/{private => Private}/Set-LabDSC.ps1 | 0 source/{private => Private}/Set-LabModulesInDSCConfig.ps1 | 0 source/{private => Private}/Set-LabSwitchAdapter.ps1 | 0 source/{private => Private}/Start-LabDSC.ps1 | 0 source/{private => Private}/Update-LabDSC.ps1 | 0 source/{private => Private}/Update-LabVMDataDisk.ps1 | 0 source/{private => Private}/Update-LabVMDvdDrive.ps1 | 0 source/{private => Private}/Update-LabVMIntegrationService.ps1 | 0 source/{private => Private}/Wait-LabVMInitializationComplete.ps1 | 0 source/{private => Private}/Wait-LabVMOff.ps1 | 0 source/{private => Private}/Wait-LabVMStarted.ps1 | 0 source/{private => Private}/Write-LabMessage.ps1 | 0 source/{public => Public}/Connect-LabVm.ps1 | 0 source/{public => Public}/Disconnect-LabVm.ps1 | 0 source/{public => Public}/Get-Lab.ps1 | 0 source/{public => Public}/Get-LabResourceIso.ps1 | 0 source/{public => Public}/Get-LabResourceModule.ps1 | 0 source/{public => Public}/Get-LabResourceMsu.ps1 | 0 source/{public => Public}/Get-LabSwitch.ps1 | 0 source/{public => Public}/Get-LabVMTemplate.ps1 | 0 source/{public => Public}/Get-LabVm.ps1 | 0 source/{public => Public}/Get-LabVmTemplateVhd.ps1 | 0 source/{public => Public}/Initialize-LabResourceIso.ps1 | 0 source/{public => Public}/Initialize-LabResourceModule.ps1 | 0 source/{public => Public}/Initialize-LabResourceMsu.ps1 | 0 source/{public => Public}/Initialize-LabSwitch.ps1 | 0 source/{public => Public}/Initialize-LabVm.ps1 | 0 source/{public => Public}/Initialize-LabVmTemplate.ps1 | 0 source/{public => Public}/Initialize-LabVmTemplateVhd.ps1 | 0 source/{public => Public}/Install-Lab.ps1 | 0 source/{public => Public}/Install-LabVm.ps1 | 0 source/{public => Public}/New-Lab.ps1 | 0 source/{public => Public}/Remove-LabSwitch.ps1 | 0 source/{public => Public}/Remove-LabVMTemplate.ps1 | 0 source/{public => Public}/Remove-LabVm.ps1 | 0 source/{public => Public}/Remove-LabVmTemplateVhd.ps1 | 0 source/{public => Public}/Start-Lab.ps1 | 0 source/{public => Public}/Stop-Lab.ps1 | 0 source/{public => Public}/Uninstall-Lab.ps1 | 0 source/{public => Public}/Update-Lab.ps1 | 0 71 files changed, 0 insertions(+), 0 deletions(-) rename source/{private => Private}/Assert-LabValidConfigurationXMLSchema.ps1 (100%) rename source/{private => Private}/Assert-LabValidIpAddress.ps1 (100%) rename source/{private => Private}/ConvertTo-LabAbsolutePath.ps1 (100%) rename source/{private => Private}/Copy-LabOdjFile.ps1 (100%) rename source/{private => Private}/Enable-LabWSMan.ps1 (100%) rename source/{private => Private}/Get-LabBuilderModulePath.ps1 (100%) rename source/{private => Private}/Get-LabCertificatePsFileContent.ps1 (100%) rename source/{private => Private}/Get-LabDSCNetworkingConfig.ps1 (100%) rename source/{private => Private}/Get-LabIntegrationServiceName.ps1 (100%) rename source/{private => Private}/Get-LabManagementSwitchName.ps1 (100%) rename source/{private => Private}/Get-LabModulesInDSCConfig.ps1 (100%) rename source/{private => Private}/Get-LabNextIpAddress.ps1 (100%) rename source/{private => Private}/Get-LabNextMacAddress.ps1 (100%) rename source/{private => Private}/Get-LabUnattendFileContent.ps1 (100%) rename source/{private => Private}/Get-LabVMManagementIPAddress.ps1 (100%) rename source/{private => Private}/Initialize-LabBootVHD.ps1 (100%) rename source/{private => Private}/Initialize-LabDSC.ps1 (100%) rename source/{private => Private}/Initialize-LabManagementSwitch.ps1 (100%) rename source/{private => Private}/Initialize-LabVHD.ps1 (100%) rename source/{private => Private}/Initialize-LabVMPath.ps1 (100%) rename source/{private => Private}/Install-LabHyperV.ps1 (100%) rename source/{private => Private}/Install-LabPackageProvider.ps1 (100%) rename source/{private => Private}/Invoke-LabDownloadAndUnzipFile.ps1 (100%) rename source/{private => Private}/Invoke-LabDownloadResourceModule.ps1 (100%) rename source/{private => Private}/New-LabCredential.ps1 (100%) rename source/{private => Private}/New-LabException.ps1 (100%) rename source/{private => Private}/New-LabHostSelfSignedCertificate.ps1 (100%) rename source/{private => Private}/New-LabVMInitializationFile.ps1 (100%) rename source/{private => Private}/Recieve-LabSelfSignedCertificate.ps1 (100%) rename source/{private => Private}/Register-LabPackageSource.ps1 (100%) rename source/{private => Private}/Request-LabSelfSignedCertificate.ps1 (100%) rename source/{private => Private}/Set-LabDSC.ps1 (100%) rename source/{private => Private}/Set-LabModulesInDSCConfig.ps1 (100%) rename source/{private => Private}/Set-LabSwitchAdapter.ps1 (100%) rename source/{private => Private}/Start-LabDSC.ps1 (100%) rename source/{private => Private}/Update-LabDSC.ps1 (100%) rename source/{private => Private}/Update-LabVMDataDisk.ps1 (100%) rename source/{private => Private}/Update-LabVMDvdDrive.ps1 (100%) rename source/{private => Private}/Update-LabVMIntegrationService.ps1 (100%) rename source/{private => Private}/Wait-LabVMInitializationComplete.ps1 (100%) rename source/{private => Private}/Wait-LabVMOff.ps1 (100%) rename source/{private => Private}/Wait-LabVMStarted.ps1 (100%) rename source/{private => Private}/Write-LabMessage.ps1 (100%) rename source/{public => Public}/Connect-LabVm.ps1 (100%) rename source/{public => Public}/Disconnect-LabVm.ps1 (100%) rename source/{public => Public}/Get-Lab.ps1 (100%) rename source/{public => Public}/Get-LabResourceIso.ps1 (100%) rename source/{public => Public}/Get-LabResourceModule.ps1 (100%) rename source/{public => Public}/Get-LabResourceMsu.ps1 (100%) rename source/{public => Public}/Get-LabSwitch.ps1 (100%) rename source/{public => Public}/Get-LabVMTemplate.ps1 (100%) rename source/{public => Public}/Get-LabVm.ps1 (100%) rename source/{public => Public}/Get-LabVmTemplateVhd.ps1 (100%) rename source/{public => Public}/Initialize-LabResourceIso.ps1 (100%) rename source/{public => Public}/Initialize-LabResourceModule.ps1 (100%) rename source/{public => Public}/Initialize-LabResourceMsu.ps1 (100%) rename source/{public => Public}/Initialize-LabSwitch.ps1 (100%) rename source/{public => Public}/Initialize-LabVm.ps1 (100%) rename source/{public => Public}/Initialize-LabVmTemplate.ps1 (100%) rename source/{public => Public}/Initialize-LabVmTemplateVhd.ps1 (100%) rename source/{public => Public}/Install-Lab.ps1 (100%) rename source/{public => Public}/Install-LabVm.ps1 (100%) rename source/{public => Public}/New-Lab.ps1 (100%) rename source/{public => Public}/Remove-LabSwitch.ps1 (100%) rename source/{public => Public}/Remove-LabVMTemplate.ps1 (100%) rename source/{public => Public}/Remove-LabVm.ps1 (100%) rename source/{public => Public}/Remove-LabVmTemplateVhd.ps1 (100%) rename source/{public => Public}/Start-Lab.ps1 (100%) rename source/{public => Public}/Stop-Lab.ps1 (100%) rename source/{public => Public}/Uninstall-Lab.ps1 (100%) rename source/{public => Public}/Update-Lab.ps1 (100%) diff --git a/source/private/Assert-LabValidConfigurationXMLSchema.ps1 b/source/Private/Assert-LabValidConfigurationXMLSchema.ps1 similarity index 100% rename from source/private/Assert-LabValidConfigurationXMLSchema.ps1 rename to source/Private/Assert-LabValidConfigurationXMLSchema.ps1 diff --git a/source/private/Assert-LabValidIpAddress.ps1 b/source/Private/Assert-LabValidIpAddress.ps1 similarity index 100% rename from source/private/Assert-LabValidIpAddress.ps1 rename to source/Private/Assert-LabValidIpAddress.ps1 diff --git a/source/private/ConvertTo-LabAbsolutePath.ps1 b/source/Private/ConvertTo-LabAbsolutePath.ps1 similarity index 100% rename from source/private/ConvertTo-LabAbsolutePath.ps1 rename to source/Private/ConvertTo-LabAbsolutePath.ps1 diff --git a/source/private/Copy-LabOdjFile.ps1 b/source/Private/Copy-LabOdjFile.ps1 similarity index 100% rename from source/private/Copy-LabOdjFile.ps1 rename to source/Private/Copy-LabOdjFile.ps1 diff --git a/source/private/Enable-LabWSMan.ps1 b/source/Private/Enable-LabWSMan.ps1 similarity index 100% rename from source/private/Enable-LabWSMan.ps1 rename to source/Private/Enable-LabWSMan.ps1 diff --git a/source/private/Get-LabBuilderModulePath.ps1 b/source/Private/Get-LabBuilderModulePath.ps1 similarity index 100% rename from source/private/Get-LabBuilderModulePath.ps1 rename to source/Private/Get-LabBuilderModulePath.ps1 diff --git a/source/private/Get-LabCertificatePsFileContent.ps1 b/source/Private/Get-LabCertificatePsFileContent.ps1 similarity index 100% rename from source/private/Get-LabCertificatePsFileContent.ps1 rename to source/Private/Get-LabCertificatePsFileContent.ps1 diff --git a/source/private/Get-LabDSCNetworkingConfig.ps1 b/source/Private/Get-LabDSCNetworkingConfig.ps1 similarity index 100% rename from source/private/Get-LabDSCNetworkingConfig.ps1 rename to source/Private/Get-LabDSCNetworkingConfig.ps1 diff --git a/source/private/Get-LabIntegrationServiceName.ps1 b/source/Private/Get-LabIntegrationServiceName.ps1 similarity index 100% rename from source/private/Get-LabIntegrationServiceName.ps1 rename to source/Private/Get-LabIntegrationServiceName.ps1 diff --git a/source/private/Get-LabManagementSwitchName.ps1 b/source/Private/Get-LabManagementSwitchName.ps1 similarity index 100% rename from source/private/Get-LabManagementSwitchName.ps1 rename to source/Private/Get-LabManagementSwitchName.ps1 diff --git a/source/private/Get-LabModulesInDSCConfig.ps1 b/source/Private/Get-LabModulesInDSCConfig.ps1 similarity index 100% rename from source/private/Get-LabModulesInDSCConfig.ps1 rename to source/Private/Get-LabModulesInDSCConfig.ps1 diff --git a/source/private/Get-LabNextIpAddress.ps1 b/source/Private/Get-LabNextIpAddress.ps1 similarity index 100% rename from source/private/Get-LabNextIpAddress.ps1 rename to source/Private/Get-LabNextIpAddress.ps1 diff --git a/source/private/Get-LabNextMacAddress.ps1 b/source/Private/Get-LabNextMacAddress.ps1 similarity index 100% rename from source/private/Get-LabNextMacAddress.ps1 rename to source/Private/Get-LabNextMacAddress.ps1 diff --git a/source/private/Get-LabUnattendFileContent.ps1 b/source/Private/Get-LabUnattendFileContent.ps1 similarity index 100% rename from source/private/Get-LabUnattendFileContent.ps1 rename to source/Private/Get-LabUnattendFileContent.ps1 diff --git a/source/private/Get-LabVMManagementIPAddress.ps1 b/source/Private/Get-LabVMManagementIPAddress.ps1 similarity index 100% rename from source/private/Get-LabVMManagementIPAddress.ps1 rename to source/Private/Get-LabVMManagementIPAddress.ps1 diff --git a/source/private/Initialize-LabBootVHD.ps1 b/source/Private/Initialize-LabBootVHD.ps1 similarity index 100% rename from source/private/Initialize-LabBootVHD.ps1 rename to source/Private/Initialize-LabBootVHD.ps1 diff --git a/source/private/Initialize-LabDSC.ps1 b/source/Private/Initialize-LabDSC.ps1 similarity index 100% rename from source/private/Initialize-LabDSC.ps1 rename to source/Private/Initialize-LabDSC.ps1 diff --git a/source/private/Initialize-LabManagementSwitch.ps1 b/source/Private/Initialize-LabManagementSwitch.ps1 similarity index 100% rename from source/private/Initialize-LabManagementSwitch.ps1 rename to source/Private/Initialize-LabManagementSwitch.ps1 diff --git a/source/private/Initialize-LabVHD.ps1 b/source/Private/Initialize-LabVHD.ps1 similarity index 100% rename from source/private/Initialize-LabVHD.ps1 rename to source/Private/Initialize-LabVHD.ps1 diff --git a/source/private/Initialize-LabVMPath.ps1 b/source/Private/Initialize-LabVMPath.ps1 similarity index 100% rename from source/private/Initialize-LabVMPath.ps1 rename to source/Private/Initialize-LabVMPath.ps1 diff --git a/source/private/Install-LabHyperV.ps1 b/source/Private/Install-LabHyperV.ps1 similarity index 100% rename from source/private/Install-LabHyperV.ps1 rename to source/Private/Install-LabHyperV.ps1 diff --git a/source/private/Install-LabPackageProvider.ps1 b/source/Private/Install-LabPackageProvider.ps1 similarity index 100% rename from source/private/Install-LabPackageProvider.ps1 rename to source/Private/Install-LabPackageProvider.ps1 diff --git a/source/private/Invoke-LabDownloadAndUnzipFile.ps1 b/source/Private/Invoke-LabDownloadAndUnzipFile.ps1 similarity index 100% rename from source/private/Invoke-LabDownloadAndUnzipFile.ps1 rename to source/Private/Invoke-LabDownloadAndUnzipFile.ps1 diff --git a/source/private/Invoke-LabDownloadResourceModule.ps1 b/source/Private/Invoke-LabDownloadResourceModule.ps1 similarity index 100% rename from source/private/Invoke-LabDownloadResourceModule.ps1 rename to source/Private/Invoke-LabDownloadResourceModule.ps1 diff --git a/source/private/New-LabCredential.ps1 b/source/Private/New-LabCredential.ps1 similarity index 100% rename from source/private/New-LabCredential.ps1 rename to source/Private/New-LabCredential.ps1 diff --git a/source/private/New-LabException.ps1 b/source/Private/New-LabException.ps1 similarity index 100% rename from source/private/New-LabException.ps1 rename to source/Private/New-LabException.ps1 diff --git a/source/private/New-LabHostSelfSignedCertificate.ps1 b/source/Private/New-LabHostSelfSignedCertificate.ps1 similarity index 100% rename from source/private/New-LabHostSelfSignedCertificate.ps1 rename to source/Private/New-LabHostSelfSignedCertificate.ps1 diff --git a/source/private/New-LabVMInitializationFile.ps1 b/source/Private/New-LabVMInitializationFile.ps1 similarity index 100% rename from source/private/New-LabVMInitializationFile.ps1 rename to source/Private/New-LabVMInitializationFile.ps1 diff --git a/source/private/Recieve-LabSelfSignedCertificate.ps1 b/source/Private/Recieve-LabSelfSignedCertificate.ps1 similarity index 100% rename from source/private/Recieve-LabSelfSignedCertificate.ps1 rename to source/Private/Recieve-LabSelfSignedCertificate.ps1 diff --git a/source/private/Register-LabPackageSource.ps1 b/source/Private/Register-LabPackageSource.ps1 similarity index 100% rename from source/private/Register-LabPackageSource.ps1 rename to source/Private/Register-LabPackageSource.ps1 diff --git a/source/private/Request-LabSelfSignedCertificate.ps1 b/source/Private/Request-LabSelfSignedCertificate.ps1 similarity index 100% rename from source/private/Request-LabSelfSignedCertificate.ps1 rename to source/Private/Request-LabSelfSignedCertificate.ps1 diff --git a/source/private/Set-LabDSC.ps1 b/source/Private/Set-LabDSC.ps1 similarity index 100% rename from source/private/Set-LabDSC.ps1 rename to source/Private/Set-LabDSC.ps1 diff --git a/source/private/Set-LabModulesInDSCConfig.ps1 b/source/Private/Set-LabModulesInDSCConfig.ps1 similarity index 100% rename from source/private/Set-LabModulesInDSCConfig.ps1 rename to source/Private/Set-LabModulesInDSCConfig.ps1 diff --git a/source/private/Set-LabSwitchAdapter.ps1 b/source/Private/Set-LabSwitchAdapter.ps1 similarity index 100% rename from source/private/Set-LabSwitchAdapter.ps1 rename to source/Private/Set-LabSwitchAdapter.ps1 diff --git a/source/private/Start-LabDSC.ps1 b/source/Private/Start-LabDSC.ps1 similarity index 100% rename from source/private/Start-LabDSC.ps1 rename to source/Private/Start-LabDSC.ps1 diff --git a/source/private/Update-LabDSC.ps1 b/source/Private/Update-LabDSC.ps1 similarity index 100% rename from source/private/Update-LabDSC.ps1 rename to source/Private/Update-LabDSC.ps1 diff --git a/source/private/Update-LabVMDataDisk.ps1 b/source/Private/Update-LabVMDataDisk.ps1 similarity index 100% rename from source/private/Update-LabVMDataDisk.ps1 rename to source/Private/Update-LabVMDataDisk.ps1 diff --git a/source/private/Update-LabVMDvdDrive.ps1 b/source/Private/Update-LabVMDvdDrive.ps1 similarity index 100% rename from source/private/Update-LabVMDvdDrive.ps1 rename to source/Private/Update-LabVMDvdDrive.ps1 diff --git a/source/private/Update-LabVMIntegrationService.ps1 b/source/Private/Update-LabVMIntegrationService.ps1 similarity index 100% rename from source/private/Update-LabVMIntegrationService.ps1 rename to source/Private/Update-LabVMIntegrationService.ps1 diff --git a/source/private/Wait-LabVMInitializationComplete.ps1 b/source/Private/Wait-LabVMInitializationComplete.ps1 similarity index 100% rename from source/private/Wait-LabVMInitializationComplete.ps1 rename to source/Private/Wait-LabVMInitializationComplete.ps1 diff --git a/source/private/Wait-LabVMOff.ps1 b/source/Private/Wait-LabVMOff.ps1 similarity index 100% rename from source/private/Wait-LabVMOff.ps1 rename to source/Private/Wait-LabVMOff.ps1 diff --git a/source/private/Wait-LabVMStarted.ps1 b/source/Private/Wait-LabVMStarted.ps1 similarity index 100% rename from source/private/Wait-LabVMStarted.ps1 rename to source/Private/Wait-LabVMStarted.ps1 diff --git a/source/private/Write-LabMessage.ps1 b/source/Private/Write-LabMessage.ps1 similarity index 100% rename from source/private/Write-LabMessage.ps1 rename to source/Private/Write-LabMessage.ps1 diff --git a/source/public/Connect-LabVm.ps1 b/source/Public/Connect-LabVm.ps1 similarity index 100% rename from source/public/Connect-LabVm.ps1 rename to source/Public/Connect-LabVm.ps1 diff --git a/source/public/Disconnect-LabVm.ps1 b/source/Public/Disconnect-LabVm.ps1 similarity index 100% rename from source/public/Disconnect-LabVm.ps1 rename to source/Public/Disconnect-LabVm.ps1 diff --git a/source/public/Get-Lab.ps1 b/source/Public/Get-Lab.ps1 similarity index 100% rename from source/public/Get-Lab.ps1 rename to source/Public/Get-Lab.ps1 diff --git a/source/public/Get-LabResourceIso.ps1 b/source/Public/Get-LabResourceIso.ps1 similarity index 100% rename from source/public/Get-LabResourceIso.ps1 rename to source/Public/Get-LabResourceIso.ps1 diff --git a/source/public/Get-LabResourceModule.ps1 b/source/Public/Get-LabResourceModule.ps1 similarity index 100% rename from source/public/Get-LabResourceModule.ps1 rename to source/Public/Get-LabResourceModule.ps1 diff --git a/source/public/Get-LabResourceMsu.ps1 b/source/Public/Get-LabResourceMsu.ps1 similarity index 100% rename from source/public/Get-LabResourceMsu.ps1 rename to source/Public/Get-LabResourceMsu.ps1 diff --git a/source/public/Get-LabSwitch.ps1 b/source/Public/Get-LabSwitch.ps1 similarity index 100% rename from source/public/Get-LabSwitch.ps1 rename to source/Public/Get-LabSwitch.ps1 diff --git a/source/public/Get-LabVMTemplate.ps1 b/source/Public/Get-LabVMTemplate.ps1 similarity index 100% rename from source/public/Get-LabVMTemplate.ps1 rename to source/Public/Get-LabVMTemplate.ps1 diff --git a/source/public/Get-LabVm.ps1 b/source/Public/Get-LabVm.ps1 similarity index 100% rename from source/public/Get-LabVm.ps1 rename to source/Public/Get-LabVm.ps1 diff --git a/source/public/Get-LabVmTemplateVhd.ps1 b/source/Public/Get-LabVmTemplateVhd.ps1 similarity index 100% rename from source/public/Get-LabVmTemplateVhd.ps1 rename to source/Public/Get-LabVmTemplateVhd.ps1 diff --git a/source/public/Initialize-LabResourceIso.ps1 b/source/Public/Initialize-LabResourceIso.ps1 similarity index 100% rename from source/public/Initialize-LabResourceIso.ps1 rename to source/Public/Initialize-LabResourceIso.ps1 diff --git a/source/public/Initialize-LabResourceModule.ps1 b/source/Public/Initialize-LabResourceModule.ps1 similarity index 100% rename from source/public/Initialize-LabResourceModule.ps1 rename to source/Public/Initialize-LabResourceModule.ps1 diff --git a/source/public/Initialize-LabResourceMsu.ps1 b/source/Public/Initialize-LabResourceMsu.ps1 similarity index 100% rename from source/public/Initialize-LabResourceMsu.ps1 rename to source/Public/Initialize-LabResourceMsu.ps1 diff --git a/source/public/Initialize-LabSwitch.ps1 b/source/Public/Initialize-LabSwitch.ps1 similarity index 100% rename from source/public/Initialize-LabSwitch.ps1 rename to source/Public/Initialize-LabSwitch.ps1 diff --git a/source/public/Initialize-LabVm.ps1 b/source/Public/Initialize-LabVm.ps1 similarity index 100% rename from source/public/Initialize-LabVm.ps1 rename to source/Public/Initialize-LabVm.ps1 diff --git a/source/public/Initialize-LabVmTemplate.ps1 b/source/Public/Initialize-LabVmTemplate.ps1 similarity index 100% rename from source/public/Initialize-LabVmTemplate.ps1 rename to source/Public/Initialize-LabVmTemplate.ps1 diff --git a/source/public/Initialize-LabVmTemplateVhd.ps1 b/source/Public/Initialize-LabVmTemplateVhd.ps1 similarity index 100% rename from source/public/Initialize-LabVmTemplateVhd.ps1 rename to source/Public/Initialize-LabVmTemplateVhd.ps1 diff --git a/source/public/Install-Lab.ps1 b/source/Public/Install-Lab.ps1 similarity index 100% rename from source/public/Install-Lab.ps1 rename to source/Public/Install-Lab.ps1 diff --git a/source/public/Install-LabVm.ps1 b/source/Public/Install-LabVm.ps1 similarity index 100% rename from source/public/Install-LabVm.ps1 rename to source/Public/Install-LabVm.ps1 diff --git a/source/public/New-Lab.ps1 b/source/Public/New-Lab.ps1 similarity index 100% rename from source/public/New-Lab.ps1 rename to source/Public/New-Lab.ps1 diff --git a/source/public/Remove-LabSwitch.ps1 b/source/Public/Remove-LabSwitch.ps1 similarity index 100% rename from source/public/Remove-LabSwitch.ps1 rename to source/Public/Remove-LabSwitch.ps1 diff --git a/source/public/Remove-LabVMTemplate.ps1 b/source/Public/Remove-LabVMTemplate.ps1 similarity index 100% rename from source/public/Remove-LabVMTemplate.ps1 rename to source/Public/Remove-LabVMTemplate.ps1 diff --git a/source/public/Remove-LabVm.ps1 b/source/Public/Remove-LabVm.ps1 similarity index 100% rename from source/public/Remove-LabVm.ps1 rename to source/Public/Remove-LabVm.ps1 diff --git a/source/public/Remove-LabVmTemplateVhd.ps1 b/source/Public/Remove-LabVmTemplateVhd.ps1 similarity index 100% rename from source/public/Remove-LabVmTemplateVhd.ps1 rename to source/Public/Remove-LabVmTemplateVhd.ps1 diff --git a/source/public/Start-Lab.ps1 b/source/Public/Start-Lab.ps1 similarity index 100% rename from source/public/Start-Lab.ps1 rename to source/Public/Start-Lab.ps1 diff --git a/source/public/Stop-Lab.ps1 b/source/Public/Stop-Lab.ps1 similarity index 100% rename from source/public/Stop-Lab.ps1 rename to source/Public/Stop-Lab.ps1 diff --git a/source/public/Uninstall-Lab.ps1 b/source/Public/Uninstall-Lab.ps1 similarity index 100% rename from source/public/Uninstall-Lab.ps1 rename to source/Public/Uninstall-Lab.ps1 diff --git a/source/public/Update-Lab.ps1 b/source/Public/Update-Lab.ps1 similarity index 100% rename from source/public/Update-Lab.ps1 rename to source/Public/Update-Lab.ps1 From b2bbfb179a198e0b484d8cc4b45128f8026ce108 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Sun, 19 Apr 2020 18:22:06 +1200 Subject: [PATCH 06/14] Moved remaining files --- .codecov.yml | 26 - .codecovio/CodeCovIo.psm1 | 345 -- .gitignore | 2 +- .vscode/analyzersettings.psd1 | 44 + .vscode/settings.json | 39 +- .vscode/tasks.json | 145 +- CHANGELOG.md | 662 +-- Deploy.PSDeploy.ps1 | 19 + GitVersion.yml | 24 + HISTORIC_CHANGELOG.md | 657 +++ LICENSE | 695 +-- PSScriptAnalyzerSettings.psd1 | 193 +- README.md | 415 +- RELEASENOTES.md | 169 - RequiredModules.psd1 | 18 + Resolve-Dependency.ps1 | 289 ++ Resolve-Dependency.psd1 | 10 + appveyor.yml | 9 - azure-pipelines.yml | 256 +- build.ps1 | 353 ++ build.yaml | 112 + psake.ps1 | 42 - psakefile.ps1 | 572 --- requirements.psd1 | 73 - src/LabBuilder.psd1 | 247 - src/LabBuilder.psm1 | 547 --- src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 | 146 - src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 | 233 - src/dsclibrary/DC_SECONDARY.DSC.ps1 | 142 - src/dsclibrary/MEMBER_ADFS.DSC.ps1 | 86 - src/dsclibrary/MEMBER_ADRMS.DSC.ps1 | 75 - .../MEMBER_BRANCHCACHE_HOST.DSC.ps1 | 76 - src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 | 141 - src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 | 51 - src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 | 172 - src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 | 85 - src/dsclibrary/MEMBER_DHCP.DSC.ps1 | 176 - src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 | 217 - src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 | 186 - src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 | 187 - src/dsclibrary/MEMBER_DNS.DSC.ps1 | 93 - src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 | 95 - .../MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 | 226 - .../MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 | 211 - .../MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 | 123 - src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 | 182 - .../MEMBER_FILESERVER_FSRMTEST.DSC.ps1 | 451 -- .../MEMBER_FILESERVER_ISCSI.DSC.ps1 | 250 - src/dsclibrary/MEMBER_IPAM.DSC.ps1 | 57 - src/dsclibrary/MEMBER_JENKINS.DSC.ps1 | 124 - src/dsclibrary/MEMBER_NANO.DSC.ps1 | 36 - src/dsclibrary/MEMBER_NLB.DSC.ps1 | 66 - src/dsclibrary/MEMBER_NPS.DSC.ps1 | 68 - src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 | 92 - src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 | 59 - .../MEMBER_REMOTEACCESS_WAP.DSC.ps1 | 67 - src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 | 343 -- src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 | 142 - src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 | 140 - src/dsclibrary/MEMBER_SUBCA.DSC.ps1 | 396 -- src/dsclibrary/MEMBER_WDS.DSC.ps1 | 89 - src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 | 238 - src/dsclibrary/MEMBER_WSUS.DSC.ps1 | 84 - src/dsclibrary/RODC_SECONDARY.DSC.ps1 | 91 - src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 | 23 - src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 | 178 - src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 | 103 - src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 | 93 - src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 | 326 -- .../STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 | 216 - src/dsclibrary/modules/LabDSCResources.psd1 | 7 - .../xCertAuthorityServer.DSC.Schema.psd1 | 1 - .../xCertAuthorityServer.DSC.Schema.psm1 | 250 - .../MyDSCResources/xDC/xDC.DSC.Schema.psd1 | 1 - .../MyDSCResources/xDC/xDC.DSC.Schema.psm1 | 111 - .../xDHCPServer/xDHCPServer.DSC.Schema.psd1 | 1 - .../xDHCPServer/xDHCPServer.DSC.Schema.psm1 | 119 - .../xFileServer/xFileServer.DSC.Schema.psd1 | 1 - .../xFileServer/xFileServer.DSC.Schema.psm1 | 184 - .../xJoinDomain/xJoinDomain.DSC.Schema.psd1 | 1 - .../xJoinDomain/xJoinDomain.DSC.Schema.psm1 | 72 - .../xNPSServer/xNPSServer.DSC.Schema.psd1 | 1 - .../xNPSServer/xNPSServer.DSC.Schema.psm1 | 40 - .../xRemoteAccessServer.DSC.Schema.psd1 | 1 - .../xRemoteAccessServer.DSC.Schema.psm1 | 31 - src/en-US/LabBuilder_LocalizedData.psd1 | 241 - .../Assert-LabValidConfigurationXMLSchema.ps1 | 88 - src/lib/private/Assert-LabValidIpAddress.ps1 | 41 - src/lib/private/ConvertTo-LabAbsolutePath.ps1 | 37 - src/lib/private/Copy-LabOdjFile.ps1 | 149 - src/lib/private/Enable-LabWSMan.ps1 | 71 - src/lib/private/Get-LabBuilderModulePath.ps1 | 15 - .../Get-LabCertificatePsFileContent.ps1 | 119 - .../private/Get-LabDSCNetworkingConfig.ps1 | 201 - .../private/Get-LabIntegrationServiceName.ps1 | 46 - .../private/Get-LabManagementSwitchName.ps1 | 42 - src/lib/private/Get-LabModulesInDSCConfig.ps1 | 82 - src/lib/private/Get-LabNextIpAddress.ps1 | 61 - src/lib/private/Get-LabNextMacAddress.ps1 | 33 - .../private/Get-LabUnattendFileContent.ps1 | 169 - .../private/Get-LabVMManagementIPAddress.ps1 | 58 - src/lib/private/Initialize-LabBootVHD.ps1 | 312 -- src/lib/private/Initialize-LabDSC.ps1 | 50 - .../Initialize-LabManagementSwitch.ps1 | 91 - src/lib/private/Initialize-LabVHD.ps1 | 331 -- src/lib/private/Initialize-LabVMPath.ps1 | 65 - src/lib/private/Install-LabHyperV.ps1 | 51 - .../private/Install-LabPackageProvider.ps1 | 67 - .../Invoke-LabDownloadAndUnzipFile.ps1 | 101 - .../Invoke-LabDownloadResourceModule.ps1 | 189 - src/lib/private/New-LabCredential.ps1 | 27 - src/lib/private/New-LabException.ps1 | 69 - .../New-LabHostSelfSignedCertificate.ps1 | 99 - .../private/New-LabVMInitializationFile.ps1 | 200 - .../Recieve-LabSelfSignedCertificate.ps1 | 135 - src/lib/private/Register-LabPackageSource.ps1 | 112 - .../Request-LabSelfSignedCertificate.ps1 | 203 - src/lib/private/Set-LabDSC.ps1 | 158 - src/lib/private/Set-LabModulesInDSCConfig.ps1 | 129 - src/lib/private/Set-LabSwitchAdapter.ps1 | 134 - src/lib/private/Start-LabDSC.ps1 | 239 - src/lib/private/Update-LabDSC.ps1 | 413 -- src/lib/private/Update-LabVMDataDisk.ps1 | 397 -- src/lib/private/Update-LabVMDvdDrive.ps1 | 108 - .../Update-LabVMIntegrationService.ps1 | 95 - .../Wait-LabVMInitializationComplete.ps1 | 151 - src/lib/private/Wait-LabVMOff.ps1 | 27 - src/lib/private/Wait-LabVMStarted.ps1 | 45 - src/lib/private/Write-LabMessage.ps1 | 93 - src/lib/public/Connect-LabVm.ps1 | 118 - src/lib/public/Disconnect-LabVm.ps1 | 73 - src/lib/public/Get-Lab.ps1 | 173 - src/lib/public/Get-LabResourceIso.ps1 | 92 - src/lib/public/Get-LabResourceModule.ps1 | 49 - src/lib/public/Get-LabResourceMsu.ps1 | 64 - src/lib/public/Get-LabSwitch.ps1 | 116 - src/lib/public/Get-LabVMTemplate.ps1 | 296 -- src/lib/public/Get-LabVm.ps1 | 989 ---- src/lib/public/Get-LabVmTemplateVhd.ps1 | 290 -- src/lib/public/Initialize-LabResourceIso.ps1 | 78 - .../public/Initialize-LabResourceModule.ps1 | 57 - src/lib/public/Initialize-LabResourceMsu.ps1 | 44 - src/lib/public/Initialize-LabSwitch.ps1 | 320 -- src/lib/public/Initialize-LabVm.ps1 | 318 -- src/lib/public/Initialize-LabVmTemplate.ps1 | 228 - .../public/Initialize-LabVmTemplateVhd.ps1 | 364 -- src/lib/public/Install-Lab.ps1 | 196 - src/lib/public/Install-LabVm.ps1 | 90 - src/lib/public/New-Lab.ps1 | 157 - src/lib/public/Remove-LabSwitch.ps1 | 138 - src/lib/public/Remove-LabVMTemplate.ps1 | 50 - src/lib/public/Remove-LabVm.ps1 | 92 - src/lib/public/Remove-LabVmTemplateVhd.ps1 | 56 - src/lib/public/Start-Lab.ps1 | 174 - src/lib/public/Stop-Lab.ps1 | 146 - src/lib/public/Uninstall-Lab.ps1 | 156 - src/lib/public/Update-Lab.ps1 | 50 - src/samples/Sample_WS2012R2_DCandDHCPOnly.xml | 166 - .../Sample_WS2012R2_DCandDHCPOnly_NAT.xml | 149 - .../Sample_WS2012R2_DCandDHCPandEdge.xml | 203 - .../Sample_WS2012R2_DomainClustering.xml | 719 --- .../Sample_WS2012R2_DomainComplete.xml | 691 --- src/samples/Sample_WS2012R2_DomainSQL2014.xml | 263 -- src/samples/Sample_WS2012R2_MultiForest.xml | 416 -- .../Sample_WS2012R2_MultiForest_ADFS.xml | 586 --- src/samples/Sample_WS2012R2_Simple.xml | 85 - src/samples/Sample_WS2016_DCandDHCPOnly.xml | 132 - src/samples/Sample_WS2016_DCandDHCPandCA.xml | 201 - .../Sample_WS2016_DCandDHCPandEdge.xml | 194 - src/samples/Sample_WS2016_DFSHubAndSpoke.xml | 311 -- .../Sample_WS2016_DomainClustering.xml | 710 --- src/samples/Sample_WS2016_DomainComplete.xml | 687 --- src/samples/Sample_WS2016_DomainFunctions.xml | 330 -- src/samples/Sample_WS2016_DomainSQL2016.xml | 257 -- src/samples/Sample_WS2016_NanoDomain.xml | 328 -- src/samples/Sample_WS2016_Simple.xml | 78 - src/samples/Sample_WS2019_AzureADConnect.xml | 225 - src/samples/Sample_WS2019_DCandDHCPandCA.xml | 201 - .../Sample_WS2019_DCandDHCPandEdge.xml | 194 - src/samples/Sample_WS2019_NanoDomain.xml | 328 -- src/samples/Sample_WS2019_Simple.xml | 78 - ...ut Windows installation ISO files here.txt | 1 - src/schema/labbuilderconfig-schema.xsd | 1569 ------- .../Convert-LabBuilderConfigSchemaToMD.ps1 | 14 - src/support/Convert-WindowsImage.ps1 | 4053 ----------------- src/support/Convert-XSDToMD.ps1 | 24 - src/support/New-SelfSignedCertificateEx.ps1 | 501 -- src/support/tools/msxsl.exe | Bin 24896 -> 0 bytes .../labbuilderconfig-schema-transformtomd.xsl | 95 - src/template/labbuilderconfig-template.xml | 145 - {test => tests}/Invoke-LabSample.ps1 | 0 .../pestertestconfig/PesterTestConfig.OK.xml | 0 .../dsclibrary/PesterTest.DSC.ps1 | 0 .../expectedcontent/ExpectedDSCConfig.txt | 0 .../expectedcontent/ExpectedDSCModules.json | 0 .../expectedcontent/ExpectedResourceISOs.json | 0 .../expectedcontent/ExpectedResourceMSUs.json | 0 .../ExpectedResourceModules.json | 0 .../expectedcontent/ExpectedSwitches.json | 0 .../expectedcontent/ExpectedTemplateVHDs.json | 0 .../ExpectedTemplates.FromVM.json | 0 .../expectedcontent/ExpectedTemplates.json | 0 .../expectedcontent/ExpectedUnattendFile.xml | 0 .../expectedcontent/ExpectedVMs.json | 0 ...IENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO | 0 ...5.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO | 0 ...R_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso | 0 .../pestertestconfig/isofiles/SQLFULL_ENU.iso | 0 ...QLServer2014SP1-FullSlipstream-x64-ENU.iso | 0 .../pestertestconfig/vhdfiles/DataDisk.vhdx | 0 .../vhdfiles/Nano Server 2016 Datacenter.vhdx | 0 .../vhdfiles/Windows 10 Enterprise.vhdx | 0 ...indows Server 2012 R2 Datacenter Core.vhdx | 0 ...indows Server 2012 R2 Datacenter Full.vhdx | 0 .../Windows Server 2016 Datacenter Core.vhdx | 0 .../Windows Server 2016 Datacenter Full.vhdx | 0 {test => tests}/testhelper/testhelper.psm1 | 0 tests/unit/CodeCoverage.xml | 1 + {test => tests}/unit/LabBuilder.tests.ps1 | 0 tests/unit/TestResults.unit.xml | 355 ++ .../lib/private/ManagementSwitch.tests.ps1 | 0 .../unit/lib/private/dsc.tests.ps1 | 0 .../unit/lib/private/utils.tests.ps1 | 0 .../unit/lib/private/vhd.tests.ps1 | 0 {test => tests}/unit/lib/private/vm.tests.ps1 | 0 {test => tests}/unit/lib/public/lab.tests.ps1 | 0 .../unit/lib/public/resource.tests.ps1 | 0 .../unit/lib/public/switch.tests.ps1 | 0 .../unit/lib/public/templatevhd.tests.ps1 | 0 {test => tests}/unit/lib/public/vm.tests.ps1 | 0 .../unit/lib/public/vmtemplate.tests.ps1 | 0 231 files changed, 2508 insertions(+), 35719 deletions(-) delete mode 100644 .codecov.yml delete mode 100644 .codecovio/CodeCovIo.psm1 create mode 100644 .vscode/analyzersettings.psd1 create mode 100644 Deploy.PSDeploy.ps1 create mode 100644 GitVersion.yml create mode 100644 HISTORIC_CHANGELOG.md delete mode 100644 RELEASENOTES.md create mode 100644 RequiredModules.psd1 create mode 100644 Resolve-Dependency.ps1 create mode 100644 Resolve-Dependency.psd1 delete mode 100644 appveyor.yml create mode 100644 build.ps1 create mode 100644 build.yaml delete mode 100644 psake.ps1 delete mode 100644 psakefile.ps1 delete mode 100644 requirements.psd1 delete mode 100644 src/LabBuilder.psd1 delete mode 100644 src/LabBuilder.psm1 delete mode 100644 src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 delete mode 100644 src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 delete mode 100644 src/dsclibrary/DC_SECONDARY.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_ADFS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_ADRMS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DHCP.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DNS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_IPAM.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_JENKINS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_NANO.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_NLB.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_NPS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_SUBCA.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_WDS.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 delete mode 100644 src/dsclibrary/MEMBER_WSUS.DSC.ps1 delete mode 100644 src/dsclibrary/RODC_SECONDARY.DSC.ps1 delete mode 100644 src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 delete mode 100644 src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 delete mode 100644 src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 delete mode 100644 src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 delete mode 100644 src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 delete mode 100644 src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 delete mode 100644 src/dsclibrary/modules/LabDSCResources.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 delete mode 100644 src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 delete mode 100644 src/en-US/LabBuilder_LocalizedData.psd1 delete mode 100644 src/lib/private/Assert-LabValidConfigurationXMLSchema.ps1 delete mode 100644 src/lib/private/Assert-LabValidIpAddress.ps1 delete mode 100644 src/lib/private/ConvertTo-LabAbsolutePath.ps1 delete mode 100644 src/lib/private/Copy-LabOdjFile.ps1 delete mode 100644 src/lib/private/Enable-LabWSMan.ps1 delete mode 100644 src/lib/private/Get-LabBuilderModulePath.ps1 delete mode 100644 src/lib/private/Get-LabCertificatePsFileContent.ps1 delete mode 100644 src/lib/private/Get-LabDSCNetworkingConfig.ps1 delete mode 100644 src/lib/private/Get-LabIntegrationServiceName.ps1 delete mode 100644 src/lib/private/Get-LabManagementSwitchName.ps1 delete mode 100644 src/lib/private/Get-LabModulesInDSCConfig.ps1 delete mode 100644 src/lib/private/Get-LabNextIpAddress.ps1 delete mode 100644 src/lib/private/Get-LabNextMacAddress.ps1 delete mode 100644 src/lib/private/Get-LabUnattendFileContent.ps1 delete mode 100644 src/lib/private/Get-LabVMManagementIPAddress.ps1 delete mode 100644 src/lib/private/Initialize-LabBootVHD.ps1 delete mode 100644 src/lib/private/Initialize-LabDSC.ps1 delete mode 100644 src/lib/private/Initialize-LabManagementSwitch.ps1 delete mode 100644 src/lib/private/Initialize-LabVHD.ps1 delete mode 100644 src/lib/private/Initialize-LabVMPath.ps1 delete mode 100644 src/lib/private/Install-LabHyperV.ps1 delete mode 100644 src/lib/private/Install-LabPackageProvider.ps1 delete mode 100644 src/lib/private/Invoke-LabDownloadAndUnzipFile.ps1 delete mode 100644 src/lib/private/Invoke-LabDownloadResourceModule.ps1 delete mode 100644 src/lib/private/New-LabCredential.ps1 delete mode 100644 src/lib/private/New-LabException.ps1 delete mode 100644 src/lib/private/New-LabHostSelfSignedCertificate.ps1 delete mode 100644 src/lib/private/New-LabVMInitializationFile.ps1 delete mode 100644 src/lib/private/Recieve-LabSelfSignedCertificate.ps1 delete mode 100644 src/lib/private/Register-LabPackageSource.ps1 delete mode 100644 src/lib/private/Request-LabSelfSignedCertificate.ps1 delete mode 100644 src/lib/private/Set-LabDSC.ps1 delete mode 100644 src/lib/private/Set-LabModulesInDSCConfig.ps1 delete mode 100644 src/lib/private/Set-LabSwitchAdapter.ps1 delete mode 100644 src/lib/private/Start-LabDSC.ps1 delete mode 100644 src/lib/private/Update-LabDSC.ps1 delete mode 100644 src/lib/private/Update-LabVMDataDisk.ps1 delete mode 100644 src/lib/private/Update-LabVMDvdDrive.ps1 delete mode 100644 src/lib/private/Update-LabVMIntegrationService.ps1 delete mode 100644 src/lib/private/Wait-LabVMInitializationComplete.ps1 delete mode 100644 src/lib/private/Wait-LabVMOff.ps1 delete mode 100644 src/lib/private/Wait-LabVMStarted.ps1 delete mode 100644 src/lib/private/Write-LabMessage.ps1 delete mode 100644 src/lib/public/Connect-LabVm.ps1 delete mode 100644 src/lib/public/Disconnect-LabVm.ps1 delete mode 100644 src/lib/public/Get-Lab.ps1 delete mode 100644 src/lib/public/Get-LabResourceIso.ps1 delete mode 100644 src/lib/public/Get-LabResourceModule.ps1 delete mode 100644 src/lib/public/Get-LabResourceMsu.ps1 delete mode 100644 src/lib/public/Get-LabSwitch.ps1 delete mode 100644 src/lib/public/Get-LabVMTemplate.ps1 delete mode 100644 src/lib/public/Get-LabVm.ps1 delete mode 100644 src/lib/public/Get-LabVmTemplateVhd.ps1 delete mode 100644 src/lib/public/Initialize-LabResourceIso.ps1 delete mode 100644 src/lib/public/Initialize-LabResourceModule.ps1 delete mode 100644 src/lib/public/Initialize-LabResourceMsu.ps1 delete mode 100644 src/lib/public/Initialize-LabSwitch.ps1 delete mode 100644 src/lib/public/Initialize-LabVm.ps1 delete mode 100644 src/lib/public/Initialize-LabVmTemplate.ps1 delete mode 100644 src/lib/public/Initialize-LabVmTemplateVhd.ps1 delete mode 100644 src/lib/public/Install-Lab.ps1 delete mode 100644 src/lib/public/Install-LabVm.ps1 delete mode 100644 src/lib/public/New-Lab.ps1 delete mode 100644 src/lib/public/Remove-LabSwitch.ps1 delete mode 100644 src/lib/public/Remove-LabVMTemplate.ps1 delete mode 100644 src/lib/public/Remove-LabVm.ps1 delete mode 100644 src/lib/public/Remove-LabVmTemplateVhd.ps1 delete mode 100644 src/lib/public/Start-Lab.ps1 delete mode 100644 src/lib/public/Stop-Lab.ps1 delete mode 100644 src/lib/public/Uninstall-Lab.ps1 delete mode 100644 src/lib/public/Update-Lab.ps1 delete mode 100644 src/samples/Sample_WS2012R2_DCandDHCPOnly.xml delete mode 100644 src/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml delete mode 100644 src/samples/Sample_WS2012R2_DCandDHCPandEdge.xml delete mode 100644 src/samples/Sample_WS2012R2_DomainClustering.xml delete mode 100644 src/samples/Sample_WS2012R2_DomainComplete.xml delete mode 100644 src/samples/Sample_WS2012R2_DomainSQL2014.xml delete mode 100644 src/samples/Sample_WS2012R2_MultiForest.xml delete mode 100644 src/samples/Sample_WS2012R2_MultiForest_ADFS.xml delete mode 100644 src/samples/Sample_WS2012R2_Simple.xml delete mode 100644 src/samples/Sample_WS2016_DCandDHCPOnly.xml delete mode 100644 src/samples/Sample_WS2016_DCandDHCPandCA.xml delete mode 100644 src/samples/Sample_WS2016_DCandDHCPandEdge.xml delete mode 100644 src/samples/Sample_WS2016_DFSHubAndSpoke.xml delete mode 100644 src/samples/Sample_WS2016_DomainClustering.xml delete mode 100644 src/samples/Sample_WS2016_DomainComplete.xml delete mode 100644 src/samples/Sample_WS2016_DomainFunctions.xml delete mode 100644 src/samples/Sample_WS2016_DomainSQL2016.xml delete mode 100644 src/samples/Sample_WS2016_NanoDomain.xml delete mode 100644 src/samples/Sample_WS2016_Simple.xml delete mode 100644 src/samples/Sample_WS2019_AzureADConnect.xml delete mode 100644 src/samples/Sample_WS2019_DCandDHCPandCA.xml delete mode 100644 src/samples/Sample_WS2019_DCandDHCPandEdge.xml delete mode 100644 src/samples/Sample_WS2019_NanoDomain.xml delete mode 100644 src/samples/Sample_WS2019_Simple.xml delete mode 100644 src/samples/isofiles/Put Windows installation ISO files here.txt delete mode 100644 src/schema/labbuilderconfig-schema.xsd delete mode 100644 src/support/Convert-LabBuilderConfigSchemaToMD.ps1 delete mode 100644 src/support/Convert-WindowsImage.ps1 delete mode 100644 src/support/Convert-XSDToMD.ps1 delete mode 100644 src/support/New-SelfSignedCertificateEx.ps1 delete mode 100644 src/support/tools/msxsl.exe delete mode 100644 src/support/transform/labbuilderconfig-schema-transformtomd.xsl delete mode 100644 src/template/labbuilderconfig-template.xml rename {test => tests}/Invoke-LabSample.ps1 (100%) rename {test => tests}/pestertestconfig/PesterTestConfig.OK.xml (100%) rename {test => tests}/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedDSCModules.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedResourceISOs.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedResourceMSUs.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedResourceModules.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedSwitches.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedTemplateVHDs.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedTemplates.FromVM.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedTemplates.json (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedUnattendFile.xml (100%) rename {test => tests}/pestertestconfig/expectedcontent/ExpectedVMs.json (100%) rename {test => tests}/pestertestconfig/isofiles/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO (100%) rename {test => tests}/pestertestconfig/isofiles/14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO (100%) rename {test => tests}/pestertestconfig/isofiles/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso (100%) rename {test => tests}/pestertestconfig/isofiles/SQLFULL_ENU.iso (100%) rename {test => tests}/pestertestconfig/isofiles/SQLServer2014SP1-FullSlipstream-x64-ENU.iso (100%) rename {test => tests}/pestertestconfig/vhdfiles/DataDisk.vhdx (100%) rename {test => tests}/pestertestconfig/vhdfiles/Nano Server 2016 Datacenter.vhdx (100%) rename {test => tests}/pestertestconfig/vhdfiles/Windows 10 Enterprise.vhdx (100%) rename {test => tests}/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Core.vhdx (100%) rename {test => tests}/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Full.vhdx (100%) rename {test => tests}/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Core.vhdx (100%) rename {test => tests}/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Full.vhdx (100%) rename {test => tests}/testhelper/testhelper.psm1 (100%) create mode 100644 tests/unit/CodeCoverage.xml rename {test => tests}/unit/LabBuilder.tests.ps1 (100%) create mode 100644 tests/unit/TestResults.unit.xml rename {test => tests}/unit/lib/private/ManagementSwitch.tests.ps1 (100%) rename {test => tests}/unit/lib/private/dsc.tests.ps1 (100%) rename {test => tests}/unit/lib/private/utils.tests.ps1 (100%) rename {test => tests}/unit/lib/private/vhd.tests.ps1 (100%) rename {test => tests}/unit/lib/private/vm.tests.ps1 (100%) rename {test => tests}/unit/lib/public/lab.tests.ps1 (100%) rename {test => tests}/unit/lib/public/resource.tests.ps1 (100%) rename {test => tests}/unit/lib/public/switch.tests.ps1 (100%) rename {test => tests}/unit/lib/public/templatevhd.tests.ps1 (100%) rename {test => tests}/unit/lib/public/vm.tests.ps1 (100%) rename {test => tests}/unit/lib/public/vmtemplate.tests.ps1 (100%) diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 411198f0..00000000 --- a/.codecov.yml +++ /dev/null @@ -1,26 +0,0 @@ -codecov: - notify: - require_ci_to_pass: no - # dev should be the baseline for reporting - branch: dev - -comment: - layout: "reach, diff" - behavior: default - -coverage: - range: 50..80 - round: down - precision: 0 - - status: - project: - default: - # Set the overall project code coverage requirement to 70% - target: 70 - patch: - default: - # Set the pull request requirement to not regress overall coverage by more than 5% - # and let codecov.io set the goal for the code changed in the patch. - target: auto - threshold: 5 diff --git a/.codecovio/CodeCovIo.psm1 b/.codecovio/CodeCovIo.psm1 deleted file mode 100644 index b3e084cf..00000000 --- a/.codecovio/CodeCovIo.psm1 +++ /dev/null @@ -1,345 +0,0 @@ -<# - .SYNOPSIS - Updates a hash table with the Unique file lines - Structure: - RootTable.[FileKey].[SubTable].[Line] - - .PARAMETER FileLine - The table to update - - .PARAMETER Command - The list of Command from pester to update the table based on - - .PARAMETER RepoRoot - The path to the root of the repo. This part of the path will not be included in the report. Needed to normalize all the reports. - - .PARAMETER TableName - The path of the file to write the report to. -#> -function Add-UniqueFileLineToTable -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $FileLine, - - [Parameter(Mandatory = $true)] - [Object] - $Command, - - [Parameter(Mandatory = $true)] - [System.String] - $RepoRoot, - - [Parameter(Mandatory = $true)] - [System.String] - $TableName - ) - - # file paths need to be relative to repo root when querying GIT - Push-Location -LiteralPath $RepoRoot - try { - Write-Verbose -Message "running git ls-files" -Verbose - - # Get the list of files as Git sees them - $fileKeys = & git.exe ls-files - - # Populate the sub-table - foreach ($command in $Command) - { - #Find the file as Git sees it - $file = $command.File - $fileKey = $file.replace($RepoRoot,'').TrimStart('\').replace('\','/') - $fileKey = $fileKeys.where{$_ -like $fileKey} - - if ($null -eq $fileKey) - { - Write-Warning -Message "Unexpected error filekey was null" - continue - } - elseif ($fileKey.Count -ne 1) - { - Write-Warning -Message "Unexpected error, more than one git file matched file ($file): $($fileKey -join ', ')" - continue - } - - $fileKey = $fileKey | Select-Object -First 1 - - if (!$FileLine.ContainsKey($fileKey)) - { - $FileLine.add($fileKey, @{ $TableName = @{}}) - } - - if (!$FileLine.$fileKey.ContainsKey($TableName)) - { - $FileLine.$fileKey.Add($TableName,@{}) - } - - $lines = $FileLine.($fileKey).$TableName - $lineKey = $($command.line) - if (!$lines.ContainsKey($lineKey)) - { - $lines.Add($lineKey,1) - } - else - { - $lines.$lineKey ++ - } - } - } - finally - { - Pop-Location - } -} - -<# - .SYNOPSIS - Tests if the specified property is a CodeCoverage object - For use in a ValidateScript attribute - - .PARAMETER CodeCoverage - The CodeCoverage property of the output of Invoke-Pester with the -PassThru and -CodeCoverage options - -#> -function Test-CodeCoverage -{ - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [Object] - $CodeCoverage - ) - - if (!($CodeCoverage | Get-Member -Name MissedCommands)) - { - throw 'Must be a Pester CodeCoverage object' - } - - return $true -} - -<# - .SYNOPSIS - Exports a Pester CodeCoverage report as a CodeCov.io json report - - .PARAMETER CodeCoverage - The CodeCoverage property of the output of Invoke-Pester with the -PassThru and -CodeCoverage options - - .PARAMETER RepoRoot - The path to the root of the repo. This part of the path will not be included in the report. Needed to normalize all the reports. - - .PARAMETER Path - The path of the file to write the report to. -#> -function Export-CodeCovIoJson -{ - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [ValidateScript({Test-CodeCoverage -CodeCoverage $_})] - [Object] - $CodeCoverage, - - [Parameter(Mandatory = $true)] - [System.String] - $RepoRoot, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $Path = (Join-Path -Path $env:TEMP -ChildPath 'codeCov.json') - ) - - Write-Verbose -Message "RepoRoot: $RepoRoot" - - # Get a list of all unique files - $files = @() - foreach ($file in ($CodeCoverage.MissedCommands | Select-Object -ExpandProperty File -Unique)) - { - if ($files -notcontains $file) - { - $files += $file - } - } - - foreach ($file in ($CodeCoverage.HitCommands | Select-Object -ExpandProperty File -Unique)) - { - if ($files -notcontains $file) - { - $files += $file - } - } - - # A table of the file key then a sub-tables of `misses` and `hits` lines. - $FileLine = @{} - - # define common parameters - $addUniqueFileLineParams= @{ - FileLine = $FileLine - RepoRoo = $RepoRoot - } - - <# - Basically indexing all the hit and miss lines by file and line. - Populate the misses sub-table - #> - Add-UniqueFileLineToTable -Command $CodeCoverage.MissedCommands -TableName 'misses' @addUniqueFileLineParams - - # Populate the hits sub-table - Add-UniqueFileLineToTable -Command $CodeCoverage.HitCommands -TableName 'hits' @addUniqueFileLineParams - - # Create the results structure - $resultLineData = @{} - $resultMessages = @{} - $result = @{ - coverage = $resultLineData - messages = $resultMessages - } - - foreach ($file in $FileLine.Keys) - { - $hit = 0 - $partial = 0 - $missed = 0 - Write-Verbose -Message "summarizing for file: $file" - - # Get the hits, if they exist - $hits = @{} - if ($FileLine.$file.ContainsKey('hits')) - { - $hits = $FileLine.$file.hits - } - - # Get the misses, if they exist - $misses = @{} - if ($FileLine.$file.ContainsKey('misses')) - { - $misses = $FileLine.$file.misses - } - - Write-Verbose -Message "fileKeys: $($FileLine.$file.Keys)" - $max = $hits.Keys | Sort-Object -Descending | Select-Object -First 1 - $maxMissLine = $misses.Keys | Sort-Object -Descending | Select-Object -First 1 - - <# - if max missed line is greater than maxed hit line - used max missed line as the max line - #> - if ($maxMissLine -gt $max) - { - $max = $maxMissLine - } - - $lineData = @() - $messages = @{} - - <# - produce the results - start at line 0 per codecov doc - #> - for ($lineNumber = 0; $lineNumber -le $max; $lineNumber++) - { - $hitInfo = $null - $missInfo = $null - switch ($true) - { - # Hit - ($hits.ContainsKey($lineNumber) -and ! $misses.ContainsKey($lineNumber)) - { - Write-Verbose -Message "Got code coverage hit at $lineNumber" - $lineData += "$($hits.$lineNumber)" - } - - # Miss - ($misses.ContainsKey($lineNumber) -and ! $hits.ContainsKey($lineNumber)) - { - Write-Verbose -Message "Got code coverage miss at $lineNumber" - $lineData += '0' - } - - # Partial Hit - ($misses.ContainsKey($lineNumber) -and $hits.ContainsKey($lineNumber)) - { - Write-Verbose -Message "Got code coverage partial at $lineNumber" - - $missInfo = $misses.$lineNumber - $hitInfo = $hits.$lineNumber - $lineData += "$hitInfo/$($hitInfo+$missInfo)" - } - - # Non-Code Line - (!$misses.ContainsKey($lineNumber) -and !$hits.ContainsKey($lineNumber)) - { - Write-Verbose -Message "Got non-code at $lineNumber" - - <# - If I put an actual null in an array ConvertTo-Json just leaves it out - I'll put this string in and clean it up later. - #> - $lineData += '!null!' - } - - default - { - throw "Unexpected error occured generating codeCov.io report for $file at line $lineNumber with hits: $($hits.ContainsKey($lineNumber)) and misses: $($misses.ContainsKey($lineNumber))" - } - } - } - - $resultLineData.Add($file,$lineData) - $resultMessages.add($file,$messages) - } - - $commitOutput = @(&git.exe log -1 --pretty=format:%H) - $commit = $commitOutput[0] - - Write-Verbose -Message "Branch: $Branch" - - $json = $result | ConvertTo-Json - $json = $json.Replace('"!null!"','null') - - $json | Out-File -Encoding ascii -LiteralPath $Path -Force - return $Path -} - -<# - .SYNOPSIS - Uploads a CodeCov.io code coverage report - - .PARAMETER Path - The path to the code coverage report (gcov not supported) -#> -function Invoke-UploadCoveCoveIoReport -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Path - ) - - $resolvedResultFile = (Resolve-Path -Path $Path).ProviderPath - - if ($env:APPVEYOR_REPO_BRANCH) - { - Push-AppVeyorArtifact $resolvedResultFile - } - - # Set the location of Python, install the pip and get the CodeCov script, and upload the code coverage report to CodeCov - $ENV:PATH = 'C:\\Python34;C:\\Python34\\Scripts;' + $ENV:PATH - $null = python -m pip install --upgrade pip - $null = pip install git+git://github.com/codecov/codecov-python.git - $uploadResults = codecov -f $resolvedResultFile -X gcov - - if ($env:APPVEYOR_REPO_BRANCH) - { - $logPath = (Join-Path -Path $env:TEMP -ChildPath 'codeCovUpload.log') - $uploadResults | Out-File -Encoding ascii -LiteralPath $logPath -Force - $resolvedLogPath = (Resolve-Path -Path $logPath).ProviderPath - Push-AppVeyorArtifact $resolvedLogPath - } -} diff --git a/.gitignore b/.gitignore index 2217b49e..bb84cc7c 100644 --- a/.gitignore +++ b/.gitignore @@ -292,4 +292,4 @@ __pycache__/ # Do not store help MAML - generated by CI LabBuilder-help.xml -staging/ +output/ diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 new file mode 100644 index 00000000..78312d2c --- /dev/null +++ b/.vscode/analyzersettings.psd1 @@ -0,0 +1,44 @@ +@{ + CustomRulePath = '.\output\RequiredModules\DscResource.AnalyzerRules' + includeDefaultRules = $true + IncludeRules = @( + # DSC Resource Kit style guideline rules. + 'PSAvoidDefaultValueForMandatoryParameter', + 'PSAvoidDefaultValueSwitchParameter', + 'PSAvoidInvokingEmptyMembers', + 'PSAvoidNullOrEmptyHelpMessageAttribute', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingComputerNameHardcoded', + 'PSAvoidUsingDeprecatedManifestFields', + 'PSAvoidUsingEmptyCatchBlock', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidShouldContinueWithoutForce', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingWriteHost', + 'PSDSCReturnCorrectTypesForDSCFunctions', + 'PSDSCStandardDSCFunctionsInResource', + 'PSDSCUseIdenticalMandatoryParametersForDSC', + 'PSDSCUseIdenticalParametersForDSC', + 'PSMisleadingBacktick', + 'PSMissingModuleManifestField', + 'PSPossibleIncorrectComparisonWithNull', + 'PSProvideCommentHelp', + 'PSReservedCmdletChar', + 'PSReservedParams', + 'PSUseApprovedVerbs', + 'PSUseCmdletCorrectly', + 'PSUseOutputTypeCorrectly', + 'PSAvoidGlobalVars', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSDSCUseVerboseMessageInDSCResource', + 'PSShouldProcess', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUsePSCredentialType', + + 'Measure-*' + ) + +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 93ebde36..a8719e0e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,19 +1,40 @@ -{ - // Use a custom PowerShell Script Analyzer settings file for this workspace. - // Relative paths for this setting are always relative to the workspace root dir. - "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", - - // When enabled, will trim trailing whitespace when you save a file. +{ "powershell.codeFormatting.openBraceOnSameLine": false, - "powershell.codeFormatting.newLineAfterOpenBrace": false, + "powershell.codeFormatting.newLineAfterOpenBrace": true, "powershell.codeFormatting.newLineAfterCloseBrace": true, "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, "powershell.codeFormatting.whitespaceBeforeOpenParen": true, "powershell.codeFormatting.whitespaceAroundOperator": true, "powershell.codeFormatting.whitespaceAfterSeparator": true, "powershell.codeFormatting.ignoreOneLineBlock": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", "powershell.codeFormatting.preset": "Custom", + "powershell.codeFormatting.alignPropertyValuePairs": true, + "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true + "files.insertFinalNewline": true, + "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", + "powershell.scriptAnalysis.enable": true, + "files.associations": { + "*.ps1xml": "xml" + }, + "cSpell.words": [ + "COMPANYNAME", + "ICONURI", + "LICENSEURI", + "PROJECTURI", + "RELEASENOTES", + "buildhelpers", + "endregion", + "gitversion", + "icontains", + "keepachangelog", + "notin", + "pscmdlet", + "steppable" + ], + "[markdown]": { + "files.trimTrailingWhitespace": false, + "files.encoding": "utf8" + } } - diff --git a/.vscode/tasks.json b/.vscode/tasks.json index da6002dd..4085e3e7 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,42 +1,125 @@ -// Available variables which can be used inside of strings. -// ${workspaceRoot}: the root folder of the team -// ${file}: the current opened file -// ${relativeFile}: the current opened file relative to workspaceRoot -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - - // Start PowerShell + "version": "2.0.0", + "_runner": "terminal", "windows": { - "command": "${env:windir}/System32/WindowsPowerShell/v1.0/powershell.exe", - //"command": "${env:ProgramFiles}/PowerShell/6.0.0/powershell.exe", - "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass" ] + "options": { + "shell": { + "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } }, "linux": { - "command": "/usr/bin/powershell", - "args": [ "-NoProfile" ] + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } }, "osx": { - "command": "/usr/local/bin/powershell", - "args": [ "-NoProfile" ] + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } }, - - // Associate with test task runner "tasks": [ { - "taskName": "Test", - "suppressTaskName": true, - "isTestCommand": true, - "args": [ - "Write-Host 'Invoking Pester...'; $ProgressPreference = 'SilentlyContinue'; Invoke-Pester -Script test -PesterOption @{IncludeVSCodeMarker=$true};", - "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" - ], - "problemMatcher": "$pester" + "label": "build", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": ["-AutoRestore"], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "new", + "clear": false + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + }, + { + "label": "test", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": ["-AutoRestore","-Tasks","test"], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] } - ] + ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index b64ece68..6e1b3b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ -# Change Log +# Changelog -## Unreleased +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed - Convert all DSC configurations to use ComputerManagementDsc version 7.1.0.0. @@ -36,654 +43,3 @@ - `dsclibrary\STNADALONE_INTERNET.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - -## 1.0.5.104 - -- Samples\Sample_WS2019_AzureADConnect.xml: Added sample for installing Azure AD - Connect. -- Convert all DSC configurations to use ActiveDirectoryDsc version - 4.1.0.0. -- `dsclibrary\RODC_SECONDARY.DSC.ps1`: - - Enable RODC creation because it is supported by ActiveDirectoryDsc. -- `dsclibrary\DC_FORESTPRIMARY.DSC.ps1`: - - Enabled customizing of Domain NetBios name. -- `Get-LabVm.ps1`: - - Clean up code style. -- `Enable-LabWSMan.ps1`: - - Improved function so that if WinRM Service is stopped it will be started. -- `Get-Lab.ps1`: - - Clean up code style. - - Fix bug reading `configpath` from `settings` node. - - Changed to use `ConvertTo-LabAbsolutePath.ps1` to simplify code. - - Changed to automatically use the `DSCLibrary` folder that comes as part of - the LabBuilder module if the `dsclibrarypath` setting is not specified - in the lab configuration - fixes [Issue-335](https://github.com/PlagueHO/LabBuilder/issues/335). -- `ConvertTo-LabAbsolutePath.ps1`: - - Added function to create an absolute path from a relative lab path. -- Removed `dsclibrarypath` setting from all samples as it is no longer required. -- `Get-LabResourceISO.ps1`: - - Clean up code style. - -## 1.0.4.83 - -- Change `psakefile.ps1` to detect Azure Pipelines correctly. -- Updated `BuildHelpers` support module for CI pipelines to 2.0.10. -- Added PowerShell Gallery badge to `README.md`. -- `Get-LabUnattendFileContent.ps1`: -- Enabled PSRemoting in Unattend.xml (allows DSC to initialize properly on - newer operating systems). -- Enabled local administrator account for Client operating systems - (Windows 10). -- Enabled PowerShell script execution for both 32-bit and 64-bit processes. -- `Connect-LabVM.ps1`: -- Test WinRM connectivity prior to initializing DSC. -- `Install-LabVM.ps1`: - - Check for DSC Configuration section in XML file prior to calling DSC. - -## 1.0.3.69 - -- `dsclibrary\MEMBER_SUBCA.DSC.ps1`: - - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- `dsclibrary\MEMBER_ROOTCA.DSC.ps1`: - - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). - - Changed CApolicy.inf RenewalKeyLength to 4096, CNGHashAlgorithm to SHA256 and - LoadDefaultTemplates to 0 - fixes [Issue-324](https://github.com/PlagueHO/LabBuilder/issues/324). -- `dsclibrary\STANDALONE_ROOTCA.DSC.ps1`: - - Correct SubCA resource name to wait for - fixes [Issue-321](https://github.com/PlagueHO/LabBuilder/issues/321). - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- `dsclibrary\STANDALONE_ROOTCA_NOSUBCA.DSC.ps1`: - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- Added `.markdownlint.json` file. -- Fix markdown rule violations in `CHANGELOG.MD`. -- `dsclibrary\MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\STANDALONE_DHCPDNS.DSC.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\STANDALONE_INTERNET.DSC.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_NPS_DFSTEST.ps1`: - - Fix to use correct name of the DFSReplicationGroup resource. -- `dsclibrary\MEMBER_WDS.DSC.ps1`: - - Fix configuration. - -## 1.0.2.58 - -- Reword module description in Manifest. -- Fix bug when connecting to a Lab VM when TrustedHosts is empty - fixes - [Issue #314](https://github.com/PlagueHO/LabBuilder/issues/314). -- Moved Schema documentation file into docs folder and converted to - PlatyPS compatible file. -- Cleaned up Schema documentation file to remove most markdown rule - violations. -- Cleaned up README.MD file to remove most markdown rule - violations. -- Fix infinite loop bug occuring in `Stop-Lab` when Lab VM does not - exist - fixes [Issue #316](https://github.com/PlagueHO/LabBuilder/issues/316). -- Fix infinite loop bug occuring in `Start-Lab` when Lab VM does not - exist. -- DSCLibrary\MEMBER_NANO.DSC.ps1: Rename xOfflineDomainJoin to - OfflineDomainJoin - fixes [Issue #317](https://github.com/PlagueHO/LabBuilder/issues/317). - -## 1.0.1.40 - -- Update to use NetworkingDsc 7.0.0.0 and converted DhcpClient - resource to NetIpInterface - fixes [Issue #304](https://github.com/PlagueHO/LabBuilder/issues/304). -- Refactored module manifest generation process to be more reliable - and robust. -- Convert module name to be a variable in PSake file to make it more - easily portable between projects. -- Added samples for Windows Server 2019 - fixes [Issue #305](https://github.com/PlagueHO/LabBuilder/issues/305). -- Cleaned up unit test initialization and fixtures. -- Refactored `Install-Lab` to move Management Switch creation into a - new function `Initialize-LabManagementSwitch` and created unit tests. -- Added more log information to `SetupComplete.cmd` to diagnose issues - with initial machine boot on Windows Server 2019. -- Fix bug in `Remove-LabSwitch` where adapter -- Correct documentation markdown errors. -- Removed `Timeout 30` from the Initial `SetupComplete.cmd` that runs - on each VM when first intialized because it fails to execute on - Windows Server 2019. Replaced with `Start-Sleep` in `SetupComplete.ps1` - that is called by `SetupComplete.cmd` - fixes [Issue #308](https://github.com/PlagueHO/LabBuilder/issues/308). -- Change `Update-LabDSC` so that module copy process to Lab Files will - continue even if destination path is too long. This is to allow DSC - Resource modules that have example filenames that result in a long - path. -- Update CI module dependencies in `Requirements.psd1` to latest version: - - PSScriptAnalyzer: 1.18.0 - - PSDeploy: 1.0.1 - - Platyps: 0.14.0 -- Split Private Lib functions into individual .ps1 files. -- Refactored Private Lib functions to improve code style standards. - -## 1.0.0.6 - -- Samples\Sample_WS2016_DCandDHCPandCA.xml: Added to easily create a Windows - Server 2016 domain with a enterprise root CA. -- Correct certificate authority DSC Resources with ADCSCertificationAuthority - to be IsSingleInstance. -- Convert xNetworking to NetworkingDsc. -- Converted repository structure and build pipeline to more modern standards. -- Convert module to require WMF 5.1 and all the samples to install WMF 5.1. -- DSCLibrary\MEMBER_MEMBER_DHCP*.ps1: Fixed to support xDHCPServer 2.0.0.0. - -## 0.8.4.1160 - -- Clean up markdown errors in README.MD. -- Updated code style to meet current best practices. -- Updated tests to meet Pester v4 guidelines. -- Convert sample labs to use ISCSIDsc resource module. -- Convert sample labs to use FSRMDsc resource module. -- Convert sample labs to use DFSDsc resource module. -- Convert sample labs to use StorageDsc resource module. -- Convert sample labs to use ActiveDirectoryCDDsc resource module. -- Convert sample labs to use SQLServerDsc resource module. -- Fix error that occurs when DSC ConfigurationData is specified - as a filename instead of a hashtable when compiling the MOF file. -- Removed Visual Studio Solution and Proejct files. -- DSCLibrary\MEMBER_DFSHUB.DSC.ps1: Added to enable Sample_WS2016_DFSHubAndSpoke - sample. -- DSCLibrary\MEMBER_DFSSPOKE.DSC.ps1: Added to enable Sample_WS2016_DFSHubAndSpoke - sample. -- Samples\Sample_WS2016_DFSHubAndSpoke.xml: Added to demonstrate Hub and Spoke DFS - replication gorup. - -## 0.8.3.1140 - -- Enforce xNetworking v5.0.0.0 is installed and used - fixes [Issue #289](https://github.com/PlagueHO/LabBuilder/issues/289). -- DSCLibrary\MEMBER_SQLSERVER2014.DSC.ps1: Updated to support v8.0.0.0 of xSQLServer -- DSCLibrary\MEMBER_SQLSERVER2016.DSC.ps1: Updated to support v8.0.0.0 of xSQLServer - -## 0.8.3.1132 - -- Added .vscode\settings.json to force code styles and enable auto formatting in - VS Code. -- Changed WaitVMStarted to check VM is running and also handle blank heartbeat - being returned in Windows 10 15063 (Creators Update) and above. -- Updated LabBuilder to support changes in xNetworking DSC Resource v5.0.0.0 -- Updated DSC sample configurations to support xStorage DSC Resource v3.2.0.0 - -## 0.8.3.1124 - -- DSCLibrary\MEMBER_DHCPNPAS2016.DSC.ps1:Added DSC Library Configuration for - DHCP with NPAS on Windows Server 2016 - see [Issue 283](https://github.com/PlagueHO/LabBuilder/issues/283). - -## 0.8.3.1116 - -- Moved Changelist.md file to root and renamed to CHANGELOG.MD. -- Cleaned up markdown errors in README.MD. -- Updated samples to use latest version of Windows Server 2016 Evaulation ISO. -- Added sample Sample_WS2016_DomainFunctions.xml for creating an Azure Functions - lab. -- Added support for codecoverage analysis using CodeCov.io. - -## 0.8.3.1107 - -- DSCLibrary\MEMBER_CONTAINER_HOST.DSC.ps1: Added DSC Configuration for configuring - a Docker Container host. -- Added support for inserting ODJ files into a VM for joining Nano Servers to an - AD domain. -- Fix error occuring when starting DSC on node with no adapters. -- LabDSCModule class: Added [Version] MinimuVersion property, converted ModuleVersion - property to [Version]. -- Corrected format of Changelist.md. -- Change SubnetMask to PrefixLength in xIPAdress DSC Config created by Get-LabDSCNetworkingConfig. -- Added support for specifying minimum module version in Update-LabDSC to enforce - xNetworking 3.0.0.0 usage. - -## 0.8.3.1068 - -- Added Jenkins build scripts. -- Fix ExposeVirtualizationExtensions when on Windows 10 build 14352 and above. -- DSCLibrary\*_ROOTCA.DSC.ps1: Fix to support 2.0.0.0 of xADCSDeployment resource. -- DSCLibrary\*_SUBCA.DSC.ps1: Fix to support 2.0.0.0 of xADCSDeployment resource. -- Converted AppVeyor.yml to pull Pester from PSGallery instead of Chocolatey. -- Changed AppVeyor.yml to use default image -- - MSFT-MWalker changes Below -- Added support for Version of VM - Only works on latest Windows 10 builds post 14352 -- Added support for generation of VM so Generation 1 VMs can now be created -- Fixed issue with Shared VHDX that prevented their creation. -- Updated SCHEMA for information on Version and Generation -- Fixed several typos in comment sections -- DSC resources created for working with composite DSC resources - non-functional - at this time -- Correctly enable PS Remoting using Enable-PSRemoting cmdlet. -- DSCLibrary\MEMEBER_DSCPULLSERVER.DSC.ps1: Added DSC Library resource for - creating DSC Pull Servers. -- Added additional logging information when copying DSC Resource modules. -- Fix bug when copying DSC Resource modules to LabBuilder Files for VM when DSC - Modules folder does not exist. -- DSCLibrary\MEMBER_SQLSERVER2016.DSC.ps1: Added DSC Library configuration for - installing a SQL Server 2016 from an ISO. -- Fix bug using a NIC team as network adapter bound to an external switch. -- Change AppVeyor script to improve and automate deployment process. -- Mock functions added to unit tests so that can run on machines without Hyper-V - installed. -- Added Windows Server 2016 sample labs. -- Removed old Lab test scripts and replaced with a single Lab test script ```Invoke-LabSample.ps1```. -- Updated all samples to use the filename of the latest Windows Server 2012 R2 - Evaluation ISO. - -## 0.8.3.0 - -- Fix bug where Administrator account is not enabled in Windows client OS. -- Added support for ModulePath attribute on Settings node. - -## 0.8.2.0 - -- Fix bug when creating a new Management adapter for a new Lab and setting a - static MAC address on it. - -## 0.8.1.0 - -- Converted all Write-Verbose calls to Write-LabMessage function. -- Fix bug when creating a new Management adapter for a new Lab. - -## 0.8.0.0 - -- DSCLibrary\MEMBER_SQLSERVER2014.DSC.ps1: Completed DSC Library configuration - for installing a SQL Server 2014 from an ISO. -- Samples\Sample_WS2012R2_DomainSQL2014.xml: Added new Sample for building a - simple domain with a SQL Server. -- Samples\*.xml: DNS Forwarders set to Google for all Samples with Edge nodes. -- Added LabBuilderConfig\Settings attribute requiredwindowsbuild. -- Get-Lab: Added support for preventing a Lab from being used on a host not at - the requiredwindowsbuild build version. -- Samples\Sample_WS2012R2_DomainClustering.xml: Required build version set to 10586. -- Samples\Sample_WS2012R2_DCandDHCPOnly_NAT.xml: Required build version set to 14295. -- Added LabBuilderConfig\Resources attribute ISOPath. -- DSCLibrary\MEMBER.DSC.ps1: Corrected filename. -- DSCLibrary\MEMBER.DSC.ps1: Fixed Configuration name. -- Added InstallRSATTools parameter DSC configurations to enable installation of - applicable RSAT Management tools: - - DC_FORESTCHILDDOMAIN.DSC.ps1 - - DC_FORESTPRIMARY.DSC.ps1 - - DC_SECONDARY.DSC.ps1 - - RODC_SECONDARY.DSC.ps1 - - MEMBER_DNS.DSC.ps1 - - MEMBER_DHCPNPAS.DSC.ps1 - - MEMBER_DHCPDNS.DSC.ps1 - - MEMBER_DHCP.DSC.ps1 - - MEMBER_ROOTCA.DSC.ps1 - - MEMBER_SUBCA.ps1 - -## 0.7.9.0 - -- Fixed failure when creating self-signed certificate on localized systems, by - replacing EKU Names with IDs. -- Fixed support for NAT switches and added Switch attributes NatSubnet and - NatGatewayAddress. -- Private function UpdateSwitchManagementAdapter added. -- Samples\Sample_WS2012R2_DCandDHCPOnly_NAT.xml: Added sample for testing NAT - based Lab switches. -- Improved ShouldProcess messages to be easier to read. -- Utils\Install-LabPackageProvider: Added function for ensuring Package Providers - are installed. -- Utils\Register-LabPackageSource: Added function for ensuring Package surces - are registered. -- Install-Lab: Added checks to ensure required PackageProviders and PackageSources - are available. - -## 0.7.8.0 - -- Install-Lab: - - Force flag added to suppress confirmation messages. - - Will attempt to install WS-Man if not installed, failure will - cause install to fail. -- Disconnect-LabVM: - - Improve handling of adding IPAddress to trusted hosts. -- Get-LabVM: - - LabID will only be prepended to VM Adapter name for adapters not - attached to an External switch. - -## 0.7.7.0 - -- Samples\Sample_WS2016TP5_DCandDHCPOnly.xml: - - Set edition in Nano Server Template VHD. - - Fixed WS2016 Template VHD edition names. - - Fixed Template name. - -## 0.7.6.0 - -- Added .vscode\tasks.json file to allow quick conversion of LabBuilder Schema - to MD. -- Moved existing Libs into Libs\Private folder. -- Updated samples and tests to support Windows Server 2016 TP5. -- Updated Visual Studio Project and Soltion files. -- Fix Nano Server localization package filename support for TP5. - -## 0.7.5.0 - -- Added VM InstanceCount attribute for creating multiple copies a VM in a Lab. -- Added $Script:CurrentBuild variable to allow easier access to OS build version. -- Fix to prevent ExposeVirtualizationExtensions from being applied on Lab Hosts - that don't support it. -- Samples\Sample_WS2012R2_DCandDHCPandEdge.ps1: Added sample for creating Lab with - DC, DHCP and Edge servers. -- DSCLibrary\MEMBER_JENKINS.DSC.ps1: Added DSC Library configuration for creating - a Domain Joined Jenkins CI Server. -- DSCLibrary\STANDALONE_JENKINS.DSC.ps1: Added DSC Library configuration for - creating a Standalone Jenkins CI Server. -- Install-Lab will now stop if error occurs creating Lab Management switch or adapter. -- Added support for Get-Lab to work with config files with a relative path. -- Improved handling of Initialize-LabSwitches when Multiple External adapters are - available and/or already in use by external switches. -- Improved Localization support for Integration Services. - -## 0.7.4.0 - -- lib\vm.ps1: WaitWMStarted - name of integrationservice "heartbeat" detected by - id to be culture neutral -- DSCLibrary\MEMBER_ADFS.DSC.ps1: Enable ADFS Firewall Rules. -- AppVeyor.yml: Module Manifest version number always set to match build version. -- DSCLibrary\MEMBER_IPAM.DSC.ps1:Added DSC Library Configuration for IPAM Server. -- DSCLibrary\MEMBER_FAILOVERCLUSTER_*.DSC.ps1: Added iSCSI Firewall Rules to allow - iSNS registration. -- DSCLibrary\MEMBER_ADFS.DSC.ps1: Added DSC Library configuration for ADRMS. -- DSCLibrary\MEMBER_SQLSERVER2014.DSC.ps1: Added Incomplete DSC Library configuration - for SQL Server 2014. -- Support\Convert-WindowsImage.ps1: Updated to March 2016 version so that DISM - path can be specified. -- labbuilder-schema.xsd: Added Settings\DismPath attribute so that path to DISM - can be specified. -- Failure to validate Lab configuration XML will terminate any cmdlet immediately. -- Any failure in Install-Lab will cause immediate build termination. -- Support\Convert-WindowsImage.ps1: Fixed incorrect error reported when invalid - Edition is specified. -- SetModulesInDSCConfig: Ensure each Import-DSCResource ends up on a new line. -- DSCLibrary\MEMBER_NANO.DSC.ps1: Added DSC Library configuration for joining a - Nano server to n AD Domain. -- labbuilder-schema.xsd: Fixed VM attribute descriptions. -- Added CertificateSource attribute to VM to support controlling where any Lab - Certificates should be generated from when initializing a Lab VM. -- Generalized Nano Server package support. -- Both ResourceMSU and Nano Server packages can now be installed on Template VHDs - and Virtual Machines. -- Automatically add Microsoft-NanoServer-DSC-Package.cab to new Nano Server VMs. -- Added BindingAdapterName and BindingAdapterMac attribute to switch element to - allow control over bound adapter. -- GetCertificatePsFileContent Changed so that PFX certificate imported into Root - store for non Nano Servers. -- Automatically set xNetworking version in DSC Networking config to that of the - highest version available on the Lab Host. - -## 0.7.3.0 - -- DSCLibrary\MEMBER_FAILOVERCLUSTER_FS.DSC.ps1: Added ServerName property to - contain name of ISCSI Server. -- samples\Sample_WS2012R2_DomainClustering.xml: Added ServerName property to all - Failover Cluster servers DSC properties. -- docs\labbuilderconfig-schema.md: Converted to UTF-8 to eliminate issues with Git. -- support\Convert-XSDToMD.ps1: Added code to convert transformed output to UTF-8. -- Start-Lab: Improved readability if timeout detect code. -- Stop-Lab: Improved readability if timeout detect code. - Ensure all VMs are stopped in a Bootphase, even if timeout occurs. -- StartDSCDebug.ps1: Added a WaitForDebugger parameter to StartDSCDebug.ps1 that - will cause LCM to start with debugging mode enabled. -- Lib\Type.ps1: File removed and content moved to header of LabBuilder.psm1 so - that types were available outside the module context. -- Stop-Lab: Removed Boot Phase timeout because Stop-VM does not return until VM shutdown. -- Added support for ISO resources to be specified in the Lab configuration. -- Added support for DVD Drives in Lab VM configuration. -- DSCLibrary\MEMBER_ADFS.DSC.ps1: Added DSC Library Configuration for ADFS. -- samples\Sample_WS2012R2_MultiForest_ADFS.ps1: Added Sample Lab for creating - multiple forests for ADFS testing. -- DSCLibrary\MEMBER_REMOTEACCESS_WAP.DSC.ps1: Added DSC Library Configuration for - Remote Access and Web Application Proxy. -- DSCLibrary\MEMBER_ADFS.DSC.ps1: Install WID. -- DSCLibrary\MEMBER_WEBSERVER.ps1: Created resource for IIS Web Servers. -- samples\Sample_WS2012R2_MultiForest_ADFS.xml: Added Web Application Servers. -- .github\*: Added general documentation on contributing to this project. - -## 0.7.2.0 - -- DSCLibrary\MEMBER_FAILOVERCLUSTER_FS.DSC.ps1: Changed to install most File - Server features on cluster nodes. -- DSCLibrary\MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1: Created resource for Failover - Cluster DHCP Server nodes. -- Readme.md: Additional Documentation added. - -## 0.7.1.0 - -- Get-LabDSCNetworkingConfig: Fix DSC error occuring when a blank DNS Server - address or Default Gateway address is set on an Adapter. -- InitializeVhd: Prevent unnecessary results of disk partitioning and volume - creation to console. -- UpdateVMDataDisks: Fix to incorrectly reported Data VHD type change error. -- DSCLibrary\MEMBER_BRANCHCACHE_HOST.DSC.ps1: Created resource for BranchCache - Hosted Servers. -- DSCLibrary\MEMBER_FILESERVER_*.DSC.ps1: Added BranchCache for File Servers feature. -- Readme.md: Added 'Lab Installation Process in Detail' section. - -## 0.7.0.0 - -- Initialize-LabSwitch: External switch correctly sets Adapter Name. -- IsAdmin: Function removed because was not useful. -- dsclibrary\DC_FORESTDOMAIN.DSC: New DSC Library config for creating child - domains in an existing forest. -- Samples\Sample_WS2012R2_MultiForest.xml: Added child domains. -- Get-LabSwitch: Converted to output array of LabSwitch objects. -- Initialize-LabSwitch: - - Converted to use LabSwitch objects. - - Fixed bug setting VLAN Id on External and Internal Switch Adapters. -- Remove-LabSwitch: Converted to use LabSwitch objects. -- Tests\Test_Sample_*.ps1: Test-StartLabVM function fixed. -- DSCLibrary\MEMBER_*.DSC.ps1: Updated parameter examples to include DCName parameter. -- DSCLibrary\DC_*.DSC.ps1: Added DNS Zone and forwarder options (setting forwarder - requires xDNSServer 1.6.0.0). -- DSCLibrary\MEMBER_DNS.DSC.ps1: Created resource for member DNS servers. -- Get-LabVMTemplateVHD: Converted to output array of LabVMTemplateVHD objects. -- Initialize-LabVMTemplateVHD: - - Converted to use LabVMTemplateVHD objects. - - Check added to ensure Drive Letter is assigned to mounted ISO. -- Remove-LabVMTemplateVHD: Converted to use LabVMTemplateVHD objects. -- Readme.md: Windows Management Framework 5.0 (WMF 5.0) section added. -- DSCLibrary\DC_FORESTDOMAIN.DSC.ps1: Changed name to DC_FORESTCHILDDOMAIN.DSC.ps1 - to better indicate purpose. -- Get-LabVMTemplate: Converted to output array of LabVMTemplate objects. -- Initialize-LabVMTemplate: Converted to use LabVMTemplate objects. -- Remove-LabVMTemplate: Converted to use LabVMTemplate objects. -- Get-LabVM: Converted to output array of LabVM objects. -- Initialize-LabVM: Converted to use LabVM objects. -- Remove-LabVM: Converted to use LabVM objects. -- Lib\dsc.ps1: All functions converted to use LabVM objects. -- Lib\vm.ps1: All functions converted to use LabVM objects. -- Lib\vhd.ps1: All functions converted to use LabVM objects. -- InitializeVhd: Fix error when attempting to create a new VHD/VHDx with a - formatted volume. - -## 0.6.0.0 - -- New-Lab: Function added for creating a new Lab configuration file and basic - folder structure. -- Get-Lab: Redundant checks for XML valid removed because convered by XSD schema - validation. -- Added Lib\Type.ps1 containing customg LabBuilder Classes and Enumerations. -- Added functions for converting XSD schema to MD. -- Fix to Nano Server Package caching bug. -- DSC Library Domain Join process improved. -- DSC\ConfigFile attribute supports rooted paths. -- VM\UnattendFile attribute supports rooted paths. -- VM\SetupComplete attribute supports rooted paths. -- DSC\ConfigFile Lab setting supports rooted paths. -- VM\UseDifferencingBootDisk default changed to 'Y'. -- Get-ModulesInDSCConfig: - - Returns Array of objects containing ModuleName and ModuleVersion. - - Now returns PSDesiredStateConfiguration module if listed -expected that - calling function will ignore if required. - - Added function to set the Module versions in a DSC Config. -- Update-LabDSC: Updated to set Module versions in DSC Config files. -- DSC Library: Module Version numbers removed from all DSC Library Configrations. -- Test Sample file code updated to remove switches when lab uninstalled. -- Uninstall-Lab: Management Switch automatically removed when Lab uninstalled. -- Configuration Schema: - - Added Resources\MSU element. - - Added Settings\Resource attribute. - - Removed VM\Install element support, superceeded by Packages attribute. -- Get-LabResourceModule: Function added. -- Initialize-LabResourceModule: Function added. -- Get-LabResourceMSU: Function added. -- Initialize-LabResourceMSU: Function added. -- Install-Lab: - - Fix CheckEnvironment bug. - - Added calls to Initialize-LabResourceModule and Initialize-LabResourceMSU. -- DownloadResources: - - Utility function removed, superceeded by Initialize-LabResourceModule and - Initialize-LabResourceMSU functions. -- Get-LabVM: Removed Install\MSU support. -- InitializeBootVM: - - Removed Install\MSU support. - - Added support for installing Packages from Resources\MSU element. -- Initialize-LabVMTemplateVHD: - - MSU Resources specified in Packages attribute are added to Template VHD when - converted. -- Initialize-LabVMTemplate: - - MSU Resources specified in Packages attribute are added to Template when copied. - -## 0.5.0.0 - -- BREKAING: Renamed Config parameter to Lab parameter to indicate the object is - actually an object that also stores Lab state information. -- Remove-LabVM: Removed parameter 'RemoveVHDs'. Added parameter RemoveVMFolder - which causes the VM folder and all contents to be deleted. -- Uninstall-Lab: Renamed "Remove" parameters to be singular names rather than plural. -- Uninstall-Lab: Added parameter 'RemoveLabFolder' which will cause the entire - Lab folder to be deleted. -- Uninstall-Lab: Added ShouldProcess support to ask user to confirm actions. -- Update-Lab: Added function which just calls Install-Lab. -- Start-LabVM: Renamed function to Install-LabVM so that it is not confused with - Start-VM. -- *-LabSwitch: Added Name array parameter to allow filtering of switches to work - with. -- *-LabVMTemplateVHD: Added Name array parameter to allow filtering of VM Template - VHDs to work with. -- *-LabVMTemplate: Added Name array parameter to allow filtering of VM Templates - to work with. -- *-LabVM: Added Name array parameter to allow filtering of VMs to work with. -- Samples: Updated sample code with additional examples. -- Help completed for all exported cmdlets. -- Get-LabVM: XML now validated against labbuilderconfig-schema.xsd in Schemas - folder when loaded -unless SkipXMLValidation switch is passed. -- All sample and test configuration XML files validated against - labbuilderconfig-schema.xsd in schemas folder when unit tests run. -- All sample and test configuration XML files updated with namespace -> xmlns="labbuilderconfig". - -## 0.4.2.0 - -- Add bootorder VM attribute for controlling stop-lab/start-lab order. -- Added Start-Lab and Stop-Lab cmdlets. -- *-Lab cmdlet documentation added to Readme.md - -## 0.4.1.0 - -- VHDParentPath setting made optional. Defaults to "Virtual Machine Hard Disks" - under config. -- Initialize-LabConfiguration function will create labpath and vhdparentpath - folders if not exist. -- Removed Test-LabConfiguration function and tests moved to Get-LabConfiguration. -- Added Disconnect-LabVM function to disconnect from a connect Lab VM. -- Fixed bug setting TrustedHosts when connecting to Lab VM. -- Added code to revert TrustedHosts when disconnecting from Lab VM. -- All non-exported supporting functions moved into separate support libraries. -- Add support for LabId setting that gets prepended to Lab resources. -- Added LabBuilderConfig schema in schema folder. -- Added LabPath parameter to Install-Lab, Uninstall-Lab and Get-LabConfiguration. -- Fix exception in Disconnect-LabVM. -- Fixed Unit tests to retain current folder location. -- Added PS ScriptAnalyzer Error tests to unit tests. -- Display PS ScriptAnalyzer Warnings when unit tests run. -- Remove-LabVMTemplateVHD function added and will be called from Uninstall-Lab. - -## 0.4.0.0 - -- Some secondary non-exported functions moved into separate support libraries. -- Initialize-LabVMTemplate caches NanoServerPackages from VHD template folder to - Lab folder. -- Fix exception connecting to VM when TrustedHosts is set to '*'. -- Fix path Lab VM files are created. -- Support for creating Certificates for Nano Servers on the host added. - -## 0.3.3.0 - -- Changed Get-LabSwitch Unit tests to use PesterTestConfig.OK.xml. -- Added support for configuring Nano Server packages for each VM. -- Removed MAC Address minimum/maximum value settings from configuration. -- Fix bug with Wait-LabInitVM failing to copy InitialSetupComplete.txt file. -- Added VMRootPath and LabBuilderFilesPath properties Get-LabVM array containing - path where VM and LabBuilder files should be stored respectively. -- Added TemplateVHD in templates/template config node for specifying the template - VHD. - -## 0.3.2.0 - -- Added Initialize-VHD function. -- Added support for formatting Data VHDs. -- Added support for copying multiple folders to DataVHDs. -- Updated Download-ResourceModule to use Invoke-LabDownloadAndUnzipFile function. -- Changed name of Settings\VMPath attribute to LabPath. - -## 0.3.1.0 - -- Disable 'Access Denied' test when connecting to new VM because this error is - reported by VM that is still booting up. -- Correct Verbose message shown when Integration Services enabled. -- Added Verbose message to indicate creation of VM Initialization files. -- Correct Verbose message not appearing when mounting VM boot disk image file. -- Moved DSC Config message into Localization data. -- Disabled automatic module push to PSGallery till version 1.0.0.0 or greater. - -## 0.3.0.0 - -- Fix to Module detection regex. -- Updated AppVeyor.yml to push more artifacts. -- Fix issue preventing timeout from triggering. -- Improved handling of Remoting connection by moving into a new function Connect-LabVM -- IP Address of VMs automatically added to WS-Man Trusted Hosts to enable HTTP - remoting connection. -- Prevent error if Panther folder doesn't exist in VHD image when creating a new - VM. -- Add support for multiple data disks for each VM. -- Add support for creating new data disks by cloning exising VHDs. -- Support for Fixed, Differencing and Shared data disks. -- JSON Object comparison unit tests fixed. -- AppVeyor build status badge added. -- Add support for VM Integration Services flag. -- Initialize-Lab- arrays made optional and will be pulled from config if not passed. -- Configuration parameter changed to Config to reduce size/typing. -- Support for creating VHD boot disks from ISO via TemplateVHD nodes in XML. - -## 0.2.0.0 - -- Code cleanup and refactoring. - -## 0.1.0.0 - -- Initial Release. diff --git a/Deploy.PSDeploy.ps1 b/Deploy.PSDeploy.ps1 new file mode 100644 index 00000000..c5c9ec8c --- /dev/null +++ b/Deploy.PSDeploy.ps1 @@ -0,0 +1,19 @@ +if ( + $Env:ProjectName -and $Env:ProjectName.Count -eq 1 -and + $Env:BuildSystem -ne 'unknown' +) { + if ($Env:BranchName -eq 'master') { + Deploy Module { + by PSGalleryModule { + FromSource $(Get-Item ".\BuildOutput\$Env:ProjectName") + To $Env:ModuleRepositoryToDeployTo + WithOptions @{ + ApiKey = $Env:NugetApiKey + } + } + } + } +} +else { + Write-Warning "Condition to Deploy not met" +} diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 00000000..05406c4a --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,24 @@ + +mode: ContinuousDelivery +next-version: 3.0.0 +major-version-bump-message: '\s?(breaking|major|breaking\schange)' +minor-version-bump-message: '\s?(add|feature|minor)' +patch-version-bump-message: '\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}' +branches: + master: + tag: preview + pull-request: + tag: PR + feature: + tag: useBranchName + increment: Minor + regex: f(eature(s)?)?[\/-] + source-branches: ['master'] + hotfix: + tag: fix + increment: Patch + regex: (hot)?fix(es)?[\/-] + source-branches: ['master'] +merge-message-formats: {} diff --git a/HISTORIC_CHANGELOG.md b/HISTORIC_CHANGELOG.md new file mode 100644 index 00000000..923c9581 --- /dev/null +++ b/HISTORIC_CHANGELOG.md @@ -0,0 +1,657 @@ +# Historic change log for LabBuilder + +The release notes in the PowerShell Module manifest cannot exceed 10000 +characters. Due to a bug in the CI deploy pipeline this is not handled. +This file is to temporary move the older change log history to keep the +change log short. + +## [[1.0.5.104] + +- Samples\Sample_WS2019_AzureADConnect.xml: Added sample for installing Azure AD + Connect. +- Convert all DSC configurations to use ActiveDirectoryDsc version + 4.1.0.0. +- `dsclibrary\RODC_SECONDARY.DSC.ps1`: + - Enable RODC creation because it is supported by ActiveDirectoryDsc. +- `dsclibrary\DC_FORESTPRIMARY.DSC.ps1`: + - Enabled customizing of Domain NetBios name. +- `Get-LabVm.ps1`: + - Clean up code style. +- `Enable-LabWSMan.ps1`: + - Improved function so that if WinRM Service is stopped it will be started. +- `Get-Lab.ps1`: + - Clean up code style. + - Fix bug reading `configpath` from `settings` node. + - Changed to use `ConvertTo-LabAbsolutePath.ps1` to simplify code. + - Changed to automatically use the `DSCLibrary` folder that comes as part of + the LabBuilder module if the `dsclibrarypath` setting is not specified + in the lab configuration - fixes [Issue-335](https://github.com/PlagueHO/LabBuilder/issues/335). +- `ConvertTo-LabAbsolutePath.ps1`: + - Added function to create an absolute path from a relative lab path. +- Removed `dsclibrarypath` setting from all samples as it is no longer required. +- `Get-LabResourceISO.ps1`: + - Clean up code style. + +## [1.0.4.83] + +- Change `psakefile.ps1` to detect Azure Pipelines correctly. +- Updated `BuildHelpers` support module for CI pipelines to 2.0.10. +- Added PowerShell Gallery badge to `README.md`. +- `Get-LabUnattendFileContent.ps1`: +- Enabled PSRemoting in Unattend.xml (allows DSC to initialize properly on + newer operating systems). +- Enabled local administrator account for Client operating systems + (Windows 10). +- Enabled PowerShell script execution for both 32-bit and 64-bit processes. +- `Connect-LabVM.ps1`: +- Test WinRM connectivity prior to initializing DSC. +- `Install-LabVM.ps1`: + - Check for DSC Configuration section in XML file prior to calling DSC. + +## [1.0.3.69] + +- `dsclibrary\MEMBER_SUBCA.DSC.ps1`: + - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). + - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when + setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). +- `dsclibrary\MEMBER_ROOTCA.DSC.ps1`: + - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). + - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set + it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). + - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when + setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). + - Changed CApolicy.inf RenewalKeyLength to 4096, CNGHashAlgorithm to SHA256 and + LoadDefaultTemplates to 0 - fixes [Issue-324](https://github.com/PlagueHO/LabBuilder/issues/324). +- `dsclibrary\STANDALONE_ROOTCA.DSC.ps1`: + - Correct SubCA resource name to wait for - fixes [Issue-321](https://github.com/PlagueHO/LabBuilder/issues/321). + - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set + it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). + - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when + setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). +- `dsclibrary\STANDALONE_ROOTCA_NOSUBCA.DSC.ps1`: + - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set + it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). + - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when + setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). +- Added `.markdownlint.json` file. +- Fix markdown rule violations in `CHANGELOG.MD`. +- `dsclibrary\MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1`: + - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\STANDALONE_DHCPDNS.DSC.DSC.ps1`: + - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\STANDALONE_INTERNET.DSC.DSC.ps1`: + - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCP.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCP.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCP.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCP.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_DHCP.DSC.ps1`: + - Update to require xDhcpServer resource 2.0.0.0. +- `dsclibrary\MEMBER_NPS_DFSTEST.ps1`: + - Fix to use correct name of the DFSReplicationGroup resource. +- `dsclibrary\MEMBER_WDS.DSC.ps1`: + - Fix configuration. + +## [1.0.2.58] + +- Reword module description in Manifest. +- Fix bug when connecting to a Lab VM when TrustedHosts is empty - fixes + [Issue #314](https://github.com/PlagueHO/LabBuilder/issues/314). +- Moved Schema documentation file into docs folder and converted to + PlatyPS compatible file. +- Cleaned up Schema documentation file to remove most markdown rule + violations. +- Cleaned up README.MD file to remove most markdown rule + violations. +- Fix infinite loop bug occuring in `Stop-Lab` when Lab VM does not + exist - fixes [Issue #316](https://github.com/PlagueHO/LabBuilder/issues/316). +- Fix infinite loop bug occuring in `Start-Lab` when Lab VM does not + exist. +- DSCLibrary\MEMBER_NANO.DSC.ps1: Rename xOfflineDomainJoin to + OfflineDomainJoin - fixes [Issue #317](https://github.com/PlagueHO/LabBuilder/issues/317). + +## [1.0.1.40] + +- Update to use NetworkingDsc 7.0.0.0 and converted DhcpClient + resource to NetIpInterface - fixes [Issue #304](https://github.com/PlagueHO/LabBuilder/issues/304). +- Refactored module manifest generation process to be more reliable + and robust. +- Convert module name to be a variable in PSake file to make it more + easily portable between projects. +- Added samples for Windows Server 2019 - fixes [Issue #305](https://github.com/PlagueHO/LabBuilder/issues/305). +- Cleaned up unit test initialization and fixtures. +- Refactored `Install-Lab` to move Management Switch creation into a + new function `Initialize-LabManagementSwitch` and created unit tests. +- Added more log information to `SetupComplete.cmd` to diagnose issues + with initial machine boot on Windows Server 2019. +- Fix bug in `Remove-LabSwitch` where adapter +- Correct documentation markdown errors. +- Removed `Timeout 30` from the Initial `SetupComplete.cmd` that runs + on each VM when first intialized because it fails to execute on + Windows Server 2019. Replaced with `Start-Sleep` in `SetupComplete.ps1` + that is called by `SetupComplete.cmd` - fixes [Issue #308](https://github.com/PlagueHO/LabBuilder/issues/308). +- Change `Update-LabDSC` so that module copy process to Lab Files will + continue even if destination path is too long. This is to allow DSC + Resource modules that have example filenames that result in a long + path. +- Update CI module dependencies in `Requirements.psd1` to latest version: + - PSScriptAnalyzer: 1.18.0 + - PSDeploy: 1.0.1 + - Platyps: 0.14.0 +- Split Private Lib functions into individual .ps1 files. +- Refactored Private Lib functions to improve code style standards. + +## [1.0.0.6] + +- Samples\Sample_WS2016_DCandDHCPandCA.xml: Added to easily create a Windows + Server 2016 domain with a enterprise root CA. +- Correct certificate authority DSC Resources with ADCSCertificationAuthority + to be IsSingleInstance. +- Convert xNetworking to NetworkingDsc. +- Converted repository structure and build pipeline to more modern standards. +- Convert module to require WMF 5.1 and all the samples to install WMF 5.1. +- DSCLibrary\MEMBER_MEMBER_DHCP*.ps1: Fixed to support xDHCPServer 2.0.0.0. + +## [0.8.4.1160] + +- Clean up markdown errors in README.MD. +- Updated code style to meet current best practices. +- Updated tests to meet Pester v4 guidelines. +- Convert sample labs to use ISCSIDsc resource module. +- Convert sample labs to use FSRMDsc resource module. +- Convert sample labs to use DFSDsc resource module. +- Convert sample labs to use StorageDsc resource module. +- Convert sample labs to use ActiveDirectoryCDDsc resource module. +- Convert sample labs to use SQLServerDsc resource module. +- Fix error that occurs when DSC ConfigurationData is specified + as a filename instead of a hashtable when compiling the MOF file. +- Removed Visual Studio Solution and Proejct files. +- DSCLibrary\MEMBER_DFSHUB.DSC.ps1: Added to enable Sample_WS2016_DFSHubAndSpoke + sample. +- DSCLibrary\MEMBER_DFSSPOKE.DSC.ps1: Added to enable Sample_WS2016_DFSHubAndSpoke + sample. +- Samples\Sample_WS2016_DFSHubAndSpoke.xml: Added to demonstrate Hub and Spoke DFS + replication gorup. + +## [0.8.3.1140] + +- Enforce xNetworking v5.0.0.0 is installed and used - fixes [Issue #289](https://github.com/PlagueHO/LabBuilder/issues/289). +- DSCLibrary\MEMBER_SQLSERVER2014.DSC.ps1: Updated to support v8.0.0.0 of xSQLServer +- DSCLibrary\MEMBER_SQLSERVER2016.DSC.ps1: Updated to support v8.0.0.0 of xSQLServer + +## [0.8.3.1132] + +- Added .vscode\settings.json to force code styles and enable auto formatting in + VS Code. +- Changed WaitVMStarted to check VM is running and also handle blank heartbeat + being returned in Windows 10 15063 (Creators Update) and above. +- Updated LabBuilder to support changes in xNetworking DSC Resource v5.0.0.0 +- Updated DSC sample configurations to support xStorage DSC Resource v3.2.0.0 + +## [0.8.3.1124] + +- DSCLibrary\MEMBER_DHCPNPAS2016.DSC.ps1:Added DSC Library Configuration for + DHCP with NPAS on Windows Server 2016 - see [Issue 283](https://github.com/PlagueHO/LabBuilder/issues/283). + +## [0.8.3.1116] + +- Moved Changelist.md file to root and renamed to CHANGELOG.MD. +- Cleaned up markdown errors in README.MD. +- Updated samples to use latest version of Windows Server 2016 Evaulation ISO. +- Added sample Sample_WS2016_DomainFunctions.xml for creating an Azure Functions + lab. +- Added support for codecoverage analysis using CodeCov.io. + +## [0.8.3.1107] + +- DSCLibrary\MEMBER_CONTAINER_HOST.DSC.ps1: Added DSC Configuration for configuring + a Docker Container host. +- Added support for inserting ODJ files into a VM for joining Nano Servers to an + AD domain. +- Fix error occuring when starting DSC on node with no adapters. +- LabDSCModule class: Added [Version] MinimuVersion property, converted ModuleVersion + property to [Version]. +- Corrected format of Changelist.md. +- Change SubnetMask to PrefixLength in xIPAdress DSC Config created by Get-LabDSCNetworkingConfig. +- Added support for specifying minimum module version in Update-LabDSC to enforce + xNetworking 3.0.0.0 usage. + +## [0.8.3.1068] + +- Added Jenkins build scripts. +- Fix ExposeVirtualizationExtensions when on Windows 10 build 14352 and above. +- DSCLibrary\*_ROOTCA.DSC.ps1: Fix to support 2.0.0.0 of xADCSDeployment resource. +- DSCLibrary\*_SUBCA.DSC.ps1: Fix to support 2.0.0.0 of xADCSDeployment resource. +- Converted AppVeyor.yml to pull Pester from PSGallery instead of Chocolatey. +- Changed AppVeyor.yml to use default image +- - MSFT-MWalker changes Below +- Added support for Version of VM - Only works on latest Windows 10 builds post 14352 +- Added support for generation of VM so Generation 1 VMs can now be created +- Fixed issue with Shared VHDX that prevented their creation. +- Updated SCHEMA for information on Version and Generation +- Fixed several typos in comment sections +- DSC resources created for working with composite DSC resources - non-functional + at this time +- Correctly enable PS Remoting using Enable-PSRemoting cmdlet. +- DSCLibrary\MEMEBER_DSCPULLSERVER.DSC.ps1: Added DSC Library resource for + creating DSC Pull Servers. +- Added additional logging information when copying DSC Resource modules. +- Fix bug when copying DSC Resource modules to LabBuilder Files for VM when DSC + Modules folder does not exist. +- DSCLibrary\MEMBER_SQLSERVER2016.DSC.ps1: Added DSC Library configuration for + installing a SQL Server 2016 from an ISO. +- Fix bug using a NIC team as network adapter bound to an external switch. +- Change AppVeyor script to improve and automate deployment process. +- Mock functions added to unit tests so that can run on machines without Hyper-V + installed. +- Added Windows Server 2016 sample labs. +- Removed old Lab test scripts and replaced with a single Lab test script ```Invoke-LabSample.ps1```. +- Updated all samples to use the filename of the latest Windows Server 2012 R2 + Evaluation ISO. + +## [0.8.3.0] + +- Fix bug where Administrator account is not enabled in Windows client OS. +- Added support for ModulePath attribute on Settings node. + +## [0.8.2.0] + +- Fix bug when creating a new Management adapter for a new Lab and setting a + static MAC address on it. + +## [0.8.1.0] + +- Converted all Write-Verbose calls to Write-LabMessage function. +- Fix bug when creating a new Management adapter for a new Lab. + +## [0.8.0.0] + +- DSCLibrary\MEMBER_SQLSERVER2014.DSC.ps1: Completed DSC Library configuration + for installing a SQL Server 2014 from an ISO. +- Samples\Sample_WS2012R2_DomainSQL2014.xml: Added new Sample for building a + simple domain with a SQL Server. +- Samples\*.xml: DNS Forwarders set to Google for all Samples with Edge nodes. +- Added LabBuilderConfig\Settings attribute requiredwindowsbuild. +- Get-Lab: Added support for preventing a Lab from being used on a host not at + the requiredwindowsbuild build version. +- Samples\Sample_WS2012R2_DomainClustering.xml: Required build version set to 10586. +- Samples\Sample_WS2012R2_DCandDHCPOnly_NAT.xml: Required build version set to 14295. +- Added LabBuilderConfig\Resources attribute ISOPath. +- DSCLibrary\MEMBER.DSC.ps1: Corrected filename. +- DSCLibrary\MEMBER.DSC.ps1: Fixed Configuration name. +- Added InstallRSATTools parameter DSC configurations to enable installation of + applicable RSAT Management tools: + - DC_FORESTCHILDDOMAIN.DSC.ps1 + - DC_FORESTPRIMARY.DSC.ps1 + - DC_SECONDARY.DSC.ps1 + - RODC_SECONDARY.DSC.ps1 + - MEMBER_DNS.DSC.ps1 + - MEMBER_DHCPNPAS.DSC.ps1 + - MEMBER_DHCPDNS.DSC.ps1 + - MEMBER_DHCP.DSC.ps1 + - MEMBER_ROOTCA.DSC.ps1 + - MEMBER_SUBCA.ps1 + +## [0.7.9.0] + +- Fixed failure when creating self-signed certificate on localized systems, by + replacing EKU Names with IDs. +- Fixed support for NAT switches and added Switch attributes NatSubnet and + NatGatewayAddress. +- Private function UpdateSwitchManagementAdapter added. +- Samples\Sample_WS2012R2_DCandDHCPOnly_NAT.xml: Added sample for testing NAT + based Lab switches. +- Improved ShouldProcess messages to be easier to read. +- Utils\Install-LabPackageProvider: Added function for ensuring Package Providers + are installed. +- Utils\Register-LabPackageSource: Added function for ensuring Package surces + are registered. +- Install-Lab: Added checks to ensure required PackageProviders and PackageSources + are available. + +## [0.7.8.0] + +- Install-Lab: + - Force flag added to suppress confirmation messages. + - Will attempt to install WS-Man if not installed, failure will + cause install to fail. +- Disconnect-LabVM: + - Improve handling of adding IPAddress to trusted hosts. +- Get-LabVM: + - LabID will only be prepended to VM Adapter name for adapters not + attached to an External switch. + +## [0.7.7.0] + +- Samples\Sample_WS2016TP5_DCandDHCPOnly.xml: + - Set edition in Nano Server Template VHD. + - Fixed WS2016 Template VHD edition names. + - Fixed Template name. + +## [0.7.6.0] + +- Added .vscode\tasks.json file to allow quick conversion of LabBuilder Schema + to MD. +- Moved existing Libs into Libs\Private folder. +- Updated samples and tests to support Windows Server 2016 TP5. +- Updated Visual Studio Project and Soltion files. +- Fix Nano Server localization package filename support for TP5. + +## [0.7.5.0] + +- Added VM InstanceCount attribute for creating multiple copies a VM in a Lab. +- Added $Script:CurrentBuild variable to allow easier access to OS build version. +- Fix to prevent ExposeVirtualizationExtensions from being applied on Lab Hosts + that don't support it. +- Samples\Sample_WS2012R2_DCandDHCPandEdge.ps1: Added sample for creating Lab with + DC, DHCP and Edge servers. +- DSCLibrary\MEMBER_JENKINS.DSC.ps1: Added DSC Library configuration for creating + a Domain Joined Jenkins CI Server. +- DSCLibrary\STANDALONE_JENKINS.DSC.ps1: Added DSC Library configuration for + creating a Standalone Jenkins CI Server. +- Install-Lab will now stop if error occurs creating Lab Management switch or adapter. +- Added support for Get-Lab to work with config files with a relative path. +- Improved handling of Initialize-LabSwitches when Multiple External adapters are + available and/or already in use by external switches. +- Improved Localization support for Integration Services. + +## [0.7.4.0] + +- lib\vm.ps1: WaitWMStarted - name of integrationservice "heartbeat" detected by + id to be culture neutral +- DSCLibrary\MEMBER_ADFS.DSC.ps1: Enable ADFS Firewall Rules. +- AppVeyor.yml: Module Manifest version number always set to match build version. +- DSCLibrary\MEMBER_IPAM.DSC.ps1:Added DSC Library Configuration for IPAM Server. +- DSCLibrary\MEMBER_FAILOVERCLUSTER_*.DSC.ps1: Added iSCSI Firewall Rules to allow + iSNS registration. +- DSCLibrary\MEMBER_ADFS.DSC.ps1: Added DSC Library configuration for ADRMS. +- DSCLibrary\MEMBER_SQLSERVER2014.DSC.ps1: Added Incomplete DSC Library configuration + for SQL Server 2014. +- Support\Convert-WindowsImage.ps1: Updated to March 2016 version so that DISM + path can be specified. +- labbuilder-schema.xsd: Added Settings\DismPath attribute so that path to DISM + can be specified. +- Failure to validate Lab configuration XML will terminate any cmdlet immediately. +- Any failure in Install-Lab will cause immediate build termination. +- Support\Convert-WindowsImage.ps1: Fixed incorrect error reported when invalid + Edition is specified. +- SetModulesInDSCConfig: Ensure each Import-DSCResource ends up on a new line. +- DSCLibrary\MEMBER_NANO.DSC.ps1: Added DSC Library configuration for joining a + Nano server to n AD Domain. +- labbuilder-schema.xsd: Fixed VM attribute descriptions. +- Added CertificateSource attribute to VM to support controlling where any Lab + Certificates should be generated from when initializing a Lab VM. +- Generalized Nano Server package support. +- Both ResourceMSU and Nano Server packages can now be installed on Template VHDs + and Virtual Machines. +- Automatically add Microsoft-NanoServer-DSC-Package.cab to new Nano Server VMs. +- Added BindingAdapterName and BindingAdapterMac attribute to switch element to + allow control over bound adapter. +- GetCertificatePsFileContent Changed so that PFX certificate imported into Root + store for non Nano Servers. +- Automatically set xNetworking version in DSC Networking config to that of the + highest version available on the Lab Host. + +## [0.7.3.0] + +- DSCLibrary\MEMBER_FAILOVERCLUSTER_FS.DSC.ps1: Added ServerName property to + contain name of ISCSI Server. +- samples\Sample_WS2012R2_DomainClustering.xml: Added ServerName property to all + Failover Cluster servers DSC properties. +- docs\labbuilderconfig-schema.md: Converted to UTF-8 to eliminate issues with Git. +- support\Convert-XSDToMD.ps1: Added code to convert transformed output to UTF-8. +- Start-Lab: Improved readability if timeout detect code. +- Stop-Lab: Improved readability if timeout detect code. + Ensure all VMs are stopped in a Bootphase, even if timeout occurs. +- StartDSCDebug.ps1: Added a WaitForDebugger parameter to StartDSCDebug.ps1 that + will cause LCM to start with debugging mode enabled. +- Lib\Type.ps1: File removed and content moved to header of LabBuilder.psm1 so + that types were available outside the module context. +- Stop-Lab: Removed Boot Phase timeout because Stop-VM does not return until VM shutdown. +- Added support for ISO resources to be specified in the Lab configuration. +- Added support for DVD Drives in Lab VM configuration. +- DSCLibrary\MEMBER_ADFS.DSC.ps1: Added DSC Library Configuration for ADFS. +- samples\Sample_WS2012R2_MultiForest_ADFS.ps1: Added Sample Lab for creating + multiple forests for ADFS testing. +- DSCLibrary\MEMBER_REMOTEACCESS_WAP.DSC.ps1: Added DSC Library Configuration for + Remote Access and Web Application Proxy. +- DSCLibrary\MEMBER_ADFS.DSC.ps1: Install WID. +- DSCLibrary\MEMBER_WEBSERVER.ps1: Created resource for IIS Web Servers. +- samples\Sample_WS2012R2_MultiForest_ADFS.xml: Added Web Application Servers. +- .github\*: Added general documentation on contributing to this project. + +## [0.7.2.0] + +- DSCLibrary\MEMBER_FAILOVERCLUSTER_FS.DSC.ps1: Changed to install most File + Server features on cluster nodes. +- DSCLibrary\MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1: Created resource for Failover + Cluster DHCP Server nodes. +- Readme.md: Additional Documentation added. + +## [0.7.1.0] + +- Get-LabDSCNetworkingConfig: Fix DSC error occuring when a blank DNS Server + address or Default Gateway address is set on an Adapter. +- InitializeVhd: Prevent unnecessary results of disk partitioning and volume + creation to console. +- UpdateVMDataDisks: Fix to incorrectly reported Data VHD type change error. +- DSCLibrary\MEMBER_BRANCHCACHE_HOST.DSC.ps1: Created resource for BranchCache + Hosted Servers. +- DSCLibrary\MEMBER_FILESERVER_*.DSC.ps1: Added BranchCache for File Servers feature. +- Readme.md: Added 'Lab Installation Process in Detail' section. + +## [0.7.0.0] + +- Initialize-LabSwitch: External switch correctly sets Adapter Name. +- IsAdmin: Function removed because was not useful. +- dsclibrary\DC_FORESTDOMAIN.DSC: New DSC Library config for creating child + domains in an existing forest. +- Samples\Sample_WS2012R2_MultiForest.xml: Added child domains. +- Get-LabSwitch: Converted to output array of LabSwitch objects. +- Initialize-LabSwitch: + - Converted to use LabSwitch objects. + - Fixed bug setting VLAN Id on External and Internal Switch Adapters. +- Remove-LabSwitch: Converted to use LabSwitch objects. +- Tests\Test_Sample_*.ps1: Test-StartLabVM function fixed. +- DSCLibrary\MEMBER_*.DSC.ps1: Updated parameter examples to include DCName parameter. +- DSCLibrary\DC_*.DSC.ps1: Added DNS Zone and forwarder options (setting forwarder + requires xDNSServer 1.6.0.0). +- DSCLibrary\MEMBER_DNS.DSC.ps1: Created resource for member DNS servers. +- Get-LabVMTemplateVHD: Converted to output array of LabVMTemplateVHD objects. +- Initialize-LabVMTemplateVHD: + - Converted to use LabVMTemplateVHD objects. + - Check added to ensure Drive Letter is assigned to mounted ISO. +- Remove-LabVMTemplateVHD: Converted to use LabVMTemplateVHD objects. +- Readme.md: Windows Management Framework 5.0 (WMF 5.0) section added. +- DSCLibrary\DC_FORESTDOMAIN.DSC.ps1: Changed name to DC_FORESTCHILDDOMAIN.DSC.ps1 + to better indicate purpose. +- Get-LabVMTemplate: Converted to output array of LabVMTemplate objects. +- Initialize-LabVMTemplate: Converted to use LabVMTemplate objects. +- Remove-LabVMTemplate: Converted to use LabVMTemplate objects. +- Get-LabVM: Converted to output array of LabVM objects. +- Initialize-LabVM: Converted to use LabVM objects. +- Remove-LabVM: Converted to use LabVM objects. +- Lib\dsc.ps1: All functions converted to use LabVM objects. +- Lib\vm.ps1: All functions converted to use LabVM objects. +- Lib\vhd.ps1: All functions converted to use LabVM objects. +- InitializeVhd: Fix error when attempting to create a new VHD/VHDx with a + formatted volume. + +## [0.6.0.0] + +- New-Lab: Function added for creating a new Lab configuration file and basic + folder structure. +- Get-Lab: Redundant checks for XML valid removed because convered by XSD schema + validation. +- Added Lib\Type.ps1 containing customg LabBuilder Classes and Enumerations. +- Added functions for converting XSD schema to MD. +- Fix to Nano Server Package caching bug. +- DSC Library Domain Join process improved. +- DSC\ConfigFile attribute supports rooted paths. +- VM\UnattendFile attribute supports rooted paths. +- VM\SetupComplete attribute supports rooted paths. +- DSC\ConfigFile Lab setting supports rooted paths. +- VM\UseDifferencingBootDisk default changed to 'Y'. +- Get-ModulesInDSCConfig: + - Returns Array of objects containing ModuleName and ModuleVersion. + - Now returns PSDesiredStateConfiguration module if listed -expected that + calling function will ignore if required. + - Added function to set the Module versions in a DSC Config. +- Update-LabDSC: Updated to set Module versions in DSC Config files. +- DSC Library: Module Version numbers removed from all DSC Library Configrations. +- Test Sample file code updated to remove switches when lab uninstalled. +- Uninstall-Lab: Management Switch automatically removed when Lab uninstalled. +- Configuration Schema: + - Added Resources\MSU element. + - Added Settings\Resource attribute. + - Removed VM\Install element support, superceeded by Packages attribute. +- Get-LabResourceModule: Function added. +- Initialize-LabResourceModule: Function added. +- Get-LabResourceMSU: Function added. +- Initialize-LabResourceMSU: Function added. +- Install-Lab: + - Fix CheckEnvironment bug. + - Added calls to Initialize-LabResourceModule and Initialize-LabResourceMSU. +- DownloadResources: + - Utility function removed, superceeded by Initialize-LabResourceModule and + Initialize-LabResourceMSU functions. +- Get-LabVM: Removed Install\MSU support. +- InitializeBootVM: + - Removed Install\MSU support. + - Added support for installing Packages from Resources\MSU element. +- Initialize-LabVMTemplateVHD: + - MSU Resources specified in Packages attribute are added to Template VHD when + converted. +- Initialize-LabVMTemplate: + - MSU Resources specified in Packages attribute are added to Template when copied. + +## [0.5.0.0] + +- BREKAING: Renamed Config parameter to Lab parameter to indicate the object is + actually an object that also stores Lab state information. +- Remove-LabVM: Removed parameter 'RemoveVHDs'. Added parameter RemoveVMFolder + which causes the VM folder and all contents to be deleted. +- Uninstall-Lab: Renamed "Remove" parameters to be singular names rather than plural. +- Uninstall-Lab: Added parameter 'RemoveLabFolder' which will cause the entire + Lab folder to be deleted. +- Uninstall-Lab: Added ShouldProcess support to ask user to confirm actions. +- Update-Lab: Added function which just calls Install-Lab. +- Start-LabVM: Renamed function to Install-LabVM so that it is not confused with + Start-VM. +- *-LabSwitch: Added Name array parameter to allow filtering of switches to work + with. +- *-LabVMTemplateVHD: Added Name array parameter to allow filtering of VM Template + VHDs to work with. +- *-LabVMTemplate: Added Name array parameter to allow filtering of VM Templates + to work with. +- *-LabVM: Added Name array parameter to allow filtering of VMs to work with. +- Samples: Updated sample code with additional examples. +- Help completed for all exported cmdlets. +- Get-LabVM: XML now validated against labbuilderconfig-schema.xsd in Schemas + folder when loaded -unless SkipXMLValidation switch is passed. +- All sample and test configuration XML files validated against + labbuilderconfig-schema.xsd in schemas folder when unit tests run. +- All sample and test configuration XML files updated with namespace -> xmlns="labbuilderconfig". + +## [0.4.2.0] + +- Add bootorder VM attribute for controlling stop-lab/start-lab order. +- Added Start-Lab and Stop-Lab cmdlets. +- *-Lab cmdlet documentation added to Readme.md + +## [0.4.1.0] + +- VHDParentPath setting made optional. Defaults to "Virtual Machine Hard Disks" + under config. +- Initialize-LabConfiguration function will create labpath and vhdparentpath + folders if not exist. +- Removed Test-LabConfiguration function and tests moved to Get-LabConfiguration. +- Added Disconnect-LabVM function to disconnect from a connect Lab VM. +- Fixed bug setting TrustedHosts when connecting to Lab VM. +- Added code to revert TrustedHosts when disconnecting from Lab VM. +- All non-exported supporting functions moved into separate support libraries. +- Add support for LabId setting that gets prepended to Lab resources. +- Added LabBuilderConfig schema in schema folder. +- Added LabPath parameter to Install-Lab, Uninstall-Lab and Get-LabConfiguration. +- Fix exception in Disconnect-LabVM. +- Fixed Unit tests to retain current folder location. +- Added PS ScriptAnalyzer Error tests to unit tests. +- Display PS ScriptAnalyzer Warnings when unit tests run. +- Remove-LabVMTemplateVHD function added and will be called from Uninstall-Lab. + +## [0.4.0.0] + +- Some secondary non-exported functions moved into separate support libraries. +- Initialize-LabVMTemplate caches NanoServerPackages from VHD template folder to + Lab folder. +- Fix exception connecting to VM when TrustedHosts is set to '*'. +- Fix path Lab VM files are created. +- Support for creating Certificates for Nano Servers on the host added. + +## [0.3.3.0] + +- Changed Get-LabSwitch Unit tests to use PesterTestConfig.OK.xml. +- Added support for configuring Nano Server packages for each VM. +- Removed MAC Address minimum/maximum value settings from configuration. +- Fix bug with Wait-LabInitVM failing to copy InitialSetupComplete.txt file. +- Added VMRootPath and LabBuilderFilesPath properties Get-LabVM array containing + path where VM and LabBuilder files should be stored respectively. +- Added TemplateVHD in templates/template config node for specifying the template + VHD. + +## [0.3.2.0] + +- Added Initialize-VHD function. +- Added support for formatting Data VHDs. +- Added support for copying multiple folders to DataVHDs. +- Updated Download-ResourceModule to use Invoke-LabDownloadAndUnzipFile function. +- Changed name of Settings\VMPath attribute to LabPath. + +## [0.3.1.0] + +- Disable 'Access Denied' test when connecting to new VM because this error is + reported by VM that is still booting up. +- Correct Verbose message shown when Integration Services enabled. +- Added Verbose message to indicate creation of VM Initialization files. +- Correct Verbose message not appearing when mounting VM boot disk image file. +- Moved DSC Config message into Localization data. +- Disabled automatic module push to PSGallery till version 1.0.0.0 or greater. + +## [0.3.0.0] + +- Fix to Module detection regex. +- Updated AppVeyor.yml to push more artifacts. +- Fix issue preventing timeout from triggering. +- Improved handling of Remoting connection by moving into a new function Connect-LabVM +- IP Address of VMs automatically added to WS-Man Trusted Hosts to enable HTTP + remoting connection. +- Prevent error if Panther folder doesn't exist in VHD image when creating a new + VM. +- Add support for multiple data disks for each VM. +- Add support for creating new data disks by cloning exising VHDs. +- Support for Fixed, Differencing and Shared data disks. +- JSON Object comparison unit tests fixed. +- AppVeyor build status badge added. +- Add support for VM Integration Services flag. +- Initialize-Lab- arrays made optional and will be pulled from config if not passed. +- Configuration parameter changed to Config to reduce size/typing. +- Support for creating VHD boot disks from ISO via TemplateVHD nodes in XML. + +## [0.2.0.0] + +- Code cleanup and refactoring. + +## [0.1.0.0] + +- Initial Release. diff --git a/LICENSE b/LICENSE index 9cecc1d4..56b73b50 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,21 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -<http://www.gnu.org/licenses/>. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -<http://www.gnu.org/philosophy/why-not-lgpl.html>. +MIT License + +Copyright (c) Daniel Scott-Raynsford + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index 1adab1f2..cbb8899a 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,194 +1,3 @@ @{ - # Use Severity when you want to limit the generated diagnostic records to a - # subset of: Error, Warning and Information. - # Uncomment the following line if you only want Errors and Warnings but - # not Information diagnostic records. - #Severity = @('Error','Warning') - - # Use IncludeRules when you want to run only a subset of the default rule set. - #IncludeRules = @('PSAvoidDefaultValueSwitchParameter', - # 'PSMissingModuleManifestField', - # 'PSReservedCmdletChar', - # 'PSReservedParams', - # 'PSShouldProcess', - # 'PSUseApprovedVerbs', - # 'PSUseDeclaredVarsMoreThanAssigments') - - # Use ExcludeRules when you want to run most of the default set of rules except - # for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules - # and ExcludeRules, the rule will be excluded. - #ExcludeRules = @('PSAvoidUsingWriteHost','PSMissingModuleManifestField') - - # You can use the following entry to supply parameters to rules that take parameters. - # For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you - # want to allow. - #Rules = @{ - # Do not flag 'cd' alias. - # PSAvoidUsingCmdletAliases = @{Whitelist = @('cd')} - - # Check if your script uses cmdlets that are compatible on PowerShell Core, - # version 6.0.0-alpha, on Linux. - # PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")} - #} + Severity = @('Error', 'Warning', 'Information') } - -# SIG # Begin signature block -# MIIdhQYJKoZIhvcNAQcCoIIddjCCHXICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB -# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR -# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUIDKoYRpNV7SJR5ACPVZ/wRlV -# XwWgghhTMIIEwjCCA6qgAwIBAgITMwAAALu2dyRxSiAAIAAAAAAAuzANBgkqhkiG -# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G -# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw -# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODQ3 -# WhcNMTgwOTA3MTc1ODQ3WjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp -# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw -# b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046 -# MERFOC0yREM1LTNDQTkxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl -# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC48+U38sLxQNu8 -# OO1wnT9mKeHv+f/jxafTFXzx9VF59IK/n/jLv4HIXt8ucy3KjBTM5Jf6D0nQlI4h -# Sizjrn6lO61q+V8oZiYYhjgR258rg8MDIrPpZMxK6OmD0d1wtksHW1cG21YKg5jg -# idT2hmQBpiL9Cra3ccY5keu0kl6OfZFoj4DF0i0JRVFSy1C9gKP4H950XIjlA2Yo -# TWN0LuHEHYMvwD1mOpAq2dVwPZh6xeNnpV8U/qLneyb9I/SqY/87tsZCn4FH7R3x -# 0TgK2eRwpWXfwGbUb1R/UTLd20aQ+my4NWwSsndeG+0vsYwaF40heB2lo1ThmByr -# OTBmEosTAgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQUj9yNX+4+R8GZ7rcy4MdnJHXO -# KkswHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ -# oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv -# TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB -# BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z -# b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG -# 9w0BAQUFAAOCAQEAcMI8Q0PxQVvxZSD1fjszuD6VF/qPZjKZj9WLTjWjZT2k9lzG -# yvSL7vy9J7lnyMATrbm5ptqAfdonNygLaBm05MnrIvgPJYK89wyTIyS1u71ro7z+ -# EVrGPaKZiD+WvH8SWP+OWZQNf55fEL8tZo+a1oHm3lUARi5rR916OQvb4UnCENyV -# g8IfmupnwpxHcmIBUWZtTKAuKmuX/c8G2z4KJ8WhruYjPDWYQXJrQ5t7PhZa19Ge -# kOOtigge9EKIAWhZUJkw9fnfRm2IFX0gWtOzRXVNhR109ISacbNxd0oUboRYHmlq -# wGrOz64/3SDdOeN7PjvLwFmThuoXIsxrjQD8ODCCBgAwggPooAMCAQICEzMAAADD -# Dpun2LLc9ywAAAAAAMMwDQYJKoZIhvcNAQELBQAwfjELMAkGA1UEBhMCVVMxEzAR -# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p -# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2ln -# bmluZyBQQ0EgMjAxMTAeFw0xNzA4MTEyMDIwMjRaFw0xODA4MTEyMDIwMjRaMHQx -# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt -# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xHjAcBgNVBAMTFU1p -# Y3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -# ggEBALtX1zjRsQZ/SS2pbbNjn3q6tjohW7SYro3UpIGgxXXFLO+CQCq3gVN382MB -# CrzON4QDQENXgkvO7R+2/YBtycKRXQXH3FZZAOEM61fe/fG4kCe/dUr8dbJyWLbF -# SJszYgXRlZSlvzkirY0STUZi2jIZzqoiXFZIsW9FyWd2Yl0wiKMvKMUfUCrZhtsa -# ESWBwvT1Zy7neR314hx19E7Mx/znvwuARyn/z81psQwLYOtn5oQbm039bUc6x9nB -# YWHylRKhDQeuYyHY9Jkc/3hVge6leegggl8K2rVTGVQBVw2HkY3CfPFUhoDhYtuC -# cz4mXvBAEtI51SYDDYWIMV8KC4sCAwEAAaOCAX8wggF7MB8GA1UdJQQYMBYGCisG -# AQQBgjdMCAEGCCsGAQUFBwMDMB0GA1UdDgQWBBSnE10fIYlV6APunhc26vJUiDUZ -# rzBRBgNVHREESjBIpEYwRDEMMAoGA1UECxMDQU9DMTQwMgYDVQQFEysyMzAwMTIr -# YzgwNGI1ZWEtNDliNC00MjM4LTgzNjItZDg1MWZhMjI1NGZjMB8GA1UdIwQYMBaA -# FEhuZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93 -# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAx -# MS0wNy0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8v -# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFf -# MjAxMS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEA -# TZdPNH7xcJOc49UaS5wRfmsmxKUk9N9E1CS6s2oIiZmayzHncJv/FB2wBzl/5DA7 -# EyLeDsiVZ7tufvh8laSQgjeTpoPTSQLBrK1Z75G3p2YADqJMJdTc510HAsooNGU7 -# OYOtlSqOyqDoCDoc/j57QEmUTY5UJQrlsccK7nE3xpteNvWnQkT7vIewDcA12SaH -# X/9n7yh094owBBGKZ8xLNWBqIefDjQeDXpurnXEfKSYJEdT1gtPSNgcpruiSbZB/ -# AMmoW+7QBGX7oQ5XU8zymInznxWTyAbEY1JhAk9XSBz1+3USyrX59MJpX7uhnQ1p -# gyfrgz4dazHD7g7xxIRDh+4xnAYAMny3IIq5CCPqVrAY1LK9Few37WTTaxUCI8aK -# M4c60Zu2wJZZLKABU4QBX/J7wXqw7NTYUvZfdYFEWRY4J1O7UPNecd/311HcMdUa -# YzUql36fZjdfz1Uz77LKvCwjqkQe7vtnSLToQsMPilFYokYCYSZaGb9clOmoQHDn -# WzBMfIDUUGeipe4O6z218eV5HuH1WBlvu4lteOIgWCX/5Eiz5q/xskAEF0ZQ1Axs -# kRR97sri9ibeGzsEZ1EuD6QX90L/P5GJMfinvLPlOlLcKjN/SmSRZdhlEbbbare0 -# bFL8v4txFsQsznOaoOldCMFFRaUphuwBMW1edMZWMQswggYHMIID76ADAgECAgph -# Fmg0AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20x -# GTAXBgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBS -# b290IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0 -# MDMxMzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw -# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x -# ITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcN -# AQEBBQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP -# 7tGn0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySH -# nfL0Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUo -# Ri4nrIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABK -# R2YRJylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSf -# rx54QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGn -# MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMP -# MAsGA1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQO -# rIJgQFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZ -# MBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJv -# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1Ud -# HwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By -# b2R1Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYI -# KwYBBQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWlj -# cm9zb2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3 -# DQEBBQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKi -# jG1iuFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV -# 3U+rkuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5 -# nGctxVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tO -# i3/FNSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbM -# UVbonXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXj -# pKh0NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh -# 0EPpK+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLax -# aj2JoXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWw -# ymO0eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma -# 7kng9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TCCB3ow -# ggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYT -# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD -# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS -# b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoX -# DTI2MDcwODIxMDkwOVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 -# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh -# dGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCC -# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6qghBNNLry -# tlghn0IbKmvpWlCquAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJDXlk -# h36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv56sI -# UM+zRLdd2MQuA3WraPPLbfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5 -# pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd -# 6IlPhBryoS9Z5JA7La4zWMW3Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9T -# upwPrRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOn -# qWbsYR9q4ShJnV+I4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKC -# X9vAFbO9G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkw -# p6uO3+xbn6/83bBm4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo -# 8e1twyiPLI9AN0/B4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE2rCIF96e -# TvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIBADAd -# BgNVHQ4EFgQUSG5k5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAweCgBT -# AHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw -# FoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov -# L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0 -# MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKG -# Qmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0 -# MjAxMV8yMDExXzAzXzIyLmNydDCBnwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMw -# gYMwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv -# ZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwA -# XwBwAG8AbABpAGMAeQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0B -# AQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVfLiw++MNy -# 0W2D/r4/6ArKO79HqaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS0LD9 -# a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELukqQUM -# m+1o+mgulaAqPyprWEljHwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMO -# r5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycSca -# f7H0J/jeLDogaZiyWYlobm+nt3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWn -# duVAKmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1 -# HxS+YWG18NzGGwS+30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnF -# sZulP0V3HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9az -# I2h15q/6/IvrC4DqaTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/ -# +6jMpF3BoYibV3FWTkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0xggScMIIE -# mAIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G -# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgw -# JgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAAww6b -# p9iy3PcsAAAAAADDMAkGBSsOAwIaBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQB -# gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkE -# MRYEFJgeU4N8HYWhJ7v7wDIUzp23ts35MFAGCisGAQQBgjcCAQwxQjBAoBaAFABQ -# AG8AdwBlAHIAUwBoAGUAbABsoSaAJGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9Q -# b3dlclNoZWxsIDANBgkqhkiG9w0BAQEFAASCAQBx+oNf0RycZFg4Au+eYV1uFy9C -# 18BtWRMwLCs4VNRu2ah06XqqFrdZZ/PvpF/Ze201Vr/JCYRZfr4HopUzRN2dlK0y -# eXpsg2hQEZV+a2n2wruJUy44N1xONiDYQ5h50yVaJHWP8vehWVG7K5lzXdmSVXyu -# z8sxG4S5+cHDIlbAywyOqsrZi0rmxUV1XlqvGQ1hDFOVB4lrct9sJ20BAGL1uYKd -# xHAYOKTljoxVPOTnYVIB0XoBoennS9Q9Y7ceM2Epcoxg1IP/7ckDjtVUn8EOHC8T -# UMpIe5wLbFtDzaHLI3D084jiyn2AcPKOmd2RP2aVIoyJ4OK5gyj//H2YsKcEoYIC -# KDCCAiQGCSqGSIb3DQEJBjGCAhUwggIRAgEBMIGOMHcxCzAJBgNVBAYTAlVTMRMw -# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN -# aWNyb3NvZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0 -# YW1wIFBDQQITMwAAALu2dyRxSiAAIAAAAAAAuzAJBgUrDgMCGgUAoF0wGAYJKoZI -# hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTcxMDI1MjMzMzE2 -# WjAjBgkqhkiG9w0BCQQxFgQUEo+3rznnFPub2LYKhg9HL0eFvs0wDQYJKoZIhvcN -# AQEFBQAEggEAKAyS359Rk1gdv0Py8HNk5LDBrjtxjKnwcKQfhSJxhkkUkk9C9brI -# ewLofxgd8T3Wdu3tLnXLycmd+1wIWJQptsdfzzrek+tv4cz8W0fJURDwRy2WQdqp -# KPMwjGwxIYqnYbI34Y2X7mEbEY8Hpln1zvgTRoefTEHOw6+gbAjTybuERxxZy4K9 -# DSiL2EXJTGufrbZpvxUbYf5qvtdi91SmXtCt5hPDfHpyL3KP7DcFeEk+dsdjCOEo -# SbPEcXutIq1ZTYyWQvwTrFEvvKnGGeq1gIwuEqH8twMZSP05FdCdDMmqnOz88KWP -# gYaiIUhDOM5J4vDaajAgk6EaEWJ1Nwp/kw== -# SIG # End signature block diff --git a/README.md b/README.md index a9e69dcf..5eda0dde 100644 --- a/README.md +++ b/README.md @@ -9,59 +9,63 @@ ## Module Build Status -| Branch | AzurePipelines CI | AppVeyor CI | Code Coverage | -| --- | --- | --- | --- | -| dev | [![ap-image-dev][]][ap-site-dev] | [![av-image-dev][]][av-site-dev] | [![cc-image-dev][]][cc-site-dev] | -| master | [![ap-image-master][]][ap-site-master] | [![av-image-master][]][av-site-master] | [![cc-image-master][]][cc-site-master] | - -[ap-image-dev]: https://dev.azure.com/dscottraynsford/GitHub/_apis/build/status/PlagueHO.LabBuilder?branchName=dev -[ap-site-dev]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=5 -[av-image-dev]: https://ci.appveyor.com/api/projects/status/rcg7xmm97qhg2bjr/branch/dev?svg=true -[av-site-dev]: https://ci.appveyor.com/project/PlagueHO/labbuilder/branch/dev -[cc-image-dev]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/dev/graph/badge.svg -[cc-site-dev]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/dev - -[ap-image-master]: https://dev.azure.com/dscottraynsford/GitHub/_apis/build/status/PlagueHO.LabBuilder?branchName=master -[ap-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=5 -[av-image-master]: https://ci.appveyor.com/api/projects/status/rcg7xmm97qhg2bjr/branch/master?svg=true -[av-site-master]: https://ci.appveyor.com/project/PlagueHO/labbuilder/branch/master -[cc-image-master]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/master/graph/badge.svg -[cc-site-master]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/master +| Branch | Azure Pipelines | Automated Tests | Code Quality | +| ------ | -------------------------------------- | -------------------------------------- | -------------------------------------- | +| master | [![ap-image-master][]][ap-site-master] | [![ts-image-master][]][ts-site-master] | [![cq-image-master][]][cq-site-master] | ## Summary -This module will build a multiple machine Hyper-V Lab environment from an XML configuration file and other optional installation scripts. +This module will build a multiple machine Hyper-V Lab environment from an XML +configuration file and other optional installation scripts. ## Introduction -While studying for some of my Microsoft certifications I had a need to quickly and easily spin up various Hyper-V Lab environments so that I could experiment with and learn the technologies involved. +While studying for some of my Microsoft certifications I had a need to quickly +and easily spin up various Hyper-V Lab environments so that I could experiment +with and learn the technologies involved. -Originally I performed this process manually, creating Hyper-V VM's and environments to suit. But as the complexity of the Lab environment increased (e.g. take multi-tier PKIs) manually building these Labs became unmanageable. Also, if I wanted to repeat a particular process multiple times I would have to either snapshot multiple VMs or manually back them all up. This quickly became unsupportable as snapshots slows VMs down and constant backups of large Hyper-V environments was slow and also limited by space. This gave me a basic set of requirements for this module. +Originally I performed this process manually, creating Hyper-V VM's and environments +to suit. But as the complexity of the Lab environment increased (e.g. take +multi-tier PKIs) manually building these Labs became unmanageable. Also, if I +wanted to repeat a particular process multiple times I would have to either +snapshot multiple VMs or manually back them all up. This quickly became +unsupportable as snapshots slows VMs down and constant backups of large Hyper-V +environments was slow and also limited by space. This gave me a basic set of +requirements for this module. -So as a solution to these problems I decided that I wanted a declarative approach to automating the process of building a Lab environment. +So as a solution to these problems I decided that I wanted a declarative +approach to automating the process of building a Lab environment. This had the following advantages: -+ Building a new Lab with multiple VMs was automated. -+ Creation of the actual Lab VMs could be done without supervision. -+ Once a basic Lab was created more complex Lab environments could be created by cloning the original XML configuration and tailoring it. -+ Configuration files could be distributed easily. -+ Because the post setup configuration of the Lab VM machines was performed via DSC this gave me an opportunity to work with DSC to a greater depth. -+ The configuration files could be created by people without knowledge of PowerShell or DSC. -+ UI based applications could be easily created to generate the configuration XML. +- Building a new Lab with multiple VMs was automated. +- Creation of the actual Lab VMs could be done without supervision. +- Once a basic Lab was created more complex Lab environments could be created by +cloning the original XML configuration and tailoring it. +- Configuration files could be distributed easily. +- Because the post setup configuration of the Lab VM machines was performed via +DSC this gave me an opportunity to work with DSC to a greater depth. +- The configuration files could be created by people without knowledge of PowerShell +or DSC. +- UI based applications could be easily created to generate the configuration XML. ## Goals The general goals of this module are: -+ **One-Click Create**: Enable "one-click" creation of a Hyper-V Lab environment. -+ **Easy Configuration**: Enable non-developers to easily define Lab environments. -+ **Multiple Labs**: Support multiple Lab environments on the same Hyper-V host. -+ **Stretched Labs**: Allow a Lab environment to span or be installed on a remote Hyper-V host. -+ **Lab Isolation**: Ensure that multiple Lab environments are completely isolated from each other. -+ **Minimal Disk Usage**: Minimize Lab footprint by utilizing differencing disks where possible. -+ **Configuration Flexibility**: Allow GUI based tools to be easily created to create Lab configurations. -+ **Extensible**: Enable new Lab VM machine types to be configured by supplying different DSC library resources. +- **One-Click Create**: Enable "one-click" creation of a Hyper-V Lab environment. +- **Easy Configuration**: Enable non-developers to easily define Lab environments. +- **Multiple Labs**: Support multiple Lab environments on the same Hyper-V host. +- **Stretched Labs**: Allow a Lab environment to span or be installed on a remote + Hyper-V host. +- **Lab Isolation**: Ensure that multiple Lab environments are completely isolated + from each other. +- **Minimal Disk Usage**: Minimize Lab footprint by utilizing differencing disks + where possible. +- **Configuration Flexibility**: Allow GUI based tools to be easily created to + create Lab configurations. +- **Extensible**: Enable new Lab VM machine types to be configured by supplying + different DSC library resources. ## Requirements @@ -69,19 +73,21 @@ To use this Module you will require on your Lab Host: 1. Operating Systems supported: - + Windows Server 2012 - + Windows Server 2012 R2 - + Windows Server 2016 - + Windows Server 2016, version 1709 - + Windows Server 2016, version 1803 - + Windows Server 2019 - + Windows 8.0 - + Windows 8.1 - + Windows 10 + - Windows Server 2012 + - Windows Server 2012 R2 + - Windows Server 2016 + - Windows Server 2016, version 1709 + - Windows Server 2016, version 1803 + - Windows Server 2019 + - Windows 8.0 + - Windows 8.1 + - Windows 10 1. **Windows Management Framework 5.1 (WMF5.1)** installed. - _WMF 5.1 is installed on Windows 10 and Windows Server 2016 out of the box, but for Windows Server 2012/R2 and Windows 8/8.1 it will need to be installed separately._ + _WMF 5.1 is installed on Windows 10 and Windows Server 2016 out of the box, + but for Windows Server 2012/R2 and Windows 8/8.1 it will need to be + installed separately._ _WMF 5.1 can be downloaded from [here](https://www.microsoft.com/en-us/download/details.aspx?id=54616)._ 1. **Hyper-V** available (which requires intel-VT CPU support). @@ -103,43 +109,58 @@ To use this Module you will require on your Lab Host: ## Contributing -If you wish to contribute to this project, please read the [Contributing.md](/.github/CONTRIBUTING.md) document first. We would be very grateful of any contributions. +If you wish to contribute to this project, please read the [Contributing.md](/.github/CONTRIBUTING.md) +document first. We would be very grateful of any contributions. ## Basic Usage Guide -The use of this module is fairly simple from a process standpoint with the bulk of the work creating a Lab going into the creation of the configuration XML that defines it. But if there is a Lab configuration already available that fits your needs then there is almost nothing to do. +The use of this module is fairly simple from a process standpoint with the bulk +of the work creating a Lab going into the creation of the configuration XML that +defines it. But if there is a Lab configuration already available that fits your +needs then there is almost nothing to do. A Lab consists of the following items: -+ A configuration XML file that defines the Virtual Machines, Switches, the DSC config files and anything else related to how the Lab is set up. -+ Copies of the Windows Operating System Images used in the Lab which are: - + Either VHDs containing Syspreped Windows images. - + Or Windows Installation media ISO files (these will be automatically converted to VHDs for you during Lab creation). -+ Any DSC configuration files that are used to configure the Lab VMs after the OS initial start up has completed. +- A configuration XML file that defines the Virtual Machines, Switches, the DSC + config files and anything else related to how the Lab is set up. +- Copies of the Windows Operating System Images used in the Lab which are: + - Either VHDs containing Syspreped Windows images. + - Or Windows Installation media ISO files (these will be automatically converted + to VHDs for you during Lab creation). +- Any DSC configuration files that are used to configure the Lab VMs after the OS + initial start up has completed. -There are a library of DSC configuration files for various machine types already defined and available for you to use in the **DSCLibrary** folder. +There are a library of DSC configuration files for various machine types already +defined and available for you to use in the **DSCLibrary** folder. ## Installing the LabBuilder Module -The easiest way to download and install the LabBuilder module is using PowerShell Get to download it from the [PowerShell Gallery](https://www.powershellgallery.com/packages/LabBuilder/): +The easiest way to download and install the LabBuilder module is using PowerShell +Get to download it from the [PowerShell Gallery](https://www.powershellgallery.com/packages/LabBuilder/): ```powershell Install-Module -Name LabBuilder ``` -PowerShell Get is built into Windows Management Framework 5.1, which is a requirement of this project, so it should already be installed onto your host. +PowerShell Get is built into Windows Management Framework 5.1, which is a +requirement of this project, so it should already be installed onto your host. If it is not installed, download it from [here](https://www.microsoft.com/en-us/download/details.aspx?id=50395). ## Installing a Lab Once the Lab files are available the process of installing the Lab is simple. -1. Make a folder where all your Lab files will go (e.g. VMs, VHDs, ISOs, scripts) - e.g. c:\MyLab -1. Copy the Lab Configuration XML file into that folder (try one of the sample configurations in the **Samples** folder). -1. Edit the Lab Configuration XML file and customize the Settings to suit (specifically the LabPath setting). -1. Make a folder in your Lab folder for your Windows ISO files called **isofiles** - e.g. c:\MyLab\ISOFiles +1. Make a folder where all your Lab files will go (e.g. VMs, VHDs, ISOs, + scripts) - e.g. c:\MyLab +1. Copy the Lab Configuration XML file into that folder (try one of the sample + configurations in the **Samples** folder). +1. Edit the Lab Configuration XML file and customize the Settings to suit + (specifically the LabPath setting). +1. Make a folder in your Lab folder for your Windows ISO files called + **isofiles** - e.g. c:\MyLab\ISOFiles 1. Copy any ISO files into this folder that your lab will use. -1. Make a folder in your Lab folder for your VHD boot templates (converted from the ISO files) **vhdfiles** - e.g. c:\MyLab\VHDFiles +1. Make a folder in your Lab folder for your VHD boot templates (converted from + the ISO files) **vhdfiles** - e.g. c:\MyLab\VHDFiles 1. Run the following command in an Administrative PowerShell window: ```powershell @@ -148,7 +169,8 @@ Install-Lab -ConfigPath 'c:\MyLab\Configuration.xml' This will create a new Lab using the c:\MyLab\Configuration.xml file. -If you want more verbose output of what is happening during the Lab Install process, use the -verbose parameter: +If you want more verbose output of what is happening during the Lab Install +process, use the -verbose parameter: ```powershell Install-Lab -ConfigPath 'c:\MyLab\Configuration.xml' -Verbose @@ -162,71 +184,105 @@ Once the Lab has been installed, it can be stopped using this PowerShell command Get-Lab -ConfigPath 'c:\MyLab\Configuration.xml' | Stop-Lab ``` -This will shutdown any running Virtual Machines in the Lab in **Reverse Boot Order**, starting with Virtual Machines that have no boot order defined. -LabBuilder will wait for all machines with the same Boot Order to be shut down before beginning shut down of VMs in the next lowest Boot Order. +This will shutdown any running Virtual Machines in the Lab in **Reverse Boot Order**, +starting with Virtual Machines that have no boot order defined. +LabBuilder will wait for all machines with the same Boot Order to be shut down +before beginning shut down of VMs in the next lowest Boot Order. Any Lab Virtual Machine that has already been stopped will be ignored. -_Note: Boot Order is an optional attribute defined in the Lab Configuration that controls the order Lab Virtual Machines should be booted in._ +_Note: Boot Order is an optional attribute defined in the Lab Configuration that +controls the order Lab Virtual Machines should be booted in._ -You can of course just shut down the Virtual Machines in a Lab yourself via Hyper-V (or some other mechanism), but using Stop-Lab ensures the Virtual Machines are shutdown in a specific order defined in the Lab (e.g. Domain Controllers shut down last). +You can of course just shut down the Virtual Machines in a Lab yourself via +Hyper-V (or some other mechanism), but using Stop-Lab ensures the Virtual Machines +are shutdown in a specific order defined in the Lab (e.g. Domain Controllers shut +down last). ## Starting a Lab -Once the Lab has been installed and then stopped, it can be started back up using this PowerShell command: +Once the Lab has been installed and then stopped, it can be started back up using +this PowerShell command: ```powershell Get-Lab -ConfigPath 'c:\MyLab\Configuration.xml' | Start-Lab ``` -This will start up any stopped Virtual Machines in the Lab in **Boot Order**, with Virtual Machines that have no boot order defined being started last. -LabBuilder will wait for all machines with the same Boot Order to be started up fully before beginning start up of VMs in the next highest Boot Order. +This will start up any stopped Virtual Machines in the Lab in **Boot Order**, +with Virtual Machines that have no boot order defined being started last. +LabBuilder will wait for all machines with the same Boot Order to be started up +fully before beginning start up of VMs in the next highest Boot Order. -_Note: Boot Order is an optional attribute defined in the Lab Configuration that controls the order Lab Virtual Machines should be booted in._ +_Note: Boot Order is an optional attribute defined in the Lab Configuration that +controls the order Lab Virtual Machines should be booted in._ -You can of course just start up the Virtual Machines in a Lab yourself via Hyper-V (or some other mechanism), but using Start-Lab ensures the Virtual Machines are started up in a specific order defined in the Lab (e.g. Domain Controllers started up first). +You can of course just start up the Virtual Machines in a Lab yourself via +Hyper-V (or some other mechanism), but using Start-Lab ensures the Virtual Machines +are started up in a specific order defined in the Lab (e.g. Domain Controllers +started up first). ## ISO Files -During the Install process of a Lab, if the template VHD files to use as boot disks for your VMs, LabBuilder will attempt to convert the required ISO files into VHD boot disks for you. -This will only occur if the ISO files required to build a specific VHD file are found in the ISO folder specified by the Lab. - -By default LabBuilder will look in the **isofiles** sub-folder of your Lab folder for any ISO files it needs. -You can change the folder that a Lab looks in for the ISO files by changing/setting the _isopath_ attribute of the _<templatevhds>_ node in the configuration -If it can't find an ISO file it needs, you will be notified of an official download location for trial or preview editions of the ISO files (as long as the LabBuilder configuration you're using contains the download URLs). +During the Install process of a Lab, if the template VHD files to use as boot +disks for your VMs, LabBuilder will attempt to convert the required ISO files +into VHD boot disks for you. +This will only occur if the ISO files required to build a specific VHD file are +found in the ISO folder specified by the Lab. + +By default LabBuilder will look in the **isofiles** sub-folder of your Lab folder +for any ISO files it needs. +You can change the folder that a Lab looks in for the ISO files by +changing/setting the _isopath_ attribute of the _<templatevhds>_ +node in the configuration +If it can't find an ISO file it needs, you will be notified of an official +download location for trial or preview editions of the ISO files (as long as +the LabBuilder configuration you're using contains the download URLs). Some common ISO download locations: -+ Windows Server 2012 R2: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2) -+ Windows 10 Enterprise: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise) -+ Windows Server 2016: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016) -+ Windows Server 2019: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019) +- Windows Server 2012 R2: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2) +- Windows 10 Enterprise: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise) +- Windows Server 2016: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016) +- Windows Server 2019: [https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019) -**Important**: If you are converting Windows Server 2016 ISO files or adding packages to VHDs please see the [Windows Server 2016](#Windows Server 2016) section. +**Important**: If you are converting Windows Server 2016 ISO files or adding +packages to VHDs please see the [Windows Server 2016](#Windows Server 2016) section. Multiple VHD templates may use the same ISO file in a Lab. For example, if multiple editions of an Operating system are used in the same lab. -Once an ISO has been converted to an VHD, it will be stored in the VHDFiles folder in your lab folder. -However, if you are using multiple Labs on the same machine you might want to share these VHD files between mutlpile Lab projects to save having to build and store copies for each Lab. -In that case, you can set the _vhdpath_ attribute of the _<templatevhds>_ node in the configuration to a different relative or absolute path. +Once an ISO has been converted to an VHD, it will be stored in the VHDFiles +folder in your lab folder. +However, if you are using multiple Labs on the same machine you might want to +share these VHD files between mutlpile Lab projects to save having to build +and store copies for each Lab. +In that case, you can set the `vhdpath` attribute of the `<templatevhds>` node +in the configuration to a different relative or absolute path. -The conversion process for a single ISO to VHD can take 10-20 minutes depending on your machine. -For this reason multiple Labs can be configured to use the same path to store these VHDs by changing the _vhdpath_ attribute of the _<templatevhds>_ node in the configuration. +The conversion process for a single ISO to VHD can take 10-20 minutes depending +on your machine. +For this reason multiple Labs can be configured to use the same path to store +these VHDs by changing the `vhdpath` attribute of the `<templatevhds>` node +in the configuration. ## Windows Server 2016 If you are converting a Windows Server 2016 image and your Lab Host is running either: -+ Windows Server 2012 R2 or older -+ Windows 8.1 or older +- Windows Server 2012 R2 or older +- Windows 8.1 or older -**Important:** Only Windows Server 2016 Technical Preview 5 and above are supported with LabBuilder. +**Important:** Only Windows Server 2016 Technical Preview 5 and above are +supported with LabBuilder. -You will need to install an updated version of the DISM before you will be able to add any packages to a Windows Server 2016 ISO. +You will need to install an updated version of the DISM before you will be able +to add any packages to a Windows Server 2016 ISO. This includes building Nano Server Images. -You can get the latest version of the DISM by downloading and installing the [Windows ADK](http://go.microsoft.com/fwlink/?LinkId=293394). -After installing the Windows ADK, you can force LabBuilder to use this version by configuring the "dismpath" attribute in the "settings" element of your LabBuilder configuration file: +You can get the latest version of the DISM by downloading and installing the +[Windows ADK](http://go.microsoft.com/fwlink/?LinkId=293394). +After installing the Windows ADK, you can force LabBuilder to use this version +by configuring the "dismpath" attribute in the "settings" element of your +LabBuilder configuration file: ```xml <labbuilderconfig xmlns="labbuilderconfig" @@ -242,72 +298,125 @@ After installing the Windows ADK, you can force LabBuilder to use this version b ## Lab Installation Process in Detail -When a new Lab is installed onto a Lab Host from a configuration file, LabBuilder performs the following tasks (in order): +When a new Lab is installed onto a Lab Host from a configuration file, LabBuilder +performs the following tasks (in order): 1. **Load Config**: The Lab configuration file is Loaded and validated. - _If the Lab Configuration file is invalid it will be rejected and the Lab will not be installed._ -1. **Create Lab Folder**: A folder on the Lab Host is created to store the Lab files (VM Templates, VMs, Resources etc.). - _The user specifies the location of this folder in the Lab Configuration file or by passing the LabPath parameter to the Install-Lab cmdlet._ -1. **Download Modules**: All Module Resources listed in the Lab Configuration are downloaded to the PowerShell Modules folder if they don't already exist. -1. **Download MSUs**: All MSU Resources listed in the Lab Configuration are downloaded to the Resources folder in the Lab folder if they don't already exist. -1. **Create Lab Switches**: Each Virtual Switch specified in the Lab Configuration is created or updated in Hyper-V. -1. **Create Management Switch**: A Management Internal Virtual Switch is created in Hyper-V for this Lab. - _Each Lab has it's own Management Internal Virtual Switch which will be named "Lab Management" with the Lab ID prepended to it._ - _It will be assigned a VLAN Id of 99, which can be overridden in the Lab Configuration file._ - _The Lab Management switch is for the Lab Host to communicate with the Lab Guest Virtual Machines so must not be removed._ -1. **Create Template VHDs**: Any Template VHD files that don't exist but should are created from the appropriate ISO files. - _A Lab Configuration file may not list any Template VHD files, or it may list them but not specify source ISO files to create the VHD files from._ - _Instead the Templates may directly link to a VHD file or an existing Hyper-V Virtual Machine._ -1. **Create VHD Templates Folder**: A folder within the Lab folder will be created to store the Virtual Hard Disk Template files. - _This folder is usually called 'Virtual Hard Disk Templates'._ - _This folder will be used to store Template VHD files or Differencing Disk Parent VHD files for use as a Boot Disk for Lab Virtual Machines._ - _A VHD file in this folder can be used as a Template or a Differencing Disk Parent or both._ -1. **Copy VHD Templates**: Get the list of Templates in the Lab Configuration and copy the VHD specified to the Lab Virtual Hard Disk Templates folder. + _If the Lab Configuration file is invalid it will be rejected and the Lab + will not be installed._ +1. **Create Lab Folder**: A folder on the Lab Host is created to store the Lab + files (VM Templates, VMs, Resources etc.). + _The user specifies the location of this folder in the Lab Configuration file + or by passing the LabPath parameter to the Install-Lab cmdlet._ +1. **Download Modules**: All Module Resources listed in the Lab Configuration + are downloaded to the PowerShell Modules folder if they don't already exist. +1. **Download MSUs**: All MSU Resources listed in the Lab Configuration are + downloaded to the Resources folder in the Lab folder if they don't already + exist. +1. **Create Lab Switches**: Each Virtual Switch specified in the Lab + Configuration is created or updated in Hyper-V. +1. **Create Management Switch**: A Management Internal Virtual Switch is created + in Hyper-V for this Lab. + _Each Lab has it's own Management Internal Virtual Switch which will be named + "Lab Management" with the Lab ID prepended to it._ + _It will be assigned a VLAN Id of 99, which can be overridden in the Lab + Configuration file._ + _The Lab Management switch is for the Lab Host to communicate with the Lab + Guest Virtual Machines so must not be removed._ +1. **Create Template VHDs**: Any Template VHD files that don't exist but should + are created from the appropriate ISO files. + _A Lab Configuration file may not list any Template VHD files, or it may list + them but not specify source ISO files to create the VHD files from._ + _Instead the Templates may directly link to a VHD file or an existing Hyper-V + Virtual Machine._ +1. **Create VHD Templates Folder**: A folder within the Lab folder will be + created to store the Virtual Hard Disk Template files. + _This folder is usually called 'Virtual Hard Disk Templates'._ + _This folder will be used to store Template VHD files or Differencing Disk + Parent VHD files for use as a Boot Disk for Lab Virtual Machines._ + _A VHD file in this folder can be used as a Template or a Differencing Disk + Parent or both._ +1. **Copy VHD Templates**: Get the list of Templates in the Lab Configuration + and copy the VHD specified to the Lab Virtual Hard Disk Templates folder. _Any packages listed in the Template will be applied to the Template at this point._ - _A Template VHD file will only be copied into this folder if it does not already exist._ - _The Template VHD file will also be optimized in this folder and then marked as Read Only to ensure it is not changed (as it may be used as a Differencing Disk Parent)._ -1. **Create Virtual Machines**: Create the Lab Virtual Machines, one at a time, for each one performing the following steps: - 1. **Create Hyper-V VM**: Create the Virtual Machine and attach any Network Adapters listed in the Lab Configuration with it. - 1. **Create Boot VHD**: Copy (if not using a Differencing Boot VHD) the Boot VHD from the Virtual Hard Disk Templates folder and attach it to the VM. - 1. **Add Packages**: Add any listed packages to the VM. - 1. **Create Data VHDs**: Copy/Create and attach any Data VHDs listed in the Lab Configuration with the VM. - 1. **Create Config Files**: Create any VM configuration files required for first boot of the VM (e.g. Unattend.xml, SetupComplete.cmd) and copy them into the Boot VHD. - 1. **Boot VM**: Boot the VM for the first time and wait for Initial Setup of the VM to complete. - 1. **Create Certificate**: A Self-Signed certificate will be created by the VM and made available on the VM Boot VHD. - 1. **Download Certificate**: The Self-Signed certificate will be downloaded by the Lab Host and used to encrypt the credentials in the DSC MOF file that will be created. - 1. **Create DSC Configuration**: The DSC Configuration file will be assembled from the speficied DSC Configuration and the required networking information. - 1. **Compile DSC Configuration**: The DSC Configuration file will be compiled and a MOF file produced. - 1. **Upload DSC Files**: This DSC MOF file, DSC LCM (Meta) MOF File and any DSC Resource Modules required will be uploaded into the VM. - 1. **Start DSC**: DSC configuration will be started on the VM. - _Note: The LCM is configured to re-apply DSC every 15 minutes, so changing any settings managed by DSC on the server will be reverted within 15 minutes of them being changed._ + _A Template VHD file will only be copied into this folder if it does not already + exist._ + _The Template VHD file will also be optimized in this folder and then marked as + Read Only to ensure it is not changed (as it may be used as a Differencing Disk + Parent)._ +1. **Create Virtual Machines**: Create the Lab Virtual Machines, one at a time, + for each one performing the following steps: + 1. **Create Hyper-V VM**: Create the Virtual Machine and attach any Network + Adapters listed in the Lab Configuration with it. + 1. **Create Boot VHD**: Copy (if not using a Differencing Boot VHD) the Boot + VHD from the Virtual Hard Disk Templates folder and attach it to the VM. + 1. **Add Packages**: Add any listed packages to the VM. + 1. **Create Data VHDs**: Copy/Create and attach any Data VHDs listed in the + Lab Configuration with the VM. + 1. **Create Config Files**: Create any VM configuration files required for + first boot of the VM (e.g. Unattend.xml, SetupComplete.cmd) and copy them + into the Boot VHD. + 1. **Boot VM**: Boot the VM for the first time and wait for Initial Setup of + the VM to complete. + 1. **Create Certificate**: A Self-Signed certificate will be created by the + VM and made available on the VM Boot VHD. + 1. **Download Certificate**: The Self-Signed certificate will be downloaded + by the Lab Host and used to encrypt the credentials in the DSC MOF file + that will be created. + 1. **Create DSC Configuration**: The DSC Configuration file will be assembled + from the speficied DSC Configuration and the required networking information. + 1. **Compile DSC Configuration**: The DSC Configuration file will be compiled + and a MOF file produced. + 1. **Upload DSC Files**: This DSC MOF file, DSC LCM (Meta) MOF File and any + DSC Resource Modules required will be uploaded into the VM. + 1. **Start DSC**: DSC configuration will be started on the VM. + _Note: The LCM is configured to re-apply DSC every 15 minutes, so changing + any settings managed by DSC on the server will be reverted within 15 minutes + of them being changed._ The entire process above is automated. -As long as you a valid Lab Configuration file and any required Windows Installation Media ISO files then the Lab will be installed for you. -Depending on the size of the Lab you are building and whether or not the ISO files need to be converted to VHD files, this could take from 5 minutes to many hours. -For example, an Lab containing eight Windows Server 2012 R2 Virtual Machines configured as an AD Domain containing various services installed on a Host with four CPU cores, 32 GB RAM and an SSD will take about 45 minutes to install. +As long as you a valid Lab Configuration file and any required Windows Installation +Media ISO files then the Lab will be installed for you. +Depending on the size of the Lab you are building and whether or not the ISO files +need to be converted to VHD files, this could take from 5 minutes to many hours. +For example, an Lab containing eight Windows Server 2012 R2 Virtual Machines +configured as an AD Domain containing various services installed on a Host with +four CPU cores, 32 GB RAM and an SSD will take about 45 minutes to install. ## Windows Management Framework 5.1 (WMF 5.1) -All Lab Guest Virtual Machines must have WMF 5.1 installed onto them before they are first booted in a Lab environment. This is to ensure the Self-Signed certificate can be generated and returned to the host for DSC MOF encryption. +All Lab Guest Virtual Machines must have WMF 5.1 installed onto them before they +are first booted in a Lab environment. This is to ensure the Self-Signed certificate +can be generated and returned to the host for DSC MOF encryption. -If WMF 5.1 is not installed before the Lab VM Guest first boot then DSC configuration will not proceed, and the Lab Guest VM will boot with a clean OS, but none of the specific features installed or configured (e.g. DC's not promoted). +If WMF 5.1 is not installed before the Lab VM Guest first boot then DSC +configuration will not proceed, and the Lab Guest VM will boot with a clean OS, +but none of the specific features installed or configured (e.g. DC's not promoted). -WMF 5.1 is only required to be installed onto Windows 7, 8 and 8.1 or Windows Server 2008 R2, Windows Server 2012 and Windows Server 2012 R2. Windows 10 and Windows Server 2016 already include WMF 5.1 so it doesn't need to be installed. +WMF 5.1 is only required to be installed onto Windows 7, 8 and 8.1 or Windows +Server 2008 R2, Windows Server 2012 and Windows Server 2012 R2. Windows 10 and +Windows Server 2016 already include WMF 5.1 so it doesn't need to be installed. -_Most Labs_ are configured to install WMF 5.1 **completely automatically** so you don't need to install worry about it. +_Most Labs_ are configured to install WMF 5.1 **completely automatically** so you +don't need to install worry about it. -Note: It is possible to change a Lab Configuration file to prevent automatic installation of the WMF 5.1 MSU package onto Guest Lab VM's, but this is not recommended unless there is a good reason for doing so. +Note: It is possible to change a Lab Configuration file to prevent automatic +installation of the WMF 5.1 MSU package onto Guest Lab VM's, but this is not +recommended unless there is a good reason for doing so. -LabBuilder supports automatically installing any MSU package that can be downloaded from the internet onto the Lab Guest VMs during installation of the Lab. +LabBuilder supports automatically installing any MSU package that can be downloaded +from the internet onto the Lab Guest VMs during installation of the Lab. These MSU packages can be installed during any of the following phases of Lab installation: -+ Convert Windows Install Media ISO to Template VHD. -+ Copy Template VHD to ParentVHD folder in Lab. -+ Create new VM Boot VHD from ParentVHD folder in Lab. +- Convert Windows Install Media ISO to Template VHD. +- Copy Template VHD to ParentVHD folder in Lab. +- Create new VM Boot VHD from ParentVHD folder in Lab. -By default, Lab configuration files are configured to ensure WMF 5.1 is installed at each of the above phases. +By default, Lab configuration files are configured to ensure WMF 5.1 is installed +at each of the above phases. -The WMF 5.1 MSU package is controlled by adding a new MSU element to the &lt;Resources&gt; element in a Lab Configuration. +The WMF 5.1 MSU package is controlled by adding a new MSU element to the +&lt;Resources&gt; element in a Lab Configuration. E.g. ```xml @@ -316,7 +425,8 @@ E.g. ``` This defines the name of the MSU package and the Download location. -The package can then be added to the &lt;Template&gt;, &lt;TemplateVHD&gt; or &lt;VM&gt; element in the **Packages** attribute. +The package can then be added to the &lt;Template&gt;, &lt;TemplateVHD&gt; or +&lt;VM&gt; element in the **Packages** attribute. E.g. ```xml @@ -334,7 +444,8 @@ E.g. ``` Other MSU packages can also be installed in the same way. -Multiple MSU Packages can be installed to the same VHD by comma delimiting the Packages attribute. +Multiple MSU Packages can be installed to the same VHD by comma delimiting the +Packages attribute. ## Configuration XML @@ -344,7 +455,8 @@ Documentation for the LabBuilder Configuration XML can be found in the file [doc Complete documentation for the LabBuilder Lab Cmdlets can be found in the file [docs/cmdlets-lab.md](LabBuilder/docs/cmdlets-lab.md). -A list of Cmdlets in the LabBuilder module can be found by running the following PowerShell commands: +A list of Cmdlets in the LabBuilder module can be found by running the following +PowerShell commands: ```PowerShell Import-Module LabBuilder @@ -367,5 +479,12 @@ For a list of changes to versions, see the [CHANGELOG.md](CHANGELOG.md) file. ## Links -+ [GitHub Repository](https://github.com/PlagueHO/LabBuilder/) -+ [Blog](https://dscottraynsford.wordpress.com/) +- [GitHub Repository](https://github.com/PlagueHO/LabBuilder/) +- [Blog](https://dscottraynsford.wordpress.com/) + +[ap-image-master]: https://dev.azure.com/dscottraynsford/GitHub/_apis/build/status/PlagueHO.LabBuilder?branchName=master +[ap-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=5 +[ts-image-master]: https://img.shields.io/azure-devops/tests/dscottraynsford/GitHub/5/master +[ts-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=5&branchName=master +[cq-image-master]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/master/graph/badge.svg +[cq-site-master]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/master diff --git a/RELEASENOTES.md b/RELEASENOTES.md deleted file mode 100644 index 50082c47..00000000 --- a/RELEASENOTES.md +++ /dev/null @@ -1,169 +0,0 @@ -# Release Notes - -## What is New in LabBuilder 1.0.5.104 - -- Samples\Sample_WS2019_AzureADConnect.xml: Added sample for installing Azure AD - Connect. -- Convert all DSC configurations to use ActiveDirectoryDsc version - 4.1.0.0. -- `dsclibrary\RODC_SECONDARY.DSC.ps1`: - - Enable RODC creation because it is supported by ActiveDirectoryDsc. -- `dsclibrary\DC_FORESTPRIMARY.DSC.ps1`: - - Enabled customizing of Domain NetBios name. -- `Get-LabVm.ps1`: - - Clean up code style. -- `Enable-LabWSMan.ps1`: - - Improved function so that if WinRM Service is stopped it will be started. -- `Get-Lab.ps1`: - - Clean up code style. - - Fix bug reading `configpath` from `settings` node. - - Changed to use `ConvertTo-LabAbsolutePath.ps1` to simplify code. - - Changed to automatically use the `DSCLibrary` folder that comes as part of - the LabBuilder module if the `dsclibrarypath` setting is not specified - in the lab configuration - fixes [Issue-335](https://github.com/PlagueHO/LabBuilder/issues/335). -- `ConvertTo-LabAbsolutePath.ps1`: - - Added function to create an absolute path from a relative lab path. -- Removed `dsclibrarypath` setting from all samples as it is no longer required. -- `Get-LabResourceISO.ps1`: - - Clean up code style. - -## What is New in LabBuilder 1.0.4.83 - -- `Get-LabUnattendFileContent.ps1`: - - Enabled PSRemoting in Unattend.xml (allows DSC to initialize properly on - newer operating systems). - - Enabled local administrator account for Client operating systems - (Windows 10). - - Enabled PowerShell script execution for both 32-bit and 64-bit processes. -- `Connect-LabVM.ps1`: - - Test WinRM connectivity prior to initializing DSC. -- `Install-LabVM.ps1`: - - Check for DSC Configuration section in XML file prior to calling DSC. - -## What is New in LabBuilder 1.0.3.69 - -July 21, 2019 - -- `dsclibrary\MEMBER_SUBCA.DSC.ps1`: - - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- `dsclibrary\MEMBER_ROOTCA.DSC.ps1`: - - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). - - Changed CApolicy.inf RenewalKeyLength to 4096, CNGHashAlgorithm to SHA256 and - LoadDefaultTemplates to 0 - fixes [Issue-324](https://github.com/PlagueHO/LabBuilder/issues/324). -- `dsclibrary\STANDALONE_ROOTCA.DSC.ps1`: - - Correct SubCA resource name to wait for - fixes [Issue-321](https://github.com/PlagueHO/LabBuilder/issues/321). - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- `dsclibrary\STANDALONE_ROOTCA_NOSUBCA.DSC.ps1`: - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- Added `.markdownlint.json` file. -- Fix markdown rule violations in `CHANGELOG.MD`. -- `dsclibrary\MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\STANDALONE_DHCPDNS.DSC.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\STANDALONE_INTERNET.DSC.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_NPS_DFSTEST.ps1`: - - Fix to use correct name of the DFSReplicationGroup resource. -- `dsclibrary\MEMBER_WDS.DSC.ps1`: - - Fix configuration. - -## What is New in LabBuilder 1.0.2.58 - -May 5, 2019 - -- Reword module description in Manifest. -- Fix bug when connecting to a Lab VM when TrustedHosts is empty - fixes - [Issue #314](https://github.com/PlagueHO/LabBuilder/issues/314). -- Moved Schema documentation file into docs folder and converted to - PlatyPS compatible file. -- Cleaned up Schema documentation file to remove most markdown rule - violations. -- Cleaned up README.MD file to remove most markdown rule - violations. -- Fix infinite loop bug occuring in `Stop-Lab` when Lab VM does not - exist - fixes [Issue #316](https://github.com/PlagueHO/LabBuilder/issues/316). -- Fix infinite loop bug occuring in `Start-Lab` when Lab VM does not - exist. -- DSCLibrary\MEMBER_NANO.DSC.ps1: Rename xOfflineDomainJoin to - OfflineDomainJoin - fixes [Issue #317](https://github.com/PlagueHO/LabBuilder/issues/317). - -## What is New in LabBuilder 1.0.1.40 - -April 8, 2019 - -- Update to use NetworkingDsc 7.0.0.0 and converted DhcpClient - resource to NetIpInterface - fixes [Issue #304](https://github.com/PlagueHO/LabBuilder/issues/304). -- Refactored module manifest generation process to be more reliable - and robust. -- Convert module name to be a variable in PSake file to make it more - easily portable between projects. -- Added samples for Windows Server 2019 - fixes [Issue #305](https://github.com/PlagueHO/LabBuilder/issues/305). -- Cleaned up unit test initialization and fixtures. -- Refactored `Install-Lab` to move Management Switch creation into a - new function `Initialize-LabManagementSwitch` and created unit tests. -- Added more log information to `SetupComplete.cmd` to diagnose issues - with initial machine boot on Windows Server 2019. -- Fix bug in `Remove-LabSwitch` where adapter -- Correct documentation markdown errors. -- Removed `Timeout 30` from the Initial `SetupComplete.cmd` that runs - on each VM when first intialized because it fails to execute on - Windows Server 2019. Replaced with `Start-Sleep` in `SetupComplete.ps1` - that is called by `SetupComplete.cmd` - fixes [Issue #308](https://github.com/PlagueHO/LabBuilder/issues/308). -- Change `Update-LabDSC` so that module copy process to Lab Files will - continue even if destination path is too long. This is to allow DSC - Resource modules that have example filenames that result in a long - path. -- Update CI module dependencies in `Requirements.psd1` to latest version: - - PSScriptAnalyzer: 1.18.0 - - PSDeploy: 1.0.1 - - Platyps: 0.14.0 -- Split Private Lib functions into individual .ps1 files. -- Refactored Private Lib functions to improve code style standards. - -## What is New in LabBuilder 1.0.0.6 - -December 8, 2018 - -- Samples\Sample_WS2016_DCandDHCPandCA.xml: Added to easily create a Windows - Server 2016 domain with a enterprise root CA. -- Correct certificate authority DSC Resources with ADCSCertificationAuthority - to be IsSingleInstance. -- Convert xNetworking to NetworkingDsc. -- Converted repository structure and build pipeline to more modern standards. -- Enabled Azure DevOps build pipeline. -- Convert module to require WMF 5.1 and all the samples to install WMF 5.1. -- DSCLibrary\MEMBER_MEMBER_DHCP*.ps1: Fixed to support xDHCPServer 2.0.0.0. - -## Feedback - -Please send your feedback to [http://github.com/PlagueHO/LabBuilder/issues](http://github.com/PlagueHO/LabBuilder/issues). diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 new file mode 100644 index 00000000..f415b400 --- /dev/null +++ b/RequiredModules.psd1 @@ -0,0 +1,18 @@ +@{ + PSDependOptions = @{ + AddToPath = $true + Target = 'output\RequiredModules' + Parameters = @{ + } + } + + InvokeBuild = 'latest' + PSScriptAnalyzer = 'latest' + Pester = 'latest' + Plaster = 'latest' + Platyps = 'latest' + ModuleBuilder = '1.0.0' + ChangelogManagement = 'latest' + Sampler = 'latest' + MarkdownLinkCheck = 'latest' +} diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 new file mode 100644 index 00000000..4928e624 --- /dev/null +++ b/Resolve-Dependency.ps1 @@ -0,0 +1,289 @@ +[CmdletBinding()] +param +( + + [Parameter()] + [String] + $DependencyFile = 'RequiredModules.psd1', + + [Parameter()] + [String] + # Path for PSDepend to be bootstrapped and save other dependencies. + # Can also be CurrentUser or AllUsers if you wish to install the modules in such scope + # Default to $PWD.Path/output/modules + $PSDependTarget = (Join-Path $PSScriptRoot './output/RequiredModules'), + + [Parameter()] + [uri] + # URI to use for Proxy when attempting to Bootstrap PackageProvider & PowerShellGet + $Proxy, + + [Parameter()] + # Credential to contact the Proxy when provided + [PSCredential]$ProxyCredential, + + [Parameter()] + [ValidateSet('CurrentUser', 'AllUsers')] + [String] + # Scope to bootstrap the PackageProvider and PSGet if not available + $Scope = 'CurrentUser', + + [Parameter()] + [String] + # Gallery to use when bootstrapping PackageProvider, PSGet and when calling PSDepend (can be overridden in Dependency files) + $Gallery = 'PSGallery', + + [Parameter()] + [PSCredential] + # Credentials to use with the Gallery specified above + $GalleryCredential, + + + [Parameter()] + [switch] + # Allow you to use a locally installed version of PowerShellGet older than 1.6.0 (not recommended, default to $False) + $AllowOldPowerShellGetModule, + + [Parameter()] + [String] + # Allow you to specify a minimum version fo PSDepend, if you're after specific features. + $MinimumPSDependVersion, + + [Parameter()] + [Switch] + $AllowPrerelease, + + [Parameter()] + [Switch] + $WithYAML +) + +# Load Defaults for parameters values from Resolve-Dependency.psd1 if not provided as parameter +try +{ + Write-Verbose -Message "Importing Bootstrap default parameters from '$PSScriptRoot/Resolve-Dependency.psd1'." + $ResolveDependencyDefaults = Import-PowerShellDataFile -Path (Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1' -Resolve -ErrorAction Stop) + $ParameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + if ($ParameterToDefault.Count -eq 0) + { + $ParameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option + foreach ($ParamName in $ParameterToDefault) + { + if (-Not $PSBoundParameters.Keys.Contains($ParamName) -and $ResolveDependencyDefaults.ContainsKey($ParamName)) + { + Write-Verbose -Message "Setting $ParamName with $($ResolveDependencyDefaults[$ParamName])" + try + { + $variableValue = $ResolveDependencyDefaults[$ParamName] + if ($variableValue -is [string]) + { + $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) + } + $PSBoundParameters.Add($ParamName, $variableValue) + Set-Variable -Name $ParamName -value $variableValue -Force -ErrorAction SilentlyContinue + } + catch + { + Write-Verbose -Message "Error adding default for $ParamName : $($_.Exception.Message)" + } + } + } +} +catch +{ + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from $(Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1'): $($_.Exception.Message)." +} + +Write-Progress -Activity "Bootstrap:" -PercentComplete 0 -CurrentOperation "NuGet Bootstrap" + +if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyContinue)) +{ + $providerBootstrapParams = @{ + Name = 'nuget' + force = $true + ForceBootstrap = $true + ErrorAction = 'Stop' + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $providerBootstrapParams.Add('Proxy', $Proxy) + } + 'ProxyCredential' + { + $providerBootstrapParams.Add('ProxyCredential', $ProxyCredential) + } + 'Scope' + { + $providerBootstrapParams.Add('Scope', $Scope) + } + } + + if ($AllowPrerelease) + { + $providerBootstrapParams.Add('AllowPrerelease', $true) + } + + Write-Information "Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed)" + $null = Install-PackageProvider @providerBootstrapParams + $latestNuGetVersion = (Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1).Version.ToString() + Write-Information "Bootstrap: Importing NuGet Package Provider version $latestNuGetVersion to current session." + $Null = Import-PackageProvider -Name NuGet -RequiredVersion $latestNuGetVersion -Force +} + +Write-Progress -Activity "Bootstrap:" -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + +# Fail if the given PSGallery is not Registered +$Policy = (Get-PSRepository $Gallery -ErrorAction Stop).InstallationPolicy +Set-PSRepository -Name $Gallery -InstallationPolicy Trusted -ErrorAction Ignore +try +{ + Write-Progress -Activity "Bootstrap:" -PercentComplete 25 -CurrentOperation "Checking PowerShellGet" + # Ensure the module is loaded and retrieve the version you have + $PowerShellGetVersion = (Import-Module PowerShellGet -PassThru -ErrorAction SilentlyContinue).Version + + Write-Verbose "Bootstrap: The PowerShellGet version is $PowerShellGetVersion" + # Versions below 1.6.0 are considered old, unreliable & not recommended + if (!$PowerShellGetVersion -or ($PowerShellGetVersion -lt [System.version]'1.6.0' -and !$AllowOldPowerShellGetModule)) + { + Write-Progress -Activity "Bootstrap:" -PercentComplete 40 -CurrentOperation "Installing newer version of PowerShellGet" + $InstallPSGetParam = @{ + Name = 'PowerShellGet' + Force = $True + SkipPublisherCheck = $true + AllowClobber = $true + Scope = $Scope + Repository = $Gallery + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $InstallPSGetParam.Add('Proxy', $Proxy) + } + 'ProxyCredential' + { + $InstallPSGetParam.Add('ProxyCredential', $ProxyCredential) + } + 'GalleryCredential' + { + $InstallPSGetParam.Add('Credential', $GalleryCredential) + } + } + + Install-Module @InstallPSGetParam + Remove-Module PowerShellGet -force -ErrorAction SilentlyContinue + Import-Module PowerShellGet -Force + $NewLoadedVersion = (Get-Module PowerShellGet).Version.ToString() + Write-Information "Bootstrap: PowerShellGet version loaded is $NewLoadedVersion" + Write-Progress -Activity "Bootstrap:" -PercentComplete 60 -CurrentOperation "Installing newer version of PowerShellGet" + } + + # Try to import the PSDepend module from the available modules + try + { + $ImportPSDependParam = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($MinimumPSDependVersion) + { + $ImportPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + } + $null = Import-Module @ImportPSDependParam + } + catch + { + # PSDepend module not found, installing or saving it + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug "PSDepend module not found. Attempting to install from Gallery $Gallery" + Write-Warning "Installing PSDepend in $PSDependTarget Scope" + $InstallPSDependParam = @{ + Name = 'PSDepend' + Repository = $Gallery + Force = $true + Scope = $PSDependTarget + SkipPublisherCheck = $true + AllowClobber = $true + } + + if ($MinimumPSDependVersion) + { + $InstallPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + Install-Module @InstallPSDependParam + } + else + { + Write-Debug "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ + Name = 'PSDepend' + Repository = $Gallery + Path = $PSDependTarget + } + + if ($MinimumPSDependVersion) + { + $SaveModuleParam.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" + Save-Module @SaveModuleParam + } + } + finally + { + Write-Progress -Activity "Bootstrap:" -PercentComplete 100 -CurrentOperation "Loading PSDepend" + # We should have successfully bootstrapped PSDepend. Fail if not available + Import-Module PSDepend -ErrorAction Stop + } + + if ($WithYAML) + { + if (-Not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + { + Write-Verbose "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ + Name = 'PowerShell-Yaml' + Repository = $Gallery + Path = $PSDependTarget + } + + Save-Module @SaveModuleParam + Import-Module "PowerShell-Yaml" -ErrorAction Stop + } + else + { + Write-Verbose "PowerShell-Yaml is already available" + } + } + + Write-Progress -Activity "PSDepend:" -PercentComplete 0 -CurrentOperation "Restoring Build Dependencies" + if (Test-Path $DependencyFile) + { + $PSDependParams = @{ + Force = $true + Path = $DependencyFile + } + + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified + Invoke-PSDepend @PSDependParams + } + Write-Progress -Activity "PSDepend:" -PercentComplete 100 -CurrentOperation "Dependencies restored" -Completed +} +finally +{ + # Reverting the Installation Policy for the given gallery + Set-PSRepository -Name $Gallery -InstallationPolicy $Policy + Write-Verbose "Project Bootstrapped, returning to Invoke-Build" +} diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 new file mode 100644 index 00000000..29e16dde --- /dev/null +++ b/Resolve-Dependency.psd1 @@ -0,0 +1,10 @@ +@{ # Defaults Parameter value to be loaded by the Resolve-Dependency command (unless set in Bound Parameters) + #PSDependTarget = './output/modules' + #Proxy = '' + #ProxyCredential = '$MyCredentialVariable' #TODO: find a way to support credentials in build (resolve variable) + Gallery = 'PSGallery' + #AllowOldPowerShellGetModule = $true + #MinimumPSDependVersion = '0.3.0' + AllowPrerelease = $false + WithYAML = $true # Will also bootstrap PowerShell-Yaml to read other config files +} diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 31fd7c98..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,9 +0,0 @@ -#---------------------------------# -# environment configuration # -#---------------------------------# -version: 1.0.0.{build} -environment: - image: Visual Studio 2017 - -build_script: - - ps: . .\psake.ps1 -TaskList Test -Verbose diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 99fa7658..39d427d0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,74 +1,184 @@ name: $(rev:r) -jobs: - - job: Build_PS_Win2016 - pool: - vmImage: vs2017-win2016 - - steps: - - checkout: self - persistCredentials: true - - - powershell: | - .\psake.ps1 -TaskList Test -Verbose - displayName: 'Execute Tests' - env: - githubRepoToken: $(githubRepoToken) - - - task: PublishTestResults@2 - inputs: - testRunner: 'NUnit' - testResultsFiles: '**/Unit/TestResults.unit.xml' - testRunTitle: 'PS_Win2016_Unit' - displayName: 'Publish Unit Test Results' - condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Failed') - - - task: PublishCodeCoverageResults@1 - inputs: - summaryFileLocation: '**/Unit/CodeCoverage.xml' - failIfCoverageEmpty: true - displayName: 'Publish Unit Test Code Coverage' - condition: and(in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Failed'), eq(variables['System.PullRequest.IsFork'], false)) - - - task: PublishTestResults@2 - inputs: - testRunner: 'NUnit' - testResultsFiles: '**/Integration/TestResults.integration.xml' - testRunTitle: 'PS_Win2016_Integration' - displayName: 'Publish Integration Test Results' - condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Failed') - - - powershell: | - .\psake.ps1 -TaskList Build -Verbose - displayName: 'Build and Stage Module' - env: - githubRepoToken: $(githubRepoToken) - - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: 'staging/LabBuilder' - artifactName: 'LabBuilder' - displayName: 'Publish Module' - - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: 'staging/zip' - artifactName: 'zip' - displayName: 'Publish Module Zip' - - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: 'psakefile.ps1' - artifactName: 'scripts' - displayName: 'Publish PSake File' - - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: 'requirements.psd1' - artifactName: 'scripts' - displayName: 'Publish PSDepend File' - - - task: PublishBuildArtifacts@1 - inputs: - pathtoPublish: 'psake.ps1' - artifactName: 'scripts' - displayName: 'Publish Psake Bootstrap File' +trigger: + branches: + include: + - master + paths: + exclude: + - CHANGELOG.md + tags: + include: + - "v*" + exclude: + - "*-*" +pr: none + +stages: + - stage: Build + jobs: + - job: Build_Module + displayName: 'Build Module' + pool: + vmImage: ubuntu-16.04 + steps: + - task: GitVersion@5 + name: gitversion + displayName: 'Evaluate Next Version' + inputs: + runtime: 'core' + configFilePath: 'GitVersion.yml' + + - task: PowerShell@2 + name: package + displayName: 'Build & Package Module' + inputs: + filePath: './build.ps1' + arguments: '-Tasks pack -ResolveDependency' + pwsh: true + env: + ModuleVersion: $(gitVersion.Informationalversion) + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Build Artifact' + inputs: + PathtoPublish: 'output/' + ArtifactName: 'output' + publishLocation: 'Container' + + - stage: Test + dependsOn: Build + jobs: + - job: Unit_Test_PS_Win2016 + displayName: 'Unit Test (Powershell 5.1 on Windows Server 2016)' + pool: + vmImage: vs2017-win2016 + + steps: + - powershell: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: moduleBuildVariable + displayName: 'Set Environment Variables' + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: test + displayName: 'Run Unit Test' + inputs: + filePath: './build.ps1' + arguments: "-tasks test -PesterScript 'tests/Unit'" + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Unit (PowerShell 5.1 on Windows Server 2016)' + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish Code Coverage' + condition: succeededOrFailed() + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: 'output/testResults/CodeCov*.xml' + pathToSources: '$(Build.SourcesDirectory)/output/$(moduleBuildVariable.RepositoryName)' + + - job: Unit_Test_PS_Win2019 + displayName: 'Unit Test (Powershell 5.1 on Windows Server 2019)' + pool: + vmImage: windows-2019 + + steps: + - powershell: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: moduleBuildVariable + displayName: 'Set Environment Variables' + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: test + displayName: 'Run Unit Test' + inputs: + filePath: './build.ps1' + arguments: "-tasks test -PesterScript 'tests/Unit'" + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Unit (PowerShell 5.1 on Windows Server 2019)' + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish Code Coverage' + condition: succeededOrFailed() + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: 'output/testResults/CodeCov*.xml' + pathToSources: '$(Build.SourcesDirectory)/output/$(moduleBuildVariable.RepositoryName)' + + - stage: Deploy + dependsOn: Test + # Only execute deploy stage if we're on master and previous stage succeeded + condition: | + and( + succeeded(), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/master'), + startsWith(variables['Build.SourceBranch'], 'refs/tags/') + ), + eq(variables['System.TeamFoundationCollectionUri'], 'https://dscottraynsford.visualstudio.com/'), + endsWith(variables['Build.DefinitionName'],'master') + ) + jobs: + - job: Deploy_Module + displayName: 'Deploy Module' + pool: + vmImage: ubuntu-16.04 + + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + name: publish_release + displayName: 'Publish Release' + inputs: + filePath: './build.ps1' + arguments: '-tasks publish' + pwsh: true + env: + GitHubToken: $(GitHubToken) + GalleryApiToken: $(GalleryApiToken) + + - task: PowerShell@2 + name: send_changelog_PR + displayName: 'Send CHANGELOG PR' + inputs: + filePath: './build.ps1' + arguments: '-tasks Create_ChangeLog_GitHub_PR' + pwsh: true + env: + GitHubToken: $(GitHubToken) diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 00000000..a94f13bd --- /dev/null +++ b/build.ps1 @@ -0,0 +1,353 @@ +<# + +.DESCRIPTION + Bootstrap and build script for PowerShell module pipeline + +#> +[CmdletBinding()] +param +( + [Parameter(Position = 0)] + [string[]]$Tasks = '.', + + [Parameter()] + [String] + $CodeCoverageThreshold = '', + + [Parameter()] + [validateScript( + { Test-Path -Path $_ } + )] + $BuildConfig = './build.yaml', + + [Parameter()] + # A Specific folder to build the artefact into. + $OutputDirectory = 'output', + + [Parameter()] + # Subdirectory name to build the module (under $OutputDirectory) + $BuiltModuleSubdirectory = '', + + # Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency & PSDepend where to save the required modules, + # or use CurrentUser, AllUsers to target where to install missing dependencies + # You can override the value for PSDepend in the Build.psd1 build manifest + # This defaults to $OutputDirectory/modules (by default: ./output/modules) + [Parameter()] + $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), + + # Filter which tags to run when invoking Pester tests + # This is used in the Invoke-Pester.pester.build.ps1 tasks + [Parameter()] + [string[]] + $PesterTag, + + [Parameter()] + [string[]] + $PesterScript, + + # Filter which tags to exclude when invoking Pester tests + # This is used in the Invoke-Pester.pester.build.ps1 tasks + [Parameter()] + [string[]] + $PesterExcludeTag, + + [Parameter()] + [Alias('bootstrap')] + [switch]$ResolveDependency, + + [Parameter(DontShow)] + [AllowNull()] + $BuildInfo, + + [Parameter()] + [switch] + $AutoRestore +) + +# The BEGIN block (at the end of this file) handles the Bootstrap of the Environment before Invoke-Build can run the tasks +# if the -ResolveDependency (aka Bootstrap) is specified, the modules are already available, and can be auto loaded + +process +{ + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + # Only run the process block through InvokeBuild (Look at the Begin block at the bottom of this script) + return + } + + # Execute the Build Process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName BeforeBuild + + try + { + Write-Host -ForeGroundColor magenta "[build] Parsing defined tasks" + + # Load Default BuildInfo if not provided as parameter + if (!$PSBoundParameters.ContainsKey('BuildInfo')) + { + try + { + if (Test-Path $BuildConfig) + { + $ConfigFile = (Get-Item -Path $BuildConfig) + Write-Host "[build] Loading Configuration from $ConfigFile" + $BuildInfo = switch -Regex ($ConfigFile.Extension) + { + # Native Support for PSD1 + '\.psd1' + { + Import-PowerShellDataFile -Path $BuildConfig + } + # Support for yaml when module PowerShell-Yaml is available + '\.[yaml|yml]' + { + Import-Module -ErrorAction Stop -Name 'powershell-yaml' + ConvertFrom-Yaml -Yaml (Get-Content -Raw $ConfigFile) + } + # Native Support for JSON and JSONC (by Removing comments) + '\.[json|jsonc]' + { + $JSONC = (Get-Content -Raw -Path $ConfigFile) + $JSON = $JSONC -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + # This should probably be converted to hashtable for splatting + $JSON | ConvertFrom-Json + } + default + { + Write-Error "Extension '$_' not supported. using @{}" + @{ } + } + } + } + else + { + Write-Host -Object "Configuration file $BuildConfig not found" -ForegroundColor Red + $BuildInfo = @{ } + } + } + catch + { + Write-Host -Object "Error loading Config $ConfigFile.`r`n Are you missing dependencies?" -ForegroundColor Yellow + Write-Host -Object "Make sure you run './build.ps1 -ResolveDependency -tasks noop' to restore the Required modules the first time" -ForegroundColor Yellow + $BuildInfo = @{ } + Write-Error $_.Exception.Message + } + } + + # If the Invoke-Build Task Header is specified in the Build Info, set it + if ($BuildInfo.TaskHeader) + { + Set-BuildHeader ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + # Import Tasks from modules via their exported aliases when defined in BUild Manifest + # https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + if ($BuildInfo.containsKey('ModuleBuildTasks')) + { + foreach ($Module in $BuildInfo['ModuleBuildTasks'].Keys) + { + try + { + Write-Host -ForegroundColor DarkGray -Verbose "Importing tasks from module $Module" + $LoadedModule = Import-Module $Module -PassThru -ErrorAction Stop + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($Module)) + { + $LoadedModule.ExportedAliases.GetEnumerator().Where{ + # using -like to support wildcard + Write-Host -ForegroundColor DarkGray "`t Loading $($_.Key)..." + $_.Key -like $TaskToExport + }.ForEach{ + # Dot sourcing the Tasks via their exported aliases + . (Get-Alias $_.Key) + } + } + } + catch + { + Write-Host -ForegroundColor Red -Object "Could not load tasks for module $Module." + Write-Error $_ + } + } + } + + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name) + Get-ChildItem -Path ".build/" -Recurse -Include *.ps1 -ErrorAction Ignore | ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose + . $_.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process + task noop { } + + # Define default task sequence ("."), can be overridden in the $BuildInfo + task . { + Write-Build Yellow "No sequence currently defined for the default task" + } + + # Load Invoke-Build task sequences/workflows from $BuildInfo + Write-Host -ForegroundColor DarkGray "Adding Workflow from configuration:" + foreach ($Workflow in $BuildInfo.BuildWorkflow.keys) + { + Write-Verbose "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')" + $WorkflowItem = $BuildInfo.BuildWorkflow.($Workflow) + if ($WorkflowItem.Trim() -match '^\{(?<sb>[\w\W]*)\}$') + { + $WorkflowItem = [ScriptBlock]::Create($Matches['sb']) + } + Write-Host -ForegroundColor DarkGray " +-> $Workflow" + task $Workflow $WorkflowItem + } + + Write-Host -ForeGroundColor magenta "[build] Executing requested workflow: $($Tasks -join ', ')" + + } + finally + { + Pop-Location -StackName BeforeBuild + } +} + +Begin +{ + # Bootstrapping the environment before using Invoke-Build as task runner + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + Write-Host -foregroundColor Green "[pre-build] Starting Build Init" + Push-Location $PSScriptRoot -StackName BuildModule + } + + if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) + { + # Installing modules instead of saving them + Write-Host -foregroundColor Green "[pre-build] Required Modules will be installed for $RequiredModulesDirectory, not saved." + # Tell Resolve-Dependency to use provided scope as the -PSDependTarget if not overridden in Build.psd1 + $PSDependTarget = $RequiredModulesDirectory + } + else + { + if (-Not (Split-Path -IsAbsolute -Path $OutputDirectory)) + { + $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory + } + + # Resolving the absolute path to save the required modules to + if (-Not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + { + $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory + } + + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise + if (Resolve-Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + { + Write-Debug "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + $RequiredModulesPath = Convert-Path $RequiredModulesDirectory + } + else + { + Write-Host -foregroundColor Green "[pre-build] Creating required modules directory $RequiredModulesDirectory." + $RequiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName + } + + # Prepending $RequiredModulesPath folder to PSModulePath to resolve from this folder FIRST + if ($RequiredModulesDirectory -notIn @('CurrentUser', 'AllUsers') -and + (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $RequiredModulesDirectory)) + { + Write-Host -foregroundColor Green "[pre-build] Prepending '$RequiredModulesDirectory' folder to PSModulePath" + $Env:PSModulePath = $RequiredModulesDirectory + [io.path]::PathSeparator + $Env:PSModulePath + } + + # Checking if the user should -ResolveDependency + if ((!(Get-Module -ListAvailable powershell-yaml) -or !(Get-Module -ListAvailable InvokeBuild) -or !(Get-Module -ListAvailable PSDepend)) -and !$ResolveDependency) + { + if ($AutoRestore -or !$PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') + { + Write-Host -ForegroundColor Yellow "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" + $ResolveDependency = $true + } + else + { + Write-Warning "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter." + Write-Warning "Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." + } + } + + if ($BuiltModuleSubdirectory) + { + if (-Not (Split-Path -IsAbsolute $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path $OutputDirectory $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } + else + { + $BuildModuleOutput = $OutputDirectory + } + + # Prepending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder + if (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $BuildModuleOutput) + { + Write-Host -foregroundColor Green "[pre-build] Prepending '$BuildModuleOutput' folder to PSModulePath" + $Env:PSModulePath = $BuildModuleOutput + [io.path]::PathSeparator + $Env:PSModulePath + } + + # Tell Resolve-Dependency to use $RequiredModulesPath as -PSDependTarget if not overridden in Build.psd1 + $PSDependTarget = $RequiredModulesPath + } + + if ($ResolveDependency) + { + Write-Host -Object "[pre-build] Resolving dependencies." -foregroundColor Green + $ResolveDependencyParams = @{ } + + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency + if ($BuildConfig -match '\.[yaml|yml]$') + { + $ResolveDependencyParams.add('WithYaml', $True) + } + + $ResolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').parameters.keys + foreach ($CmdParameter in $ResolveDependencyAvailableParams) + { + + # The parameter has been explicitly used for calling the .build.ps1 + if ($MyInvocation.BoundParameters.ContainsKey($CmdParameter)) + { + $ParamValue = $MyInvocation.BoundParameters.ContainsKey($CmdParameter) + Write-Debug " adding $CmdParameter :: $ParamValue [from user-provided parameters to Build.ps1]" + $ResolveDependencyParams.Add($CmdParameter, $ParamValue) + } + # Use defaults parameter value from Build.ps1, if any + else + { + if ($ParamValue = Get-Variable -Name $CmdParameter -ValueOnly -ErrorAction Ignore) + { + Write-Debug " adding $CmdParameter :: $ParamValue [from default Build.ps1 variable]" + $ResolveDependencyParams.add($CmdParameter, $ParamValue) + } + } + } + + Write-Host -foregroundColor Green "[pre-build] Starting bootstrap process." + .\Resolve-Dependency.ps1 @ResolveDependencyParams + } + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + Write-Verbose "Bootstrap completed. Handing back to InvokeBuild." + if ($PSBoundParameters.ContainsKey('ResolveDependency')) + { + Write-Verbose "Dependency already resolved. Removing task" + $null = $PSBoundParameters.Remove('ResolveDependency') + } + Write-Host -foregroundColor Green "[build] Starting build with InvokeBuild." + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path + Pop-Location -StackName BuildModule + return + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 00000000..6090f942 --- /dev/null +++ b/build.yaml @@ -0,0 +1,112 @@ +--- +#################################################### +# ModuleBuilder Configuration # +#################################################### +CopyDirectories: + - dsclibrary + - en-US + - samples + - schema + - support + - template +prefix: prefix.ps1 +suffix: suffix.ps1 +Encoding: UTF8 +VersionedOutputDirectory: true + + +#################################################### +# Sampler Pipeline Configuration # +#################################################### +BuildWorkflow: + Compile_Help: | + { + <# + Execute in a separate PWSH session because platyPS tries + to load YamlDotNet.dll which conflicts with one loaded by + Module-Builder. + #> + if ([System.String]::IsNullOrEmpty($ModuleVersion)) + { + $ModuleVersion = '0.0.1' + } + + $outputPath = "$BuildModuleOutput\$ProjectName\$(($ModuleVersion -Split '-')[0])" + $execute = "New-ExternalHelp -Path '$ProjectPath\docs' -OutputPath '$OutputPath' -Force" + + if ($IsCoreCLR) + { + $PowerShellExe = 'pwsh' + } + else + { + $PowerShellExe = 'powershell' + } + + & $PowerShellExe -Command "`"$execute`"" + } + + '.': + - build + - test + + build: + - Clean + - Build_Module_ModuleBuilder + - Build_NestedModules_ModuleBuilder + - Create_changelog_release_output + - Compile_Help + + pack: + - build + - package_module_nupkg + + test: + - Pester_Tests_Stop_On_Fail + - Pester_if_Code_Coverage_Under_Threshold + + publish: + - Publish_release_to_GitHub + - publish_module_to_gallery + + +#################################################### +# PESTER Configuration # +#################################################### + +Pester: + OutputFormat: NUnitXML + ExcludeFromCodeCoverage: + Script: + - tests/Unit + - tests/Integration + ExcludeTag: + Tag: + CodeCoverageThreshold: 70 + +Resolve-Dependency: + Gallery: 'PSGallery' + AllowPrerelease: false + Verbose: false + +ModuleBuildTasks: + Sampler: + - '*.build.Sampler.ib.tasks' + +TaskHeader: | + param($Path) + "" + "=" * 79 + Write-Build Cyan "`t`t`t$($Task.Name.replace("_"," ").ToUpper())" + Write-Build DarkGray "$(Get-BuildSynopsis $Task)" + "-" * 79 + Write-Build DarkGray " $Path" + Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" + "" + +GitHubConfig: + GitHubFilesToAdd: + - 'CHANGELOG.md' + GitHubConfigUserName: Daniel Scott-Raynsford + GitHubConfigUserEmail: plagueho@gmail.com + UpdateChangelogOnPrerelease: false diff --git a/psake.ps1 b/psake.ps1 deleted file mode 100644 index be1555c3..00000000 --- a/psake.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -[CmdletBinding()] -param ( - [Parameter()] - [System.String[]] - $TaskList = 'Default', - - [Parameter()] - [System.Collections.Hashtable] - $Parameters, - - [Parameter()] - [System.Collections.Hashtable] - $Properties -) - -Write-Verbose -Message ('Beginning ''{0}'' process...' -f ($TaskList -join ',')) - -# Bootstrap the environment -$null = Get-PackageProvider -Name NuGet -ForceBootstrap - -# Install PSake module if it is not already installed -if (-not (Get-Module -Name PSDepend -ListAvailable)) -{ - Install-Module -Name PSDepend -Scope CurrentUser -Force -Confirm:$false -} - -# Install build dependencies required for Init task -Import-Module -Name PSDepend -Invoke-PSDepend ` - -Path $PSScriptRoot ` - -Force ` - -Import ` - -Install ` - -Tags 'Bootstrap' - -# Execute the PSake tasts from the psakefile.ps1 -Invoke-Psake ` - -buildFile (Join-Path -Path $PSScriptRoot -ChildPath 'psakefile.ps1') ` - -nologo ` - @PSBoundParameters - -exit ( [System.Int32]( -not $psake.build_success ) ) diff --git a/psakefile.ps1 b/psakefile.ps1 deleted file mode 100644 index de55b9cf..00000000 --- a/psakefile.ps1 +++ /dev/null @@ -1,572 +0,0 @@ -# PSake makes variables declared here available in other scriptblocks -# Init some things -Properties { - # Prepare the folder variables - $ProjectRoot = $ENV:BHProjectPath - if (-not $ProjectRoot) - { - $ProjectRoot = $PSScriptRoot - } - - $ModuleName = 'LabBuilder' - $Timestamp = Get-Date -uformat "%Y%m%d-%H%M%S" - $PSVersion = $PSVersionTable.PSVersion.Major - $separator = '----------------------------------------------------------------------' -} - -Task Default -Depends Test, Build - -Task Init { - Set-Location -Path $ProjectRoot - - # Install any dependencies required for the Init stage - Invoke-PSDepend ` - -Path $PSScriptRoot ` - -Force ` - -Import ` - -Install ` - -Tags 'Init' - - Set-BuildEnvironment -Force - - $separator - 'Build System Details:' - Get-Item -Path ENV:BH* - "`n" - - $separator - 'Other Environment Variables:' - Get-ChildItem -Path ENV: - "`n" - - $separator - 'PowerShell Details:' - $PSVersionTable - "`n" -} - -Task PrepareTest -Depends Init { - # Install any dependencies required for testing - Invoke-PSDepend ` - -Path $PSScriptRoot ` - -Force ` - -Import ` - -Install ` - -Tags 'Test',('Test_{0}' -f $PSVersionTable.PSEdition) -} - -Task Test -Depends UnitTest, IntegrationTest - -Task UnitTest -Depends Init, PrepareTest { - $separator - - # Execute tests - $testScriptsPath = Join-Path -Path $ProjectRoot -ChildPath 'test\unit' - - if (-not (Test-Path -Path $testScriptsPath)) - { - 'Skipping unit tests because none exist' - return - } - - $testResultsFile = Join-Path -Path $testScriptsPath -ChildPath 'TestResults.unit.xml' - $codeCoverageFile = Join-Path -Path $testScriptsPath -ChildPath 'CodeCoverage.xml' - $codeCoverageSource = Get-ChildItem -Path (Join-Path -Path $ProjectRoot -ChildPath 'src\lib\*.ps1') -Recurse - $testResults = Invoke-Pester ` - -Script $testScriptsPath ` - -OutputFormat NUnitXml ` - -OutputFile $testResultsFile ` - -PassThru ` - -ExcludeTag Incomplete ` - -CodeCoverage $codeCoverageSource ` - -CodeCoverageOutputFile $codeCoverageFile ` - -CodeCoverageOutputFileFormat JaCoCo - - # Prepare and uploade code coverage - if ($testResults.CodeCoverage) - { - # Only bother generating code coverage in AppVeyor - if ($ENV:BHBuildSystem -eq 'AppVeyor') - { - 'Preparing CodeCoverage' - Import-Module ` - -Name (Join-Path -Path $ProjectRoot -ChildPath '.codecovio\CodeCovio.psm1') - - $jsonPath = Export-CodeCovIoJson ` - -CodeCoverage $testResults.CodeCoverage ` - -RepoRoot $ProjectRoot - - 'Uploading CodeCoverage to CodeCov.io' - - try - { - Invoke-UploadCoveCoveIoReport -Path $jsonPath - } - catch - { - # CodeCov currently reports an error when uploading - # This is not fatal and can be ignored - Write-Warning -Message $_ - } - } - } - else - { - Write-Warning -Message 'Could not create CodeCov.io report because pester results object did not contain a CodeCoverage object' - } - - # Upload tests - if ($ENV:BHBuildSystem -eq 'AppVeyor') - { - 'Publishing test results to AppVeyor' - (New-Object 'System.Net.WebClient').UploadFile( - "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", - (Resolve-Path $testResultsFile)) - - "Publishing test results to AppVeyor as Artifact" - Push-AppveyorArtifact $testResultsFile - - if ($testResults.FailedCount -gt 0) - { - throw "$($testResults.FailedCount) unit tests failed." - } - } - else - { - if ($testResults.FailedCount -gt 0) - { - Write-Error -Exception "$($testResults.FailedCount) unit tests failed." - } - } - - "`n" -} - -Task IntegrationTest -Depends Init, PrepareTest { - $separator - - # Execute tests - $testScriptsPath = Join-Path -Path $ProjectRoot -ChildPath 'test\integration' - - if (-not (Test-Path -Path $testScriptsPath)) - { - 'Skipping integration tests because none exist' - return - } - - $testResultsFile = Join-Path -Path $testScriptsPath -ChildPath 'TestResults.integration.xml' - $testResults = Invoke-Pester ` - -Script $testScriptsPath ` - -OutputFormat NUnitXml ` - -OutputFile $testResultsFile ` - -PassThru ` - -ExcludeTag Incomplete - - # Upload tests - if ($ENV:BHBuildSystem -eq 'AppVeyor') - { - 'Publishing test results to AppVeyor' - (New-Object 'System.Net.WebClient').UploadFile( - "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", - (Resolve-Path $testResultsFile)) - - "Publishing test results to AppVeyor as Artifact" - Push-AppveyorArtifact $testResultsFile - - if ($testResults.FailedCount -gt 0) - { - throw "$($testResults.FailedCount) integration tests failed." - } - } - else - { - if ($testResults.FailedCount -gt 0) - { - Write-Error -Exception "$($testResults.FailedCount) integration tests failed." - } - } - - "`n" -} - -Task Build -Depends Init { - $separator - - # Install any dependencies required for the Build stage - Invoke-PSDepend ` - -Path $PSScriptRoot ` - -Force ` - -Import ` - -Install ` - -Tags 'Build' - - # Generate the next version by adding the build system build number to the manifest version - $manifestPath = Join-Path -Path $ProjectRoot -ChildPath "src/$ModuleName.psd1" - $newVersion = Get-VersionNumber ` - -ManifestPath $manifestPath ` - -Build $ENV:BHBuildNumber - - if ($ENV:BHBuildSystem -eq 'AppVeyor') - { - # Update AppVeyor build version number - Update-AppveyorBuild -Version $newVersion - } - - # Determine the folder names for staging the module - $StagingFolder = Join-Path -Path $ProjectRoot -ChildPath 'staging' - $ModuleFolder = Join-Path -Path $StagingFolder -ChildPath $ModuleName - - # Determine the folder names for staging the module - $versionFolder = Join-Path -Path $ModuleFolder -ChildPath $newVersion - - # Stage the module - $null = New-Item -Path $StagingFolder -Type directory -ErrorAction SilentlyContinue - $null = New-Item -Path $ModuleFolder -Type directory -ErrorAction SilentlyContinue - Remove-Item -Path $versionFolder -Recurse -Force -ErrorAction SilentlyContinue - $null = New-Item -Path $versionFolder -Type directory - - # Populate Version Folder - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath "src/$ModuleName.psm1") -Destination $versionFolder - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/en-US') -Destination $versionFolder -Recurse - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/samples') -Destination $VersionFolder -Recurse - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/dsclibrary') -Destination $VersionFolder -Recurse - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/support') -Destination $VersionFolder -Recurse - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/schema') -Destination $VersionFolder -Recurse - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/template') -Destination $VersionFolder -Recurse - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'LICENSE') -Destination $versionFolder - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'README.md') -Destination $versionFolder - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'CHANGELOG.md') -Destination $versionFolder - $null = Copy-Item -Path (Join-Path -Path $ProjectRoot -ChildPath 'RELEASENOTES.md') -Destination $versionFolder - - # Load the Libs files into the PSM1 - $libFiles = Get-ChildItem ` - -Path (Join-Path -Path $ProjectRoot -ChildPath 'src/lib') ` - -Include '*.ps1' ` - -Recurse - - # Assemble all the libs content into a single string - $libFilesStringBuilder = [System.Text.StringBuilder]::new() - foreach ($libFile in $libFiles) - { - $libContent = Get-Content -Path $libFile -Raw - $null = $libFilesStringBuilder.AppendLine($libContent) - } - - <# - Load the PSM1 file into an array of lines and step through each line - adding it to a string builder if the line is not part of the ImportFunctions - Region. Then add the content of the $libFilesStringBuilder string builder - immediately following the end of the region. - #> - $modulePath = Join-Path -Path $versionFolder -ChildPath "$ModuleName.psm1" - $moduleContent = Get-Content -Path $modulePath - $moduleStringBuilder = [System.Text.StringBuilder]::new() - $importFunctionsRegionFound = $false - foreach ($moduleLine in $moduleContent) - { - if ($importFunctionsRegionFound) - { - if ($moduleLine -eq '#endregion') - { - $null = $moduleStringBuilder.AppendLine('#region Functions') - $null = $moduleStringBuilder.AppendLine($libFilesStringBuilder) - $null = $moduleStringBuilder.AppendLine('#endregion') - $importFunctionsRegionFound = $false - } - } - else - { - if ($moduleLine -eq '#region ImportFunctions') - { - $importFunctionsRegionFound = $true - } - else - { - $null = $moduleStringBuilder.AppendLine($moduleLine) - } - } - } - Set-Content -Path $modulePath -Value $moduleStringBuilder -Force - - # Prepare external help - 'Building external help file' - New-ExternalHelp ` - -Path (Join-Path -Path $ProjectRoot -ChildPath 'docs\') ` - -OutputPath $versionFolder ` - -Force - - # Create the module manifest in the staging folder - 'Updating module manifest' - $stagedManifestPath = Join-Path -Path $versionFolder -ChildPath "$ModuleName.psd1" - $tempManifestPath = Join-Path -Path $ENV:Temp -ChildPath "$ModuleName.psd1" - - Import-LocalizedData ` - -BindingVariable 'stagedManifestContent' ` - -FileName "$ModuleName.psd1" ` - -BaseDirectory (Join-Path -Path $ProjectRoot -ChildPath 'src') - $stagedManifestContent.ModuleVersion = $newVersion - $stagedManifestContent.Copyright = "(c) $((Get-Date).Year) Daniel Scott-Raynsford. All rights reserved." - - # Extract the PrivateData values and remove it because it can not be splatted - 'LicenseUri','Tags','ProjectUri','IconUri','ReleaseNotes' | Foreach-Object -Process { - $privateDataValue = $stagedManifestContent.PrivateData.PSData.$_ - if ($privateDataValue) - { - $null = $stagedManifestContent.Add($_, $privateDataValue) - } - } - - $stagedManifestContent.ReleaseNotes = $stagedManifestContent.ReleaseNotes -replace "## What is New in $ModuleName Unreleased", "## What is New in $ModuleName $newVersion" - $stagedManifestContent.Remove('PrivateData') - - # Create the module manifest file - New-ModuleManifest ` - -Path $tempManifestPath ` - @stagedManifestContent - - # Make sure the manifest is encoded as UTF8 - 'Convert manifest to UTF8' - $temporaryManifestContent = Get-Content -Path $tempManifestPath -Raw - $utf8NoBomEncoding = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList ($false) - [System.IO.File]::WriteAllLines($stagedManifestPath, $temporaryManifestContent, $utf8NoBomEncoding) - - # Remove the temporary manifest - $null = Remove-Item -Path $tempManifestPath -Force - - # Validate the module manifest - if (-not (Test-ModuleManifest -Path $stagedManifestPath)) - { - throw "The generated module manifest '$stagedManifestPath' was invalid" - } - - # Set the new version number in the staged CHANGELOG.md - 'Updating CHANGELOG.MD' - $stagedChangeLogPath = Join-Path -Path $versionFolder -ChildPath 'CHANGELOG.md' - $stagedChangeLogContent = Get-Content -Path $stagedChangeLogPath -Raw - $stagedChangeLogContent = $stagedChangeLogContent -replace '# Unreleased', "# $newVersion" - Set-Content -Path $stagedChangeLogPath -Value $stagedChangeLogContent -NoNewLine -Force - - # Set the new version number in the staged RELEASENOTES.md - 'Updating RELEASENOTES.MD' - $stagedReleaseNotesPath = Join-Path -Path $versionFolder -ChildPath 'RELEASENOTES.md' - $stagedReleaseNotesContent = Get-Content -Path $stagedReleaseNotesPath -Raw - $stagedReleaseNotesContent = $stagedReleaseNotesContent -replace "## What is New in $ModuleName Unreleased", "## What is New in $ModuleName $newVersion" - Set-Content -Path $stagedReleaseNotesPath -Value $stagedReleaseNotesContent -NoNewLine -Force - - # Create zip artifact - $zipFileFolder = Join-Path ` - -Path $StagingFolder ` - -ChildPath 'zip' - - $null = New-Item -Path $zipFileFolder -Type directory -ErrorAction SilentlyContinue - - $zipFilePath = Join-Path ` - -Path $zipFileFolder ` - -ChildPath "${ENV:BHProjectName}_$newVersion.zip" - if (Test-Path -Path $zipFilePath) - { - $null = Remove-Item -Path $zipFilePath - } - $null = Add-Type -assemblyname System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::CreateFromDirectory($ModuleFolder, $zipFilePath) - - # Update the Git Repo if this is the master branch build in Azure Pipelines - if ($ENV:BHBuildSystem -eq 'Azure Pipelines') - { - if ($ENV:BHBranchName -eq 'master') - { - # This is a push to master so update GitHub with release info - 'Beginning update to master branch with deployed information' - - $commitMessage = $ENV:BHCommitMessage.TrimEnd() - "Commit to master branch triggered with commit message: '$commitMessage'" - - if ($commitMessage -match '^Azure DevOps Deploy updating Version Number to [0-9/.]*') - { - # This was a deploy commit so no need to do anything - 'Skipping update to master branch with deployed information because this was triggered by Azure DevOps Updating the Version Number' - } - else - { - # Pull the master branch, update the readme.md and manifest - Set-Location -Path $ProjectRoot - - Invoke-Git -GitParameters @('config', '--global', 'credential.helper', 'store') - - # Configure Azure DevOps to be able to Push back to GitHub - Add-Content ` - -Path "$ENV:USERPROFILE\.git-credentials" ` - -Value "https://$($ENV:githubRepoToken):x-oauth-basic@github.com`n" - - Invoke-Git -GitParameters @('config', '--global', 'user.email', 'plagueho@gmail.com') - Invoke-Git -GitParameters @('config', '--global', 'user.name', 'Daniel Scott-Raynsford') - - 'Display list of Git Remotes' - Invoke-Git -GitParameters @('remote', '-v') - Invoke-Git -GitParameters @('checkout', '-f', 'master') - - # Replace the manifest with the one that was published - 'Updating files changed during deployment' - Copy-Item ` - -Path $stagedManifestPath ` - -Destination (Join-Path -Path $ProjectRoot -ChildPath 'src') ` - -Force - Copy-Item ` - -Path $stagedChangeLogPath ` - -Destination $ProjectRoot ` - -Force - Copy-Item ` - -Path $stagedReleaseNotesPath ` - -Destination $ProjectRoot ` - -Force - - # Update the master branch - 'Pushing deployment changes to Master' - Invoke-Git -GitParameters @('add', '.') - Invoke-Git -GitParameters @('commit', '-m', "Azure DevOps Deploy updating Version Number to $NewVersion") - Invoke-Git -GitParameters @('status') - Invoke-Git -GitParameters @('push') - - # Create the version tag and push it - "Pushing $newVersion tag to Master" - Invoke-Git -GitParameters @('tag', '-a', $newVersion, '-m', $newVersion) - Invoke-Git -GitParameters @('status') - Invoke-Git -GitParameters @('push') - - # Merge the changes to the Dev branch as well - 'Pushing deployment changes to Dev' - Invoke-Git -GitParameters @('checkout', '-f', 'dev') - Invoke-Git -GitParameters @('merge', 'origin/master') - Invoke-Git -GitParameters @('push') - } - } - else - { - "Skipping update to master branch with deployed information because branch is: '$ENV:BHBranchName'" - } - } - else - { - "Skipping update to master branch with deployed information because build system is: '$ENV:BHBuildSystem'" - } - "`n" -} - -Task Deploy { - $separator - - # Determine the folder name for the Module - $ModuleFolder = Join-Path -Path $ProjectRoot -ChildPath $ModuleName - - # Install any dependencies required for the Deploy stage - Invoke-PSDepend ` - -Path $PSScriptRoot ` - -Force ` - -Import ` - -Install ` - -Tags 'Deploy' - - # Copy the module to the PSModulePath - $PSModulePath = ($ENV:PSModulePath -split ';')[0] - $destinationPath = Join-Path -Path $PSModulePath -ChildPath $ModuleName - - "Copying Module from $ModuleFolder to $destinationPath" - Copy-Item ` - -Path $ModuleFolder ` - -Destination $destinationPath ` - -Container ` - -Recurse ` - -Force - - $installedModule = Get-Module -Name $ModuleName -ListAvailable - - $versionNumber = $installedModule.Version | - Sort-Object -Descending | - Select-Object -First 1 - - if (-not $versionNumber) - { - Throw "$ModuleName Module could not be found after copying to $PSModulePath" - } - - # This is a deploy from the staging folder - "Publishing $ModuleName Module version '$versionNumber' to PowerShell Gallery" - $null = Get-PackageProvider ` - -Name NuGet ` - -ForceBootstrap - - Publish-Module ` - -Name $ModuleName ` - -RequiredVersion $versionNumber ` - -NuGetApiKey $ENV:PowerShellGalleryApiKey ` - -Confirm:$false -} - -<# - .SYNOPSIS - Generate a new version number. -#> -function Get-VersionNumber -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $ManifestPath, - - [Parameter(Mandatory = $true)] - [System.String] - $Build - ) - - # Get version number from the existing manifest - $manifestContent = Get-Content -Path $ManifestPath -Raw - $regex = '(?<=ModuleVersion\s+=\s+'')(?<ModuleVersion>.*)(?='')' - $matches = @([regex]::matches($manifestContent, $regex, 'IgnoreCase')) - $version = $null - if ($matches) - { - $version = $matches[0].Value - } - - # Determine the new version number - $versionArray = $version -split '\.' - $newVersion = '' - Foreach ($ver in (0..2)) - { - $sem = $versionArray[$ver] - if ([System.String]::IsNullOrEmpty($sem)) - { - $sem = '0' - } - $newVersion += "$sem." - } - $newVersion += $Build - return $newVersion -} - -<# - .SYNOPSIS - Safely execute a Git command. -#> -function Invoke-Git -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String[]] - $GitParameters - ) - - try - { - "Executing 'git $($GitParameters -join ' ')'" - exec { & git $GitParameters } - } - catch - { - Write-Warning -Message $_ - } -} diff --git a/requirements.psd1 b/requirements.psd1 deleted file mode 100644 index 73bd7f38..00000000 --- a/requirements.psd1 +++ /dev/null @@ -1,73 +0,0 @@ -@{ - psake = @{ - Name = 'psake' - DependencyType = 'PSGalleryModule' - Parameters = @{ - Repository = 'PSGallery' - SkipPublisherCheck = $true - } - Target = 'CurrentUser' - Version = '4.7.4' - Tags = 'Bootstrap' - } - - Pester = @{ - Name = 'Pester' - DependencyType = 'PSGalleryModule' - Parameters = @{ - Repository = 'PSGallery' - SkipPublisherCheck = $true - } - Target = 'CurrentUser' - Version = '4.7.3' - Tags = 'Test' - } - - PSScriptAnalyzer = @{ - Name = 'PSScriptAnalyzer' - DependencyType = 'PSGalleryModule' - Parameters = @{ - Repository = 'PSGallery' - SkipPublisherCheck = $true - } - Target = 'CurrentUser' - Version = '1.18.0' - Tags = 'Test' - } - - BuildHelpers = @{ - Name = 'BuildHelpers' - DependencyType = 'PSGalleryModule' - Parameters = @{ - Repository = 'PSGallery' - SkipPublisherCheck = $true - } - Target = 'CurrentUser' - Version = '2.0.10' - Tags = 'Init' - } - - PSDeploy = @{ - Name = 'PSDeploy' - DependencyType = 'PSGalleryModule' - Parameters = @{ - Repository = 'PSGallery' - SkipPublisherCheck = $true - } - Target = 'CurrentUser' - Version = '1.0.1' - Tags = 'Deploy' - } - - Platyps = @{ - Name = 'Platyps' - DependencyType = 'PSGalleryModule' - Parameters = @{ - Repository = 'PSGallery' - SkipPublisherCheck = $true - } - Target = 'CurrentUser' - Version = '0.14.0' - Tags = 'Build' - } -} diff --git a/src/LabBuilder.psd1 b/src/LabBuilder.psd1 deleted file mode 100644 index 5de1f96a..00000000 --- a/src/LabBuilder.psd1 +++ /dev/null @@ -1,247 +0,0 @@ -# -# Module manifest for module 'LabBuilder' -# -# Generated by: Daniel Scott-Raynsford -# -# Generated on: 11/9/2019 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = 'LabBuilder.psm1' - -# Version number of this module. -ModuleVersion = '1.0.5.104' - -# Supported PSEditions -CompatiblePSEditions = 'Desktop' - -# ID used to uniquely identify this module -GUID = 'e229850e-7a90-4123-9a30-37814119d3a3' - -# Author of this module -Author = 'Daniel Scott-Raynsford' - -# Company or vendor of this module -CompanyName = 'None' - -# Copyright statement for this module -Copyright = '(c) 2019 Daniel Scott-Raynsford. All rights reserved.' - -# Description of the functionality provided by this module -Description = 'Builds Hyper-V Windows multi-machine/Active Directory labs using XML configuration files and DSC Resources.' - -# Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '5.1' - -# Name of the Windows PowerShell host required by this module -# PowerShellHostName = '' - -# Minimum version of the Windows PowerShell host required by this module -# PowerShellHostVersion = '' - -# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# DotNetFrameworkVersion = '' - -# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. -# CLRVersion = '' - -# Processor architecture (None, X86, Amd64) required by this module -ProcessorArchitecture = 'None' - -# Modules that must be imported into the global environment prior to importing this module -# RequiredModules = @() - -# Assemblies that must be loaded prior to importing this module -RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -FormatsToProcess = @() - -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() - -# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'Get-LabResourceModule', 'Initialize-LabResourceModule', - 'Get-LabResourceMSU', 'Initialize-LabResourceMSU', - 'Get-LabResourceISO', 'Initialize-LabResourceISO', 'Get-LabSwitch', - 'Initialize-LabSwitch', 'Remove-LabSwitch', 'Get-LabVMTemplateVHD', - 'Initialize-LabVMTemplateVHD', 'Remove-LabVMTemplateVHD', - 'Get-LabVMTemplate', 'Initialize-LabVMTemplate', - 'Remove-LabVMTemplate', 'Get-LabVM', 'Initialize-LabVM', - 'Install-LabVM', 'Remove-LabVM', 'Get-Lab', 'New-Lab', 'Install-Lab', - 'Update-Lab', 'Uninstall-Lab', 'Start-Lab', 'Stop-Lab' - -# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. -CmdletsToExport = '*' - -# Variables to export from this module -VariablesToExport = '*' - -# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = @() - -# DSC resources to export from this module -# DscResourcesToExport = @() - -# List of all modules packaged with this module -# ModuleList = @() - -# List of all files packaged with this module -FileList = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'Hyper-V', 'Lab', 'DesiredStateConfiguration', 'DSC', 'PSEdition_Desktop' - - # A URL to the license for this module. - LicenseUri = 'https://github.com/PlagueHO/LabBuilder/blob/master/LICENSE' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/PlagueHO/LabBuilder' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - ReleaseNotes = ' -## What is New in LabBuilder 1.0.5.104 - -- Samples\Sample_WS2019_AzureADConnect.xml: Added sample for installing Azure AD - Connect. -- Convert all DSC configurations to use ActiveDirectoryDsc version - 4.1.0.0. -- `dsclibrary\RODC_SECONDARY.DSC.ps1`: - - Enable RODC creation because it is supported by ActiveDirectoryDsc. -- `dsclibrary\DC_FORESTPRIMARY.DSC.ps1`: - - Enabled customizing of Domain NetBios name. -- `Get-LabVm.ps1`: - - Clean up code style. -- `Enable-LabWSMan.ps1`: - - Improved function so that if WinRM Service is stopped it will be started. -- `Get-Lab.ps1`: - - Clean up code style. - - Fix bug reading `configpath` from `settings` node. - - Changed to use `ConvertTo-LabAbsolutePath.ps1` to simplify code. - - Changed to automatically use the `DSCLibrary` folder that comes as part of - the LabBuilder module if the `dsclibrarypath` setting is not specified - in the lab configuration - fixes [Issue-335](https://github.com/PlagueHO/LabBuilder/issues/335). -- `ConvertTo-LabAbsolutePath.ps1`: - - Added function to create an absolute path from a relative lab path. -- Removed `dsclibrarypath` setting from all samples as it is no longer required. -- `Get-LabResourceISO.ps1`: - - Clean up code style. - -## What is New in LabBuilder 1.0.4.83 - -- `Get-LabUnattendFileContent.ps1`: -- Enabled PSRemoting in Unattend.xml (allows DSC to initialize properly on - newer operating systems). -- Enabled local administrator account for Client operating systems - (Windows 10). -- Enabled PowerShell script execution for both 32-bit and 64-bit processes. -- `Connect-LabVM.ps1`: -- Test WinRM connectivity prior to initializing DSC. -- `Install-LabVM.ps1`: - - Check for DSC Configuration section in XML file prior to calling DSC. - -July 21, 2019 - -- `dsclibrary\MEMBER_SUBCA.DSC.ps1`: - - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- `dsclibrary\MEMBER_ROOTCA.DSC.ps1`: - - CAServer parameter removed from ADCSWebEnrollment - fixes [Issue-320](https://github.com/PlagueHO/LabBuilder/issues/320). - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). - - Changed CApolicy.inf RenewalKeyLength to 4096, CNGHashAlgorithm to SHA256 and - LoadDefaultTemplates to 0 - fixes [Issue-324](https://github.com/PlagueHO/LabBuilder/issues/324). -- `dsclibrary\STANDALONE_ROOTCA.DSC.ps1`: - - Correct SubCA resource name to wait for - fixes [Issue-321](https://github.com/PlagueHO/LabBuilder/issues/321). - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- `dsclibrary\STANDALONE_ROOTCA_NOSUBCA.DSC.ps1`: - - Change `DiscreteSignatureAlgorithm` to `AlternateSignatureAlgorithm` and set - it to 0 - fixes [Issue-322](https://github.com/PlagueHO/LabBuilder/issues/322). - - Fix error occuring when `c:\windows\setup\scripts\` folder does not exist when - setting the advanced CA configuration settings - fixes [Issue-325](https://github.com/PlagueHO/LabBuilder/issues/325). -- Added `.markdownlint.json` file. -- Fix markdown rule violations in `CHANGELOG.MD`. -- `dsclibrary\MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\STANDALONE_DHCPDNS.DSC.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\STANDALONE_INTERNET.DSC.DSC.ps1`: - - Fix DHCP scope to work with newer version of xDhcpServerScope DSC resource. - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_DHCP.DSC.ps1`: - - Update to require xDhcpServer resource 2.0.0.0. -- `dsclibrary\MEMBER_NPS_DFSTEST.ps1`: - - Fix to use correct name of the DFSReplicationGroup resource. -- `dsclibrary\MEMBER_WDS.DSC.ps1`: - - Fix configuration. - -## What is New in LabBuilder 1.0.2.58 - -May 5, 2019 - -- Reword module description in Manifest. -- Fix bug when connecting to a Lab VM when TrustedHosts is empty - fixes - [Issue #314](https://github.com/PlagueHO/LabBuilder/issues/314). -- Moved Schema documentation file into docs folder and converted to - PlatyPS compatible file. -- Cleaned up Schema documentation file to remove most markdown rule - violations. -- Cleaned up README.MD file to remove most markdown rule - violations. -- Fix infinite loop bug occuring in `Stop-Lab` when Lab VM does not - exist - fixes [Issue #316](https://github.com/PlagueHO/LabBuilder/issues/316). -- Fix infinite loop bug occuring in `Start-Lab` when Lab VM does not - exist. -- DSCLibrary\MEMBER_NANO.DSC.ps1: Rename xOfflineDomainJoin to - OfflineDomainJoin - fixes [Issue #317](https://github.com/PlagueHO/LabBuilder/issues/317). -' - - } # End of PSData hashtable - -} # End of PrivateData hashtable - -# HelpInfo URI of this module -HelpInfoURI = 'https://github.com/PlagueHO/LabBuilder/blob/master/README.md' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} - - diff --git a/src/LabBuilder.psm1 b/src/LabBuilder.psm1 deleted file mode 100644 index b19d2d4f..00000000 --- a/src/LabBuilder.psm1 +++ /dev/null @@ -1,547 +0,0 @@ -<# -.EXTERNALHELP LabBuilder-help.xml -#> -#Requires -version 5.1 -#Requires -RunAsAdministrator - -$script:LabBuidlerModuleRoot = Split-Path ` - -Path $MyInvocation.MyCommand.Path ` - -Parent - -#region LocalizedData -$culture = 'en-us' - -if (Test-Path -Path (Join-Path -Path $script:LabBuidlerModuleRoot -ChildPath $PSUICulture)) -{ - $culture = $PSUICulture -} - -Import-LocalizedData ` - -BindingVariable LocalizedData ` - -Filename 'LabBuilder_LocalizedData.psd1' ` - -BaseDirectory $script:LabBuidlerModuleRoot ` - -UICulture $culture -#endregion - -#region Types -Enum LabOStype { - Server = 1 - Nano = 2 - Client = 3 -} # Enum LabOStype - -Enum LabVHDType { - Fixed = 1 - Dynamic = 2 - Differencing = 3 -} # Enum LabVHDType - -Enum LabVHDFormat { - VHD = 1 - VHDx = 2 -} # Enum LabVHDFormat - -Enum LabSwitchType { - Private = 1 - Internal = 2 - External = 3 - NAT = 4 -} # Enum LabSwitchType - -Enum LabPartitionStyle { - MBR = 1 - GPT = 2 -} # Enum LabPartitionStyle - -Enum LabFileSystem { - FAT32 = 1 - exFAT = 2 - NTFS = 3 - ReFS = 4 -} # Enum LabFileSystem - -Enum LabCertificateSource { - Guest = 1 - Host = 2 -} # Enum LabCertificateSource - -class LabResourceModule:System.ICloneable { - [System.String] $Name - [System.String] $URL - [System.String] $Folder - [System.String] $MinimumVersion - [System.String] $RequiredVersion - - LabResourceModule() {} - - LabResourceModule($Name) { - $this.Name = $Name - } # Constructor - - [Object] Clone () { - $New = [LabResourceModule]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabResourceModule - -class LabResourceMSU:System.ICloneable { - [System.String] $Name - [System.String] $URL - [System.String] $Path - [System.String] $Filename - - LabResourceMSU() {} - - LabResourceMSU($Name) { - $this.Name = $Name - } # Constructor - - LabResourceMSU($Name,$URL) { - $this.Name = $Name - $this.URL = $URL - } # Constructor - - [Object] Clone () { - $New = [LabResourceMSU]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabResourceMSU - -class LabResourceISO:System.ICloneable { - [System.String] $Name - [System.String] $URL - [System.String] $Path - - LabResourceISO() {} - - LabResourceISO($Name) { - $this.Name = $Name - } # Constructor - - [Object] Clone () { - $New = [LabResourceISO]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabResourceISO - -class LabSwitchAdapter:System.ICloneable { - [System.String] $Name - [System.String] $MACAddress - [Byte] $Vlan - - LabSwitchAdapter() {} - - LabSwitchAdapter($Name) { - $this.Name = $Name - } # Constructor - - LabSwitchAdapter($Name,$Type) { - $this.Name = $Name - $this.Type = $Type - } # Constructor - - [Object] Clone () { - $New = [LabSwitchAdapter]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabSwitchAdapter - -class LabVMAdapterIPv4:System.ICloneable { - [System.String] $Address - [System.String] $DefaultGateway - [Byte] $SubnetMask - [System.String] $DNSServer - - LabVMAdapterIPv4() {} - - LabVMAdapterIPv4($Address,$SubnetMask) { - $this.Address = $Address - $this.SubnetMask = $SubnetMask - } # Constructor - - [Object] Clone () { - $New = [LabVMAdapterIPv4]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabVMAdapterIPv4 - -class LabVMAdapterIPv6:System.ICloneable { - [System.String] $Address - [System.String] $DefaultGateway - [Byte] $SubnetMask - [System.String] $DNSServer - - LabVMAdapterIPv6() {} - - LabVMAdapterIPv6($Address,$SubnetMask) { - $this.Address = $Address - $this.SubnetMask = $SubnetMask - } # Constructor - - [Object] Clone () { - $New = [LabVMAdapterIPv6]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabVMAdapterIPv6 - -class LabVMAdapter:System.ICloneable { - [System.String] $Name - [System.String] $SwitchName - [System.String] $MACAddress - [System.Boolean] $MACAddressSpoofing - [Byte] $Vlan - [LabVMAdapterIPv4] $IPv4 - [LabVMAdapterIPv6] $IPv6 - - LabVMAdapter() {} - - LabVMAdapter($Name) { - $this.Name = $Name - } # Constructor - - [Object] Clone () { - $New = [LabVMAdapter]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabVMAdapter - -class LabDataVHD:System.ICloneable { - [System.String] $VHD - [LabVHDType] $VHDType - [Uint64] $Size - [System.String] $SourceVHD - [System.String] $ParentVHD - [System.Boolean] $MoveSourceVHD - [System.String] $CopyFolders - [LabFileSystem] $FileSystem - [LabPartitionStyle] $PartitionStyle - [System.String] $FileSystemLabel - [System.Boolean] $Shared = $false - [System.Boolean] $SupportPR = $false - - LabDataVHD() {} - - LabDataVHD($VHD) { - $this.VHD = $VHD - } # Constructor - - [Object] Clone () { - $New = [LabDataVHD]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabDataVHD - -class LabDVDDrive:System.ICloneable { - [System.String] $ISO - [System.String] $Path - - LabDVDDrive() {} - - LabDVDDrive($ISO) { - $this.ISO = $ISO - } # Constructor - - [Object] Clone () { - $New = [LabDVDDrive]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabDVDDrive - -class LabVMTemplateVHD:System.ICloneable { - [System.String] $Name - [System.String] $ISOPath - [System.String] $VHDPath - [LabOStype] $OSType = [LabOStype]::Server - [System.String] $Edition - [Byte] $Generation = 2 - [LabVHDFormat] $VHDFormat = [LabVHDFormat]::VHDx - [LabVHDType] $VHDType = [LabVHDType]::Dynamic - [Uint64] $VHDSize = 0 - [System.String[]] $Packages - [System.String[]] $Features - - LabVMTemplateVHD() {} - - LabVMTemplateVHD($Name) { - $this.Name = $Name - } # Constructor - - [Object] Clone () { - $New = [LabVMTemplateVHD]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabVMTemplateVHD - -class LabVMTemplate:System.ICloneable { - [System.String] $Name - [System.String] $VHD - [System.String] $SourceVHD - [System.String] $ParentVHD - [System.String] $TemplateVHD - [Uint64] $MemoryStartupBytes = 1GB - [System.Boolean] $DynamicMemoryEnabled = $true - [System.Boolean] $ExposeVirtualizationExtensions = $false - [Byte] $ProcessorCount = 1 - [System.String] $AdministratorPassword - [System.String] $ProductKey - [System.String] $Timezone="Pacific Standard Time" - [LabOStype] $OSType = [LabOStype]::Server - [System.String[]] $IntegrationServices = @('Guest Service Interface','Heartbeat','Key-Value Pair Exchange','Shutdown','Time Synchronization','VSS') - [System.String[]] $Packages - [ValidateRange(1,2)][Byte] $Generation = 2 - [ValidateSet("5.0","6.2","7.0","7.1","8.0","254.0","255.0")][System.String] $Version = '8.0' - - LabVMTemplate() {} - - LabVMTemplate($Name) { - $this.Name = $Name - } # Constructor - - [Object] Clone () { - $New = [LabVMTemplate]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabVMTemplate - -class LabSwitch:System.ICloneable { - [System.String] $Name - [LabSwitchType] $Type - [Byte] $VLAN - [System.String] $BindingAdapterName - [System.String] $BindingAdapterMac - [System.String] $NatSubnet - [System.String] $NatGatewayAddress - [LabSwitchAdapter[]] $Adapters - - LabSwitch() {} - - LabSwitch($Name) { - $this.Name = $Name - } # Constructor - - LabSwitch($Name,$Type) { - $this.Name = $Name - $this.Type = $Type - } # Constructor - - [Object] Clone () { - $New = [LabSwitch]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabSwitch - -class LabDSC:System.ICloneable { - [System.String] $ConfigName - [System.String] $ConfigFile - [System.String] $Parameters - [System.Boolean] $Logging = $false - - LabDSC() {} - - LabDSC($ConfigName) { - $this.ConfigName = $ConfigName - } # Constructor - - LabDSC($ConfigName,$ConfigFile) { - $this.ConfigName = $ConfigName - $this.ConfigFile = $ConfigFile - } # Constructor - - [Object] Clone () { - $New = [LabDSC]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabDSC - -class LabVM:System.ICloneable { - [System.String] $Name - [System.String] $Template - [System.String] $ComputerName - [Byte] $ProcessorCount - [Uint64] $MemoryStartupBytes = 1GB - [System.Boolean] $DynamicMemoryEnabled = $true - [System.Boolean] $ExposeVirtualizationExtensions = $false - [System.String] $ParentVHD - [System.Boolean] $UseDifferencingDisk = $true - [System.String] $AdministratorPassword - [System.String] $ProductKey - [System.String] $Timezone="Pacific Standard Time" - [LabOStype] $OSType = [LabOStype]::Server - [System.String] $UnattendFile - [System.String] $SetupComplete - [System.String[]] $Packages - [ValidateRange(1,2)][Byte] $Generation = 2 - [ValidateSet("5.0","6.2","7.0","7.1","8.0","254.0","255.0")][System.String] $Version = '8.0' - [System.Int32] $BootOrder - [System.String[]] $IntegrationServices = @('Guest Service Interface','Heartbeat','Key-Value Pair Exchange','Shutdown','Time Synchronization','VSS') - [LabVMAdapter[]] $Adapters - [LabDataVHD[]] $DataVHDs - [LabDVDDrive[]] $DVDDrives - [LabDSC] $DSC - [System.String] $VMRootPath - [System.String] $LabBuilderFilesPath - [LabCertificateSource] $CertificateSource = [LabCertificateSource]::Guest - [System.String] $NanoODJPath - - LabVM() {} - - LabVM($Name) { - $this.Name = $Name - } # Constructor - - LabVM($Name,$ComputerName) { - $this.Name = $Name - $this.ComputerName = $ComputerName - } # Constructor - - [Object] Clone () { - $New = [LabVM]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabVM - -class LabDSCModule:System.ICloneable { - [System.String] $ModuleName - [Version] $ModuleVersion - [Version] $MinimumVersion - - LabDSCModule() {} - - LabDSCModule($ModuleName) { - $this.ModuleName = $ModuleName - } # Constructor - - LabDSCModule($ModuleName,$ModuleVersion) { - $this.ModuleName = $ModuleName - $this.ModuleVersion = [Version] $ModuleVersion - } # Constructor - - [Object] Clone () { - $New = [LabDSCModule]::New() - foreach ($Property in ($this | Get-Member -MemberType Property)) - { - $New.$($Property.Name) = $this.$($Property.Name) - } # foreach - return $New - } # Clone -} # class LabDSCModule -#endregion - - -#region ModuleVariables -[System.String] $Script:WorkingFolder = $ENV:Temp - -# Supporting files -[System.String] $Script:SupportConvertWindowsImagePath = Join-Path ` - -Path $PSScriptRoot ` - -ChildPath 'support\Convert-WindowsImage.ps1' -[System.String] $Script:SupportGertGenPath = Join-Path ` - -Path $PSScriptRoot ` - -ChildPath 'support\New-SelfSignedCertificateEx.ps1' - -# DSC Library -[System.String] $Script:DSCLibraryPath = Join-Path ` - -Path $PSScriptRoot ` - -ChildPath 'dsclibrary' - -# Virtual Networking Parameters -[System.Int32] $Script:DefaultManagementVLan = 99 - -# Self-signed Certificate Parameters -[System.Int32] $Script:SelfSignedCertKeyLength = 2048 -# Warning - using KSP causes the Private Key to not be accessible to PS. -[System.String] $Script:SelfSignedCertProviderName = 'Microsoft Enhanced Cryptographic Provider v1.0' # 'Microsoft Software Key Storage Provider' -[System.String] $Script:SelfSignedCertAlgorithmName = 'RSA' # 'ECDH_P256' Or 'ECDH_P384' Or 'ECDH_P521' -[System.String] $Script:SelfSignedCertSignatureAlgorithm = 'SHA256' # 'SHA1' -[System.String] $Script:DSCEncryptionCert = 'DSCEncryption.cer' -[System.String] $Script:DSCEncryptionPfxCert = 'DSCEncryption.pfx' -[System.String] $Script:DSCCertificateFriendlyName = 'DSC Credential Encryption' -[System.String] $Script:DSCCertificatePassword = 'E3jdNkd903mDn43NEk2nbDENjw' -[System.Int32] $Script:RetryConnectSeconds = 5 -[System.Int32] $Script:RetryHeartbeatSeconds = 1 -[System.Int32] $Script:StartupTimeout = 90 - -# System Info -[System.Int32] $Script:CurrentBuild = (Get-ItemProperty ` - -Path 'hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuild - -# XML Stuff -[System.String] $Script:ConfigurationXMLSchema = Join-Path ` - -Path $PSScriptRoot ` - -ChildPath 'schema\labbuilderconfig-schema.xsd' -[System.String] $Script:ConfigurationXMLTemplate = Join-Path ` - -Path $PSScriptRoot ` - -ChildPath 'template\labbuilderconfig-template.xml' - -# Nano Stuff -[System.String] $Script:NanoPackageCulture = 'en-us' - -#region ImportFunctions -# Dot source any functions in the libs folder -$libFiles = Get-ChildItem ` - -Path (Join-Path -Path $script:LabBuidlerModuleRoot -ChildPath 'lib') ` - -Include '*.ps1' ` - -Recurse - -foreach ($libFile in $libFiles) -{ - Write-Verbose -Message $($LocalizedData.ImportingLibFileMessage -f $libFile.Fullname) - . $libFile.Fullname -} -#endregion diff --git a/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 b/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 deleted file mode 100644 index cb4a305a..00000000 --- a/src/dsclibrary/DC_FORESTCHILDDOMAIN.DSC.ps1 +++ /dev/null @@ -1,146 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - DC_FORESTCHILDDOMAIN -.Desription - Builds a Domain Controller and creates it as the first DC in a new child domain within the - existing forest specified in the DomainName parameter. - Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional - configuration of the DNS Server. -.Parameters: - ParentDomainName = 'LABBUILDER.COM' - DomainName = 'DEV' - DomainAdminPassword = 'P@ssword!1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - ADZones = @( - @{ Name = 'ALPHA.LOCAL'; - DynamicUpdate = 'Secure'; - ReplicationScope = 'Forest'; - } - ) - PrimaryZones = @( - @{ Name = 'BRAVO.LOCAL'; - ZoneFile = 'bravo.local.dns'; - DynamicUpdate = 'None'; - } - ) -###################################################################################################> - -Configuration DC_FORESTCHILDDOMAIN -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.ParentDomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature BackupInstall - { - Ensure = 'Present' - Name = 'Windows-Server-Backup' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - WindowsFeature ADDSInstall - { - Ensure = 'Present' - Name = 'AD-Domain-Services' - DependsOn = '[WindowsFeature]DNSInstall' - } - - WindowsFeature RSAT-AD-PowerShellInstall - { - Ensure = 'Present' - Name = 'RSAT-AD-PowerShell' - DependsOn = '[WindowsFeature]ADDSInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]ADDSInstall' - } - } - - WaitForADDomain DscDomainWait - { - DomainName = $Node.ParentDomainName - Credential = $DomainAdminCredential - WaitTimeout = 300 - RestartCount = 5 - DependsOn = '[WindowsFeature]ADDSInstall' - } - - ADDomain PrimaryDC - { - DomainName = $Node.DomainName - ParentDomainName = $Node.ParentDomainName - Credential = $DomainAdminCredential - SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = '[WaitForADDomain]DscDomainWait' - } - - # DNS Server Settings - if ($Node.Forwarders) - { - xDnsServerForwarder DNSForwarders - { - IsSingleInstance = 'Yes' - IPAddresses = $Node.Forwarders - DependsOn = '[ADDomain]PrimaryDC' - } - } - - $count = 0 - foreach ($ADZone in $Node.ADZones) - { - $count++ - xDnsServerADZone "ADZone$count" - { - Ensure = 'Present' - Name = $ADZone.Name - DynamicUpdate = $ADZone.DynamicUpdate - ReplicationScope = $ADZone.ReplicationScope - DependsOn = '[ADDomain]PrimaryDC' - } - } - - $count = 0 - foreach ($PrimaryZone in $Node.PrimaryZones) - { - $count++ - xDnsServerPrimaryZone "PrimaryZone$count" - { - Ensure = 'Present' - Name = $PrimaryZone.Name - ZoneFile = $PrimaryZone.ZoneFile - DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = '[ADDomain]PrimaryDC' - } - } - } -} diff --git a/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 b/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 deleted file mode 100644 index 06156eef..00000000 --- a/src/dsclibrary/DC_FORESTPRIMARY.DSC.ps1 +++ /dev/null @@ -1,233 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - DC_FORESTPRIMARY -.Desription - Builds a Domain Controller as the first DC in a forest with the name of the Domain Name - parameter passed. - The optional parameter DomainNetBiosName can be used to set the NetBios name of the domain - if it needs to be different from the DomainName. - Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional - configuration of the DNS Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainNetBiosName = 'LABBUILDER' - DomainAdminPassword = 'P@ssword!1' - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - ADZones = @( - @{ Name = 'ALPHA.LOCAL'; - DynamicUpdate = 'Secure'; - ReplicationScope = 'Forest'; - } - ) - PrimaryZones = @( - @{ Name = 'BRAVO.LOCAL'; - ZoneFile = 'bravo.local.dns'; - DynamicUpdate = 'None'; - } - ) -###################################################################################################> - -Configuration DC_FORESTPRIMARY -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature BackupInstall - { - Ensure = 'Present' - Name = 'Windows-Server-Backup' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - WindowsFeature ADDSInstall - { - Ensure = 'Present' - Name = 'AD-Domain-Services' - DependsOn = '[WindowsFeature]DNSInstall' - } - - WindowsFeature RSAT-AD-PowerShellInstall - { - Ensure = 'Present' - Name = 'RSAT-AD-PowerShell' - DependsOn = '[WindowsFeature]ADDSInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]ADDSInstall' - } - } - - if ($Node.DomainNetBiosName) - { - ADDomain PrimaryDC - { - DomainName = $Node.DomainName - DomainNetBiosName = $Node.DomainNetBiosName - Credential = $DomainAdminCredential - SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = '[WindowsFeature]ADDSInstall' - } - } - else - { - ADDomain PrimaryDC - { - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = '[WindowsFeature]ADDSInstall' - } - } - - WaitForADDomain DscDomainWait - { - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - WaitTimeout = 300 - RestartCount = 5 - DependsOn = '[ADDomain]PrimaryDC' - } - - # Enable AD Recycle bin - ADOptionalFeature RecycleBin - { - FeatureName = 'Recycle Bin Feature' - EnterpriseAdministratorCredential = $DomainAdminCredential - ForestFQDN = $Node.DomainName - DependsOn = '[WaitForADDomain]DscDomainWait' - } - - # Install a KDS Root Key so we can create MSA/gMSA accounts - Script CreateKDSRootKey - { - SetScript = { - Add-KDSRootKey -EffectiveTime ((Get-Date).AddHours(-10)) } - GetScript = { - Return @{ - KDSRootKey = (Get-KDSRootKey) - } - } - TestScript = { - if (-not (Get-KDSRootKey)) - { - Write-Verbose -Message 'KDS Root Key Needs to be installed...' - Return $false - } - Return $true - } - DependsOn = '[WaitForADDomain]DscDomainWait' - } - - # DNS Server Settings - if ($Node.Forwarders) - { - xDnsServerForwarder DNSForwarders - { - IsSingleInstance = 'Yes' - IPAddresses = $Node.Forwarders - DependsOn = '[WaitForADDomain]DscDomainWait' - } - } - - $count = 0 - foreach ($ADZone in $Node.ADZones) - { - $count++ - xDnsServerADZone "ADZone$count" - { - Ensure = 'Present' - Name = $ADZone.Name - DynamicUpdate = $ADZone.DynamicUpdate - ReplicationScope = $ADZone.ReplicationScope - DependsOn = '[WaitForADDomain]DscDomainWait' - } - } - - $count = 0 - foreach ($PrimaryZone in $Node.PrimaryZones) - { - $count++ - xDnsServerPrimaryZone "PrimaryZone$count" - { - Ensure = 'Present' - Name = $PrimaryZone.Name - ZoneFile = $PrimaryZone.ZoneFile - DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = '[WaitForADDomain]DscDomainWait' - } - } - - <# - # Create a Reverse Lookup Zone - xDnsServerPrimaryZone GlobalNamesZone - { - Name = $Node.ReverseZone - DynamicUpdate = - Ensure = 'Present' - DependsOn = '[WaitForADDomain]DscDomainWait' - } - - # Create a Global Names zone - can't do this until the resource supports it - xDnsServerPrimaryZone GlobalNamesZone - { - Name = 'GlobalNames' - DynamicUpdate = - Ensure = 'Present' - DependsOn = '[WaitForADDomain]DscDomainWait' - } - - # Enable GlobalNames in DNS Server - Script InstallRootCACert - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Write-Verbose -Message 'Enabling Global Name Zone...' - Set-DNSServerGlobalNameZone -Enable - } - GetScript = { - Return @{ - Enable = (Get-DNSServerGlobalNameZone).Enable - } - } - TestScript = { - if (-not (Get-DNSServerGlobalNameZone).Enable) { - Write-Verbose -Message 'Global Name Zone needs to be enabled...' - Return $false - } - Return $true - } - DependsOn = '[xDnsServerPrimaryZone]GlobalNamesZone' - } -#> - } -} diff --git a/src/dsclibrary/DC_SECONDARY.DSC.ps1 b/src/dsclibrary/DC_SECONDARY.DSC.ps1 deleted file mode 100644 index 87459107..00000000 --- a/src/dsclibrary/DC_SECONDARY.DSC.ps1 +++ /dev/null @@ -1,142 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - DC_SECONDARY -.Desription - Builds a Domain Controller and adds it to the existing domain provided in the Parameter - DomainName. - Setting optional parameters Forwarders, ADZones and PrimaryZones will allow additional - configuration of the DNS Server. -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - ADZones = @( - @{ Name = 'ALPHA.LOCAL'; - DynamicUpdate = 'Secure'; - ReplicationScope = 'Forest'; - } - ) - PrimaryZones = @( - @{ Name = 'BRAVO.LOCAL'; - ZoneFile = 'bravo.local.dns'; - DynamicUpdate = 'None'; - } - ) -###################################################################################################> - -Configuration DC_SECONDARY -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentName ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentName ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature BackupInstall - { - Ensure = 'Present' - Name = 'Windows-Server-Backup' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - WindowsFeature ADDSInstall - { - Ensure = 'Present' - Name = 'AD-Domain-Services' - DependsOn = '[WindowsFeature]DNSInstall' - } - - WindowsFeature RSAT-AD-PowerShellInstall - { - Ensure = 'Present' - Name = 'RSAT-AD-PowerShell' - DependsOn = '[WindowsFeature]ADDSInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]ADDSInstall' - } - } - - WaitForADDomain DscDomainWait - { - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - WaitTimeout = 300 - RestartCount = 5 - DependsOn = '[WindowsFeature]ADDSInstall' - } - - ADDomainController SecondaryDC - { - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = '[WaitForADDomain]DscDomainWait' - } - - # DNS Server Settings - if ($Node.Forwarders) - { - xDnsServerForwarder DNSForwarders - { - IsSingleInstance = 'Yes' - IPAddresses = $Node.Forwarders - DependsOn = '[ADDomainController]SecondaryDC' - } - } - [System.Int32]$count = 0 - foreach ($ADZone in $Node.ADZones) - { - $count++ - xDnsServerADZone "ADZone$count" - { - Ensure = 'Present' - Name = $ADZone.Name - DynamicUpdate = $ADZone.DynamicUpdate - ReplicationScope = $ADZone.ReplicationScope - DependsOn = '[ADDomainController]SecondaryDC' - } - } - [System.Int32]$count = 0 - foreach ($PrimaryZone in $Node.PrimaryZones) - { - $count++ - xDnsServerPrimaryZone "PrimaryZone$count" - { - Ensure = 'Present' - Name = $PrimaryZone.Name - ZoneFile = $PrimaryZone.ZoneFile - DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = '[ADDomainController]SecondaryDC' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_ADFS.DSC.ps1 b/src/dsclibrary/MEMBER_ADFS.DSC.ps1 deleted file mode 100644 index 1e43ca36..00000000 --- a/src/dsclibrary/MEMBER_ADFS.DSC.ps1 +++ /dev/null @@ -1,86 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_ADFS -.Desription - Builds a Server that is joined to a domain and then made into an ADFS Server using WID. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_ADFS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature WIDInstall - { - Ensure = 'Present' - Name = 'Windows-Internal-Database' - } - - WindowsFeature ADFSInstall - { - Ensure = 'Present' - Name = 'ADFS-Federation' - DependsOn = '[WindowsFeature]WIDInstall' - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Enable ADFS FireWall rules - Firewall ADFSFirewall1 - { - Name = 'ADFSSrv-HTTP-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall ADFSFirewall2 - { - Name = 'ADFSSrv-HTTPS-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall ADFSFirewall3 - { - Name = 'ADFSSrv-SmartcardAuthN-HTTPS-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - } -} diff --git a/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 b/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 deleted file mode 100644 index 018b7ad9..00000000 --- a/src/dsclibrary/MEMBER_ADRMS.DSC.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_ADRMS -.Desription - Builds a Server that is joined to a domain and then made into an ADRMS Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - ADFSSupport = $true -###################################################################################################> - -Configuration MEMBER_ADRMS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature WIDInstall - { - Ensure = 'Present' - Name = 'Windows-Internal-Database' - } - - WindowsFeature ADRMSServerInstall - { - Ensure = 'Present' - Name = 'ADRMS-Server' - DependsOn = '[WindowsFeature]WIDInstall' - } - - if ($Node.ADFSSupport) - { - WindowsFeature ADRMSIdentityInstall - { - Ensure = 'Present' - Name = 'ADRMS-Identity' - DependsOn = '[WindowsFeature]ADRMSServerInstall' - } - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 b/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 deleted file mode 100644 index 40cc46ee..00000000 --- a/src/dsclibrary/MEMBER_BRANCHCACHE_HOST.DSC.ps1 +++ /dev/null @@ -1,76 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_BRANCHCACHE_HOST -.Desription - Builds a Server that is joined to a domain and then made into a BranchCache Hosted Mode Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_BRANCHCACHE_HOST -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature BranchCache - { - Ensure = 'Present' - Name = 'BranchCache' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Enable BranchCache Hosted Mode Firewall Fules - Firewall FSRMFirewall1 - { - Name = 'Microsoft-Windows-PeerDist-HostedServer-In' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall2 - { - Name = 'Microsoft-Windows-PeerDist-HostedServer-Out' - Ensure = 'Present' - Enabled = 'True' - } - } -} diff --git a/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 b/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 deleted file mode 100644 index 67f293fc..00000000 --- a/src/dsclibrary/MEMBER_CONTAINER_HOST.DSC.ps1 +++ /dev/null @@ -1,141 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_CONTAINER_HOST -.Desription - Builds a Server that is joined to a domain and then made into a Container Host with Docker. - - This should only be used on a Windows Server 2016 RTM host. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_CONTAINER_HOST -{ - $ProgramFiles = $ENV:ProgramFiles - $DockerPath = Join-Path -Path $ProgramFiles -ChildPath 'Docker' - $DockerZipFileName = 'docker.zip' - $DockerZipPath = Join-Path -Path $ProgramFiles -ChildPath $DockerZipFilename - $DockerUri = 'https://download.docker.com/components/engine/windows-server/cs-1.12/docker.zip' - - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArguementList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArguementList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Install containers feature - WindowsFeature ContainerInstall - { - Ensure = 'Present' - Name = 'Containers' - } - - # Download Docker Engine - xRemoteFile DockerEngineDownload - { - DestinationPath = $ProgramFiles - Uri = $DockerUri - MatchSource = $false - } - - # Extract Docker Engine zip file - xArchive DockerEngineExtract - { - Destination = $ProgramFiles - Path = $DockerZipPath - Ensure = 'Present' - Validate = $false - Force = $true - DependsOn = '[xRemoteFile]DockerEngineDownload' - } - - # Add Docker to the Path - xEnvironment DockerPath - { - Ensure = 'Present' - Name = 'Path' - Value = $DockerPath - Path = $true - DependsOn = '[xArchive]DockerEngineExtract' - } - - <# - Reboot the system to complete Containers feature setup - Perform this after setting the Environment variable - so that PowerShell and other consoles can access it. - #> - PendingReboot Reboot - { - Name = 'Reboot After Containers' - } - - # Install the Docker Daemon as a service - Script DockerService - { - SetScript = { - $DockerDPath = (Join-Path -Path $Using:DockerPath -ChildPath 'dockerd.exe') - & $DockerDPath @('--register-service') - } - GetScript = { - return @{ - 'Service' = (Get-Service -Name Docker).Name - } - } - TestScript = { - if (Get-Service -Name Docker -ErrorAction SilentlyContinue) - { - return $true - } - return $false - } - DependsOn = '[xArchive]DockerEngineExtract' - } - - <# - Start up the Docker Service and ensure it is set - to start up automatically. - #> - xServiceSet DockerService - { - Ensure = 'Present' - Name = 'Docker' - StartupType = 'Automatic' - State = 'Running' - DependsOn = '[Script]DockerService' - } - } -} diff --git a/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 b/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 deleted file mode 100644 index c15fb938..00000000 --- a/src/dsclibrary/MEMBER_DEFAULT.DSC.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DEFAULT -.Desription - Builds a Server that is joined to a domain. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_DEFAULT -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 b/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 deleted file mode 100644 index bf2c1d8a..00000000 --- a/src/dsclibrary/MEMBER_DFSHUB.DSC.ps1 +++ /dev/null @@ -1,172 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FILESERVER -.Desription - Builds a Server that is joined to a domain and then made into a File Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - SpokeComputerName = @('Spoke1','Spoke2') - ResourceGroupName = 'WebSite' - ResourceGroupDescription = 'Files for web server' - ResourceGroupFolderName = 'WebSiteFiles' - ResourceGroupContentPath = 'd:\inetpub\wwwroot\WebSiteFiles' -###################################################################################################> - -Configuration MEMBER_DFSHUB -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName DFSDsc - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FileServerInstall - { - Ensure = 'Present' - Name = 'FS-FileServer' - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Namespace' - DependsOn = '[WindowsFeature]FileServerInstall' - } - - WindowsFeature DFSReplicationInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Replication' - DependsOn = '[WindowsFeature]DFSNameSpaceInstall' - } - - WindowsFeature RSATDFSMgmtConInstall - { - Ensure = 'Present' - Name = 'RSAT-DFS-Mgmt-Con' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - - WaitForAll WaitForAllSpokes - { - ResourceName = '[Disk]DVolume' - NodeName = $Node.SpokeComputerName - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Computer]JoinDomain' - } - - # Configure the Replication Group - DFSReplicationGroup RGWebSite - { - GroupName = $Node.ResourceGroupName - Description = $Node.ResourceGroupDescription - Ensure = 'Present' - DomainName = $Node.DomainName - Members = @() + $Node.NodeName + $Node.SpokeComputerName - Folders = $Node.ResourceGroupFolderName - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[Disk]DVolume' - } # End of RGWebSite Resource - - DFSReplicationGroupFolder RGWebSiteFolder - { - GroupName = $Node.ResourceGroupName - FolderName = $Node.ResourceGroupFolderName - DomainName = $Node.DomainName - Description = $Node.ResourceGroupDescription - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[DFSReplicationGroup]RGWebSite' - } # End of RGWebSiteFolder Resource - - DFSReplicationGroupMembership RGWebSiteMembershipHub - { - GroupName = $Node.ResourceGroupName - FolderName = $Node.ResourceGroupFolderName - DomainName = $Node.DomainName - ComputerName = $Node.NodeName - ContentPath = $Node.ResourceGroupContentPath - PrimaryMember = $true - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' - } # End of RGWebSiteMembershipHub Resource - - # Configure the connection and membership for each Spoke - foreach ($spoke in $Node.SpokeComputerName) - { - DFSReplicationGroupConnection "RGWebSiteConnection$spoke" - { - GroupName = $Node.ResourceGroupName - DomainName = $Node.DomainName - Ensure = 'Present' - SourceComputerName = $Node.NodeName - DestinationComputerName = $spoke - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[DFSReplicationGroupFolder]RGWebSiteFolder' - } # End of RGWebSiteConnection$spoke Resource - - DFSReplicationGroupMembership "RGWebSiteMembership$spoke" - { - GroupName = $Node.ResourceGroupName - FolderName = $Node.ResourceGroupFolderName - DomainName = $Node.DomainName - ComputerName = $spoke - ContentPath = $Node.ResourceGroupContentPath - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = "[DFSReplicationGroupConnection]RGWebSiteConnection$spoke" - } # End of RGWebSiteMembership$spoke Resource - } - } -} diff --git a/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 b/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 deleted file mode 100644 index f2fc8cc8..00000000 --- a/src/dsclibrary/MEMBER_DFSSPOKE.DSC.ps1 +++ /dev/null @@ -1,85 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DFSSPOKE -.Desription - Builds a Server that is joined to a domain and then made into a Spoke for a DFS Hub and Spoke - replication group. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_DFSSPOKE -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName DFSDsc - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FileServerInstall - { - Ensure = 'Present' - Name = 'FS-FileServer' - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Namespace' - DependsOn = '[WindowsFeature]FileServerInstall' - } - - WindowsFeature DFSReplicationInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Replication' - DependsOn = '[WindowsFeature]DFSNameSpaceInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - } -} diff --git a/src/dsclibrary/MEMBER_DHCP.DSC.ps1 b/src/dsclibrary/MEMBER_DHCP.DSC.ps1 deleted file mode 100644 index b5038e38..00000000 --- a/src/dsclibrary/MEMBER_DHCP.DSC.ps1 +++ /dev/null @@ -1,176 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DHCP -.Desription - Builds a Server that is joined to a domain and then made into a DHCP Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Scopes = @( - @{ - Name = 'Site A Primary' - ScopeID = '192.168.128.0' - Start = '192.168.128.50' - End = '192.168.128.254' - SubnetMask = '255.255.255.0' - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ - Name = 'SA-DC1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000000' - IPAddress = '192.168.128.10' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DC2' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000001' - IPAddress = '192.168.128.11' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DHCP1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000002' - IPAddress = '192.168.128.16' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-EDGE1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000005' - IPAddress = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ - ScopeID = '192.168.128.0' - DNServerIPAddress = @('192.168.128.10','192.168.128.11') - Router = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) -###################################################################################################> - -Configuration MEMBER_DHCP -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-DHCP', 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # DHCP Server Settings - Script DHCPAuthorize - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Add-DHCPServerInDC - } - GetScript = { - Return @{ - 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); - } - } - TestScript = { - Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) - } - DependsOn = '[Computer]JoinDomain' - } - - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - } - } - } -} diff --git a/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 b/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 deleted file mode 100644 index 130e2ea8..00000000 --- a/src/dsclibrary/MEMBER_DHCPDNS.DSC.ps1 +++ /dev/null @@ -1,217 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DHCPDNS -.Desription - Builds a Server that is joined to a domain and then made into a DHCP Server and DNS Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Scopes = @( - @{ - Name = 'Site A Primary' - ScopeID = '192.168.128.0' - Start = '192.168.128.50' - End = '192.168.128.254' - SubnetMask = '255.255.255.0' - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ - Name = 'SA-DC1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000000' - IPAddress = '192.168.128.10' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DC2' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000001' - IPAddress = '192.168.128.11' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DHCP1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000002' - IPAddress = '192.168.128.16' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-EDGE1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000005' - IPAddress = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ - ScopeID = '192.168.128.0' - DNServerIPAddress = @('192.168.128.10','192.168.128.11') - Router = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) - Forwarders = @('8.8.8.8','8.8.4.4') - PrimaryZones = @( - @{ - Name = 'BRAVO.LOCAL' - ZoneFile = 'bravo.local.dns' - DynamicUpdate = 'None' - } - ) -###################################################################################################> - -Configuration MEMBER_DHCPDNS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - DependsOn = '[WindowsFeature]DHCPInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-DHCP', 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]DNSInstall' - } - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # DHCP Server Settings - Script DHCPAuthorize - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Add-DHCPServerInDC - } - GetScript = { - Return @{ - 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); - } - } - TestScript = { - Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) - } - DependsOn = '[Computer]JoinDomain' - } - - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - } - } - - # DNS Server Settings - if ($Node.Forwarders) - { - xDnsServerForwarder DNSForwarders - { - IsSingleInstance = 'Yes' - IPAddresses = $Node.Forwarders - DependsOn = '[Computer]JoinDomain' - } - } - - $count = 0 - foreach ($PrimaryZone in $Node.PrimaryZones) - { - $count++ - xDnsServerPrimaryZone "PrimaryZone$count" - { - Ensure = 'Present' - Name = $PrimaryZone.Name - ZoneFile = $PrimaryZone.ZoneFile - DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = '[Computer]JoinDomain' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 b/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 deleted file mode 100644 index d10f6240..00000000 --- a/src/dsclibrary/MEMBER_DHCPNPAS.DSC.ps1 +++ /dev/null @@ -1,186 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DHCPNPAS -.Desription - Builds a Server that is joined to a domain and then made into a DHCP Server. NPAS is also installed. - - This is for use on Windows Server 2012 R2 only. -.Notes - NPAS requires a full server install, so ensure that this OS is not a Core version. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Scopes = @( - @{ - Name = 'Site A Primary' - ScopeID = '192.168.128.0' - Start = '192.168.128.50' - End = '192.168.128.254' - SubnetMask = '255.255.255.0' - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ - Name = 'SA-DC1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000000' - IPAddress = '192.168.128.10' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DC2' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000001' - IPAddress = '192.168.128.11' - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000002' - IPAddress = '192.168.128.16' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-EDGE1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000005' - IPAddress = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ - ScopeID = '192.168.128.0' - DNServerIPAddress = @('192.168.128.10','192.168.128.11') - Router = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) -###################################################################################################> - -Configuration MEMBER_DHCPNPAS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature NPASPolicyServerInstall - { - Ensure = 'Present' - Name = 'NPAS-Policy-Server' - } - - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - DependsOn = '[WindowsFeature]NPASPolicyServerInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-DHCP', 'RSAT-NPAS' - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # DHCP Server Settings - Script DHCPAuthorize - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Add-DHCPServerInDC - } - GetScript = { - Return @{ - 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); - } - } - TestScript = { - Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) - } - DependsOn = '[Computer]JoinDomain' - } - - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - } - } - } -} diff --git a/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 b/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 deleted file mode 100644 index ab6d5ce9..00000000 --- a/src/dsclibrary/MEMBER_DHCPNPAS2016.DSC.ps1 +++ /dev/null @@ -1,187 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DHCPNPAS2016 -.Desription - Builds a Server that is joined to a domain and then made into a DHCP Server. NPAS is also installed. - - This is for use on Windows Server 2016 only. -.Notes - NPAS requires a full server install, so ensure that this OS is not a Core version. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Scopes = @( - @{ - Name = 'Site A Primary' - ScopeID = '192.168.128.0' - Start = '192.168.128.50' - End = '192.168.128.254' - SubnetMask = '255.255.255.0' - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ - Name = 'SA-DC1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000000' - IPAddress = '192.168.128.10' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DC2' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000001' - IPAddress = '192.168.128.11' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-DHCP1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000002' - IPAddress = '192.168.128.16' - AddressFamily = 'IPv4' - }, - @{ - Name = 'SA-EDGE1' - ScopeID = '192.168.128.0' - ClientMACAddress = '000000000005' - IPAddress = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ - ScopeID = '192.168.128.0' - DNServerIPAddress = @('192.168.128.10','192.168.128.11') - Router = '192.168.128.19' - AddressFamily = 'IPv4' - } - ) -###################################################################################################> - -Configuration MEMBER_DHCPNPAS2016 -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature NPASPolicyServerInstall - { - Ensure = 'Present' - Name = 'NPAS' - } - - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - DependsOn = '[WindowsFeature]NPASPolicyServerInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-DHCP', 'RSAT-NPAS' - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # DHCP Server Settings - Script DHCPAuthorize - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Add-DHCPServerInDC - } - GetScript = { - Return @{ - 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); - } - } - TestScript = { - Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) - } - DependsOn = '[Computer]JoinDomain' - } - - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - } - } - } -} diff --git a/src/dsclibrary/MEMBER_DNS.DSC.ps1 b/src/dsclibrary/MEMBER_DNS.DSC.ps1 deleted file mode 100644 index c3a95178..00000000 --- a/src/dsclibrary/MEMBER_DNS.DSC.ps1 +++ /dev/null @@ -1,93 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DNS -.Desription - Builds a Server that is joined to a domain and then made into a DNS Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - PrimaryZones = @( - @{ Name = 'BRAVO.LOCAL'; - ZoneFile = 'bravo.local.dns'; - DynamicUpdate = 'None'; - } - ) -###################################################################################################> - -Configuration MEMBER_DNS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]DNSInstall' - } - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # DNS Server Settings - if ($Node.Forwarders) - { - xDnsServerForwarder DNSForwarders - { - IsSingleInstance = 'Yes' - IPAddresses = $Node.Forwarders - DependsOn = '[Computer]JoinDomain' - } - } - $count = 0 - foreach ($PrimaryZone in $Node.PrimaryZones) - { - $count++ - xDnsServerPrimaryZone "PrimaryZone$count" - { - Ensure = 'Present' - Name = $PrimaryZone.Name - ZoneFile = $PrimaryZone.ZoneFile - DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = '[Computer]JoinDomain' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 b/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 deleted file mode 100644 index db1c252f..00000000 --- a/src/dsclibrary/MEMBER_DSCPULLSERVER.DSC.ps1 +++ /dev/null @@ -1,95 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_DSCPULLSERVER -.Desription - Builds a Server that is joined to a domain and then made into an DSC Pull Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - Port = 8080 - PhysicalPath = 'D:\inetpub\PSDSCPullServer' - # Set to a valid certificate thumbprint to allow HTTP traffic - CertificateThumbprint = 'AllowUnencryptedTraffic' - RegistrationKey = '140a952b-b9d6-406b-b416-e0f759c9c0e4' -###################################################################################################> - -Configuration MEMBER_DSCPULLSERVER -{ - Import-DSCResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xWebAdministration - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature IISInstall - { - Ensure = 'Present' - Name = 'Web-Server' - } - - WindowsFeature AspNet45Install - { - Ensure = 'Present' - Name = 'Web-Asp-Net45' - } - - WindowsFeature WebMgmtServiceInstall - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - } - - WindowsFeature DSCServiceFeature - { - Ensure = 'Present' - Name = 'DSC-Service' - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - xDscWebService PSDSCPullServer - { - Ensure = 'Present' - EndpointName = 'PSDSCPullServer' - Port = $Node.Port - PhysicalPath = $Node.PhysicalPath - CertificateThumbPrint = $Node.CertificateThumbprint - ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" - ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" - State = 'Started' - DependsOn = '[WindowsFeature]DSCServiceFeature' - } - - File RegistrationKeyFile - { - Ensure = 'Present' - Type = 'File' - DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt" - Contents = $Node.RegistrationKey - } - } -} diff --git a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 deleted file mode 100644 index b6cdc93b..00000000 --- a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_DHCP.DSC.ps1 +++ /dev/null @@ -1,226 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FAILOVERCLUSTER_DHCP -.Desription - Builds a Network failover clustering node for use as a DHCP Server. - It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - ISCSIServerName = 'SA-FS1' - ServerTargetName = 'sa-foc-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.28' - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) -###################################################################################################> - -Configuration MEMBER_FAILOVERCLUSTER_FS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FailoverClusteringInstall - { - Ensure = 'Present' - Name = 'Failover-Clustering' - } - - WindowsFeature FailoverClusteringPSInstall - { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' - DependsOn = '[WindowsFeature]FailoverClusteringInstall' - } - - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - DependsOn = '[WindowsFeature]FailoverClusteringPSInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain so that it can be an Enterprise CA. - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - if ($Node.ServerTargetName) - { - # Ensure the iSCSI Initiator service is running - Service iSCSIService - { - Name = 'MSiSCSI' - StartupType = 'Automatic' - State = 'Running' - } - - # Wait for the iSCSI Server Target to become available - WaitForAny WaitForiSCSIServerTarget - { - ResourceName = '[ISCSIServerTarget]ClusterServerTarget' - NodeName = $Node.ServerName - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Service]iSCSIService' - } - - # Connect the Initiator - ISCSIInitiator iSCSIInitiator - { - Ensure = 'Present' - NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" - TargetPortalAddress = $Node.TargetPortalAddress - InitiatorPortalAddress = $Node.InitiatorPortalAddress - IsPersistent = $true - DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' - } # End of ISCSITarget Resource - - # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS - Firewall iSCSIFirewallIn - { - Name = 'MsiScsi-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - Firewall iSCSIFirewallOut - { - Name = 'MsiScsi-Out-TCP' - Ensure = 'Present' - Enabled = 'True' - } - } - - # DHCP Server Settings - Script DHCPAuthorize - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Add-DHCPServerInDC - } - GetScript = { - Return @{ - 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); - } - } - TestScript = { - Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) - } - DependsOn = '[Computer]JoinDomain' - } - - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - } - } - } -} diff --git a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 deleted file mode 100644 index 5c055007..00000000 --- a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_FS.DSC.ps1 +++ /dev/null @@ -1,211 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FAILOVERCLUSTER_FS -.Desription - Builds a Network failover clustering node for use as a File Server. - It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - ServerName = 'SA-FS1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.28' -###################################################################################################> - -Configuration MEMBER_FAILOVERCLUSTER_FS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName ISCSIDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FailoverClusteringInstall - { - Ensure = 'Present' - Name = 'Failover-Clustering' - } - - WindowsFeature FailoverClusteringPSInstall - { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' - DependsOn = '[WindowsFeature]FailoverClusteringInstall' - } - - WindowsFeature FileServerInstall - { - Ensure = 'Present' - Name = 'FS-FileServer' - DependsOn = '[WindowsFeature]FailoverClusteringPSInstall' - } - - WindowsFeature DataDedupInstall - { - Ensure = 'Present' - Name = 'FS-Data-Deduplication' - DependsOn = '[WindowsFeature]FileServerInstall' - } - - WindowsFeature BranchCacheInstall - { - Ensure = 'Present' - Name = 'FS-BranchCache' - DependsOn = '[WindowsFeature]DataDedupInstall' - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Namespace' - DependsOn = '[WindowsFeature]BranchCacheInstall' - } - - WindowsFeature DFSReplicationInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Replication' - DependsOn = '[WindowsFeature]DFSNameSpaceInstall' - } - - WindowsFeature FSResourceManagerInstall - { - Ensure = 'Present' - Name = 'FS-Resource-Manager' - DependsOn = '[WindowsFeature]DFSReplicationInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain so that it can be an Enterprise CA. - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - if ($Node.ServerTargetName) - { - # Ensure the iSCSI Initiator service is running - Service iSCSIService - { - Name = 'MSiSCSI' - StartupType = 'Automatic' - State = 'Running' - } - - # Wait for the iSCSI Server Target to become available - WaitForAny WaitForiSCSIServerTarget - { - ResourceName = '[ISCSIServerTarget]ClusterServerTarget' - NodeName = $Node.ServerName - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Service]iSCSIService' - } - - # Connect the Initiator - ISCSIInitiator iSCSIInitiator - { - Ensure = 'Present' - NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" - TargetPortalAddress = $Node.TargetPortalAddress - InitiatorPortalAddress = $Node.InitiatorPortalAddress - IsPersistent = $true - DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' - } # End of ISCSITarget Resource - - # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS - Firewall iSCSIFirewallIn - { - Name = 'MsiScsi-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - Firewall iSCSIFirewallOut - { - Name = 'MsiScsi-Out-TCP' - Ensure = 'Present' - Enabled = 'True' - } - } - - # Enable FSRM FireWall rules so we can remote manage FSRM - Firewall FSRMFirewall1 - { - Name = 'FSRM-WMI-ASYNC-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall2 - { - Name = 'FSRM-WMI-WINMGMT-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall3 - { - Name = 'FSRM-RemoteRegistry-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall4 - { - Name = 'FSRM-Task-Scheduler-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall5 - { - Name = 'FSRM-SrmReports-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall6 - { - Name = 'FSRM-RpcSs-In (RPC-EPMAP)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall7 - { - Name = 'FSRM-System-In (TCP-445)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall8 - { - Name = 'FSRM-SrmSvc-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - } -} diff --git a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 b/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 deleted file mode 100644 index 3463cd87..00000000 --- a/src/dsclibrary/MEMBER_FAILOVERCLUSTER_HV.DSC.ps1 +++ /dev/null @@ -1,123 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FAILOVERCLUSTER_HV -.Desription - Builds a Network failover clustering node Hyper-V. - It also optionally starts the iSCSI Initiator and connects to any specified iSCSI Targets. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - ISCSIServerName = 'SA-FS1' - ServerTargetName = 'sa-foc-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.28' -###################################################################################################> - -Configuration MEMBER_FAILOVERCLUSTER_HV -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xPSDesiredStateConfiguration - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FailoverClusteringInstall - { - Ensure = 'Present' - Name = 'Failover-Clustering' - } - - WindowsFeature FailoverClusteringPSInstall - { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' - } - - WindowsFeature InstallHyperV - { - Ensure = 'Present' - Name = 'Hyper-V' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain so that it can be an Enterprise CA. - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - if ($Node.ServerTargetName) - { - # Ensure the iSCSI Initiator service is running - Service iSCSIService - { - Name = 'MSiSCSI' - StartupType = 'Automatic' - State = 'Running' - } - - # Wait for the iSCSI Server Target to become available - WaitForAny WaitForiSCSIServerTarget - { - ResourceName = '[ISCSIServerTarget]ClusterServerTarget' - NodeName = $Node.ServerName - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Service]iSCSIService' - } - - # Connect the Initiator - ISCSIInitiator iSCSIInitiator - { - Ensure = 'Present' - NodeAddress = "iqn.1991-05.com.microsoft:$($Node.ServerTargetName)" - TargetPortalAddress = $Node.TargetPortalAddress - InitiatorPortalAddress = $Node.InitiatorPortalAddress - IsPersistent = $true - DependsOn = '[WaitForAny]WaitForiSCSIServerTarget' - } # End of ISCSITarget Resource - - # Enable iSCSI FireWall rules so that the Initiator can be added to iSNS - Firewall iSCSIFirewallIn - { - Name = 'MsiScsi-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - Firewall iSCSIFirewallOut - { - Name = 'MsiScsi-Out-TCP' - Ensure = 'Present' - Enabled = 'True' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 b/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 deleted file mode 100644 index a2222ea9..00000000 --- a/src/dsclibrary/MEMBER_FILESERVER.DSC.ps1 +++ /dev/null @@ -1,182 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FILESERVER -.Desription - Builds a Server that is joined to a domain and then made into a File Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_FILESERVER -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FileServerInstall - { - Ensure = 'Present' - Name = 'FS-FileServer' - } - - WindowsFeature DataDedupInstall - { - Ensure = 'Present' - Name = 'FS-Data-Deduplication' - DependsOn = '[WindowsFeature]FileServerInstall' - } - - WindowsFeature BranchCacheInstall - { - Ensure = 'Present' - Name = 'FS-BranchCache' - DependsOn = '[WindowsFeature]DataDedupInstall' - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Namespace' - DependsOn = '[WindowsFeature]BranchCacheInstall' - } - - WindowsFeature DFSReplicationInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Replication' - DependsOn = '[WindowsFeature]DFSNameSpaceInstall' - } - - WindowsFeature FSResourceManagerInstall - { - Ensure = 'Present' - Name = 'FS-Resource-Manager' - DependsOn = '[WindowsFeature]DFSReplicationInstall' - } - - WindowsFeature FSSyncShareInstall - { - Ensure = 'Present' - Name = 'FS-SyncShareService' - DependsOn = '[WindowsFeature]FSResourceManagerInstall' - } - - WindowsFeature StorageServicesInstall - { - Ensure = 'Present' - Name = 'Storage-Services' - DependsOn = '[WindowsFeature]FSSyncShareInstall' - } - - WindowsFeature ISCSITargetServerInstall - { - Ensure = 'Present' - Name = 'FS-iSCSITarget-Server' - DependsOn = '[WindowsFeature]StorageServicesInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Enable FSRM FireWall rules so we can remote manage FSRM - Firewall FSRMFirewall1 - { - Name = 'FSRM-WMI-ASYNC-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall2 - { - Name = 'FSRM-WMI-WINMGMT-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall3 - { - Name = 'FSRM-RemoteRegistry-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall4 - { - Name = 'FSRM-Task-Scheduler-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall5 - { - Name = 'FSRM-SrmReports-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall6 - { - Name = 'FSRM-RpcSs-In (RPC-EPMAP)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall7 - { - Name = 'FSRM-System-In (TCP-445)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall8 - { - Name = 'FSRM-SrmSvc-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - } -} diff --git a/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 b/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 deleted file mode 100644 index 30db7a88..00000000 --- a/src/dsclibrary/MEMBER_FILESERVER_FSRMTEST.DSC.ps1 +++ /dev/null @@ -1,451 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FILESERVER_FSRMTEST -.Desription - Builds a Server that is joined to a domain and then made into a File Server. - Includes tests for FSRM Resources. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_FILESERVER_FSRMTEST -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - Import-DscResource -ModuleName FSRMDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FileServerInstall - { - Ensure = 'Present' - Name = 'FS-FileServer' - } - - WindowsFeature DataDedupInstall - { - Ensure = 'Present' - Name = 'FS-Data-Deduplication' - DependsOn = '[WindowsFeature]FileServerInstall' - } - - WindowsFeature BranchCacheInstall - { - Ensure = 'Present' - Name = 'FS-BranchCache' - DependsOn = '[WindowsFeature]DataDedupInstall' - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Namespace' - DependsOn = '[WindowsFeature]BranchCacheInstall' - } - - WindowsFeature DFSReplicationInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Replication' - DependsOn = '[WindowsFeature]DFSNameSpaceInstall' - } - - WindowsFeature FSResourceManagerInstall - { - Ensure = 'Present' - Name = 'FS-Resource-Manager' - DependsOn = '[WindowsFeature]DFSReplicationInstall' - } - - WindowsFeature FSSyncShareInstall - { - Ensure = 'Present' - Name = 'FS-SyncShareService' - DependsOn = '[WindowsFeature]FSResourceManagerInstall' - } - - WindowsFeature StorageServicesInstall - { - Ensure = 'Present' - Name = 'Storage-Services' - DependsOn = '[WindowsFeature]FSSyncShareInstall' - } - - WindowsFeature ISCSITargetServerInstall - { - Ensure = 'Present' - Name = 'FS-iSCSITarget-Server' - DependsOn = '[WindowsFeature]StorageServicesInstall' - } - - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Enable FSRM FireWall rules so we can remote manage FSRM - Firewall FSRMFirewall1 - { - Name = 'FSRM-WMI-ASYNC-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall2 - { - Name = 'FSRM-WMI-WINMGMT-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall3 - { - Name = 'FSRM-RemoteRegistry-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall4 - { - Name = 'FSRM-Task-Scheduler-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall5 - { - Name = 'FSRM-SrmReports-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall6 - { - Name = 'FSRM-RpcSs-In (RPC-EPMAP)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall7 - { - Name = 'FSRM-System-In (TCP-445)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall8 - { - Name = 'FSRM-SrmSvc-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - - File UsersFolder - { - DestinationPath = 'd:\Users' - Ensure = 'Present' - Type = 'Directory' - DependsOn = '[Disk]DVolume' - } - - FSRMQuotaTemplate HardLimit5GB - { - Name = '5 GB Limit' - Description = '5 GB Hard Limit' - Ensure = 'Present' - Size = 5GB - SoftLimit = $false - ThresholdPercentages = @( 85, 100 ) - DependsOn = '[File]UsersFolder' - } - - FSRMQuotaTemplateAction HardLimit5GBEmail85 - { - Name = '5 GB Limit' - Percentage = 85 - Ensure = 'Present' - Type = 'Email' - Subject = '[Quota Threshold]% quota threshold exceeded' - Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' - MailBCC = '' - MailCC = 'fileserveradmins@contoso.com' - MailTo = '[Source Io Owner Email]' - DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' - } # End of FSRMQuotaTemplateAction Resource - - FSRMQuotaTemplateAction HardLimit5GBEvent85 - { - Name = '5 GB Limit' - Percentage = 85 - Ensure = 'Present' - Type = 'Event' - Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' - EventType = 'Warning' - DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' - } # End of FSRMQuotaTemplateAction Resource - - FSRMQuotaTemplateAction HardLimit5GBEmail100 - { - Name = '5 GB Limit' - Percentage = 100 - Ensure = 'Present' - Type = 'Email' - Subject = '[Quota Threshold]% quota threshold exceeded' - Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' - MailBCC = '' - MailCC = 'fileserveradmins@contoso.com' - MailTo = '[Source Io Owner Email]' - DependsOn = '[FSRMQuotaTemplate]HardLimit5GB' - } # End of FSRMQuotaTemplateAction Resource - - FSRMQuota DUsersQuota - { - Path = 'd:\users' - Description = '5 GB Hard Limit, YEAH!' - Ensure = 'Present' - Template = '5 GB Limit' - MatchesTemplate = $true - DependsOn = '[FSRMQuotaTemplateAction]HardLimit5GBEmail100' - } # End of FSRMQuota Resource - - File SharedFolder - { - DestinationPath = 'd:\shared' - Ensure = 'Present' - Type = 'Directory' - DependsOn = '[Disk]DVolume' - } - - FSRMQuota DSharedQuota - { - Path = 'd:\shared' - Description = '5 GB Hard Limit' - Ensure = 'Present' - Size = 5GB - SoftLimit = $false - ThresholdPercentages = @( 75, 100 ) - DependsOn = '[File]SharedFolder' - } # End of FSRMQuota Resource - - FSRMQuotaAction DSharedEmail75 - { - Path = 'd:\shared' - Percentage = 75 - Ensure = 'Present' - Type = 'Email' - Subject = '[Quota Threshold]% quota threshold exceeded' - Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' - MailBCC = '' - MailCC = 'fileserveradmins@contoso.com' - MailTo = '[Source Io Owner Email]' - DependsOn = '[FSRMQuota]DSharedQuota' - } # End of FSRMQuotaAction Resource - - FSRMQuotaAction DSharedEmail100 - { - Path = 'd:\shared' - Percentage = 100 - Ensure = 'Present' - Type = 'Email' - Subject = '[Quota Threshold]% quota threshold exceeded' - Body = 'User [Source Io Owner] has exceed the [Quota Threshold]% quota threshold for quota on [Quota Path] on server [Server]. The quota limit is [Quota Limit MB] MB and the current usage is [Quota Used MB] MB ([Quota Used Percent]% of limit).' - MailBCC = '' - MailCC = 'fileserveradmins@contoso.com' - MailTo = '[Source Io Owner Email]' - DependsOn = '[FSRMQuota]DSharedQuota' - } # End of FSRMQuotaAction Resource - - File AutoFolder - { - DestinationPath = 'd:\auto' - Ensure = 'Present' - Type = 'Directory' - DependsOn = '[Disk]DVolume' - } - - FSRMAutoQuota DAutoQuota - { - Path = 'd:\auto' - Ensure = 'Present' - Template = '100 MB Limit' - DependsOn = '[File]SharedFolder' - } # End of FSRMQuota Resource - - FSRMFileGroup FSRMFileGroupPortableFiles - { - Name = 'Portable Document Files' - Description = 'Files containing portable document formats' - Ensure = 'Present' - IncludePattern = '*.eps','*.pdf','*.xps' - } - - FSRMFileScreenTemplate FileScreenSomeFiles - { - Name = 'Block Some Files' - Description = 'File Screen for Blocking Some Files' - Ensure = 'Present' - Active = $true - IncludeGroup = 'Audio and Video Files','Executable Files','Backup Files' - } # End of FSRMFileScreenTemplate Resource - - FSRMFileScreenTemplateAction FileScreenSomeFilesEmail - { - Name = 'Block Some Files' - Ensure = 'Present' - Type = 'Email' - Subject = 'Unauthorized file matching [Violated File Group] file group detected' - Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' - MailBCC = '' - MailCC = 'fileserveradmins@contoso.com' - MailTo = '[Source Io Owner Email]' - DependsOn = '[FSRMFileScreenTemplate]FileScreenSomeFiles' - } # End of FSRMFileScreenTemplateAction Resource - - FSRMFileScreenTemplateAction FileScreenSomeFilesEvent - { - Name = 'Block Some Files' - Ensure = 'Present' - Type = 'Event' - Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' - EventType = 'Warning' - DependsOn = '[FSRMFileScreenTemplate]FileScreenSomeFiles' - } # End of FSRMFileScreenTemplateAction Resource - - FSRMFileScreen DUsersFileScreen - { - Path = 'd:\users' - Description = 'File Screen for Blocking Some Files' - Ensure = 'Present' - Active = $true - IncludeGroup = 'Audio and Video Files','Executable Files','Backup Files' - } # End of FSRMFileScreen Resource - - FSRMFileScreenAction DUsersFileScreenSomeFilesEmail - { - Path = 'd:\users' - Ensure = 'Present' - Type = 'Email' - Subject = 'Unauthorized file matching [Violated File Group] file group detected' - Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' - MailBCC = '' - MailCC = 'fileserveradmins@contoso.com' - MailTo = '[Source Io Owner Email]' - DependsOn = '[FSRMFileScreen]DUsersFileScreen' - } # End of FSRMFileScreenAction Resource - - FSRMFileScreenAction DUsersFileScreenSomeFilesEvent - { - Path = 'd:\users' - Ensure = 'Present' - Type = 'Event' - Body = 'The system detected that user [Source Io Owner] attempted to save [Source File Path] on [File Screen Path] on server [Server]. This file matches the [Violated File Group] file group which is not permitted on the system.' - EventType = 'Warning' - DependsOn = '[FSRMFileScreen]DUsersFileScreen' - } # End of FSRMFileScreenAction Resource - - FSRMFileScreenException DUsersFileScreenException - { - Path = 'd:\users' - Description = 'File Screen Exclusion' - Ensure = 'Present' - IncludeGroup = 'E-mail Files' - } # End of FSRMFileScreenException Resource - - FSRMClassificationProperty PrivacyClasificationProperty - { - Name = 'Privacy' - DisplayName = 'File Privacy' - Description = 'File Privacy Property' - Ensure = 'Present' - Type = 'SingleChoice' - PossibleValue = 'Top Secret','Secret','Confidential','Public' - Parameters = 'Parameter1=Value1','Parameter2=Value2' - } # End of FSRMClassificationProperty Resource - - FSRMClassificationPropertyValue PublicClasificationPropertyValue - { - Name = 'Public' - PropertyName = 'Privacy' - Description = 'Publically accessible files.' - Ensure = 'Present' - DependsOn = '[FSRMClassificationProperty]PrivacyClasificationProperty' - } # End of FSRMClassificationPropertyValue Resource - - FSRMClassificationPropertyValue SecretClasificationPropertyValue - { - Name = 'Secret' - PropertyName = 'Privacy' - Ensure = 'Present' - DependsOn = '[FSRMClassificationProperty]PrivacyClasificationProperty' - } # End of FSRMClassificationPropertyValue Resource - FSRMClassification FSRMClassificationSettings - { - Id = 'Default' - Continuous = $true - ContinuousLog = $true - ContinuousLogSize = 2048 - ScheduleWeekly = 'Monday','Tuesday','Wednesday' - ScheduleRunDuration = 4 - ScheduleTime = '23:30' - } # End of FSRMClassification Resource - FSRMClassificationRule ConfidentialPrivacyClasificationRule - { - Name = 'Confidential' - Description = 'Set Confidential' - Ensure = 'Present' - Property = 'Privacy' - PropertyValue = 'Confidential' - ClassificationMechanism = 'Content Classifier' - ContentString = 'Confidential' - Namespace = '[FolderUsage_MS=User Files]','d:\Users' - ReevaluateProperty = 'Overwrite' - } # End of FSRMClassificationRule Resource - } -} diff --git a/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 b/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 deleted file mode 100644 index 8e834ec8..00000000 --- a/src/dsclibrary/MEMBER_FILESERVER_ISCSI.DSC.ps1 +++ /dev/null @@ -1,250 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FILESERVER_ISCSI -.Desription - Builds a Server that is joined to a domain and then made into a File Server. - Configures a iSCSI targets and virtual disks. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - TargetName = 'sa-foc-target' - VirtualDisks = @( - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-witness.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 500MB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk1.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk2.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk3.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - } - ) - ClusterInitiatorIds = @( - 'Iqn:iqn.1991-05.com.microsoft:sa-foc1.labbuilder.com' - 'Iqn:iqn.1991-05.com.microsoft:sa-foc2.labbuilder.com' - 'Iqn:iqn.1991-05.com.microsoft:sa-foc3.labbuilder.com' - ) -###################################################################################################> - -Configuration MEMBER_FILESERVER_ISCSI -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - Import-DscResource -ModuleName ISCSI - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FileServerInstall - { - Ensure = 'Present' - Name = 'FS-FileServer' - } - - WindowsFeature DataDedupInstall - { - Ensure = 'Present' - Name = 'FS-Data-Deduplication' - DependsOn = '[WindowsFeature]FileServerInstall' - } - - WindowsFeature BranchCacheInstall - { - Ensure = 'Present' - Name = 'FS-BranchCache' - DependsOn = '[WindowsFeature]DataDedupInstall' - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Namespace' - DependsOn = '[WindowsFeature]BranchCacheInstall' - } - - WindowsFeature DFSReplicationInstall - { - Ensure = 'Present' - Name = 'FS-DFS-Replication' - DependsOn = '[WindowsFeature]DFSNameSpaceInstall' - } - - WindowsFeature FSResourceManagerInstall - { - Ensure = 'Present' - Name = 'FS-Resource-Manager' - DependsOn = '[WindowsFeature]DFSReplicationInstall' - } - - WindowsFeature FSSyncShareInstall - { - Ensure = 'Present' - Name = 'FS-SyncShareService' - DependsOn = '[WindowsFeature]FSResourceManagerInstall' - } - - WindowsFeature StorageServicesInstall - { - Ensure = 'Present' - Name = 'Storage-Services' - DependsOn = '[WindowsFeature]FSSyncShareInstall' - } - - WindowsFeature ISCSITargetServerInstall - { - Ensure = 'Present' - Name = 'FS-iSCSITarget-Server' - DependsOn = '[WindowsFeature]StorageServicesInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Enable FSRM FireWall rules so we can remote manage FSRM - Firewall FSRMFirewall1 - { - Name = 'FSRM-WMI-ASYNC-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall2 - { - Name = 'FSRM-WMI-WINMGMT-In-TCP' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall3 - { - Name = 'FSRM-RemoteRegistry-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall4 - { - Name = 'FSRM-Task-Scheduler-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall5 - { - Name = 'FSRM-SrmReports-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall6 - { - Name = 'FSRM-RpcSs-In (RPC-EPMAP)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall7 - { - Name = 'FSRM-System-In (TCP-445)' - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall8 - { - Name = 'FSRM-SrmSvc-In (RPC)' - Ensure = 'Present' - Enabled = 'True' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - - File VirtualDisksFolder - { - Ensure = 'Present' - DestinationPath = 'D:\iSCSIVirtualDisks' - Type = 'Directory' - DependsOn = '[Disk]DVolume' - } - - $DependsOn = '[File]VirtualDisksFolder' - [System.Int32] $count = 0 - foreach ($VirtualDisk in $Node.VirtualDisks) - { - $count++ - $Name = "$($Node.TargetName)_Disk_$count" - ISCSIVirtualDisk $Name - { - Ensure = 'Present' - Path = $VirtualDisk.Path - DiskType = $VirtualDisk.DiskType - SizeBytes = $VirtualDisk.SizeBytes - Description = $VirtualDisk.Description - DependsOn = $DependsOn - } - $DependsOn = "[ISCSIVirtualDisk]$Name" - } - - ISCSIServerTarget ClusterServerTarget - { - Ensure = 'Present' - TargetName = $Node.TargetName - InitiatorIds = $Node.ClusterInitiatorIds - Paths = $Node.VirtualDisks.Path - DependsOn = $DependsOn - } - } -} diff --git a/src/dsclibrary/MEMBER_IPAM.DSC.ps1 b/src/dsclibrary/MEMBER_IPAM.DSC.ps1 deleted file mode 100644 index 70e066d0..00000000 --- a/src/dsclibrary/MEMBER_IPAM.DSC.ps1 +++ /dev/null @@ -1,57 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_IPAM -.Desription - Builds a Server that is joined to a domain and then made into an IPAM Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_IPAM -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature WIDInstall - { - Ensure = 'Present' - Name = 'Windows-Internal-Database' - } - - WindowsFeature IPAMInstall - { - Ensure = 'Present' - Name = 'IPAM' - DependsOn = '[WindowsFeature]WIDInstall' - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 b/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 deleted file mode 100644 index e45ebc05..00000000 --- a/src/dsclibrary/MEMBER_JENKINS.DSC.ps1 +++ /dev/null @@ -1,124 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_JENKINS -.Desription - Builds a Windows Server, joins it to a Domain and installs Jenkins CI on it. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - JenkinsPort = 80 -###################################################################################################> - -Configuration MEMBER_JENKINS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName cChoco - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature NetFrameworkCore - { - Ensure = 'Present' - Name = 'NET-Framework-Core' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Install Chocolatey - cChocoInstaller installChoco - { - InstallDir = 'c:\choco' - DependsOn = '[WindowsFeature]NetFrameworkCore' - } - - # Install JDK8 - cChocoPackageInstaller installJdk8 - { - Name = 'jdk8' - DependsOn = '[cChocoInstaller]installChoco' - } - - # Install Jenkins - cChocoPackageInstaller installJenkins - { - Name = 'Jenkins' - DependsOn = '[cChocoInstaller]installChoco' - } - - # Set the Jenkins Port - $JenkinsPort = 8080 - if ($Node.JenkinsPort) - { - $JenkinsPort = $Node.JenkinsPort - } - Script SetJenkinsPort - { - SetScript = { - Write-Verbose -Message "Setting Jenkins Port to $Using:JenkinsPort" - $Config = Get-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" - $NewConfig = $Config ` - -replace '--httpPort=[0-9]*\s', "--httpPort=$Using:JenkinsPort " - Set-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" ` - -Value $NewConfig ` - -Force - Write-Verbose -Message 'Restarting Jenkins' - Restart-Service ` - -Name Jenkins - } - GetScript = { - $Config = Get-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" - $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) - $CurrentPort = $Matches.Groups[1].Value - Return @{ - 'JenkinsPort' = $CurrentPort - } - } - TestScript = { - $Config = Get-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" - $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) - $CurrentPort = $Matches.Groups[1].Value - - if ($Using:JenkinsPort -ne $CurrentPort) - { - # Jenkins port must be changed - Return $false - } - # Jenkins is already on correct port - Return $true - } - DependsOn = '[cChocoPackageInstaller]installJenkins' - } - } -} diff --git a/src/dsclibrary/MEMBER_NANO.DSC.ps1 b/src/dsclibrary/MEMBER_NANO.DSC.ps1 deleted file mode 100644 index 4be2c839..00000000 --- a/src/dsclibrary/MEMBER_NANO.DSC.ps1 +++ /dev/null @@ -1,36 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_NANO -.Desription - Builds a Nano Server and joins it to a Domain using an ODJ Request File. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - ODJRequestFile = 'C:\ODJRequest.txt' -###################################################################################################> - -Configuration MEMBER_NANO -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - OfflineDomainJoin JoinDomain - { - IsSingleInstance = 'Yes' - RequestFile = $Node.ODJRequestFile - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_NLB.DSC.ps1 b/src/dsclibrary/MEMBER_NLB.DSC.ps1 deleted file mode 100644 index 5bc98ae8..00000000 --- a/src/dsclibrary/MEMBER_NLB.DSC.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_NLB -.Desription - Builds a Network Load Balancing cluster node. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_NLB -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xPSDesiredStateConfiguration - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - - WindowsFeature InstallWebServer - { - Ensure = 'Present' - Name = 'Web-Server' - } - - WindowsFeature InstallWebMgmtService - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - } - - WindowsFeature InstallNLB - { - Ensure = 'Present' - Name = 'NLB' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_NPS.DSC.ps1 b/src/dsclibrary/MEMBER_NPS.DSC.ps1 deleted file mode 100644 index f2912c60..00000000 --- a/src/dsclibrary/MEMBER_NPS.DSC.ps1 +++ /dev/null @@ -1,68 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_NPS -.Desription - Builds a Server that is joined to a domain and then contains NPS/Radius components. -.Requires - Windows Server 2012 R2 Full (Server core not supported). -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_NPS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature NPASPolicyServerInstall - { - Ensure = 'Present' - Name = 'NPAS-Policy-Server' - } - - WindowsFeature NPASHealthInstall - { - Ensure = 'Present' - Name = 'NPAS-Health' - DependsOn = '[WindowsFeature]NPASPolicyServerInstall' - } - - WindowsFeature RSATNPAS - { - Ensure = 'Present' - Name = 'RSAT-NPAS' - DependsOn = '[WindowsFeature]NPASPolicyServerInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 b/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 deleted file mode 100644 index 86024ee1..00000000 --- a/src/dsclibrary/MEMBER_NPS_DFSTEST.DSC.ps1 +++ /dev/null @@ -1,92 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_NPS_SPECIAL -.Desription - Builds a Server that is joined to a domain and then contains NPS/Radius components. - - ** This is a special version that is used for testing the DFSDsc resource because - ** it requires a full server (not core) installation to work. -.Requires - Windows Server 2012 R2 Full (Server core not supported). -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_NPS_DFSTEST -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName DFSDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature NPASPolicyServerInstall - { - Ensure = 'Present' - Name = 'NPAS-Policy-Server' - } - - WindowsFeature NPASHealthInstall - { - Ensure = 'Present' - Name = 'NPAS-Health' - DependsOn = '[WindowsFeature]NPASPolicyServerInstall' - } - - WindowsFeature RSATNPAS - { - Ensure = 'Present' - Name = 'RSAT-NPAS' - DependsOn = '[WindowsFeature]NPASPolicyServerInstall' - } - - WindowsFeature RSATDFSMgmtConInstall - { - Ensure = 'Present' - Name = 'RSAT-DFS-Mgmt-Con' - DependsOn = '[WindowsFeature]RSATNPAS' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - DFSReplicationGroup RGPublic - { - GroupName = 'Public' - Description = 'Public files for use by all departments' - Ensure = 'Present' - Members = 'SA_FS1', 'SA_FS2' - Folders = 'Software', 'Misc' - Topology = 'Fullmesh' - ContentPaths = 'd:\public\Software', 'd:\public\Misc' - PSDSCRunAsCredential = $DomainAdminCredential - DependsOn = '[Computer]JoinDomain' - } # End of RGPublic Resource - } -} diff --git a/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 b/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 deleted file mode 100644 index 467a95aa..00000000 --- a/src/dsclibrary/MEMBER_REMOTEACCESS.DSC.ps1 +++ /dev/null @@ -1,59 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_REMOTEACCESS -.Desription - Builds a Server that is joined to a domain and then contains Remote Access components. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_REMOTEACCESS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature DirectAccessVPNInstall - { - Ensure = 'Present' - Name = 'DirectAccess-VPN' - } - - WindowsFeature RoutingInstall - { - Ensure = 'Present' - Name = 'Routing' - DependsOn = '[WindowsFeature]DirectAccessVPNInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 b/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 deleted file mode 100644 index e71aa746..00000000 --- a/src/dsclibrary/MEMBER_REMOTEACCESS_WAP.DSC.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_REMOTEACCESS_WAP -.Desription - Builds a Server that is joined to a domain and then contains Remote Access and - Web Application Proxy components. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_REMOTEACCESS_WAP -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature DirectAccessVPNInstall - { - Ensure = 'Present' - Name = 'DirectAccess-VPN' - } - - WindowsFeature RoutingInstall - { - Ensure = 'Present' - Name = 'Routing' - DependsOn = '[WindowsFeature]DirectAccessVPNInstall' - } - - WindowsFeature WebApplicationProxyInstall - { - Ensure = 'Present' - Name = 'Web-Application-Proxy' - DependsOn = '[WindowsFeature]RoutingInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - } -} diff --git a/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 b/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 deleted file mode 100644 index 985bf08b..00000000 --- a/src/dsclibrary/MEMBER_ROOTCA.DSC.ps1 +++ /dev/null @@ -1,343 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_ROOTCA -.Desription - Builds an Enterprise Root CA. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - CACommonName = 'LABBUILDER.COM Root CA' - CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' - CRLPublicationURLs = '65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' - CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 -###################################################################################################> - -Configuration MEMBER_ROOTCA -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName ActiveDirectoryCSDsc - Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - # Install the CA Service - WindowsFeature ADCSCA - { - Name = 'ADCS-Cert-Authority' - Ensure = 'Present' - } - - # Install the Web Enrollment Service - WindowsFeature ADCSWebEnrollment - { - Name = 'ADCS-Web-Enrollment' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - - WindowsFeature InstallWebMgmtService - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - DependsOn = '[WindowsFeature]ADCSWebEnrollment' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-AD-Tools' - DependsOn = '[WindowsFeature]ADCSCA' - } - } - - if ($Node.InstallOnlineResponder) - { - # Install the Online Responder Service - WindowsFeature OnlineResponderCA - { - Name = 'ADCS-Online-Cert' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - } - - if ($Node.InstallEnrollmentWebService) - { - # Install the Enrollment Web Service/Enrollment Policy Web Service - WindowsFeature EnrollmentWebSvc - { - Name = 'ADCS-Enroll-Web-Svc' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - - WindowsFeature EnrollmentWebPol - { - Name = 'ADCS-Enroll-Web-Pol' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. - File CAPolicy - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\CAPolicy.inf' - Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" - Type = 'File' - DependsOn = '[Computer]JoinDomain' - } - - <# - Make a CertEnroll folder to put the Root CA certificate into. - The CA Web Enrollment server would also create this but we need it now. - #> - File CertEnrollFolder - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' - Type = 'Directory' - DependsOn = '[File]CAPolicy' - } - - <# - Configure the Root CA which will create the Certificate REQ file that Root CA will use - to issue a certificate for this Sub CA. - #> - ADCSCertificationAuthority ConfigCA - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $DomainAdminCredential - CAType = 'EnterpriseRootCA' - CACommonName = $Node.CACommonName - CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix - OverwriteExistingCAinDS = $true - CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' - HashAlgorithmName = 'SHA256' - KeyLength = 4096 - DependsOn = '[File]CertEnrollFolder' - } - - # Configure the Web Enrollment Feature - ADCSWebEnrollment ConfigWebEnrollment - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[ADCSCertificationAuthority]ConfigCA' - } - - <# - Perform final configuration of the CA which will cause the CA service to startup - Set the advanced CA properties - #> - Script ADCSAdvConfig - { - SetScript = { - if ($Using:Node.CADistinguishedNameSuffix) - { - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" - } - - if ($Using:Node.CRLPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) - } - - if ($Using:Node.CACertPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) - } - - if ($Using:Node.CRLPeriodUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" - } - - if ($Using:Node.CRLOverlapUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" - } - - if ($Using:Node.ValidityPeriodUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" - } - - if ($Using:Node.AuditFilter) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) - } - - Restart-Service -Name CertSvc - New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue - Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' - } - - GetScript = { - return @{ - 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); - 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); - 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); - 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') - 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') - 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') - 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') - 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') - 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') - 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') - 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') - } - } - - TestScript = { - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) - { - return $false - } - - if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) - { - return $false - } - - if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) - { - return $false - } - - if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) - { - return $false - } - - if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) - { - return $false - } - - if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) - { - return $false - } - - if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) - { - return $false - } - - if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) - { - return $false - } - - if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) - { - return $false - } - - return $true - } - - DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' - } - - if ($Node.InstallOnlineResponder) - { - # Configure the Online Responder Feature - ADCSOnlineResponder ConfigOnlineResponder - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[Script]ADCSAdvConfig' - } - - # Enable Online Responder FireWall rules so we can remote manage Online Responder - Firewall OnlineResponderFirewall1 - { - Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In' - Enabled = 'True' - DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' - } - - Firewall OnlineResponderirewall2 - { - Name = 'Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In' - Enabled = 'True' - DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' - } - - Firewall OnlineResponderFirewall3 - { - Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out' - Enabled = 'True' - DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 b/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 deleted file mode 100644 index 2cdf1b7f..00000000 --- a/src/dsclibrary/MEMBER_SQLSERVER2014.DSC.ps1 +++ /dev/null @@ -1,142 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_SQLSERVER2014 -.Desription - Builds a Server that is joined to a domain and then installs SQL Server 2014. - It will install SQLServer from a locally mounted ISO file. -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallerUsername = 'Administrator' - InstallerPassword = 'P@ssword!1' - SQLAdminAccount = 'Administrator' - SQLDataDrive = 'E' - SourcePath = 'D:\' - Instances = @( - @{ - Name = 'MSSQLSERVER' - Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } - ) - InstallManagementTools = $true -###################################################################################################> - -Configuration MEMBER_SQLSERVER2014 -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName SQLServerDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - if ($Node.InstallerPassword) - { - $InstallerCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) - } - - # Install the SQL Server Dependencies - WindowsFeature Net35Install - { - Name = 'NET-Framework-Core' - Ensure = 'Present' - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = $Node.SQLDataDrive - DependsOn = '[WaitforDisk]Disk2' - } - - foreach ($Instance in $Node.Instances) - { - $Features = $Instance.Features - - if ([System.String]::IsNullOrEmpty($Features)) - { - $Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } # if - - SqlServerSetup ($Instance.Name) - { - SourcePath = $Node.SourcePath - InstanceName = $Instance.Name - Features = $Features - SQLSysAdminAccounts = "$($Node.DomainName)\$($Node.SQLAdminAccount)" - InstallSharedDir = "C:\Program Files\Microsoft SQL Server" - InstallSharedWOWDir = "C:\Program Files (x86)\Microsoft SQL Server" - InstanceDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server" - InstallSQLDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLUserDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLUserDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLTempDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLTempDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - ASDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Data" - ASLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Log" - ASBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Backup" - ASTempDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Temp" - ASConfigDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Config" - PsDscRunAsCredential = $InstallerCredential - DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' - } - - SqlServerFirewall ($Instance.Name) - { - SourcePath = $Node.SourcePath - InstanceName = $Instance.Name - Features = $Features - DependsOn = "[SqlServerSetup]$($Instance.Name)" - } - } - - if ($Node.InstallManagementTools) - { - SqlServerSetup SQLMT - { - SourcePath = $Node.SourcePath - InstanceName = 'NULL' - Features = 'SSMS,ADV_SSMS' - PsDscRunAsCredential = $InstallerCredential - DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 b/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 deleted file mode 100644 index 8c4b752e..00000000 --- a/src/dsclibrary/MEMBER_SQLSERVER2016.DSC.ps1 +++ /dev/null @@ -1,140 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_SQLSERVER2016 -.Desription - Builds a Server that is joined to a domain and then installs SQL Server 2016. - It will install SQLServer from a locally mounted ISO file. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallerUsername = 'Administrator' - InstallerPassword = 'P@ssword!1' - SQLAdminAccount = 'Administrator' - SQLDataDrive = 'E' - SourcePath = 'D:\' - Instances = @( - @{ - Name = 'MSSQLSERVER' - Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } - ) - InstallManagementTools = $true -###################################################################################################> - -Configuration MEMBER_SQLSERVER2016 -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName SQLServerDsc - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - if ($Node.InstallerPassword) - { - $InstallerCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\$($Node.InstallerUsername)", (ConvertTo-SecureString $Node.InstallerPassword -AsPlainText -Force)) - } - - # Install the SQL Server Dependencies - WindowsFeature Net35Install - { - Name = 'NET-Framework-Core' - Ensure = 'Present' - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = $Node.SQLDataDrive - DependsOn = '[WaitforDisk]Disk2' - } - - foreach ($Instance in $Node.Instances) - { - $Features = $Instance.Features - if ([System.String]::IsNullOrEmpty($Features)) - { - $Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } # if - SqlServerSetup ($Instance.Name) - { - SourcePath = $Node.SourcePath - InstanceName = $Instance.Name - Features = $Features - SQLSysAdminAccounts = "$($Node.DomainName)\$($Node.SQLAdminAccount)" - InstallSharedDir = "C:\Program Files\Microsoft SQL Server" - InstallSharedWOWDir = "C:\Program Files (x86)\Microsoft SQL Server" - InstanceDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server" - InstallSQLDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLUserDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLUserDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLTempDBDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLTempDBLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - SQLBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Data" - ASDataDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Data" - ASLogDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Log" - ASBackupDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Backup" - ASTempDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Temp" - ASConfigDir = "$($Node.SQLDataDrive):\Program Files\Microsoft SQL Server\MSAS11.MSSQLSERVER\OLAP\Config" - PsDscRunAsCredential = $InstallerCredential - DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' - } - - SqlServerFirewall ($Instance.Name) - { - SourcePath = $Node.SourcePath - InstanceName = $Instance.Name - Features = $Features - DependsOn = "[SqlServerSetup]$($Instance.Name)" - } - } - - if ($Node.InstallManagementTools) - { - SqlServerSetup SQLMT - { - SourcePath = $Node.SourcePath - InstanceName = 'NULL' - Features = 'SSMS,ADV_SSMS' - PsDscRunAsCredential = $InstallerCredential - DependsOn = '[Computer]JoinDomain', '[WindowsFeature]NET35Install' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 b/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 deleted file mode 100644 index a3ddd952..00000000 --- a/src/dsclibrary/MEMBER_SUBCA.DSC.ps1 +++ /dev/null @@ -1,396 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_SUBCA -.Desription - Builds a Enterprise Subordinate\Issuing CA. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true - CACommonName = 'LABBUILDER.COM Issuing CA' - CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' - CRLPublicationURLs = '65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' - CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' - RootCAName = 'SS_ROOTCA' - RootCACommonName = 'LABBUILDER.COM Root CA' -###################################################################################################> - -Configuration MEMBER_SUBCA -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName ActiveDirectoryCSDsc - Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - # Install the CA Service - WindowsFeature ADCSCA - { - Name = 'ADCS-Cert-Authority' - Ensure = 'Present' - } - - # Install the Web Enrollment Service - WindowsFeature WebEnrollmentCA - { - Name = 'ADCS-Web-Enrollment' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - - WindowsFeature InstallWebMgmtService - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - DependsOn = '[WindowsFeature]ADCSWebEnrollment' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-AD-Tools' - DependsOn = '[WindowsFeature]ADCSCA' - } - } - - if ($Node.InstallOnlineResponder) - { - # Install the Online Responder Service - WindowsFeature OnlineResponderCA - { - Name = 'ADCS-Online-Cert' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - } - - if ($Node.InstallEnrollmentWebService) - { - # Install the Enrollment Web Service/Enrollment Policy Web Service - WindowsFeature EnrollmentWebSvc - { - Name = 'ADCS-Enroll-Web-Svc' - Ensure = 'Present' - DependsOn = '[WindowsFeature]ADCSCA' - } - - WindowsFeature EnrollmentWebPol - { - Name = 'ADCS-Enroll-Web-Pol' - Ensure = 'Present' - DependsOn = '[WindowsFeature]WebEnrollmentCA' - } - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. - File CAPolicy - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\CAPolicy.inf' - Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=10`r`n AlternateSignatureAlgorithm=1`r`n CNGHashAlgorithm=SHA256`r`n LoadDefaultTemplates=0`r`n" - Type = 'File' - DependsOn = '[Computer]JoinDomain' - } - - <# - Make a CertEnroll folder to put the Root CA certificate into. - The CA Web Enrollment server would also create this but we need it now. - #> - File CertEnrollFolder - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' - Type = 'Directory' - DependsOn = '[File]CAPolicy' - } - - <# - Wait for the RootCA Web Enrollment to complete so we can grab the Root CA - certificate file. - #> - WaitForAny RootCA - { - ResourceName = '[ADCSWebEnrollment]ConfigWebEnrollment' - NodeName = $Node.RootCAName - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[File]CertEnrollFolder' - } - - # Download the Root CA certificate file. - xRemoteFile DownloadRootCACRTFile - { - DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCAName)_$($Node.RootCACommonName).crt" - Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCAName)_$($Node.RootCACommonName).crt" - DependsOn = '[WaitForAny]RootCA' - } - - # Download the Root CA certificate revocation list. - xRemoteFile DownloadRootCACRLFile - { - DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" - Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.RootCACommonName).crl" - DependsOn = '[xRemoteFile]DownloadRootCACRTFile' - } - - # Install the Root CA Certificate to the LocalMachine Root Store and DS - Script InstallRootCACert - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Write-Verbose -Message "Registering the Root CA Certificate C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt in DS..." - & "$($ENV:SystemRoot)\system32\certutil.exe" -f -dspublish "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt" RootCA - Write-Verbose -Message "Registering the Root CA CRL C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl in DS..." - & "$($ENV:SystemRoot)\system32\certutil.exe" -f -dspublish "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" "$($Using:Node.RootCAName)" - Write-Verbose -Message "Installing the Root CA Certificate C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt..." - & "$($ENV:SystemRoot)\system32\certutil.exe" -addstore -f root "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.RootCAName)_$($Using:Node.RootCACommonName).crt" - Write-Verbose -Message "Installing the Root CA CRL C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl..." - & "$($ENV:SystemRoot)\system32\certutil.exe" -addstore -f root "C:\Windows\System32\CertSrv\CertEnroll\$($Node.RootCACommonName).crl" - } - GetScript = { - return @{ - Installed = ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) - } - } - TestScript = { - if ((Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object -FilterScript { ($_.Subject -Like "CN=$($Using:Node.RootCACommonName),*") -and ($_.Issuer -Like "CN=$($Using:Node.RootCACommonName),*") } ).Count -EQ 0) - { - Write-Verbose -Message 'Root CA Certificate Needs to be installed...' - return $false - } - return $true - } - DependsOn = '[xRemoteFile]DownloadRootCACRTFile' - } - - <# - Configure the Sub CA which will create the Certificate REQ file that Root CA will use - to issue a certificate for this Sub CA. - #> - ADCSCertificationAuthority ConfigCA - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $DomainAdminCredential - CAType = 'EnterpriseSubordinateCA' - CACommonName = $Node.CACommonName - CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix - OverwriteExistingCAinDS = $true - OutputCertRequestFile = "c:\windows\system32\certsrv\certenroll\$($Node.NodeName).req" - CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' - HashAlgorithmName = 'SHA256' - KeyLength = 2048 - DependsOn = '[Script]InstallRootCACert' - } - - # Configure the Web Enrollment Feature - ADCSWebEnrollment ConfigWebEnrollment - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[ADCSCertificationAuthority]ConfigCA' - } - - # Set the IIS Mime Type to allow the REQ request to be downloaded by the Root CA - Script SetREQMimeType - { - SetScript = { - Add-WebConfigurationProperty -PSPath IIS:\ -Filter //staticContent -Name "." -Value @{fileExtension = '.req'; mimeType = 'application/pkcs10' } - } - GetScript = { - return @{ - 'MimeType' = ((Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *).mimeType); - } - } - TestScript = { - if (-not (Get-WebConfigurationProperty -Filter "//staticContent/mimeMap[@fileExtension='.req']" -PSPath IIS:\ -Name *)) - { - # Mime type is not set - return $false - } - # Mime Type is already set - return $true - } - DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' - } - - # Wait for the Root CA to have completed issuance of the certificate for this SubCA. - WaitForAny SubCACer - { - ResourceName = "[Script]IssueCert_$($Node.NodeName)" - NodeName = $Node.RootCAName - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Script]SetREQMimeType' - } - - # Download the Certificate for this SubCA but rename it so that it'll match the name expected by the CA - xRemoteFile DownloadSubCACERFile - { - DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$($Node.NodeName)_$($Node.CACommonName).crt" - Uri = "http://$($Node.RootCAName)/CertEnroll/$($Node.NodeName).crt" - DependsOn = '[WaitForAny]SubCACer' - } - - # Register the Sub CA Certificate with the Certification Authority - Script RegisterSubCA - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Write-Verbose -Message "Registering the Sub CA Certificate with the Certification Authority C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.NodeName)_$($Using:Node.CACommonName).crt..." - & "$($ENV:SystemRoot)\system32\certutil.exe" -installCert "C:\Windows\System32\CertSrv\CertEnroll\$($Using:Node.NodeName)_$($Using:Node.CACommonName).crt" - } - GetScript = { - return @{ - } - } - TestScript = { - if (-not (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertHash')) - { - Write-Verbose -Message 'Sub CA Certificate needs to be registered with the Certification Authority...' - return $false - } - return $true - } - DependsOn = '[xRemoteFile]DownloadSubCACERFile' - } - - <# - Perform final configuration of the CA which will cause the CA service to startup - It should be able to start up once the SubCA certificate has been installed. - #> - Script ADCSAdvConfig - { - SetScript = { - if ($Using:Node.CADistinguishedNameSuffix) - { - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" - } - - if ($Using:Node.CRLPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) - } - - if ($Using:Node.CACertPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) - } - - Restart-Service -Name CertSvc - New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue - Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' - } - - GetScript = { - return @{ - 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); - 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); - 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); - 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') - } - } - - TestScript = { - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) - { - return $false - } - - if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) - { - return $false - } - - return $true - } - - DependsOn = '[Script]RegisterSubCA' - } - - if ($Node.InstallOnlineResponder) - { - # Configure the Online Responder Feature - ADCSOnlineResponder ConfigOnlineResponder - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[Script]ADCSAdvConfig' - } - - # Enable Online Responder FireWall rules so we can remote manage Online Responder - Firewall OnlineResponderFirewall1 - { - Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In' - Enabled = 'True' - DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' - } - - Firewall OnlineResponderirewall2 - { - Name = 'Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In' - Enabled = 'True' - DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' - } - - Firewall OnlineResponderFirewall3 - { - Name = 'Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out' - Enabled = 'True' - DependsOn = '[ADCSOnlineResponder]ConfigOnlineResponder' - } - } - } -} diff --git a/src/dsclibrary/MEMBER_WDS.DSC.ps1 b/src/dsclibrary/MEMBER_WDS.DSC.ps1 deleted file mode 100644 index 069cec06..00000000 --- a/src/dsclibrary/MEMBER_WDS.DSC.ps1 +++ /dev/null @@ -1,89 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_WDS -.Desription - Builds a Server that is joined to a domain and then installs WSUS components. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_WDS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature WDSDeploymentInstall - { - Ensure = 'Present' - Name = 'WDS-Deployment' - } - - WindowsFeature WDSTransportInstall - { - Ensure = 'Present' - Name = 'WDS-Transport' - DependsOn = '[WindowsFeature]WDSDeploymentInstall' - } - - WindowsFeature BitLockerNetworkUnlockInstall - { - Ensure = 'Present' - Name = 'BitLocker-NetworkUnlock' - DependsOn = '[WindowsFeature]WDSTransportInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - } -} diff --git a/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 b/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 deleted file mode 100644 index e862b1a4..00000000 --- a/src/dsclibrary/MEMBER_WEBSERVER.DSC.ps1 +++ /dev/null @@ -1,238 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_WEBSERVER -.Desription - Builds a Server that is joined to a domain and then made into an IIS Web Application Server. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - WebAppPools = @( - @{ Name = 'MyAppPool'; - State = 'Started'; - Ensure = 'Present'; - } - ) - WebSites = @( - @{ Name = 'MySite1'; - Ensure = 'Present'; - State = 'Started'; - SourcePath = '\\fileserver\MySite1'; - PhysicalPath = 'c:\MySite1'; - BindingInfo = @( - MSFT_xWebBindingInformation - { - Protocol = 'HTTPS' - Port = 8443 - CertificateThumbprint = '71AD93562316F21F74606F1096B85D66289ED60F' - CertificateStoreName = 'WebHosting' - }, - MSFT_xWebBindingInformation - { - Protocol = 'HTTPS' - Port = 8444 - CertificateThumbprint = 'DEDDD963B28095837F558FE14DA1FDEFB7FA9DA7' - CertificateStoreName = 'MY' - } - ) - ApplicationPool = 'MyAppPool'; - } - ) - WebApplications = @( - @{ WebSite = 'MySite1'; - Name = 'MyWebApp'; - WebAppPool = 'MyAppPool'; - PhysicalPath = 'c:\MyApp1'; - SourcePath = '\\fileserver\MyApp1'; - Ensure = 'Present'; - } - ) - WebVirtualDirectories = @( - @{ WebSite = 'MySite1' - WebApplication = 'MyWebApp'; - PhysicalPath = 'c:\Images'; - SourcePath = '\\fileserver\MySite1\Images; - Name = 'Images'; - Ensure = 'Present'; - } - ) -###################################################################################################> - -Configuration MEMBER_WEBSERVER -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xWebAdministration - - Node $AllNodes.NodeName { - # Assemble the Admin Credentials - if ($Node.DomainAdminPassword) { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature IISInstall - { - Ensure = 'Present' - Name = 'Web-Server' - } - - WindowsFeature AspNet45Install - { - Ensure = 'Present' - Name = 'Web-Asp-Net45' - } - - WindowsFeature WebMgmtServiceInstall - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - } - - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - # Create the Web App Pools - $count=0 - foreach ($WebAppPool in $Node.WebAppPools) { - $count++ - xWebAppPool "WebAppPool$count" - { - Ensure = $WebAppPool.Ensure - Name = $WebAppPool.Name - State = $WebAppPool.State - } - } - - # Create the Web Sites - $count=0 - foreach ($WebSite in $Node.WebSites) { - $count++ - - # Create an empty folder or copy content from Source Path - if ($WebSite.SourcePath) - { - File "WebSiteContent$count" - { - Ensure = 'Present' - SourcePath = $WebSite.SourcePath - DestinationPath = $WebSite.PhysicalPath - Recurse = $true - Type = 'Directory' - } - } - else - { - File "WebSiteContent$count" - { - Ensure = 'Present' - Type = 'Directory' - DestinationPath = $WebSite.PhysicalPath - } - } # if - - xWebsite "WebSite$count" - { - Ensure = $WebSite.Ensure - Name = $WebSite.Name - State = $WebSite.State - PhysicalPath = $WebSite.PhysicalPath - BindingInfo = $WebSite.BindingInfo - ApplicationPool = $WebSite.ApplicationPool - DependsOn = "[File]WebSiteContent$count" - } - } - - # Create the Web Applications - $count=0 - foreach ($WebApplication in $Node.WebApplications) { - $count++ - - # Create an empty folder or copy content from Source Path - if ($WebApplication.SourcePath) - { - File "WebApplicationContent$count" - { - Ensure = 'Present' - SourcePath = $WebApplication.SourcePath - DestinationPath = $WebApplication.PhysicalPath - Recurse = $true - Type = 'Directory' - } - } - else - { - File "WebApplicationContent$count" - { - Ensure = 'Present' - Type = 'Directory' - DestinationPath = $WebApplication.PhysicalPath - } - } # if - - xWebApplication "WebApplication$count" - { - Ensure = $WebApplication.Ensure - WebSite = $WebApplication.WebSite - Name = $WebApplication.Name - WebAppPool = $WebApplication.WebAppPool - PhysicalPath = $WebApplication.PhysicalPath - DependsOn = "[File]WebApplicationContent$count" - } - } - - # Create the Web Virtual Directories - $count=0 - foreach ($WebVirtualDirectory in $Node.WebVirtualDirectories) { - $count++ - - # Create an empty folder or copy content from Source Path - if ($WebVirtualDirectory.SourcePath) - { - File "WebVirtualDirectoryContent$count" - { - Ensure = 'Present' - SourcePath = $WebVirtualDirectory.SourcePath - DestinationPath = $WebVirtualDirectory.PhysicalPath - Recurse = $true - Type = 'Directory' - } - } - else - { - File "WebVirtualDirectoryContent$count" - { - Ensure = 'Present' - Type = 'Directory' - DestinationPath = $WebVirtualDirectory.PhysicalPath - } - } # if - - xWebVirtualDirectory "WebVirtualDirectory$count" - { - Ensure = $WebVirtualDirectory.Ensure - WebSite = $WebVirtualDirectory.WebSite - WebApplication = $WebVirtualDirectory.WebApplication - PhysicalPath = $WebVirtualDirectory.PhysicalPath - Name = $WebVirtualDirectory.Name - DependsOn = "[File]WebVirtualDirectoryContent$count" - } - } - } -} diff --git a/src/dsclibrary/MEMBER_WSUS.DSC.ps1 b/src/dsclibrary/MEMBER_WSUS.DSC.ps1 deleted file mode 100644 index 83bafb9e..00000000 --- a/src/dsclibrary/MEMBER_WSUS.DSC.ps1 +++ /dev/null @@ -1,84 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_WSUS -.Desription - Builds a Server that is joined to a domain and then installs WSUS components. - Requires cMicrosoftUpdate resource from https://github.com/fabiendibot/cMicrosoftUpdate -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true -###################################################################################################> - -Configuration MEMBER_WSUS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName xWindowsUpdate - Import-DscResource -ModuleName StorageDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature UpdateServicesWIDDBInstall - { - Ensure = 'Present' - Name = 'UpdateServices-WidDB' - } - - WindowsFeature UpdateServicesServicesInstall - { - Ensure = 'Present' - Name = 'UpdateServices-Services' - DependsOn = '[WindowsFeature]UpdateServicesWIDDBInstall' - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - # Join this Server to the Domain - Computer JoinDomain - { - Name = $Node.NodeName - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - DependsOn = '[WaitForAll]DC' - } - - WaitforDisk Disk2 - { - DiskId = 1 - RetryIntervalSec = 60 - RetryCount = 60 - DependsOn = '[Computer]JoinDomain' - } - - Disk DVolume - { - DiskId = 1 - DriveLetter = 'D' - DependsOn = '[WaitforDisk]Disk2' - } - } -} diff --git a/src/dsclibrary/RODC_SECONDARY.DSC.ps1 b/src/dsclibrary/RODC_SECONDARY.DSC.ps1 deleted file mode 100644 index e77976cd..00000000 --- a/src/dsclibrary/RODC_SECONDARY.DSC.ps1 +++ /dev/null @@ -1,91 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - RODC_SECONDARY -.Desription - Builds a Read Only Domain Controller and adds it to the existing domain provided in the Parameter DomainName. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' - DCName = 'SA-DC1' - PSDscAllowDomainUser = $true - InstallRSATTools = $true -###################################################################################################> - -Configuration RODC_SECONDARY -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - if ($Node.DomainAdminPassword) - { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature BackupInstall - { - Ensure = 'Present' - Name = 'Windows-Server-Backup' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - WindowsFeature ADDSInstall - { - Ensure = 'Present' - Name = 'AD-Domain-Services' - DependsOn = '[WindowsFeature]DNSInstall' - } - - WindowsFeature RSAT-AD-PowerShellInstall - { - Ensure = 'Present' - Name = 'RSAT-AD-PowerShell' - DependsOn = '[WindowsFeature]ADDSInstall' - } - - if ($InstallRSATTools) - { - WindowsFeature RSAT-ManagementTools - { - Ensure = 'Present' - Name = 'RSAT-AD-Tools', 'RSAT-DNS-Server' - DependsOn = '[WindowsFeature]ADDSInstall' - } - } - - # Wait for the Domain to be available so we can join it. - WaitForAll DC - { - ResourceName = '[ADDomain]PrimaryDC' - NodeName = $Node.DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - - ADDomainController SecondaryDC - { - DomainName = $Node.DomainName - Credential = $DomainAdminCredential - SafemodeAdministratorPassword = $LocalAdminCredential - ReadOnlyReplica = $true - DependsOn = '[WaitForADDomain]DscDomainWait' - } - } -} diff --git a/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 b/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 deleted file mode 100644 index 46954ce4..00000000 --- a/src/dsclibrary/STANDALONE_DEFAULT.DSC.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - STANDALONE_DEFAULT -.Desription - Builds a Standalone computer with no additional DSC resources. -.Parameters: -###################################################################################################> - -Configuration STANDALONE_DEFAULT -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - } -} diff --git a/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 b/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 deleted file mode 100644 index 0bddeb61..00000000 --- a/src/dsclibrary/STANDALONE_DHCPDNS.DSC.ps1 +++ /dev/null @@ -1,178 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - STANDALONE_DHCPDNS -.Desription - Builds a Standalone DHCP and DNS Server. -.Parameters: - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - Forwarders = @('8.8.8.8','8.8.4.4') - ADZones = @( - @{ Name = 'ALPHA.LOCAL'; - DynamicUpdate = 'Secure'; - ReplicationScope = 'Forest'; - } - ) - PrimaryZones = @( - @{ Name = 'BRAVO.LOCAL'; - ZoneFile = 'bravo.local.dns'; - DynamicUpdate = 'None'; - } - ) -###################################################################################################> - -Configuration STANDALONE_DHCPDNS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - Node $AllNodes.NodeName { - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - <# - Add the DHCP Scope, Reservation and Options from - the node configuration - #> - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - # DNS Server Settings - if ($Node.Forwarders) - { - xDnsServerForwarder DNSForwarders - { - IsSingleInstance = 'Yes' - IPAddresses = $Node.Forwarders - DependsOn = '[Computer]JoinDomain' - } - } - - $count = 0 - foreach ($ADZone in $Node.ADZones) - { - $count++ - xDnsServerADZone "ADZone$count" - { - Ensure = 'Present' - Name = $ADZone.Name - DynamicUpdate = $ADZone.DynamicUpdate - ReplicationScope = $ADZone.ReplicationScope - Credential = $DomainAdminCredential - DependsOn = '[Computer]JoinDomain' - } - } - - $count = 0 - foreach ($PrimaryZone in $Node.PrimaryZones) - { - $count++ - xDnsServerPrimaryZone "PrimaryZone$count" - { - Ensure = 'Present' - Name = $PrimaryZone.Name - ZoneFile = $PrimaryZone.ZoneFile - DynamicUpdate = $PrimaryZone.DynamicUpdate - DependsOn = '[Computer]JoinDomain' - } - } - } -} diff --git a/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 b/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 deleted file mode 100644 index ede854e3..00000000 --- a/src/dsclibrary/STANDALONE_INTERNET.DSC.ps1 +++ /dev/null @@ -1,103 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - STANDALONE_INTERNET -.Desription - Builds a Standalone DHCP, DNS and IIS Server to simulate the Internet. - See http://blog.superuser.com/2011/05/16/windows-7-network-awareness/ - for details on how Windows computers detect Internet connectivity. -.Parameters: -###################################################################################################> - -Configuration STANDALONE_INTERNET -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - Import-DscResource -ModuleName xWebAdministration - - Node $AllNodes.NodeName { - WindowsFeature WebServerInstall - { - Ensure = 'Present' - Name = 'Web-WebServer' - } - - WindowsFeature DHCPInstall - { - Ensure = 'Present' - Name = 'DHCP' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - # Create the default ncsi.txt. - File CAPolicy - { - Ensure = 'Present' - DestinationPath = 'c:\inetpub\wwwroot\ncsi.txt' - Contents = 'Microsoft NCSI' - Type = 'File' - DependsOn = '[WindowsFeature]WebServerInstall' - } - - <# - Add the DHCP Scope, Reservation and Options from - the node configuration - #> - $count = 0 - foreach ($Scope in $Node.Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - $count = 0 - foreach ($Reservation in $Node.Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - - $count = 0 - foreach ($ScopeOption in $Node.ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $Node.DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - DependsOn = '[WindowsFeature]DHCPInstall' - } - } - } -} diff --git a/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 b/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 deleted file mode 100644 index 4173d3b8..00000000 --- a/src/dsclibrary/STANDALONE_JENKINS.DSC.ps1 +++ /dev/null @@ -1,93 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - STANDALONE_JENKINS -.Desription - Builds a Windows Server and installs Jenkins CI on it. -.Parameters: - JenkinsPort = 80 -###################################################################################################> - -Configuration STANDALONE_JENKINS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName cChoco - Import-DscResource -ModuleName NetworkingDsc - - Node $AllNodes.NodeName { - WindowsFeature NetFrameworkCore - { - Ensure = 'Present' - Name = 'NET-Framework-Core' - } - - # Install Chocolatey - cChocoInstaller installChoco - { - InstallDir = 'c:\choco' - DependsOn = '[WindowsFeature]NetFrameworkCore' - } - - # Install JDK8 - cChocoPackageInstaller installJdk8 - { - Name = 'jdk8' - DependsOn = '[cChocoInstaller]installChoco' - } - - # Install Jenkins - cChocoPackageInstaller installJenkins - { - Name = 'Jenkins' - DependsOn = '[cChocoInstaller]installChoco' - } - - # Set the Jenkins Port - $JenkinsPort = 8080 - if ($Node.JenkinsPort) - { - $JenkinsPort = $Node.JenkinsPort - } - Script SetJenkinsPort - { - SetScript = { - Write-Verbose -Message "Setting Jenkins Port to $Using:JenkinsPort" - $Config = Get-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" - $NewConfig = $Config ` - -replace '--httpPort=[0-9]*\s', "--httpPort=$Using:JenkinsPort " - Set-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" ` - -Value $NewConfig ` - -Force - Write-Verbose -Message "Restarting Jenkins" - Restart-Service ` - -Name Jenkins - } - GetScript = { - $Config = Get-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" - $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) - $CurrentPort = $Matches.Groups[1].Value - Return @{ - 'JenkinsPort' = $CurrentPort - } - } - TestScript = { - $Config = Get-Content ` - -Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" - $Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase')) - $CurrentPort = $Matches.Groups[1].Value - - if ($Using:JenkinsPort -ne $CurrentPort) - { - # Jenkins port must be changed - Return $false - } - # Jenkins is already on correct port - Return $true - } - DependsOn = '[cChocoPackageInstaller]installJenkins' - } - } -} diff --git a/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 b/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 deleted file mode 100644 index 62ae7f3f..00000000 --- a/src/dsclibrary/STANDALONE_ROOTCA.DSC.ps1 +++ /dev/null @@ -1,326 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - STANDALONE_ROOTCA -.Desription - Builds a Standalone Root CA and creates Issuing CA certificates for Sub CAs. -.Parameters: - CACommonName = 'LABBUILDER.COM Root CA' - CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' - CRLPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' - CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - SubCAs = @('SA_SUBCA') -###################################################################################################> - -Configuration STANDALONE_ROOTCA -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryCSDsc - Import-DscResource -ModuleName xPSDesiredStateConfiguration - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - # Install the ADCS Certificate Authority - WindowsFeature ADCSCA - { - Name = 'ADCS-Cert-Authority' - Ensure = 'Present' - } - - <# - Install ADCS Web Enrollment - only required because it creates the CertEnroll virtual folder - Which we use to pass certificates to the Issuing/Sub CAs - #> - WindowsFeature ADCSWebEnrollment - { - Ensure = 'Present' - Name = 'ADCS-Web-Enrollment' - DependsOn = '[WindowsFeature]ADCSCA' - } - - WindowsFeature InstallWebMgmtService - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - DependsOn = '[WindowsFeature]ADCSWebEnrollment' - } - - # Create the CAPolicy.inf file which defines basic properties about the ROOT CA certificate - File CAPolicy - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\CAPolicy.inf' - Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" - Type = 'File' - DependsOn = '[WindowsFeature]ADCSCA' - } - - # Configure the CA as Standalone Root CA - ADCSCertificationAuthority ConfigCA - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - CAType = 'StandaloneRootCA' - CACommonName = $Node.CACommonName - CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix - ValidityPeriod = 'Years' - ValidityPeriodUnits = 20 - CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' - HashAlgorithmName = 'SHA256' - KeyLength = 4096 - DependsOn = '[File]CAPolicy' - } - - # Configure the ADCS Web Enrollment - ADCSWebEnrollment ConfigWebEnrollment - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - CAConfig = 'CertSrv' - Credential = $LocalAdminCredential - DependsOn = '[ADCSCertificationAuthority]ConfigCA' - } - - # Set the advanced CA properties - Script ADCSAdvConfig - { - SetScript = { - if ($Using:Node.CADistinguishedNameSuffix) - { - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" - } - - if ($Using:Node.CRLPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) - } - - if ($Using:Node.CACertPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) - } - - if ($Using:Node.CRLPeriodUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" - } - - if ($Using:Node.CRLOverlapUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" - } - - if ($Using:Node.ValidityPeriodUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" - } - - if ($Using:Node.AuditFilter) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) - } - - Restart-Service -Name CertSvc - New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue - Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' - } - - GetScript = { - return @{ - 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); - 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); - 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); - 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') - 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') - 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') - 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') - 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') - 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') - 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') - 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') - } - } - - TestScript = { - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) - { - return $false - } - - if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) - { - return $false - } - - if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) - { - return $false - } - - if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) - { - return $false - } - - if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) - { - return $false - } - - if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) - { - return $false - } - - if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) - { - return $false - } - - if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) - { - return $false - } - - if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) - { - return $false - } - - return $true - } - - DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' - } - - # Generate Issuing certificates for any SubCAs - foreach ($SubCA in $Node.SubCAs) - { - - # Wait for SubCA to generate REQ - WaitForAny "WaitForSubCA_$SubCA" - { - ResourceName = '[ADCSCertificationAuthority]ConfigCA' - NodeName = $SubCA - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = '[Script]ADCSAdvConfig' - } - - # Download the REQ from the SubCA - xRemoteFile "DownloadSubCA_$SubCA" - { - DestinationPath = "C:\Windows\System32\CertSrv\CertEnroll\$SubCA.req" - Uri = "http://$SubCA/CertEnroll/$SubCA.req" - DependsOn = "[WaitForAny]WaitForSubCA_$SubCA" - } - - # Generate the Issuing Certificate from the REQ - Script "IssueCert_$SubCA" - { - SetScript = { - Write-Verbose -Message "Submitting C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.req to $($Using:Node.CACommonName)" - [System.String]$RequestResult = & "$($ENV:SystemRoot)\System32\Certreq.exe" -Config ".\$($Using:Node.CACommonName)" -Submit "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.req" - $Matches = [Regex]::Match($RequestResult, 'RequestId:\s([0-9]*)') - - if ($Matches.Groups.Count -lt 2) - { - Write-Verbose -Message 'Error getting Request ID from SubCA certificate submission.' - Throw 'Error getting Request ID from SubCA certificate submission.' - } - - [System.Int32]$RequestId = $Matches.Groups[1].Value - Write-Verbose -Message "Issuing $RequestId in $($Using:Node.CACommonName)" - [System.String]$SubmitResult = & "$($ENV:SystemRoot)\System32\CertUtil.exe" -Resubmit $RequestId - - if ($SubmitResult -notlike 'Certificate issued.*') - { - Write-Verbose -Message 'Unexpected result issuing SubCA request.' - throw 'Unexpected result issuing SubCA request.' - } - - Write-Verbose -Message "Retrieving C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.req from $($Using:Node.CACommonName)" - [System.String]$RetrieveResult = & "$($ENV:SystemRoot)\System32\Certreq.exe" -Config ".\$($Using:Node.CACommonName)" -Retrieve $RequestId "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.crt" - } - - GetScript = { - return @{ - 'Generated' = (Test-Path -Path "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.crt"); - } - } - - TestScript = { - if (-not (Test-Path -Path "C:\Windows\System32\CertSrv\CertEnroll\$Using:SubCA.crt")) - { - # SubCA Cert is not yet created - return $false - } - - # SubCA Cert has been created - return $true - } - - DependsOn = "[xRemoteFile]DownloadSubCA_$SubCA" - } - - # Wait for SubCA to install the CA Certificate - WaitForAny "WaitForComplete_$SubCA" - { - ResourceName = '[Script]RegisterSubCA' - NodeName = $SubCA - RetryIntervalSec = 30 - RetryCount = 30 - DependsOn = "[Script]IssueCert_$SubCA" - } - - # Shutdown the Root CA - it is no longer needed because it has issued all SubCAs - Script ShutdownRootCA - { - SetScript = { - Stop-Computer - } - - GetScript = { - return @{ - } - } - - TestScript = { - # SubCA Cert is not yet created - return $false - } - - DependsOn = "[WaitForAny]WaitForComplete_$SubCA" - } - } - } -} diff --git a/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 b/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 deleted file mode 100644 index 9d1b8ff5..00000000 --- a/src/dsclibrary/STANDALONE_ROOTCA_NOSUBCA.DSC.ps1 +++ /dev/null @@ -1,216 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - STANDALONE_ROOTCA_NOSUBCA -.Desription - Builds a Standalone Root CA with no Sub CAs. -.Parameters: - CACommonName = 'LABBUILDER.COM Root CA' - CADistinguishedNameSuffix = 'DC=LABBUILDER,DC=COM' - CRLPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl' - CACertPublicationURLs = '1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt' -###################################################################################################> - -Configuration STANDALONE_ROOTCA_NOSUBCA -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryCSDsc - - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) - { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - - # Install the ADCS Certificate Authority - WindowsFeature ADCSCA - { - Name = 'ADCS-Cert-Authority' - Ensure = 'Present' - } - - <# - Install ADCS Web Enrollment - only required because it creates the CertEnroll virtual folder - Which we use to pass certificates to the Issuing/Sub CAs - #> - WindowsFeature ADCSWebEnrollment - { - Ensure = 'Present' - Name = 'ADCS-Web-Enrollment' - DependsOn = '[WindowsFeature]ADCSCA' - } - - WindowsFeature InstallWebMgmtService - { - Ensure = 'Present' - Name = 'Web-Mgmt-Service' - DependsOn = '[WindowsFeature]ADCSWebEnrollment' - } - - # Create the CAPolicy.inf file which defines basic properties about the ROOT CA certificate - File CAPolicy - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\CAPolicy.inf' - Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" - Type = 'File' - DependsOn = '[WindowsFeature]ADCSCA' - } - - # Configure the CA as Standalone Root CA - ADCSCertificationAuthority ConfigCA - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - CAType = 'StandaloneRootCA' - CACommonName = $Node.CACommonName - CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix - ValidityPeriod = 'Years' - ValidityPeriodUnits = 20 - CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' - HashAlgorithmName = 'SHA256' - KeyLength = 4096 - DependsOn = '[File]CAPolicy' - } - - # Configure the ADCS Web Enrollment - ADCSWebEnrollment ConfigWebEnrollment - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - CAConfig = 'CertSrv' - Credential = $LocalAdminCredential - DependsOn = '[ADCSCertificationAuthority]ConfigCA' - } - - # Set the advanced CA properties - Script ADCSAdvConfig - { - SetScript = { - if ($Using:Node.CADistinguishedNameSuffix) - { - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" - } - - if ($Using:Node.CRLPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) - } - - if ($Using:Node.CACertPublicationURLs) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) - } - - if ($Using:Node.CRLPeriodUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" - } - - if ($Using:Node.CRLOverlapUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" - } - - if ($Using:Node.ValidityPeriodUnits) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" - } - - if ($Using:Node.AuditFilter) - { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) - } - - Restart-Service -Name CertSvc - New-Item -Path 'c:\windows\setup\scripts\' -ItemType Directory -ErrorAction SilentlyContinue - Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value 'Certificate Service Restarted ...' - } - - GetScript = { - return @{ - 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); - 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); - 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); - 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') - 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') - 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') - 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') - 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') - 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') - 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') - 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') - } - } - - TestScript = { - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) - { - return $false - } - - if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) - { - return $false - } - - if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) - { - return $false - } - - if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) - { - return $false - } - - if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) - { - return $false - } - - if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) - { - return $false - } - - if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) - { - return $false - } - - if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) - { - return $false - } - - if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) - { - return $false - } - - if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) - { - return $false - } - - return $true - } - - DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' - } - } -} diff --git a/src/dsclibrary/modules/LabDSCResources.psd1 b/src/dsclibrary/modules/LabDSCResources.psd1 deleted file mode 100644 index e4e6a2eb..00000000 --- a/src/dsclibrary/modules/LabDSCResources.psd1 +++ /dev/null @@ -1,7 +0,0 @@ -RootModule=xCertAuthorityServer.DSC.Schema.psm1 -RootModule=xDC.DSC.Schema.psm1 -RootModule=xDHCPServer.DSC.Schema.psm1 -RootModule=xFileServer.DSC.Schema.psm1 -RootModule=xJoinDomain.DSC.Schema.psm1 -RootModule=xNPSServer.DSC.Schema.psm1 -RootModule=xRemoteAccessServer.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 deleted file mode 100644 index f85e7928..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xCertAuthorityServer.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 deleted file mode 100644 index 719073f2..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xCertAuthorityServer/xCertAuthorityServer.DSC.Schema.psm1 +++ /dev/null @@ -1,250 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_ROOTCA -.Desription - Builds an Enterprise Root CA. -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - CACommonName = "LABBUILDER.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 -###################################################################################################> - -Configuration MEMBER_ROOTCA -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName ActiveDirectoryCSDsc - Import-DscResource -ModuleName xPSDesiredStateConfiguration - Import-DscResource -ModuleName NetworkingDsc - Node $AllNodes.NodeName { - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) { - $DomainAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - # Install the CA Service - WindowsFeature ADCSCA { - Name = 'ADCS-Cert-Authority' - Ensure = 'Present' - } - - # Install the Web Enrollment Service - WindowsFeature ADCSWebEnrollment { - Name = 'ADCS-Web-Enrollment' - Ensure = 'Present' - DependsOn = "[WindowsFeature]ADCSCA" - } - - WindowsFeature InstallWebMgmtService - { - Ensure = "Present" - Name = "Web-Mgmt-Service" - DependsOn = '[WindowsFeature]ADCSWebEnrollment' - } - - if ($Node.InstallOnlineResponder) { - # Install the Online Responder Service - WindowsFeature OnlineResponderCA { - Name = 'ADCS-Online-Cert' - Ensure = 'Present' - DependsOn = "[WindowsFeature]ADCSCA" - } - } - - if ($Node.InstallEnrollmentWebService) { - # Install the Enrollment Web Service/Enrollment Policy Web Service - WindowsFeature EnrollmentWebSvc { - Name = 'ADCS-Enroll-Web-Svc' - Ensure = 'Present' - DependsOn = "[WindowsFeature]ADCSCA" - } - - WindowsFeature EnrollmentWebPol { - Name = 'ADCS-Enroll-Web-Pol' - Ensure = 'Present' - DependsOn = "[WindowsFeature]ADCSCA" - } - } - - - # Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA. - File CAPolicy - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\CAPolicy.inf' - Contents = "[Version]`r`n Signature= `"$Windows NT$`"`r`n[Certsrv_Server]`r`n AlternateSignatureAlgorithm=0`r`n HashAlgorithm=RSASHA256`r`n RenewalKeyLength=4096`r`n RenewalValidityPeriod=Years`r`n RenewalValidityPeriodUnits=20`r`n CRLDeltaPeriod=Days`r`n CRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n" - Type = 'File' - DependsOn = '[Computer]JoinDomain' - } - - # Make a CertEnroll folder to put the Root CA certificate into. - # The CA Web Enrollment server would also create this but we need it now. - File CertEnrollFolder - { - Ensure = 'Present' - DestinationPath = 'C:\Windows\System32\CertSrv\CertEnroll' - Type = 'Directory' - DependsOn = '[File]CAPolicy' - } - - # Configure the Root CA which will create the Certificate REQ file that Root CA will use - # to issue a certificate for this Sub CA. - ADCSCertificationAuthority ConfigCA - { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $DomainAdminCredential - CAType = 'EnterpriseRootCA' - CACommonName = $Node.CACommonName - CADistinguishedNameSuffix = $Node.CADistinguishedNameSuffix - OverwriteExistingCAinDS = $true - CryptoProviderName = 'RSA#Microsoft Software Key Storage Provider' - HashAlgorithmName = 'SHA256' - KeyLength = 4096 - DependsOn = '[File]CertEnrollFolder' - } - - # Configure the Web Enrollment Feature - ADCSWebEnrollment ConfigWebEnrollment { - Ensure = 'Present' - Name = 'ConfigWebEnrollment' - Credential = $LocalAdminCredential - DependsOn = '[ADCSCertificationAuthority]ConfigCA' - } - - # Perform final configuration of the CA which will cause the CA service to startup - # Set the advanced CA properties - Script ADCSAdvConfig - { - SetScript = { - if ($Using:Node.CADistinguishedNameSuffix) { - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSConfigDN "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)" - & "$($ENV:SystemRoot)\system32\certutil.exe" -setreg CA\DSDomainDN "$($Using:Node.CADistinguishedNameSuffix)" - } - if ($Using:Node.CRLPublicationURLs) { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPublicationURLs $($Using:Node.CRLPublicationURLs) - } - if ($Using:Node.CACertPublicationURLs) { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CACertPublicationURLs $($Using:Node.CACertPublicationURLs) - } - if ($Using:Node.CRLPeriodUnits) { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriodUnits $($Using:Node.CRLPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLPeriod "$($Using:Node.CRLPeriod)" - } - if ($Using:Node.CRLOverlapUnits) { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapUnits $($Using:Node.CRLOverlapUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\CRLOverlapPeriod "$($Using:Node.CRLOverlapPeriod)" - } - if ($Using:Node.ValidityPeriodUnits) { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriodUnits $($Using:Node.ValidityPeriodUnits) - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\ValidityPeriod "$($Using:Node.ValidityPeriod)" - } - if ($Using:Node.AuditFilter) { - & "$($ENV:SystemRoot)\System32\certutil.exe" -setreg CA\AuditFilter $($Using:Node.AuditFilter) - } - Restart-Service -Name CertSvc - Add-Content -Path 'c:\windows\setup\scripts\certutil.log' -Value "Certificate Service Restarted ..." - } - GetScript = { - Return @{ - 'DSConfigDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN'); - 'DSDomainDN' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN'); - 'CRLPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs'); - 'CACertPublicationURLs' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') - 'CRLPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') - 'CRLPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') - 'CRLOverlapUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') - 'CRLOverlapPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') - 'ValidityPeriodUnits' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') - 'ValidityPeriod' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') - 'AuditFilter' = (Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') - } - } - TestScript = { - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSConfigDN') -ne "CN=Configuration,$($Using:Node.CADistinguishedNameSuffix)")) { - Return $false - } - if (((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('DSDomainDN') -ne "$($Using:Node.CADistinguishedNameSuffix)")) { - Return $false - } - if (($Using:Node.CRLPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPublicationURLs') -ne $Using:Node.CRLPublicationURLs)) { - Return $false - } - if (($Using:Node.CACertPublicationURLs) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CACertPublicationURLs') -ne $Using:Node.CACertPublicationURLs)) { - Return $false - } - if (($Using:Node.CRLPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriodUnits') -ne $Using:Node.CRLPeriodUnits)) { - Return $false - } - if (($Using:Node.CRLPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLPeriod') -ne $Using:Node.CRLPeriod)) { - Return $false - } - if (($Using:Node.CRLOverlapUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapUnits') -ne $Using:Node.CRLOverlapUnits)) { - Return $false - } - if (($Using:Node.CRLOverlapPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('CRLOverlapPeriod') -ne $Using:Node.CRLOverlapPeriod)) { - Return $false - } - if (($Using:Node.ValidityPeriodUnits) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriodUnits') -ne $Using:Node.ValidityPeriodUnits)) { - Return $false - } - if (($Using:Node.ValidityPeriod) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('ValidityPeriod') -ne $Using:Node.ValidityPeriod)) { - Return $false - } - if (($Using:Node.AuditFilter) -and ((Get-ChildItem 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration').GetValue('AuditFilter') -ne $Using:Node.AuditFilter)) { - Return $false - } - Return $true - } - DependsOn = '[ADCSWebEnrollment]ConfigWebEnrollment' - } - - if ($Node.InstallOnlineResponder) { - # Configure the Online Responder Feature - ADCSOnlineResponder ConfigOnlineResponder { - Ensure = 'Present' - IsSingleInstance = 'Yes' - Credential = $LocalAdminCredential - DependsOn = '[Script]ADCSAdvConfig' - } - - # Enable Online Responder FireWall rules so we can remote manage Online Responder - Firewall OnlineResponderFirewall1 - { - Name = "Microsoft-Windows-OnlineRevocationServices-OcspSvc-DCOM-In" - Enabled = "True" - DependsOn = "[ADCSOnlineResponder]ConfigOnlineResponder" - } - - Firewall OnlineResponderirewall2 - { - Name = "Microsoft-Windows-CertificateServices-OcspSvc-RPC-TCP-In" - Enabled = "True" - DependsOn = "[ADCSOnlineResponder]ConfigOnlineResponder" - } - - Firewall OnlineResponderFirewall3 - { - Name = "Microsoft-Windows-OnlineRevocationServices-OcspSvc-TCP-Out" - Enabled = "True" - DependsOn = "[ADCSOnlineResponder]ConfigOnlineResponder" - } - } - } -} diff --git a/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 deleted file mode 100644 index 29e79fa8..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xDC.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 deleted file mode 100644 index 8a116abd..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xDC/xDC.DSC.Schema.psm1 +++ /dev/null @@ -1,111 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - DC_FORESTPRIMARY -.Desription - Builds a Domain Controller as the first DC in a forest with the name of the Domain Name parameter passed. -.Parameters: - DomainName = 'LABBUILDER.COM' - DomainAdminPassword = 'P@ssword!1' -###################################################################################################> - -Configuration DC -{ - Param - ( - # Set the Domain Name - [Parameter(Mandatory=$true,Position=1)] - [System.String] - $DomainName, - - # Set the Domain Controller Name - [Parameter(Mandatory=$true)] - [System.String] - $DCName, - - # Local Administrator Credentials - [Parameter(Mandatory=$true)] - [System.String] - $LocalAdminPassword, - - # Domain Administrator Credentials - [Parameter(Mandatory=$true)] - [System.String] - $DomainAdminPassword, - - #OUs to Create - [Parameter(Mandatory=$false)] - [System.String] - $OUName - ) - - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 4.1.0.0 - Import-DscResource -ModuleName xDNSServer -ModuleVersion 1.16.0.0 - - # Assemble the Local Admin Credentials - if ($LocalAdminPassword) { - $LocalAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) - } - - if ($DomainAdminPassword) { - $DomainAdminCredential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ('Administrator', (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature BackupInstall - { - Ensure = 'Present' - Name = 'Windows-Server-Backup' - } - - WindowsFeature DNSInstall - { - Ensure = 'Present' - Name = 'DNS' - } - - WindowsFeature ADDSInstall - { - Ensure = 'Present' - Name = 'AD-Domain-Services' - DependsOn = '[WindowsFeature]DNSInstall' - } - - WindowsFeature RSAT-AD-PowerShellInstall - { - Ensure = 'Present' - Name = 'RSAT-AD-PowerShell' - DependsOn = '[WindowsFeature]ADDSInstall' - } - - ADDomain ADDomainCreateDC - { - DomainName = $DomainName - Credential = $DomainAdminCredential - SafemodeAdministratorPassword = $LocalAdminCredential - DependsOn = '[WindowsFeature]ADDSInstall' - } - - WaitForADDomain DscDomainWait - { - DomainName = $Node.ParentDomainName - Credential = $DomainAdminCredential - WaitTimeout = 300 - RestartCount = 5 - DependsOn = '[WindowsFeature]ADDSInstall' - } - - ADOrganizationalUnit NewOU - { - Name = $OUName - Path = $OUPath - ProtectedFromAccidentalDeletion = $true - Description = $OUDescription - Ensure = 'Present' - DependsOn = '[WaitForADDomain]DscDomainWait' - } -} diff --git a/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 deleted file mode 100644 index fb6fd6c0..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xDHCPServer.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 deleted file mode 100644 index 4a1bc236..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xDHCPServer/xDHCPServer.DSC.Schema.psm1 +++ /dev/null @@ -1,119 +0,0 @@ -Configuration DHCP -{ - Param - ( - # Domain Admin Password - [Parameter(Mandatory = $true)] - [System.String] - $DomainAdminPassword, - - # Local Admin Password - [Parameter(Mandatory = $true)] - [System.String] - $LocalAdminPassword, - - # Domain Name - [Parameter(Mandatory = $true)] - [System.String] - $DomainName, - - # Scope Options to add - [Parameter(AttributeValues)] - [hashtable] - $ScopeOptions, - - # Scopes to create - [Parameter(AttributeValues)] - [hashtable] - $Scopes, - - # Scope Reservations - [Parameter(AttributeValues)] - [hashtable] - $Reservations - ) - - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName xDHCPServer -ModuleVersion 2.0.0.0 - - # Assemble the Local Admin Credentials - if ($LocalAdminPassword) - { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) - } - if ($DomainAdminPassword) - { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$DomainName\Administrator", (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature DHCPInstall - { - Ensure = "Present" - Name = "DHCP" - } - - Script DHCPAuthorize - { - PSDSCRunAsCredential = $DomainAdminCredential - SetScript = { - Add-DHCPServerInDC - } - GetScript = { - Return @{ - 'Authorized' = (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -gt 0); - } - } - TestScript = { - Return (-not (@(Get-DHCPServerInDC | Where-Object { $_.IPAddress -In (Get-NetIPAddress).IPAddress }).Count -eq 0)) - } - DependsOn = '[WindowsFeature]DHCPInstall' - } - - $count = 0 - foreach ($Scope in $Scopes) - { - $count++ - xDhcpServerScope "Scope$count" - { - Ensure = 'Present' - ScopeId = $Scope.Name - IPStartRange = $Scope.Start - IPEndRange = $Scope.End - Name = $Scope.Name - SubnetMask = $Scope.SubnetMask - State = 'Active' - LeaseDuration = '00:08:00' - AddressFamily = $Scope.AddressFamily - } - } - - $count = 0 - foreach ($Reservation in $Reservations) - { - $count++ - xDhcpServerReservation "Reservation$count" - { - Ensure = 'Present' - ScopeID = $Reservation.ScopeId - ClientMACAddress = $Reservation.ClientMACAddress - IPAddress = $Reservation.IPAddress - Name = $Reservation.Name - AddressFamily = $Reservation.AddressFamily - } - } - - $count = 0 - foreach ($ScopeOption in $ScopeOptions) - { - $count++ - xDhcpServerOption "ScopeOption$count" - { - Ensure = 'Present' - ScopeID = $ScopeOption.ScopeId - DnsDomain = $DomainName - DnsServerIPAddress = $ScopeOption.DNServerIPAddress - Router = $ScopeOption.Router - AddressFamily = $ScopeOption.AddressFamily - } - } -} diff --git a/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 deleted file mode 100644 index b13d327a..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xFileServer.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 deleted file mode 100644 index a8749b30..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xFileServer/xFileServer.DSC.Schema.psm1 +++ /dev/null @@ -1,184 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_FILESERVER -.Desription - Builds a Server that is joined to a domain and then made into a File Server. -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" -###################################################################################################> - -Configuration FILESERVER -{ - - Param - ( - # Set the Domain Name - [Parameter(Mandatory=$true,Position=1)] - [System.String] - $DomainName, - - # Local Administrator Credentials - [Parameter(Mandatory=$true)] - [System.String] - $LocalAdminPassword, - - # Domain Administrator Credentials - [Parameter(Mandatory=$true)] - [ParameterType] - $DomainAdminPassword, - - # Disks for File server use - [Parameter(AttributeValues)] - [hashtable] - $Disks - - - ) - - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName StorageDsc - Import-DscResource -ModuleName NetworkingDsc - - # Assemble the Local Admin Credentials - if ($Node.LocalAdminPassword) { - $LocalAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("Administrator", (ConvertTo-SecureString $Node.LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) { - $DomainAdminCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ("$($Node.DomainName)\Administrator", (ConvertTo-SecureString $Node.DomainAdminPassword -AsPlainText -Force)) - } - - WindowsFeature FileServerInstall - { - Ensure = "Present" - Name = "FS-FileServer" - } - - WindowsFeature DataDedupInstall - { - Ensure = "Present" - Name = "FS-Data-Deduplication" - DependsOn = "[WindowsFeature]FileServerInstall" - } - - WindowsFeature DFSNameSpaceInstall - { - Ensure = "Present" - Name = "FS-DFS-Namespace" - DependsOn = "[WindowsFeature]DataDedupInstall" - } - - WindowsFeature DFSReplicationInstall - { - Ensure = "Present" - Name = "FS-DFS-Replication" - DependsOn = "[WindowsFeature]DFSNameSpaceInstall" - } - - WindowsFeature FSResourceManagerInstall - { - Ensure = "Present" - Name = "FS-Resource-Manager" - DependsOn = "[WindowsFeature]DFSReplicationInstall" - } - - WindowsFeature FSSyncShareInstall - { - Ensure = "Present" - Name = "FS-SyncShareService" - DependsOn = "[WindowsFeature]FSResourceManagerInstall" - } - - WindowsFeature StorageServicesInstall - { - Ensure = "Present" - Name = "Storage-Services" - DependsOn = "[WindowsFeature]FSSyncShareInstall" - } - - WindowsFeature ISCSITargetServerInstall - { - Ensure = "Present" - Name = "FS-iSCSITarget-Server" - DependsOn = "[WindowsFeature]StorageServicesInstall" - } - - - # Enable FSRM FireWall rules so we can remote manage FSRM - Firewall FSRMFirewall1 - { - Name = "FSRM-WMI-ASYNC-In-TCP" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall2 - { - Name = "FSRM-WMI-WINMGMT-In-TCP" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall3 - { - Name = "FSRM-RemoteRegistry-In (RPC)" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall4 - { - Name = "FSRM-Task-Scheduler-In (RPC)" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall5 - { - Name = "FSRM-SrmReports-In (RPC)" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall6 - { - Name = "FSRM-RpcSs-In (RPC-EPMAP)" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall7 - { - Name = "FSRM-System-In (TCP-445)" - Ensure = 'Present' - Enabled = 'True' - } - - Firewall FSRMFirewall8 - { - Name = "FSRM-SrmSvc-In (RPC)" - Ensure = 'Present' - Enabled = 'True' - } - - [System.Int32]$count=0 - ForEach ($Disk in $Disks) { - $count++ - - WaitforDisk Disk$count - { - DiskNumber = $Disk.Number - RetryIntervalSec = 60 - RetryCount = 60 - } - - Disk Volume$count - { - DiskNumber = $Disk.Number - DriveLetter = $Disk.Letter - DependsOn = "[WaitforDisk]Disk$count" - } - } -} diff --git a/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 deleted file mode 100644 index c808a252..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xJoindomain.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 deleted file mode 100644 index 82c7c49a..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xJoinDomain/xJoinDomain.DSC.Schema.psm1 +++ /dev/null @@ -1,72 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - Join Domain DSC Module -.Desription - Joins Server to Domain -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - ComputerName = "Server01" - DomainControllerName = "DC01" -###################################################################################################> - -Configuration JOINDOMAIN -{ - Param - ( - # Set the Domain Name - [Parameter(Mandatory=$true,Position=1)] - [System.String] - $DomainName, - - - # Set the Domain Controller Name - [Parameter(Mandatory=$true)] - [System.String] - $DCName, - - # Domain Administrator Credentials - [Parameter(Mandatory=$true)] - [System.String] - $DomainAdminPassword, - - # Set the Computer Name - [Parameter(Mandatory=$true)] - [System.String] - $ComputerName - - - - ) - - Import-DscResource -ModuleName PSDesiredStateConfiguration - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 7.1.0.0 - Import-DscResource -ModuleName NetworkingDsc - - # Assemble the Local Admin Credentials - if ($LocalAdminPassword) { - [PSCredential]$LocalAdminCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force)) - } - if ($Node.DomainAdminPassword) { - [PSCredential]$DomainAdminCredential = New-Object System.Management.Automation.PSCredential ("$DomainName\Administrator", (ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force)) - } - - WaitForAll DC - { - ResourceName = '[ADDomain]CreateDC' - NodeName = $DCname - RetryIntervalSec = 15 - RetryCount = 60 - } - - - Computer JoinDomain - { - Name = $ComputerName - DomainName = $DomainName - Credential = $DomainAdminCredential - DependsOn = "[WaitForAll]DC" - } - -} diff --git a/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 deleted file mode 100644 index 404cfee8..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xNPSServer.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 deleted file mode 100644 index 79d1a1e2..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xNPSServer/xNPSServer.DSC.Schema.psm1 +++ /dev/null @@ -1,40 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_NPS -.Desription - Builds a Server that is joined to a domain and then contains NPS/Radius components. -.Requires - Windows Server 2012 R2 Full (Server core not supported). -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" -###################################################################################################> - -Configuration NPS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - - - WindowsFeature NPASPolicyServerInstall - { - Ensure = "Present" - Name = "NPAS-Policy-Server" - } - - WindowsFeature NPASHealthInstall - { - Ensure = "Present" - Name = "NPAS-Health" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" - } - - WindowsFeature RSATNPAS - { - Ensure = "Present" - Name = "RSAT-NPAS" - DependsOn = "[WindowsFeature]NPASPolicyServerInstall" - } - - -} diff --git a/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 b/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 deleted file mode 100644 index 26b54472..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psd1 +++ /dev/null @@ -1 +0,0 @@ -RootModule=xRemoteAccessServer.DSC.Schema.psm1 diff --git a/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 b/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 deleted file mode 100644 index 73da8730..00000000 --- a/src/dsclibrary/modules/MyDSCResources/xRemoteAccessServer/xRemoteAccessServer.DSC.Schema.psm1 +++ /dev/null @@ -1,31 +0,0 @@ -<################################################################################################### -DSC Template Configuration File For use by LabBuilder -.Title - MEMBER_EDGE -.Desription - Builds a Server that is joined to a domain and then contains Remote Access components. -.Parameters: - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" -###################################################################################################> - -Configuration REMOTEACCESS -{ - Import-DscResource -ModuleName PSDesiredStateConfiguration - - - WindowsFeature DirectAccessVPNInstall - { - Ensure = "Present" - Name = "DirectAccess-VPN" - } - - WindowsFeature RoutingInstall - { - Ensure = "Present" - Name = "Routing" - DependsOn = "[WindowsFeature]DirectAccessVPNInstall" - } - - -} diff --git a/src/en-US/LabBuilder_LocalizedData.psd1 b/src/en-US/LabBuilder_LocalizedData.psd1 deleted file mode 100644 index 008d98fd..00000000 --- a/src/en-US/LabBuilder_LocalizedData.psd1 +++ /dev/null @@ -1,241 +0,0 @@ -# culture = "en-US" -ConvertFrom-StringData -StringData @' - FileNotFoundError = The {0} file '{1}' was not found. - InitializeVHDNotInitializedError = The VHD '{0}' failed to initialize because a Partition Style was not provided. - InitializeVHDNotFormattedError = The VHD '{0}' failed to format because a File System was not provided. - InitializeVHDAccessPathNotFoundError = The VHD '{0}' could not be assigned to the access path '{1}' because it does not exist. - FileDownloadError = Error downloading {0} from '{1}'; {2}. - FileExtractError = Error extracting {0}; {1}. - ConfigurationFileNotFoundError = Configuration file {0} is not found. - ConfigurationFileEmptyError = Configuration file {0} is empty. - RequiredBuildNotMetError = The Windows build of this host '{0}' does not meet the minimum required build of '{1}' to install this Lab. - ConfigurationFileAlreadyExistsError = Configuration file {0} already exists. - ConfigurationInvalidError = Configuration is invalid. - PathNotFoundError = {0} path '{1}' is not found. - ResourceModuleNameIsEmptyError = Resource Module Name is missing or empty. - ResourceMSUNameIsEmptyError = Resource MSU Name is missing or empty. - ResourceISONameIsEmptyError = Resource ISO Name is missing or empty. - ResourceISOPathIsEmptyError = Resource ISO '{0}' path is missing or empty. - ResourceISOFileNotFoundAndNoURLError = Resource ISO '{0}' file '{1}' is not found and no URL provided. - ResourceISOFileNotDownloadedError = Resource ISO '{0}' file '{1}' was not downloaded successfully from '{2}'. Please download this file manually and check the content and filename. - ModuleNotAvailableError = Error installing Module '{0}' ({1}); {2}. - SwitchNameIsEmptyError = Switch name is missing or empty. - UnknownSwitchTypeError = Unknown switch type '{0}' specified for switch '{1}'. - AdapterSpecifiedError = Adater specified on '{0}' switch '{1}'. - NatSwitchNotSupportedError = NAT Switch '{0}' is not supported. NAT Switches are only supported on build Windows 10 or Windows Server 2016 build 14295 and above. - NatSubnetEmptyError = NAT Switch '{0}' subnet is empty. - NatSubnetInvalidError = NAT Switch '{0}' subnet format '{1}' is invalid. It must contain IP address and prefix length. E.g '192.168.1.1/24'. - NatSubnetAddressInvalidError = NAT Switch '{0}' subnet address '{1}' is invalid. - NatSubnetPrefixLengthInvalidError = NAT Switch '{0}' subnet prefix length '{1}' is invalid. - NatSwitchDefaultAdapterMacEmptyError = NAT Switch '{0}' default virtual network adapter MAC address is empty. - EmptyVMTemplateVHDNameError = Template VHD name is missing or empty. - EmptyVMTemplateVHDISOPathError = The ISO Path in VM Template VHD '{0}' is empty. - EmptyVMTemplateVHDPathError = The VHD Path in VM Template VHD '{0}' is empty. - VMTemplateVHDISORootPathNotFoundError = The default ISO Folder '{0}' for VM template VHDs is not found. - VMTemplateVHDISOPathNotFoundError = The ISO file '{1}' for VM Template VHD '{0}' could not be found. - VMTemplateVHDRootPathNotFoundError = The default VHD Folder '{0}' for VM template VHDs is not found. - InvalidVMTemplateVHDOSTypeError = The OSType '{1}' in VM template VHD '{0}' is invalid. Valid settings are Server, Client or Nano. - InvalidVMTemplateVHDVHDFormatError = The VHDFormat '{1}' in VM template VHD '{0}' is invalid. Valid settings are VHDx or VHD. - InvalidVMTemplateVHDVHDTypeError = The VHDType '{1}' in VM template VHD '{0}' is invalid. Valid settings are Dynamic or Fixed. - InvalidVMTemplateVHDGenerationError = The Generation '{1}' in VM template VHD '{0}' is invalid. Valid settings are 1 or 2. - EmptyTemplateNameError = Template Name is missing or empty. - TemplateSourceVHDAndTemplateVHDConflictError = Both the Template SourceVHD and TemplateVHD parameters are set for Template '{0}'. Only one of these may be set for each Template. - TemplateSourceVHDandTemplateVHDMissingError = Either the Template SourceVHD or TemplateVHD parameter must be set in Template '{0}'. - TemplateTemplateVHDNotFoundError = The Template VHD '{1}' in Template '{0}' could not be found. - TemplateSourceVHDNotFoundError = The Template Source VHD '{1}' in Template '{0}' could not be found. - DSCModuleDownloadError = Module '{2}' required by DSC Config File '{0}' in VM '{1}' could not be found or downloaded. - DSCModuleNotFoundError = Module '{2}' required by DSC Config File '{0}' in VM '{1}' could not be found in the module path. - CertificateCreateError = The self-signed certificate for VM '{0}' could not be created and downloaded. - CertificateDownloadError = The self-signed certificate for VM '{0}' could not be downloaded. - DSCConfigMetaMOFCreateError = A Meta MOF File was not created by the DSC LCM Config for VM '{0}'. - DSCConfigMoreThanOneNodeError = A single Node element cannot be found in the DSC Config File '{0}' in VM '{1}'. - DSCConfigMOFCreateError = A MOF File was not created by the DSC Config File '{0}' in VM '{1}'. - NetworkAdapterNotFoundError = VM Network Adapter '{0}' could not be found attached to VM '{1}'. - NetworkAdapterBlankMacError = VM Network Adapter '{0}' attached to VM '{1}' has a blank MAC Address. - ManagmentIPAddressError = An IPv4 address for the network adapter connected to the {0} for VM '{1}' could not be identified. - DSCInitializationError = An error occurred initializing DSC for VM '{0}'. - RemotingConnectionError = An error occurred connecting to VM '{0}' using PowerShell Remoting. - InitialSetupCompleteError = The Initial Setup for VM '{0}' did not complete before the timeout occurred. - InitializationDidNotCompleteError = Initialization for VM '{0}' did not complete. - SetupCompleteScriptMissingError = The Setup Complete Script file '{1}' specified in VM '{0}' could not be found. - UnattendFileMissingError = The Unattend file '{1}' specified in VM '{0}' could not be found. - SetupCompleteFileMissingError = The Setup Complete file '{1}' specified in VM '{0}' could not be found. - SetupCompleteFileBadTypeError = The Setup Complete file '{1}' specified in VM '{0}' must be either a PS1 or CMD file. - DSCConfigFileMissingError = The DSC Config file '{1}' specified in VM '{0}' could not be found. - DSCConfigFileBadTypeError = The DSC Config file '{1}' specified in VM '{0}' must be a PS1 file. - DSCConfigNameIsEmptyError = The DSC Config Name specified in VM '{0}' is empty. - VMNameError = The VM name cannot be 'VM' or empty. - VMTemplateNameEmptyError = The template name in VM '{0}' is empty. - VMTemplateNotFoundError = The template '{1}' specified in VM '{0}' could not be found. - VMTemplateVHDPathEmptyError = The template VHD path set in template '{0}' is empty. - VMAdapterNameError = The Adapter Name in VM '{0}' cannot be 'adapter' or empty. - VMAdapterSwitchNameError = The Switch Name specified in adapter '{1}' specified in VM '{0}' cannot be empty. - VMAdapterSwitchNotFoundError = The switch '{2}' specified in adapter '{1}' in VM '{0}' could not be found in Switches. - VMDataDiskVHDEmptyError = The Data Disk VHD in VM '{0}' cannot be 'datavhd' or empty. - VMDataDiskCantBeCreatedError = The Data Disk VHD '{1}' specified in VM '{0}' does not exist but the size and type or Source VHD was not provided so it not be created. - VMDataDiskParentVHDNotFoundError = The Data Disk Parent VHD '{1}' specified in VM '{0}' could not be found. - VMDataDiskParentVHDMissingError = The Differencing Data Disk Parent VHD specified in VM '{0}' is empty. - VMDataDiskSourceVHDNotFoundError = The Data Disk Source VHD '{1}' specified in VM '{0}' could not be found. - VMDataDiskUnknownTypeError = Unknown Data Disk type '{2}' specified in VM '{0}' for VHD '{1}'. - VMDataDiskSharedDifferencingError = The Differencing Data Disk VHD '{1}' specified in VM '{0}' can not be set as Shared. - VMDataDiskSourceVHDIfMoveError = The Data Disk VHD '{1}' specified in VM '{0}' must have a Source VHD specified if MoveSourceVHD is set. - VMDataDiskVHDConvertError = The Data Disk '{1}' in VM '{0}' cannot be converted to a {2} type. - VMDataDiskVHDShrinkError = The Data Disk '{1}' in VM '{0}' cannot be shrunk to {2}. - DownloadFolderDoesNotExistError = The folder '{0}' to download '{1}' to does not exist. - VMDataDiskPartitionStyleError = '{2}' is not a valid partition style for the Data Disk '{1}' in VM '{0}'. - VMDataDiskFileSystemError = '{2}' is not a valid file system for the Data Disk '{1}' in VM '{0}'. - VMDataDiskPartitionStyleMissingError = The Data Disk '{1}' in VM '{0}' does not have a partition style definied. - VMDataDiskFileSystemMissingError = The Data Disk '{1}' in VM '{0}' does not have a file format definied. - VMDataDiskCopyFolderMissingError = The CopyFolder '{2}' that should be copied to Data Disk '{1}' in VM '{0}' does not exist. - VMDVDDriveISOResourceNotFOundError = The ISO Resource '{1}' to be mounted into a Virtual DVD Drive specified in VM '{0}' does not exist. - NanoServerPackagesFolderMissingError = The NanoServerPackages folder '{0}' does not exist. - VMDoesNotExistError = The VM '{0}' does not exist. - VMVirtualizationExtError = The VM '{0}' requires Virtualization Extensions to be exposed, but this is not supported by your version of Windows. Either set the ExposeVirtualizationExtensions attribute to 'N' for the VM or update to a Windows Build 10565 or above. - NanoPackageNotFoundError = The Nano Server Package '{0}' could not be found. - PackageNotFoundError = The Package MSU '{0}' is not listed in the Lab Resource MSU list. - PackageMSUNotFoundError = The file '{1}' for Package MSU '{0}' does not exist. - BootPhaseStartVMsTimeoutError = One or more Virtual Machines with Bootorder '{0}' failed to start completely in the required time. - ConfigurationXMLValidationError = Lab Configuration XML '{0}'- {1}. - DSCConfiguartionMissingError = Start of Configuration could not be correctly identified in DSC Config. - VolumeNotAvailableAfterMountError = The volume was not found after ISO File '{0}' was mounted. - DriveLetterNotAssignedError = The volume was not found after ISO File '{0}' was mounted but a Drive Letter was not assigned. - ConvertWindowsImageError = An error occured converting {2} in '{1}' from ISO File '{0}' to a bootable {3}; {4}. - BindingAdapterNotFoundError = A physical network adapter {1}was not found to bind to the External Switch {0}. - BindingAdapterUsedError = Error binding physical network adapter '{1}' to External Switch '{0}' because it is already bound to another External Switch. - IPAddressError = The IP Address '{0}' is invalid. - WSManNotEnabledError = WS-Man is not enabled. - WinRMServiceFailedToStartError = The WinRM service failed to start. - PackageProviderNotInstalledError = The required package provider '{0}' is not installed. - PackageSourceNotTrustedError = The required package source '{0}' is not trusted. - PackageSourceNotRegisteredError = The required package source '{0}' is not registered. - ODJCopyError = Error copying Offline Domain Join file '{1}' to VM '{0}'. - VMNotRunningHeartbeatError = Virtual Machine '{0}' is not running, so waiting for heartbeat failed. - - ImportingLibFileMessage = Importing function library '{0}'. - EnablingWSManMessage = Enabling WS-Man for communication with Lab Guests. - InstallingHyperVComponentsMesage = Installing {0} Hyper-V Components. - InitializingLabFoldersMesage = Initializing Lab Folders. - CreatingLabFolderMessage = Creating {0} folder '{1}' for Lab. - InitializingHyperVComponentsMesage = Initializing Hyper-V Components. - InitializeVHDMountingMessage = Mounting VHD {0} for Initialization. - InitializeVHDInitializingMessage = Initializing {1} partition table on VHD {0}. - InitializeVHDCreatePartitionMessage = Creating partition on VHD {0}. - InitializeVHDFormatVolumeMessage = Formatting volume on partition {2} as {1} on VHD '{0}'. - InitializeVHDSetLabelVolumeMessage = Setting volume label to {1} on VHD '{0}'. - InitializeVHDDriveLetterMessage = Assigning drive letter {1}: to VHD {0}. - InitializeVHDAccessPathMessage = Assigning access path {1} to VHD {0}. - DownloadingFileMessage = Downloading File '{0}' from '{1}' to '{2}'. - ExtractingFileMessage = Extracting downloaded File '{0}' to '{1}'. - DownloadingResourceModuleMessage = Downloading Lab Resource Module '{0}' from '{1}'. - DownloadingResourceMSUMessage = Downloading Lab Resource MSU Package '{0}' from '{1}'. - DownloadingResourceISOMessage = Downloading Lab Resource ISO File '{0}' from '{1}'. - InitializingLabManagementVirtualNetworkMesage = Initializing Lab Management virtual switch '{0}'. - CreatingLabManagementSwitchMessage = Creating Lab Management Switch '{0}' on Vlan {1}. - UpdatingLabManagementSwitchMessage = Updating Lab Management Switch '{0}' to Vlan {1}. - RemovingLabManagementSwitchMessage = Removing Lab Management Switch '{0}'. - ModuleNotInstalledMessage = Module {0} ({1}) is not installed. - DownloadingLabResourceWebMessage = Downloading Module {0} ({1}) from '{2}'. - InstalledLabResourceWebMessage = Installed Module {0} ({1}) to '{2}'. - CreatingVirtualSwitchMessage = Creating {0} Virtual Switch '{1}'. - DeleteingVirtualSwitchMessage = Deleting {0} Virtual Switch '{1}'. - CopyingTemplateSourceVHDMessage = Copying template source VHD '{0}' to '{1}'. - OptimizingParentVHDMessage = Optimizing parent VHD '{0}'. - SettingParentVHDReadonlyMessage = Setting parent VHD '{0}' as readonly. - SkipParentVHDFileMessage = Skip copying parent VHD file '{1}' for '{0}' because it already exists. - SkipVMTemplateVHDFileMessage = Skip building VM template VHD file '{1}' for '{0}' because it already exists. - DeletingVMTemplateVHDFileMessage = Deleting VM template VHD file '{1}' for '{0}'. - DeletingParentVHDMessage = Deleting Parent VHD '{0}'. - DSCConfigIdentifyModulesMessage = Identifying Modules used by DSC Config File '{0}' in VM '{1}'. - DSCConfigSearchingForModuleMessage = Searching for Module '{2}' required by DSC Config File '{0}' in VM '{1}'. - DSCConfigInstallingModuleMessage = Installing Module '{2}' required by DSC Config File '{0}' in VM '{1}'. - DSCConfigSavingModuleMessage = Saving Module '{2}' required by DSC Config File '{0}' in VM '{1}' to LabBuilder files. - DSCConfigCopyingModuleMessage = Copying Module '{2}' required by DSC Config File '{0}' in VM '{1}' from '{3}' to '{4}'. - DSCConfigCreatingLCMMOFMessage = Creating DSC LCM Config file '{0}' in VM '{1}'. - DSCConfigPrepareMessage = Preparing to compile DSC Config '{0}' for VM '{1}'. - DSCConfigCreatingMOFMessage = Creating DSC Config file '{0}' in VM '{1}'. - DSCConfigMOFCreatedMessage = DSC MOF File '{0}' for VM '{1}'. was created successfully. - ConnectingVMMessage = Connecting to VM '{0}' on '{1}'. - DisconnectingVMMessage = Disconnecting from VM '{0}' on '{1}'. - VMSessionDoesNotExistMessage = LabBuilder Remoting Session to VM '{0}' does not exist. - ConnectingVMFailedMessage = Connection to VM '{0}' failed ({2}), retrying in {1} seconds. - ConnectingVMAccessDeniedMessage = Access Denied connecting to VM '{0}', the connection will not be retried. - CopyingFilesToVMMessage = Copying {1} Files to VM '{0}'. - CopyingFilesToVMFailedMessage = Copying {1} Files to VM '{0}' failed, retrying in {2} seconds. - CreatingVMMessage = Creating VM '{0}'. - CreatingVMDiskMessage = Creating {2} disk '{1}' for VM '{0}'. - CreatingVMDiskByMovingSourceVHDMessage = Creating disk {1} for VM '{0}' by moving Source VHD '{2}'. - CreatingVMDiskByCopyingSourceVHDMessage = Creating disk {1} for VM '{0}' by copying Source VHD '{2}'. - VMDiskAlreadyExistsMessage = {2} disk '{1}' for VM '{0}' already exists. - ExpandingVMDiskMessage = Expanding {2} disk '{1}' for VM '{0}' to {3}. - AddingVMDiskMessage = Adding {2} disk '{1}' to VM '{0}'. - AddingVMDVDDriveMessage = Adding DVD Drive to VM '{0}'. - MountingVMDVDDriveISOMessage = Mounting ISO '{1}' to DVD Drive in VM '{0}'. - DismountingVMDVDDriveISOMessage = Dismounting ISO '{1}' from DVD Drive in VM '{0}'. - CopyingFoldersToVMDiskMessage = Copying folder '{2}' to VM Disk '{1}' for VM '{0}'. - InitializingVMDiskMessage = Initializing VM Disk '{1}' for VM '{0}'. - MountingVMDiskMessage = Mounting VM Disk '{1}' for VM '{0}' to '{2}'. - DismountingVMDiskMessage = Dismounting VM Disk '{1}' for VM '{0}'. - DeletingVMFolderMessage = Deleting folder for VM '{0}'. - AddingVMNetworkAdapterMessage = Adding {2} network adapter {1} to VM '{0}'. - SettingVMNetworkAdapterVlanMessage = Setting VLAN on {2} network adapter {1} in VM '{0}' to {3}. - ClearingVMNetworkAdapterVlanMessage = Clearing VLAN on {2} network adapter {1} in VM '{0}'. - StartingVMMessage = Starting VM '{0}'. - StoppingVMMessage = Stopping VM '{0}'. - RemovingVMMessage = Removing VM '{0}'. - RemovedVMMessage = Removed VM '{0}'. - StartingBootPhaseVMsMessage = Starting Virtual Machines with Bootorder '{0}'. - AllBootPhaseVMsStartedMessage = All Virtual Machines with Bootorder '{0}' have started. - StoppingBootPhaseVMsMessage = Stopping Virtual Machines with Bootorder '{0}'. - AllBootPhaseVMsStoppedMessage = All Virtual Machines with Bootorder '{0}' have stopped. - StartingDSCMessage = Starting DSC on VM '{0}'. - MountingVMBootDiskMessage = Mounting VM '{0}' VHD Boot Disk '{1}'. - DownloadingVMBootDiskFileMessage = Downloading VM '{0}' {1} file '{2}'. - ApplyingVMBootDiskFileMessage = Applying {1} file '{2}' to VHD Boot Disk for VM '{0}'. - CreatingVMBootDiskPantherFolderMessage = Creating Panther folder to VHD Boot Disk for VM '{0}'. - DismountingVMBootDiskMessage = Dismounting VM '{0}' VHD Boot Disk '{1}'. - MountingTemplateBootDiskMessage = Mounting Template '{0}' VHD Boot Disk '{1}'. - ApplyingTemplateBootDiskFileMessage = Applying {1} file '{2}' to VHD Boot Disk for Template '{0}'. - DismountingTemplateBootDiskMessage = Dismounting Template '{0}' VHD Boot Disk '{1}'. - AddingIPAddressToTrustedHostsMessage = Adding IP Address '{1}' to WS-Man Trusted Hosts to allow remoting to '{0}'. - RemovingIPAddressFromTrustedHostsMessage = Removing IP Address '{1}' from WS-Man Trusted Hosts. - WaitingForIPAddressAssignedMessage = Waiting for valid IP Address to be assigned to VM '{0}', retrying in {1} seconds. - WaitingForInitialSetupCompleteMessage = Waiting for Initial Setup to be complete on VM '{0}', retrying in {1} seconds. - WaitingForCertificateMessage = Waiting for Certificate file on VM '{0}', retrying in {1} seconds. - FailedToUploadCertificateCreateScriptMessage = Failed to upload certificate create script to VM '{0}', retrying in {1} seconds. - FailedToDownloadCertificateMessage = Failed to download certificate from VM '{0}', retrying in {1} seconds. - FailedToExecuteCertificateCreateScriptMessage = Failed to execute certificate create script to VM '{0}', retrying in {1} seconds. - InitialSetupIsAlreadyCompleteMessaage = Initial Setup on VM '{0}' has already been completed. - CertificateDownloadStartedMessage = Certificate download from VM '{0}' started. - CertificateDownloadCompleteMessage = Certificate download from VM '{0}' complete. - VMNotFoundMessage = VM '{0}' was not found in Hyper-V server. - EnableVMIntegrationServiceMessage = The '{1}' Integration Service has been enabled in VM '{0}'. - DisableVMIntegrationServiceMessage = The '{1}' Integration Service has been disabled in VM '{0}'. - ISONotFoundDownloadURLMessage = The ISO '{1}' for VM template VHD '{0}' could not be found. It can be downloaded from '{2}'. - CreatingMountFolderMessage = Creating a temporary mount folder '{0}'. - CreatingVMTemplateVHDMessage = Creating the '{0}' VM Template VHD '{1}'. - CachingNanoServerPackagesMessage = Caching Nano Server packages from '{0}' to '{1}'. - ConvertingWIMtoVHDMessage = Converting '{3}' in '{0}' to a bootable {4} {5} {2} '{1}'. - CreatedVMInitializationFiles = Created Initialization files for VM '{0}'. - MountingVMTemplateVHDISOMessage = Mounting {1} to use source WIM to create Template VHD {0} - LabInstallCompleteMessage = The Lab '{0}' has been installed into folder '{1}'. - LabUpdateCompleteMessage = The Lab '{0}' in folder '{1}' has been updated. - LabUninstallCompleteMessage = The Lab '{0}' has been uninstalled from folder '{1}'. - LabStartCompleteMessage = The Lab '{0}' in folder '{1}' has been started. - LabStopCompleteMessage = The Lab '{0}' in folder '{1}' has been stopped. - ConfigurationXMLValidationMessage = Lab Configuration XML '{0}'- {1} - InstallPackageProviderMessage = Installing Package Provider '{0}'. - RegisterPackageSourceMessage = Registering Package Source '{0}' with '{1}'. - WaitingForVMHeartbeatMessage = Waiting for Virtual Machine '{0}' heartbeat, retrying in {1} seconds. - - ShouldUninstallLab = Uninstall the Lab '{0}' in folder '{1}' - ShouldRemoveVMTemplate = Delete the Parent VM Template VHDs used by Lab '{0}' in folder '{1}' - ShouldRemoveSwitch = Delete the virtual switches used by Lab '{0}' - ShouldRemoveVMTemplateVHD = Delete the VM Template VHDs used by Lab '{0}'. - ShouldRemoveLabFolder = Delete the folder '{1}' containing Lab '{0}' - ShouldOverwriteLab = Install a new Lab into the existing folder '{0}' - ShouldOverwriteLabConfig = Overwrite the existing Lab Configuration file '{0}' - DismountingVMTemplateVHDISOMessage = Dismounting {1} used for source WIM to create Template VHD {0} - ShouldInstallPackageProvider = Install Package Provider '{0}' - ShouldRegisterPackageSource = Register Package Source '{0}' with '{1}' - ShouldTrustPackageSource = Trust Package Source '{0}' with '{1}' -'@ diff --git a/src/lib/private/Assert-LabValidConfigurationXMLSchema.ps1 b/src/lib/private/Assert-LabValidConfigurationXMLSchema.ps1 deleted file mode 100644 index 5ec7b6a3..00000000 --- a/src/lib/private/Assert-LabValidConfigurationXMLSchema.ps1 +++ /dev/null @@ -1,88 +0,0 @@ -<# - .SYNOPSIS - Validates the provided configuration XML against the Schema. - - .DESCRIPTION - This function will ensure that the provided Configration XML - is compatible with the LabBuilderConfig.xsd Schema file. - - .PARAMETER ConfigPath - Contains the path to the Configuration XML file. - - .EXAMPLE - Assert-LabValidConfigurationXMLSchema -ConfigPath c:\mylab\config.xml - Validates the XML configuration and downloads any resources required by it. - - .OUTPUTS - None. If the XML is invalid an exception will be thrown. -#> -function Assert-LabValidConfigurationXMLSchema -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ConfigPath - ) - - # Define these variables so they are accesible inside the event handler. - $Script:XMLErrorCount = 0 - $Script:XMLFirstError = '' - $Script:XMLPath = $ConfigPath - $Script:ConfigurationXMLValidationMessage = $LocalizedData.ConfigurationXMLValidationMessage - - # Perform the XSD Validation - $readerSettings = New-Object -TypeName System.Xml.XmlReaderSettings - $readerSettings.ValidationType = [System.Xml.ValidationType]::Schema - $null = $readerSettings.Schemas.Add("labbuilderconfig", $Script:ConfigurationXMLSchema) - $readerSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessInlineSchema -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation - $readerSettings.add_ValidationEventHandler( - { - # Triggered each time an error is found in the XML file - if ([System.String]::IsNullOrWhitespace($Script:XMLFirstError)) - { - $Script:XMLFirstError = $_.Message - } # if - Write-LabMessage -Message ($Script:ConfigurationXMLValidationMessage ` - -f $Script:XMLPath, $_.Message) - $Script:XMLErrorCount++ - }) - - $reader = [System.Xml.XmlReader]::Create([System.String] $ConfigPath, $readerSettings) - - try - { - while ($reader.Read()) - { - } # while - } # try - catch - { - # XML is NOT valid - $exceptionParameters = @{ - errorId = 'ConfigurationXMLValidationError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ConfigurationXMLValidationError ` - -f $ConfigPath, $_.Exception.Message) - } - New-LabException @exceptionParameters - } # catch - finally - { - $null = $reader.Close() - } # finally - - # Verify the results of the XSD validation - if ($script:XMLErrorCount -gt 0) - { - # XML is NOT valid - $exceptionParameters = @{ - errorId = 'ConfigurationXMLValidationError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ConfigurationXMLValidationError -f $ConfigPath, $Script:XMLFirstError) - } - New-LabException @exceptionParameters - } # if -} diff --git a/src/lib/private/Assert-LabValidIpAddress.ps1 b/src/lib/private/Assert-LabValidIpAddress.ps1 deleted file mode 100644 index 98420efa..00000000 --- a/src/lib/private/Assert-LabValidIpAddress.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -<# - .SYNOPSIS - Validates the IP Address. - - .PARAMETER IpAddress - Contains the IP Address to validate. - - .EXAMPLE - Assert-LabValidIpAddress -IpAddress '192.168.123.44' - Does not throw an exception and returns '192.168.123.44'. - - .EXAMPLE - Assert-LabValidIpAddress -IpAddress '192.168.123.4432' - Throws an exception. - - .OUTPUTS - The IP address if valid. -#> -function Assert-LabValidIpAddress -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $IpAddress - ) - - $ip = [System.Net.IPAddress]::Any - if (-not [System.Net.IPAddress]::TryParse($IpAddress, [ref] $ip)) - { - $exceptionParameters = @{ - errorId = 'IPAddressError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.IPAddressError -f $IpAddress) - } - New-LabException @exceptionParameters - } - return $ip -} diff --git a/src/lib/private/ConvertTo-LabAbsolutePath.ps1 b/src/lib/private/ConvertTo-LabAbsolutePath.ps1 deleted file mode 100644 index 2b446007..00000000 --- a/src/lib/private/ConvertTo-LabAbsolutePath.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -<# - .SYNOPSIS - Convert a path to be an absolute by adding it to the base path. - If the path is already absolute then it is just returned as is. - - .PARAMETER Path - The path to convert to an absolute path. - - .PARAMETER BasePath - The full path to the lab. - - .OUTPUTS - The path converted to an absolute path. -#> -function ConvertTo-LabAbsolutePath -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Path, - - [Parameter(Mandatory = $true)] - [System.String] - $BasePath - ) - - if (-not [System.IO.Path]::IsPathRooted($Path)) - { - $Path = Join-Path ` - -Path $BasePath ` - -ChildPath $Path - } # if - - return $Path -} diff --git a/src/lib/private/Copy-LabOdjFile.ps1 b/src/lib/private/Copy-LabOdjFile.ps1 deleted file mode 100644 index 6c0dc735..00000000 --- a/src/lib/private/Copy-LabOdjFile.ps1 +++ /dev/null @@ -1,149 +0,0 @@ -<# - .SYNOPSIS - Uploads Precreated ODJ files to Nano systems or others as required. - - .DESCRIPTION - This function will perform the following tasks: - 1. Connect to the VM via remoting. - 2. Upload the ODJ file to c:\windows\setup\ODJFiles folder of the VM. - If the ODJ file does not exist in the LabFiles folder for the VM then the - copy will not be performed. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .PARAMETER Timeout - The maximum amount of time that this function can take to perform the copy. - If the timeout is reached before the process is complete an error will be thrown. - The timeout defaults to 300 seconds. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Copy-LabOdjFile -Lab $Lab -VM $VMs[0] - - .OUTPUTS - None. -#> -function Copy-LabOdjFile -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [System.Int32] - $Timeout = 300 - ) - - $startTime = Get-Date - $session = $null - $complete = $false - $odjCopyComplete = $false - $odjFilename = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath "$($VM.ComputerName).txt" - - # If ODJ file does not exist then return - if (-not (Test-Path -Path $odjFilename)) - { - return - } # if - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - # Connect to the VM - $session = Connect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - # Failed to connnect to the VM - if (-not $session) - { - $exceptionParameters = @{ - errorId = 'ODJCopyError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.ODJCopyError ` - -f $VM.Name,$odjFilename) - } - New-LabException @exceptionParameters - return - } # if - - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $odjCopyComplete)) - { - $CopyParameters = @{ - Destination = 'C:\Windows\Setup\ODJFiles\' - ToSession = $session - Force = $true - ErrorAction = 'Stop' - } - - # Connection has been made OK, upload the ODJ files - while ((-not $odjCopyComplete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - try - { - Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMMessage ` - -f $VM.Name,'ODJ') - - Copy-Item ` - @CopyParameters ` - -Path (Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath "$($VM.ComputerName).txt") ` - -Verbose - $odjCopyComplete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` - -f $VM.Name,'ODJ',$Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # try - } # while - } # if - - # If the copy didn't complete and we're out of time throw an exception - if ((-not $odjCopyComplete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $exceptionParameters = @{ - errorId = 'ODJCopyError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.ODJCopyError ` - -f $VM.Name,$odjFilename) - } - New-LabException @exceptionParameters - } # if - - - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $complete = $true - } # while -} diff --git a/src/lib/private/Enable-LabWSMan.ps1 b/src/lib/private/Enable-LabWSMan.ps1 deleted file mode 100644 index 44e1c45b..00000000 --- a/src/lib/private/Enable-LabWSMan.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -<# - .SYNOPSIS - Ensures the WS-Man is configured on this system. - - .DESCRIPTION - If WS-Man is not enabled on this system it will be enabled. - This is required to communicate with the managed Lab Virtual Machines. - - .EXAMPLE - Enable-LabWSMan - Enables WS-Man on this machine. - - .OUTPUTS - None -#> -function Enable-LabWSMan -{ - [CmdLetBinding()] - param - ( - [Parameter()] - [Switch] - $Force - ) - - if (-not (Get-PSPRovider -PSProvider WSMan -ErrorAction SilentlyContinue)) - { - Write-LabMessage -Message ($LocalizedData.EnablingWSManMessage) - - try - { - Start-Service -Name WinRm -ErrorAction Stop - } - catch - { - $null = Enable-PSRemoting ` - @PSBoundParameters ` - -SkipNetworkProfileCheck ` - -ErrorAction Stop - } - - # Check WS-Man was enabled - if (-not (Get-PSProvider -PSProvider WSMan -ErrorAction SilentlyContinue)) - { - $exceptionParameters = @{ - errorId = 'WSManNotEnabledError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.WSManNotEnabledError) - } - New-LabException @exceptionParameters - } # if - } # if - - # Make sure the WinRM service is running - if ((Get-Service -Name WinRM).Status -ne 'Running') - { - try - { - Start-Service -Name WinRm -ErrorAction Stop - } - catch - { - $exceptionParameters = @{ - errorId = 'WinRMServiceFailedToStartError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.WinRMServiceFailedToStartError) - } - New-LabException @exceptionParameters - } - } -} diff --git a/src/lib/private/Get-LabBuilderModulePath.ps1 b/src/lib/private/Get-LabBuilderModulePath.ps1 deleted file mode 100644 index e0127175..00000000 --- a/src/lib/private/Get-LabBuilderModulePath.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -<# - .SYNOPSIS - Returns the path of the currently loaded LabBuilder module. - - .OUTPUTS - The path to the currently loaded LabBuilder module. -#> -function Get-LabBuilderModulePath -{ - [CmdLetBinding()] - [OutputType([System.String])] - param () - - return $script:LabBuidlerModuleRoot -} diff --git a/src/lib/private/Get-LabCertificatePsFileContent.ps1 b/src/lib/private/Get-LabCertificatePsFileContent.ps1 deleted file mode 100644 index 1601f983..00000000 --- a/src/lib/private/Get-LabCertificatePsFileContent.ps1 +++ /dev/null @@ -1,119 +0,0 @@ -<# - .SYNOPSIS - Assemble the the PowerShell commands required to create a self-signed certificate. - - .DESCRIPTION - This function creates the content that can be written into a PS1 file to create a self-signed - certificate. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - $CertificatePS = Get-LabCertificatePsFileContent -Lab $Lab -VM $VMs[0] - Return the Create Self-Signed Certificate script for the first VM in the - Lab c:\mylab\config.xml for DSC configuration. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .PARAMETER CertificateSource - A CertificateSource to use instead of the one contained in the VM. - - .OUTPUTS - A string containing the Create Self-Signed Certificate PowerShell code. -#> -function Get-LabCertificatePsFileContent -{ - [CmdLetBinding()] - [OutputType([System.String])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [Parameter()] - [LabCertificateSource] - $CertificateSource - ) - - # If a CertificateSource is not provided get it from the VM. - if (-not $CertificateSource) - { - $CertificateSource = $VM.CertificateSource - } # if - - if ($CertificateSource -eq [LabCertificateSource]::Guest) - { - $createCertificatePs = @" -`$CertificateFriendlyName = '$($Script:DSCCertificateFriendlyName)' -`$Cert = Get-ChildItem -Path cert:\LocalMachine\My `` - | Where-Object { `$_.FriendlyName -eq `$CertificateFriendlyName } `` - | Select-Object -First 1 -if (-not `$Cert) -{ - . `"`$(`$ENV:SystemRoot)\Setup\Scripts\New-SelfSignedCertificateEx.ps1`" - New-SelfsignedCertificateEx `` - -Subject 'CN=$($VM.ComputerName)' `` - -EKU '1.3.6.1.4.1.311.80.1','1.3.6.1.5.5.7.3.1','1.3.6.1.5.5.7.3.2' `` - -KeyUsage 'DigitalSignature, KeyEncipherment, DataEncipherment' `` - -SAN '$($VM.ComputerName)' `` - -FriendlyName `$CertificateFriendlyName `` - -Exportable `` - -StoreLocation 'LocalMachine' `` - -StoreName 'My' `` - -KeyLength $($Script:SelfSignedCertKeyLength) `` - -ProviderName '$($Script:SelfSignedCertProviderName)' `` - -AlgorithmName $($Script:SelfSignedCertAlgorithmName) `` - -SignatureAlgorithm $($Script:SelfSignedCertSignatureAlgorithm) - # There is a slight delay before new cert shows up in Cert: - # So wait for it to show. - While (-not `$Cert) - { - `$Cert = Get-ChildItem -Path cert:\LocalMachine\My `` - | Where-Object { `$_.FriendlyName -eq `$CertificateFriendlyName } - } -} -Export-Certificate `` - -Type CERT `` - -Cert `$Cert `` - -FilePath `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionCert`" -Add-Content `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'Encryption Certificate Imported from CER ...' `` - -Encoding Ascii -"@ - } - else - { - [System.String] $createCertificatePs = @" -if (Test-Path -Path `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`") -{ - `$CertificatePassword = ConvertTo-SecureString `` - -String '$Script:DSCCertificatePassword' `` - -Force `` - -AsPlainText - Add-Content `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'Importing Encryption Certificate from PFX ...' `` - -Encoding Ascii - Import-PfxCertificate `` - -Password '$Script:DSCCertificatePassword' `` - -FilePath `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`" `` - -CertStoreLocation cert:\localMachine\root - Add-Content `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'Encryption Certificate from PFX Imported...' `` - -Encoding Ascii -} -"@ - } # if - - return $createCertificatePs -} diff --git a/src/lib/private/Get-LabDSCNetworkingConfig.ps1 b/src/lib/private/Get-LabDSCNetworkingConfig.ps1 deleted file mode 100644 index 6f67ee96..00000000 --- a/src/lib/private/Get-LabDSCNetworkingConfig.ps1 +++ /dev/null @@ -1,201 +0,0 @@ -<# - .SYNOPSIS - Assemble the content of the Networking DSC config file. - - .DESCRIPTION - This function creates the content that will be written to the Networking DSC Config file - from the networking details stored in the VM object. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - $NetworkingDsc = Get-LabDSCNetworkingConfig -Lab $Lab -VM $VMs[0] - Return the Networking DSC for the first VM in the Lab c:\mylab\config.xml for DSC configuration. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .OUTPUTS - A string containing the DSC Networking config. -#> -function Get-LabDSCNetworkingConfig -{ - [CmdLetBinding()] - [OutputType([System.String])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - $NetworkingDscVersion = (` - Get-Module -Name NetworkingDsc -ListAvailable ` - | Sort-Object version -Descending ` - | Select-Object -First 1 ` - ).Version.ToString() - - $dscNetworkingConfig = @" -Configuration Networking { - Import-DscResource -ModuleName NetworkingDsc -ModuleVersion $NetworkingDscVersion - -"@ - $adapterCount = 0 - - foreach ($Adapter in $VM.Adapters) - { - $adapterCount++ - - if ($adapter.IPv4) - { - if (-not [System.String]::IsNullOrWhitespace($adapter.IPv4.Address)) - { - $dscNetworkingConfig += @" - IPAddress IPv4_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv4' - IPAddress = '$($adapter.IPv4.Address.Replace(',',"','"))/$($adapter.IPv4.SubnetMask)' - } - -"@ - if (-not [System.String]::IsNullOrWhitespace($adapter.IPv4.DefaultGateway)) - { - $dscNetworkingConfig += @" - DefaultGatewayAddress IPv4G_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv4' - Address = '$($adapter.IPv4.DefaultGateway)' - } - -"@ - } - else - { - $dscNetworkingConfig += @" - DefaultGatewayAddress IPv4G_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv4' - } - -"@ - } # if - } - else - { - $dscNetworkingConfig += @" - NetIPInterface IPv4DHCP_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv4' - Dhcp = 'Enabled' - } - -"@ - - } # if - - if (-not [System.String]::IsNullOrWhitespace($adapter.IPv4.DNSServer)) - { - $dscNetworkingConfig += @" - DnsServerAddress IPv4D_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv4' - Address = '$($adapter.IPv4.DNSServer.Replace(',',"','"))' - } - -"@ - } # if - } # if - - if ($adapter.IPv6) - { - if (-not [System.String]::IsNullOrWhitespace($adapter.IPv6.Address)) - { - $dscNetworkingConfig += @" - IPAddress IPv6_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv6' - IPAddress = '$($adapter.IPv6.Address.Replace(',',"','"))/$($adapter.IPv6.SubnetMask)' - } - -"@ - if (-not [System.String]::IsNullOrWhitespace($adapter.IPv6.DefaultGateway)) - { - $dscNetworkingConfig += @" - DefaultGatewayAddress IPv6G_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv6' - Address = '$($adapter.IPv6.DefaultGateway)' - } - -"@ - } - else - { - $dscNetworkingConfig += @" - DefaultGatewayAddress IPv6G_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv6' - } - -"@ - } # if - } - else - { - $dscNetworkingConfig += @" - NetIPInterface IPv6DHCP_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv6' - Dhcp = 'Enabled' - } - -"@ - - } # if - - if (-not [System.String]::IsNullOrWhitespace($adapter.IPv6.DNSServer)) - { - $dscNetworkingConfig += @" - DnsServerAddress IPv6D_$adapterCount { - InterfaceAlias = '$($adapter.Name)' - AddressFamily = 'IPv6' - Address = '$($adapter.IPv6.DNSServer.Replace(',',"','"))' - } - -"@ - } # if - } # if - } # endfor - - $dscNetworkingConfig += @" -} -"@ - - return $dscNetworkingConfig -} # Get-LabDSCNetworkingConfig - -[DSCLocalConfigurationManager()] -Configuration ConfigLCM { - param ( - [System.String] $ComputerName, - [System.String] $Thumbprint - ) - Node $ComputerName { - Settings - { - RefreshMode = 'Push' - ConfigurationMode = 'ApplyAndAutoCorrect' - CertificateId = $Thumbprint - ConfigurationModeFrequencyMins = 15 - RefreshFrequencyMins = 30 - RebootNodeIfNeeded = $true - ActionAfterReboot = 'ContinueConfiguration' - } - } -} diff --git a/src/lib/private/Get-LabIntegrationServiceName.ps1 b/src/lib/private/Get-LabIntegrationServiceName.ps1 deleted file mode 100644 index dad9421b..00000000 --- a/src/lib/private/Get-LabIntegrationServiceName.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -<# - .SYNOPSIS - Get list of Integration Service names (localized) - - .DESCRIPTION - This cmdlet will get the list of Integration services available on a Hyper-V host. - The list of Integration Services will contain the localized names. - - .EXAMPLE - Get-LabIntegrationServiceName - - .OUTPUTS - An array of localized Integration Serivce names. -#> -function Get-LabIntegrationServiceName -{ - [CmdLetBinding()] - param - ( - ) - - $captions = @() - $classes = @( - 'Msvm_VssComponentSettingData' - 'Msvm_ShutdownComponentSettingData' - 'Msvm_TimeSyncComponentSettingData' - 'Msvm_HeartbeatComponentSettingData' - 'Msvm_GuestServiceInterfaceComponentSettingData' - 'Msvm_KvpExchangeComponentSettingData' - ) - - <# - This Integration Service is registered in CIM but is not exposed in Hyper-V: - 'Msvm_RdvComponentSettingData' - #> - - foreach ($class in $classes) - { - $captions += (Get-CimInstance ` - -Class $class ` - -Namespace Root\Virtualization\V2 ` - -Property Caption | Select-Object -First 1).Caption - } # foreach - - return $captions -} diff --git a/src/lib/private/Get-LabManagementSwitchName.ps1 b/src/lib/private/Get-LabManagementSwitchName.ps1 deleted file mode 100644 index bbfacd07..00000000 --- a/src/lib/private/Get-LabManagementSwitchName.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -<# - .SYNOPSIS - Returns the name of the Management Switch to use for this lab. - - .DESCRIPTION - Each lab has a unique private management switch created for it. - All Virtual Machines in the Lab are connected to the switch. - This function returns the name of this swtich for the provided - lab configuration. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $ManagementSwitch = Get-LabManagementSwitchName -Lab $Lab - Returns the Management Switch for the Lab c:\mylab\config.xml. - - .OUTPUTS - A management switch name. -#> -function Get-LabManagementSwitchName -{ - [CmdLetBinding()] - [OutputType([System.String])] - param - ( - [Parameter(Mandatory = $true)] - $Lab - ) - - $LabId = $Lab.labbuilderconfig.settings.labid - - if (-not $LabId) - { - $LabId = $Lab.labbuilderconfig.name - } # if - - $managementSwitchName = ('{0}Lab Management' -f $LabId) - - return $managementSwitchName -} diff --git a/src/lib/private/Get-LabModulesInDSCConfig.ps1 b/src/lib/private/Get-LabModulesInDSCConfig.ps1 deleted file mode 100644 index 62a4f302..00000000 --- a/src/lib/private/Get-LabModulesInDSCConfig.ps1 +++ /dev/null @@ -1,82 +0,0 @@ -<# - .SYNOPSIS - Get a list of all Resources imported in a DSC Config - - .DESCRIPTION - Uses RegEx to pull a list of Resources that are imported in a DSC Configuration using the - Import-DSCResource cmdlet. - - If The -ModuleVersion parameter is included then the ModuleVersion property in the returned - LabDSCModule object will be set, otherwise it will be null. - - .PARAMETER DscConfigFile - Contains the path to the DSC Config file to extract resource module names from. - - .PARAMETER DscConfigContent - Contains the content of the DSC Config to extract resource module names from. - - .EXAMPLE - Get-LabModulesInDSCConfig -DscConfigFile c:\mydsc\Server01.ps1 - Return the DSC Resource module list from file c:\mydsc\server01.ps1 - - .EXAMPLE - Get-LabModulesInDSCConfig -DscConfigContent $DSCConfig - Return the DSC Resource module list from the DSC Config in $DSCConfig. - - .OUTPUTS - An array of LabDSCModule objects containing the DSC Resource modules required by this DSC - configuration file. -#> -function Get-LabModulesInDSCConfig -{ - [CmdLetBinding(DefaultParameterSetName = "Content")] - [OutputType([Object[]])] - param - ( - [parameter( - Position = 1, - ParameterSetName = "Content", - Mandatory = $true)] - [System.String] - $DscConfigContent, - - [parameter( - Position = 2, - ParameterSetName = "File", - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $DscConfigFile - ) - - [LabDSCModule[]] $modules = $null - - if ($PSCmdlet.ParameterSetName -eq 'File') - { - $DscConfigContent = Get-Content -Path $DscConfigFile -Raw - } # if - - $regex = "[ \t]*?Import\-DscResource[ \t]+(?:\-ModuleName[ \t])?'?`"?([A-Za-z0-9._-]+)`"?'?(([ \t]+-ModuleVersion)?[ \t]+'?`"?([0-9.]+)`"?`?)?[ \t]*?[\r\n]+?" - $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') - - foreach ($moduleMatch in $moduleMatches) - { - $moduleName = $moduleMatch.Groups[1].Value - $moduleVersion = $moduleMatch.Groups[4].Value - # Make sure this module isn't already in the list - - if ($moduleName -notin $Modules.ModuleName) - { - $module = [LabDSCModule]::New($moduleName) - - if (-not [System.String]::IsNullOrWhitespace($moduleVersion)) - { - $module.moduleVersion = [Version] $moduleVersion - } # if - - $modules += @( $module ) - } # if - } # foreach - - return $modules -} diff --git a/src/lib/private/Get-LabNextIpAddress.ps1 b/src/lib/private/Get-LabNextIpAddress.ps1 deleted file mode 100644 index 0fc50019..00000000 --- a/src/lib/private/Get-LabNextIpAddress.ps1 +++ /dev/null @@ -1,61 +0,0 @@ -<# - .SYNOPSIS - Increases the IP Address. - - .PARAMETER IpAddress - Contains the IP Address to increase. - - .PARAMETER Step - Contains the number of steps to increase the IP address by. - - .EXAMPLE - Get-LabNextIpAddress -IpAddress '192.168.123.44' -Step 2 - Returns the IP Address '192.168.123.44' - - .EXAMPLE - Get-LabNextIpAddress -IpAddress 'fe80::15b4:b934:5d23:1a2f' -Step 2 - Returns the IP Address 'fe80::15b4:b934:5d23:1a31' - - .OUTPUTS - The increased IP Address. -#> -function Get-LabNextIpAddress -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $IpAddress, - - [Parameter()] - [System.Byte] - $Step = 1 - ) - - # Check the IP Address is valid - $ip = Assert-LabValidIpAddress -IpAddress $IpAddress - - # This code will increase the next IP address by the step amount. - # It uses the IP Address byte array to do this. - $bytes = $ip.GetAddressBytes() - $position = $bytes.Length - 1 - - while ($Step -gt 0) - { - if ($bytes[$position] + $Step -gt 255) - { - $bytes[$position] = $bytes[$position] + $Step - 256 - $Step = $Step - $bytes[$position] - $position-- - } - else - { - $bytes[$position] = $bytes[$position] + $Step - $Step = 0 - } # if - } # while - - return [System.Net.IPAddress]::new($bytes).IPAddressToString -} diff --git a/src/lib/private/Get-LabNextMacAddress.ps1 b/src/lib/private/Get-LabNextMacAddress.ps1 deleted file mode 100644 index 7383f42f..00000000 --- a/src/lib/private/Get-LabNextMacAddress.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -<# - .SYNOPSIS - Increases the MAC Address. - - .PARAMETER MACAddress - Contains the MAC Address to increase. - - .PARAMETER Step - Contains the number of steps to increase the MAC address by. - - .EXAMPLE - Get-NextMacAddress -MacAddress '00155D0106ED' -Step 2 - Returns the MAC Address '00155D0106EF' - - .OUTPUTS - The increased MAC Address. -#> -function Get-NextMacAddress -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $MacAddress, - - [System.Byte] - $Step = 1 - ) - - return [System.String]::Format("{0:X}", [Convert]::ToUInt64($MACAddress, 16) + $Step).PadLeft(12, '0') -} diff --git a/src/lib/private/Get-LabUnattendFileContent.ps1 b/src/lib/private/Get-LabUnattendFileContent.ps1 deleted file mode 100644 index c6135864..00000000 --- a/src/lib/private/Get-LabUnattendFileContent.ps1 +++ /dev/null @@ -1,169 +0,0 @@ -<# - .SYNOPSIS - Assembles the content of a Unattend XML file that should be used to initialize - Windows on the specified VM. - - .DESCRIPTION - This function will return the content of a standard Windows Unattend XML file - that can be written to an VHD containing a copy of Windows that is still in - OOBE mode. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Get-LabUnattendFileContent -Lab $Lab -VM $VMs[0] - Returns the content of the Unattend File for the first VM in the Lab c:\mylab\config.xml. - - .OUTPUTS - The content of the Unattend File for the VM. -#> -function Get-LabUnattendFileContent -{ - [CmdLetBinding()] - [OutputType([System.String])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - if ($VM.UnattendFile) - { - $unattendContent = Get-Content -Path $VM.UnattendFile - } - else - { - $domainName = $Lab.labbuilderconfig.settings.domainname - $email = $Lab.labbuilderconfig.settings.email - $unattendContent = @" -<?xml version="1.0" encoding="utf-8"?> -<unattend xmlns="urn:schemas-microsoft-com:unattend"> - <settings pass="offlineServicing"> - <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <EnableLUA>false</EnableLUA> - </component> - </settings> - <settings pass="generalize"> - <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <SkipRearm>1</SkipRearm> - </component> - </settings> - <settings pass="specialize"> - <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <InputLocale>0409:00000409</InputLocale> - <SystemLocale>en-US</SystemLocale> - <UILanguage>en-US</UILanguage> - <UILanguageFallback>en-US</UILanguageFallback> - <UserLocale>en-US</UserLocale> - </component> - <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <SkipAutoActivation>true</SkipAutoActivation> - </component> - <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <CEIPEnabled>0</CEIPEnabled> - </component> - <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <ComputerName>$($VM.ComputerName)</ComputerName> - </component> - -"@ - - - if ($VM.OSType -eq [LabOSType]::Client) - { - $unattendContent += @" - <component name="Microsoft-Windows-Deployment" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <RunSynchronous> - <RunSynchronousCommand wcm:action="add"> - <Order>1</Order> - <Path>net user administrator /active:yes</Path> - </RunSynchronousCommand> - <RunSynchronousCommand wcm:action="add"> - <Order>2</Order> - <Path>powershell.exe -Command "Enable-PSRemoting -SkipNetworkProfileCheck -Force"</Path> - </RunSynchronousCommand> - </RunSynchronous> - </component> - -"@ - } # If - - $unattendContent += @" - </settings> - <settings pass="oobeSystem"> - <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> -"@ - if ($VM.OSType -eq [LabOSType]::Client) - { - $unattendContent += @" - <AutoLogon> - <Password> - <Value>$($VM.AdministratorPassword)</Value> - <PlainText>true</PlainText> - </Password> - <Username>Administrator</Username> - <Enabled>true</Enabled> - <LogonCount>2</LogonCount> - </AutoLogon> - <FirstLogonCommands> - <SynchronousCommand wcm:action="add"> - <CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> - <Description>Set Execution Policy 64 Bit</Description> - <Order>1</Order> - <RequiresUserInput>true</RequiresUserInput> - </SynchronousCommand> - <SynchronousCommand wcm:action="add"> - <CommandLine>C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> - <Description>Set Execution Policy 32 Bit</Description> - <Order>2</Order> - <RequiresUserInput>true</RequiresUserInput> - </SynchronousCommand> - </FirstLogonCommands> - -"@ -} # If -$unattendContent += @" - <OOBE> - <HideEULAPage>true</HideEULAPage> - <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> - <HideOnlineAccountScreens>true</HideOnlineAccountScreens> - <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> - <NetworkLocation>Work</NetworkLocation> - <ProtectYourPC>1</ProtectYourPC> - <SkipUserOOBE>true</SkipUserOOBE> - <SkipMachineOOBE>true</SkipMachineOOBE> - </OOBE> - <UserAccounts> - <AdministratorPassword> - <Value>$($VM.AdministratorPassword)</Value> - <PlainText>true</PlainText> - </AdministratorPassword> - </UserAccounts> - <RegisteredOrganization>$($domainName)</RegisteredOrganization> - <RegisteredOwner>$($email)</RegisteredOwner> - <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> - <TimeZone>$($VM.TimeZone)</TimeZone> - </component> - <component name="Microsoft-Windows-ehome-reg-inf" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <RestartEnabled>true</RestartEnabled> - </component> - <component name="Microsoft-Windows-ehome-reg-inf" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <RestartEnabled>true</RestartEnabled> - </component> - </settings> -</unattend> -"@ - } - - return $unattendContent -} diff --git a/src/lib/private/Get-LabVMManagementIPAddress.ps1 b/src/lib/private/Get-LabVMManagementIPAddress.ps1 deleted file mode 100644 index c99ae1ae..00000000 --- a/src/lib/private/Get-LabVMManagementIPAddress.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -<# - .SYNOPSIS - Gets the Management IP Address for a running Lab VM. - - .DESCRIPTION - This function will return the IPv4 address assigned to the network adapter that - is connected to the Management switch for the specified VM. The VM must be - running, otherwise an error will be thrown. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - $IPAddress = Get-LabVMManagementIPAddress -Lab $Lab -VM $VM[0] - - .OUTPUTS - The IP Managment IP Address. -#> -function Get-LabVMManagementIPAddress -{ - [CmdLetBinding()] - [OutputType([System.String])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - $managementSwitchName = Get-LabManagementSwitchName -Lab $Lab - $managementAdapter = Get-VMNetworkAdapter -VMName $VM.Name | - Where-Object -Property SwitchName -EQ -Value $managementSwitchName - $managementAdapterIpAddresses = $managementAdapter.IPAddresses - $managementAdapterIpAddress = $managementAdapterIpAddresses | - Where-Object -FilterScript { - $_.Contains('.') - } - - if (-not $managementAdapterIpAddress) { - $exceptionParameters = @{ - errorId = 'ManagmentIPAddressError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ManagmentIPAddressError ` - -f $managementSwitchName,$VM.Name) - } - New-LabException @exceptionParameters - } # if - - return $managementAdapterIpAddress -} diff --git a/src/lib/private/Initialize-LabBootVHD.ps1 b/src/lib/private/Initialize-LabBootVHD.ps1 deleted file mode 100644 index ca4bce72..00000000 --- a/src/lib/private/Initialize-LabBootVHD.ps1 +++ /dev/null @@ -1,312 +0,0 @@ -<# - .SYNOPSIS - Initialized a VM VHD for first boot by applying any required files to the image. - - .DESCRIPTION - This function mounts a VM boot VHD image and applies the following files from the - LabBuilder Files folder to it: - 1. Unattend.xml - a Windows Unattend.xml file. - 2. SetupComplete.cmd - the command file that gets run after the Windows OOBE is complete. - 3. SetupComplete.ps1 - this PowerShell script file that is run at the the end of the - SetupComplete.cmd. - The files should have already been prepared by the New-LabVMInitializationFile function. - The VM VHD image should contain an installed copy of Windows still in OOBE mode. - - This function also applies optional MSU package files from the Lab resource folder if - specified in the packages list in the VM. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A VMLab object pulled from the Lab Configuration file using Get-LabVM. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Initialize-LabBootVHD ` - -Lab $Lab ` - -VM $VMs[0] ` - -VMBootDiskPath $BootVHD[0] - Prepare the boot VHD in for the first VM in the Lab c:\mylab\config.xml for initial boot. - - .OUTPUTS - None. -#> -function Initialize-LabBootVHD -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [Parameter(Mandatory = $true)] - [System.String] - $VMBootDiskPath - ) - - # Get path to Lab - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - # Get Path to LabBuilder files - [System.String] $VMLabBuilderFiles = $VM.LabBuilderFilesPath - - # Mount the VMs Boot VHD so that files can be loaded into it - Write-LabMessage -Message $($LocalizedData.MountingVMBootDiskMessage ` - -f $VM.Name,$VMBootDiskPath) - - # Create a mount point for mounting the Boot VHD - [System.String] $MountPoint = Join-Path ` - -Path $VMLabBuilderFiles ` - -ChildPath 'Mount' - - if (-not (Test-Path -Path $MountPoint -PathType Container)) - { - $null = New-Item ` - -Path $MountPoint ` - -ItemType Directory - } - - # Mount the VHD to the Mount point - $null = Mount-WindowsImage ` - -ImagePath $VMBootDiskPath ` - -Path $MountPoint ` - -Index 1 - - try - { - $Packages = $VM.Packages - if ($VM.OSType -eq [LabOSType]::Nano) - { - # Now specify the Nano Server packages to add. - [System.String] $NanoPackagesFolder = Join-Path ` - -Path $LabPath ` - -ChildPath 'NanoServerPackages' - if (-not (Test-Path -Path $NanoPackagesFolder)) - { - $exceptionParameters = @{ - errorId = 'NanoServerPackagesFolderMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NanoServerPackagesFolderMissingError ` - -f $NanoPackagesFolder) - } - New-LabException @exceptionParameters - } - # Add DSC Package to packages list if missing - if ([System.String]::IsNullOrWhitespace($Packages)) - { - $Packages = 'Microsoft-NanoServer-DSC-Package.cab' - } - else - { - if (@($Packages -split ',') -notcontains 'Microsoft-NanoServer-DSC-Package.cab') - { - $Packages = "$Packages,Microsoft-NanoServer-DSC-Package.cab" - } # if - } # if - } # if - - # Apply any listed packages to the Image - if (-not [System.String]::IsNullOrWhitespace($Packages)) - { - # Get the list of Lab Resource MSUs - $ResourceMSUs = Get-LabResourceMSU ` - -Lab $Lab - - foreach ($Package in @($Packages -split ',')) - { - if (([System.IO.Path]::GetExtension($Package) -eq '.cab') ` - -and ($VM.OSType -eq [LabOSType]::Nano)) - { - # This is a Nano Server .CAB package - # Generate the path to the Nano Package - $PackagePath = Join-Path ` - -Path $NanoPackagesFolder ` - -ChildPath $Package - - # Does it exist? - if (-not (Test-Path -Path $PackagePath)) - { - $exceptionParameters = @{ - errorId = 'NanoPackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NanoPackageNotFoundError ` - -f $PackagePath) - } - New-LabException @exceptionParameters - } - - # Add the package - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,$Package,$PackagePath) - - $null = Add-WindowsPackage ` - -PackagePath $PackagePath ` - -Path $MountPoint - - # Generate the path to the Nano Language Package - $PackageLangFile = $Package -replace '.cab',"_$($Script:NanoPackageCulture).cab" - $PackageLangFile = Join-Path ` - -Path $NanoPackagesFolder ` - -ChildPath "$($Script:NanoPackageCulture)\$PackageLangFile" - - # Does it exist? - if (-not (Test-Path -Path $PackageLangFile)) - { - $exceptionParameters = @{ - errorId = 'NanoPackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NanoPackageNotFoundError ` - -f $PackageLangFile) - } - New-LabException @exceptionParameters - } - - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,$Package,$PackageLangFile) - - # Add the package - $null = Add-WindowsPackage ` - -PackagePath $PackageLangFile ` - -Path $MountPoint - } - else - { - # Tihs is a ResourceMSU type package - [System.Boolean] $Found = $false - foreach ($ResourceMSU in $ResourceMSUs) - { - if ($ResourceMSU.Name -eq $Package) - { - # Found the package - $Found = $true - break - } # if - } # foreach - if (-not $Found) - { - $exceptionParameters = @{ - errorId = 'PackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageNotFoundError ` - -f $Package) - } - New-LabException @exceptionParameters - } # if - - $PackagePath = $ResourceMSU.Filename - if (-not (Test-Path -Path $PackagePath)) - { - $exceptionParameters = @{ - errorId = 'PackageMSUNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageMSUNotFoundError ` - -f $Package,$PackagePath) - } - New-LabException @exceptionParameters - } # if - # Apply a Package - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,$Package,$PackagePath) - - $null = Add-WindowsPackage ` - -PackagePath $PackagePath ` - -Path $MountPoint - } # if - } # foreach - } # if - } - catch - { - # Dismount Disk Image before throwing exception - Write-LabMessage -Message $($LocalizedData.DismountingVMBootDiskMessage ` - -f $VM.Name,$VMBootDiskPath) - $null = Dismount-WindowsImage -Path $MountPoint -Save - $null = Remove-Item -Path $MountPoint -Recurse -Force - - Throw $_ - } # try - - # Create the scripts folder where setup scripts will be put - $null = New-Item ` - -Path "$MountPoint\Windows\Setup\Scripts" ` - -ItemType Directory - - # Create the ODJ folder where Offline domain join files can be put - $null = New-Item ` - -Path "$MountPoint\Windows\Setup\ODJFiles" ` - -ItemType Directory - - # Apply an unattended setup file - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,'Unattend','Unattend.xml') - - if (-not (Test-Path -Path "$MountPoint\Windows\Panther" -PathType Container)) - { - Write-LabMessage -Message $($LocalizedData.CreatingVMBootDiskPantherFolderMessage ` - -f $VM.Name) - - $null = New-Item ` - -Path "$MountPoint\Windows\Panther" ` - -ItemType Directory - } # if - $null = Copy-Item ` - -Path (Join-Path -Path $VMLabBuilderFiles -ChildPath 'Unattend.xml') ` - -Destination "$MountPoint\Windows\Panther\Unattend.xml" ` - -Force - - # If a Certificate PFX file is available, copy it into the c:\Windows - # folder of the VM. - $CertificatePfxPath = Join-Path ` - -Path $VMLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionPfxCert - if (Test-Path -Path $CertificatePfxPath) - { - # Apply the CMD Setup Complete File - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,'Credential Certificate PFX',$Script:DSCEncryptionPfxCert) - $null = Copy-Item ` - -Path $CertificatePfxPath ` - -Destination "$MountPoint\Windows\$Script:DSCEncryptionPfxCert" ` - -Force - } - - # Apply the CMD Setup Complete File - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,'Setup Complete CMD','SetupComplete.cmd') - $null = Copy-Item ` - -Path (Join-Path -Path $VMLabBuilderFiles -ChildPath 'SetupComplete.cmd') ` - -Destination "$MountPoint\Windows\Setup\Scripts\SetupComplete.cmd" ` - -Force - - # Apply the PowerShell Setup Complete file - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,'Setup Complete PowerShell','SetupComplete.ps1') - $null = Copy-Item ` - -Path (Join-Path -Path $VMLabBuilderFiles -ChildPath 'SetupComplete.ps1') ` - -Destination "$MountPoint\Windows\Setup\Scripts\SetupComplete.ps1" ` - -Force - - # Apply the Certificate Generator script if not a Nano Server - if ($VM.OSType -ne [LabOSType]::Nano) - { - $CertGenFilename = Split-Path -Path $Script:SupportGertGenPath -Leaf - Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,'Certificate Create Script',$CertGenFilename) - $null = Copy-Item ` - -Path $Script:SupportGertGenPath ` - -Destination "$MountPoint\Windows\Setup\Scripts\"` - -Force - } - - # Dismount the VHD in preparation for boot - Write-LabMessage -Message $($LocalizedData.DismountingVMBootDiskMessage ` - -f $VM.Name,$VMBootDiskPath) - $null = Dismount-WindowsImage -Path $MountPoint -Save - $null = Remove-Item -Path $MountPoint -Recurse -Force -} diff --git a/src/lib/private/Initialize-LabDSC.ps1 b/src/lib/private/Initialize-LabDSC.ps1 deleted file mode 100644 index a5895f5a..00000000 --- a/src/lib/private/Initialize-LabDSC.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -<# - .SYNOPSIS - This function prepares all files require to configure a VM using Desired State - Configuration (DSC). - - .DESCRIPTION - Calling this function will cause the LabBuilder folder to be populated/updated - with all files required to configure a Virtual Machine with DSC. - This includes: - 1. Required DSC Resouce Modules. - 2. DSC Credential Encryption certificate. - 3. DSC Configuration files. - 4. DSC MOF Files for general config and for LCM config. - 5. Start up scripts. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Initialize-LabDSC -Lab $Lab -VM $VMs[0] - Prepares all files required to start up Desired State Configuration for the - first VM in the Lab c:\mylab\config.xml for DSC start up. - - .OUTPUTS - None. -#> -function Initialize-LabDSC -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - # Are there any DSC Settings to manage? - Update-LabDSC -Lab $Lab -VM $VM - - # Generate the DSC Start up Script file - Set-LabDSC -Lab $Lab -VM $VM -} diff --git a/src/lib/private/Initialize-LabManagementSwitch.ps1 b/src/lib/private/Initialize-LabManagementSwitch.ps1 deleted file mode 100644 index d2f51d17..00000000 --- a/src/lib/private/Initialize-LabManagementSwitch.ps1 +++ /dev/null @@ -1,91 +0,0 @@ -<# - .SYNOPSIS - Create the LabBuilder Management Network switch and assign VLAN - - .DESCRIPTION - Each lab needs a unique private management switch created for it. - All Virtual Machines in the Lab are connected to the switch. - This function creates the virtual switch and attaches an adapter - to it and assigns it to a VLAN. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - Initialize-LabManagementSwitch -Lab $Lab - Creates or updates the Management Switch for the Lab c:\mylab\config.xml. - - .OUTPUTS - None. -#> -function Initialize-LabManagementSwitch -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab - ) - - # Used by host to communicate with Lab VMs - $managementSwitchName = Get-LabManagementSwitchName -Lab $Lab - - Write-LabMessage -Message $($LocalizedData.InitializingLabManagementVirtualNetworkMesage ` - -f $managementSwitchName) - - if ($Lab.labbuilderconfig.switches.ManagementVlan) - { - $requiredManagementVlan = $Lab.labbuilderconfig.switches.ManagementVlan - } - else - { - $requiredManagementVlan = $Script:DefaultManagementVLan - } - - $managementSwitch = Get-VMSwitch | Where-Object -Property Name -eq $managementSwitchName - - if ($managementSwitch.Count -eq 0) - { - $null = New-VMSwitch ` - -SwitchType Internal ` - -Name $managementSwitchName ` - -ErrorAction Stop - - Write-LabMessage -Message $($LocalizedData.CreatingLabManagementSwitchMessage ` - -f $managementSwitchName, $requiredManagementVlan) - } - - # Check the Vlan ID of the adapter on the switch - $existingManagementAdapter = Get-VMNetworkAdapter ` - -ManagementOS ` - -Name $managementSwitchName ` - -SwitchName $managementSwitchName ` - -ErrorAction SilentlyContinue - - if ($null -eq $existingManagementAdapter) - { - $existingManagementAdapter = Add-VMNetworkAdapter ` - -ManagementOS ` - -Name $managementSwitchName ` - -SwitchName $managementSwitchName ` - -ErrorAction Stop - } - - $existingManagementAdapterVlan = Get-VMNetworkAdapterVlan ` - -VMNetworkAdapter $existingManagementAdapter - - $existingManagementVlan = $existingManagementAdapterVlan.AccessVlanId - - if ($existingManagementVlan -ne $requiredManagementVlan) - { - Write-LabMessage -Message $($LocalizedData.UpdatingLabManagementSwitchMessage ` - -f $managementSwitchName, $requiredManagementVlan) - - Set-VMNetworkAdapterVlan ` - -VMNetworkAdapter $existingManagementAdapter ` - -Access ` - -VlanId $requiredManagementVlan ` - -ErrorAction Stop - } -} diff --git a/src/lib/private/Initialize-LabVHD.ps1 b/src/lib/private/Initialize-LabVHD.ps1 deleted file mode 100644 index aa274303..00000000 --- a/src/lib/private/Initialize-LabVHD.ps1 +++ /dev/null @@ -1,331 +0,0 @@ -<# - .SYNOPSIS - This function mounts the VHDx passed and ensures it is OK to be written to. - - .DESCRIPTION - The function checks that the disk has been paritioned and that it contains - a volume that has been formatted. - - This function will work for the following situations: - 0. VHDx is not mounted. - 1. VHDx is not initialized and PartitionStyle is passed. - 2. VHDx is initialized but has 0 partitions and FileSystem is passed. - 3. VHDx has 1 partition but 0 volumes and FileSystem is passed. - 4. VHDx has 1 partition and 1 volume that is unformatted and FileSystem is passed. - 5. VHDx has 1 partition and 1 volume that is formatted. - - If the VHDx is any other state an exception will be thrown. - - If the FileSystemLabel passed is different to the current label then it will - be updated. - - This function will not change the File System and/or Partition Type on the VHDx - if it is different to the values provided. - - .PARAMETER Path - This is the path to the VHD/VHDx file to mount and initialize. - - .PARAMETER PartitionStyle - The Partition Style to set an uninitialized VHD/VHDx to. It can be MBR or GPT. - If it is not passed and the VHD is not initialized then an exception will be - thrown. - - .PARAMETER FileSystem - The File System to format the new parition with on an VHD/VHDx. It can be - FAT, FAT32, exFAT, NTFS, ReFS. - If it is not passed and the VHD does not contain any formatted volumes then - an exception will be thrown. - - .PARAMETER FileSystemLabel - This parameter will allow the File System Label of the disk to be changed to this - value. - - .PARAMETER DriveLetter - Setting this parameter to a drive letter that is not in use will cause the VHD - to be assigned to this drive letter. - - .PARAMETER AccessPath - Setting this parameter to an existing folder will cause the VHD to be assigned - to the AccessPath defined. The folder must already exist otherwise an exception - will be thrown. - - .EXAMPLE - Initialize-LabVHD -Path c:\VMs\Tools.VHDx -AccessPath c:\mount - The VHDx c:\VMs\Tools.VHDx will be mounted and and assigned to the c:\mount folder - if it is initialized and contains a formatted partition. - - .EXAMPLE - Initialize-LabVHD -Path c:\VMs\Tools.VHDx -PartitionStyle GPT -FileSystem NTFS - The VHDx c:\VMs\Tools.VHDx will be mounted and initialized with GPT if not already - initialized. It will also be partitioned and formatted with NTFS if no partitions - already exist. - - .EXAMPLE - Initialize-LabVHD ` - -Path c:\VMs\Tools.VHDx ` - -PartitionStyle GPT ` - -FileSystem NTFS ` - -FileSystemLabel ToolsDisk - -DriveLetter X - The VHDx c:\VMs\Tools.VHDx will be mounted and initialized with GPT if not already - initialized. It will also be partitioned and formatted with NTFS if no partitions - already exist. The File System label will also be set to ToolsDisk and the disk - will be mounted to X drive. - - .OUTPUTS - It will return the Volume object that can then be mounted to a Drive Letter - or path. -#> -function Initialize-LabVHD -{ - [OutputType([Microsoft.Management.Infrastructure.CimInstance])] - [CmdletBinding(DefaultParameterSetName = 'AssignDriveLetter')] - param - ( - [Parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Path, - - [Parameter()] - [LabPartitionStyle] - $PartitionStyle, - - [Parameter()] - [LabFileSystem] - $FileSystem, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $FileSystemLabel, - - [Parameter(ParameterSetName = 'DriveLetter')] - [ValidateNotNullOrEmpty()] - [System.String] - $DriveLetter, - - [Parameter(ParameterSetName = 'AccessPath')] - [ValidateNotNullOrEmpty()] - [System.String] - $AccessPath - ) - - # Check file exists - if (-not (Test-Path -Path $Path)) - { - $exceptionParameters = @{ - errorId = 'FileNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.FileNotFoundError ` - -f "VHD",$Path) - } - New-LabException @exceptionParameters - } # if - - # Check disk is not already mounted - $VHD = Get-VHD ` - -Path $Path - if (-not $VHD.Attached) - { - Write-LabMessage -Message ($LocalizedData.InitializeVHDMountingMessage ` - -f $Path) - - $null = Mount-VHD ` - -Path $Path ` - -ErrorAction Stop - $VHD = Get-VHD ` - -Path $Path - } # if - - # Check partition style - $DiskNumber = $VHD.DiskNumber - if ((Get-Disk -Number $DiskNumber).PartitionStyle -eq 'RAW') - { - if (-not $PartitionStyle) - { - $exceptionParameters = @{ - errorId = 'InitializeVHDNotInitializedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InitializeVHDNotInitializedError ` - -f $Path) - } - New-LabException @exceptionParameters - } # if - Write-LabMessage -Message ($LocalizedData.InitializeVHDInitializingMessage ` - -f $Path,$PartitionStyle) - - $null = Initialize-Disk ` - -Number $DiskNumber ` - -PartitionStyle $PartitionStyle ` - -ErrorAction Stop - } # if - - # Check for a partition that is not 'reserved' - $Partitions = @(Get-Partition ` - -DiskNumber $DiskNumber ` - -ErrorAction SilentlyContinue) - if (-not ($Partitions) ` - -or (($Partitions | Where-Object -Property Type -ne 'Reserved').Count -eq 0)) - { - Write-LabMessage -Message ($LocalizedData.InitializeVHDCreatePartitionMessage ` - -f $Path) - - $Partitions = @(New-Partition ` - -DiskNumber $DiskNumber ` - -UseMaximumSize ` - -ErrorAction Stop) - } # if - - # Find the best partition to work with - # This will usually be the one just created if it was - # Otherwise we'll try and match by FileSystem and then - # format and failing that the first partition. - foreach ($Partition in $Partitions) - { - $VolumeFileSystem = (Get-Volume ` - -Partition $Partition).FileSystem - if ($FileSystem) - { - if (-not [System.String]::IsNullOrWhitespace($VolumeFileSystem)) - { - # Found a formatted partition - $FoundFormattedPartition = $Partition - } # if - if ($FileSystem -eq $VolumeFileSystem) - { - # Found a parition with a matching file system - $FoundPartition = $Partition - break - } # if - } - else - { - if (-not [System.String]::IsNullOrWhitespace($VolumeFileSystem)) - { - # Found an formatted partition - $FoundFormattedPartition = $Partition - break - } # if - } # if - } # foreach - if ($FoundPartition) - { - # Use the formatted partition - $Partition = $FoundPartition - } - elseif ($FoundFormattedPartition) - { - # An unformatted partition was found - $Partition = $FoundFormattedPartition - } - else - { - # There are no formatted partitions so use the first one - $Partition = $Partitions[0] - } # if - - $PartitionNumber = $Partition.PartitionNumber - - # Check for volume - $Volume = Get-Volume ` - -Partition $Partition - - # Check for file system - if ([System.String]::IsNullOrWhitespace($Volume.FileSystem)) - { - # This volume is not formatted - if (-not $FileSystem) - { - # A File System wasn't specified so can't continue - $exceptionParameters = @{ - errorId = 'InitializeVHDNotFormattedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InitializeVHDNotFormattedError ` - -f $Path) - } - New-LabException @exceptionParameters - } - - # Format the volume - Write-LabMessage -Message ($LocalizedData.InitializeVHDFormatVolumeMessage ` - -f $Path,$FileSystem,$PartitionNumber) - $FormatProperties = @{ - InputObject = $Volume - FileSystem = $FileSystem - } - if ($FileSystemLabel) - { - $FormatProperties += @{ - NewFileSystemLabel = $FileSystemLabel - } - } - $Volume = Format-Volume ` - @FormatProperties ` - -ErrorAction Stop - } - else - { - # Check the File System Label - if (($FileSystemLabel) -and ` - ($Volume.FileSystemLabel -ne $FileSystemLabel)) - { - Write-LabMessage -Message ($LocalizedData.InitializeVHDSetLabelVolumeMessage ` - -f $Path,$FileSystemLabel) - $Volume = Set-Volume ` - -InputObject $Volume ` - -NewFileSystemLabel $FileSystemLabel ` - -ErrorAction Stop - } - } - - # Assign an access path or Drive letter - if ($DriveLetter -or $AccessPath) - { - switch ($PSCmdlet.ParameterSetName) - { - 'DriveLetter' - { - # Mount the partition to a Drive Letter - $null = Set-Partition ` - -DiskNumber $Disknumber ` - -PartitionNumber $PartitionNumber ` - -NewDriveLetter $DriveLetter ` - -ErrorAction Stop - - $Volume = Get-Volume ` - -Partition $Partition - - Write-LabMessage -Message ($LocalizedData.InitializeVHDDriveLetterMessage ` - -f $Path,$DriveLetter.ToUpper()) - } - 'AccessPath' - { - # Check the Access folder exists - if (-not (Test-Path -Path $AccessPath -Type Container)) - { - $exceptionParameters = @{ - errorId = 'InitializeVHDAccessPathNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InitializeVHDAccessPathNotFoundError ` - -f $Path,$AccessPath) - } - New-LabException @exceptionParameters - } - - # Add the Partition Access Path - $null = Add-PartitionAccessPath ` - -DiskNumber $DiskNumber ` - -PartitionNumber $partitionNumber ` - -AccessPath $AccessPath ` - -ErrorAction Stop - - Write-LabMessage -Message ($LocalizedData.InitializeVHDAccessPathMessage ` - -f $Path,$AccessPath) - } - } - } - - # Return the Volume to the pipeline - return $Volume -} diff --git a/src/lib/private/Initialize-LabVMPath.ps1 b/src/lib/private/Initialize-LabVMPath.ps1 deleted file mode 100644 index e844b9b1..00000000 --- a/src/lib/private/Initialize-LabVMPath.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -<# - .SYNOPSIS - Creates the folder structure that will contain a Lab Virtual Machine. - - .DESCRIPTION - Creates a standard Hyper-V Virtual Machine folder structure as well as additional folders - for containing configuration files for DSC. - - .PARAMETER vmpath - The path to the folder where the Virtual Machine files are stored. - - .EXAMPLE - Initialize-LabVMPath -VMPath 'c:\VMs\Lab\Virtual Machine 1' - The command will create the Virtual Machine structure for a Lab VM in the folder: - 'c:\VMs\Lab\Virtual Machine 1' - - .OUTPUTS - None. -#> -function Initialize-LabVMPath -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $VMPath - ) - - if (-not (Test-Path -Path $VMPath)) - { - $null = New-Item ` - -Path $VMPath ` - -ItemType Directory - } # if - - if (-not (Test-Path -Path "$VMPath\Virtual Machines")) - { - $null = New-Item ` - -Path "$VMPath\Virtual Machines" ` - -ItemType Directory - } # if - - if (-not (Test-Path -Path "$VMPath\Virtual Hard Disks")) - { - $null = New-Item ` - -Path "$VMPath\Virtual Hard Disks" ` - -ItemType Directory - } # if - - if (-not (Test-Path -Path "$VMPath\LabBuilder Files")) - { - $null = New-Item ` - -Path "$VMPath\LabBuilder Files" ` - -ItemType Directory - } # if - - if (-not (Test-Path -Path "$VMPath\LabBuilder Files\DSC Modules")) - { - $null = New-Item ` - -Path "$VMPath\LabBuilder Files\DSC Modules" ` - -ItemType Directory - } # if -} diff --git a/src/lib/private/Install-LabHyperV.ps1 b/src/lib/private/Install-LabHyperV.ps1 deleted file mode 100644 index 0cbbf89e..00000000 --- a/src/lib/private/Install-LabHyperV.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -<# - .SYNOPSIS - Ensures the Hyper-V features are installed onto the system. - - .DESCRIPTION - If the Hyper-V features are not installed onto this system they will be installed. - - .EXAMPLE - Install-LabHyperV - Installs the appropriate Hyper-V features if they are not currently installed. - - .OUTPUTS - None -#> -function Install-LabHyperV -{ - [CmdLetBinding()] - param - ( - ) - - # Install Hyper-V Components - if ((Get-CimInstance Win32_OperatingSystem).ProductType -eq 1) - { - # Desktop OS - [Array] $feature = Get-WindowsOptionalFeature -Online -FeatureName '*Hyper-V*' ` - | Where-Object -Property State -Eq 'Disabled' - if ($feature.Count -gt 0 ) - { - Write-LabMessage -Message ($LocalizedData.InstallingHyperVComponentsMesage ` - -f 'Desktop') - $feature.Foreach( { - Enable-WindowsOptionalFeature -Online -FeatureName $_.FeatureName - } ) - } - } - Else - { - # Server OS - [Array] $feature = Get-WindowsFeature -Name Hyper-V ` - | Where-Object -Property Installed -EQ $false - if ($feature.Count -gt 0 ) - { - Write-LabMessage -Message ($LocalizedData.InstallingHyperVComponentsMesage ` - -f 'Desktop') - $feature.Foreach( { - Install-WindowsFeature -IncludeAllSubFeature -IncludeManagementTools -Name $_.Name - } ) - } - } -} diff --git a/src/lib/private/Install-LabPackageProvider.ps1 b/src/lib/private/Install-LabPackageProvider.ps1 deleted file mode 100644 index c80e1ea8..00000000 --- a/src/lib/private/Install-LabPackageProvider.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -<# - .SYNOPSIS - Ensures the Package Providers required by LabBuilder are installed. - - .DESCRIPTION - This function will check that both the NuGet and the PowerShellGet package - providers are installed. - If either of them are missing the function will attempt to install them. - - .EXAMPLE - Install-LabPackageProvider - Ensures the required Package Providers for LabBuilder are installed. - - .OUTPUTS - None -#> -function Install-LabPackageProvider -{ - [CmdLetBinding(SupportsShouldProcess = $true, - ConfirmImpact = 'High')] - param - ( - [Parameter()] - [Switch] - $Force - ) - - $requiredPackageProviders = @('PowerShellGet', 'NuGet') - $currentPackageProviders = Get-PackageProvider ` - -ListAvailable ` - -ErrorAction Stop - - foreach ($requiredPackageProvider in $requiredPackageProviders) - { - $packageProvider = $currentPackageProviders | - Where-Object { $_.Name -eq $requiredPackageProvider } - - if (-not $packageProvider) - { - # The Package provider is not installed so install it - if ($Force -or $PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldInstallPackageProvider ` - -f $packageProvider ))) - { - Write-LabMessage -Message ($LocalizedData.InstallPackageProviderMessage ` - -f $requiredPackageProvider) - - $null = Install-PackageProvider ` - -Name $requiredPackageProvider ` - -ForceBootstrap ` - -Force ` - -ErrorAction Stop - } - else - { - # Can't continue if the package provider is not installed. - $exceptionParameters = @{ - errorId = 'PackageProviderNotInstalledError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageProviderNotInstalledError ` - -f $requiredPackageProvider) - } - New-LabException @exceptionParameters - } # if - } # if - } # foreach -} diff --git a/src/lib/private/Invoke-LabDownloadAndUnzipFile.ps1 b/src/lib/private/Invoke-LabDownloadAndUnzipFile.ps1 deleted file mode 100644 index 583e4a3d..00000000 --- a/src/lib/private/Invoke-LabDownloadAndUnzipFile.ps1 +++ /dev/null @@ -1,101 +0,0 @@ -<# - .SYNOPSIS - Download the a file to a folder and optionally unzip it. - - .DESCRIPTION - If the file is a zip file the file will be downloaded to a temporary - working folder and then unzipped to the destination, otherwise it - will be downloaded straight to the destination folder. -#> -function Invoke-LabDownloadAndUnzipFile -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $URL, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $DestinationPath - ) - - $fileName = [System.IO.Path]::GetFileName($URL) - - if (-not (Test-Path -Path $DestinationPath)) - { - $exceptionParameters = @{ - errorId = 'DownloadFolderDoesNotExistError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DownloadFolderDoesNotExistError ` - -f $DestinationPath, $fileName) - } - New-LabException @exceptionParameters - } - - $extension = [System.IO.Path]::GetExtension($fileName) - - if ($extension -eq '.zip') - { - # Download to a temp folder and unzip - $downloadPath = Join-Path -Path $Script:WorkingFolder -ChildPath $fileName - } - else - { - # Download to a temp folder and unzip - $downloadPath = Join-Path -Path $DestinationPath -ChildPath $fileName - } - - Write-LabMessage -Message ($LocalizedData.DownloadingFileMessage ` - -f $fileName, $URL, $downloadPath) - - try - { - Invoke-WebRequest ` - -Uri $URL ` - -OutFile $downloadPath ` - -ErrorAction Stop - } - catch - { - $exceptionParameters = @{ - errorId = 'FileDownloadError' - errorCategory = 'InvalidOperation' - errorMessage = $($LocalizedData.FileDownloadError -f $fileName, $URL, $_.Exception.Message) - } - New-LabException @exceptionParameters - } # try - - if ($extension -eq '.zip') - { - Write-LabMessage -Message ($LocalizedData.ExtractingFileMessage ` - -f $fileName, $downloadPath) - - # Extract this to the destination folder - try - { - Expand-Archive ` - -Path $downloadPath ` - -DestinationPath $DestinationPath ` - -Force ` - -ErrorAction Stop - } - catch - { - $exceptionParameters = @{ - errorId = 'FileExtractError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.FileExtractError -f $fileName, $_.Exception.Message) - } - New-LabException @exceptionParameters - } - finally - { - # Remove the downloaded zip file - Remove-Item -Path $downloadPath - } # try - } -} diff --git a/src/lib/private/Invoke-LabDownloadResourceModule.ps1 b/src/lib/private/Invoke-LabDownloadResourceModule.ps1 deleted file mode 100644 index 178c0351..00000000 --- a/src/lib/private/Invoke-LabDownloadResourceModule.ps1 +++ /dev/null @@ -1,189 +0,0 @@ -<# - .SYNOPSIS - Downloads a resource module. - - .DESCRIPTION - It will download a specific resource module, either from PowerShell Gallery - or from a URL if the module does not already exist. - - .PARAMETER Name - Contains the Name of the module to download. - - .PARAMETER URL - If this parameter is specified, the resource module will be downloaded from a URL - rather than via PowerShell Gallery. This is a the URL to use to download a zip - file containing this resource module. - - .PARAMETER Folder - If this resource module is downloaded using a URL, this is the folder in the zip - file that contains the resource and will need to be renamed to the name of the - resource. - - .PARAMETER RequiredVersion - This is the required version of the Resource Module that is required. - If this version is not installed the a new version will be downloaded. - - .PARAMETER MinimumVersion - This is the minimum version of the Resource Module that is required. - If at least this version is not installed then a new version will be downloaded. - - .EXAMPLE - Invoke-LabDownloadResourceModule ` - -Name NetworkingDsc ` - -RequiredVersion 2.7.0.0 - Downloads the Resource Module xNetowrking version 2.7.0.0 - - .OUTPUTS - None. -#> -function Invoke-LabDownloadResourceModule -{ - [CmdLetBinding()] - param - ( - [Parameter( - position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Name, - - [Parameter( - position = 2)] - [System.String] - $URL, - - [Parameter( - position = 3)] - [System.String] - $Folder, - - [Parameter( - position = 4)] - [System.String] - $RequiredVersion, - - [Parameter( - position = 5)] - [System.String] - $MinimumVersion - ) - - $installedModules = @(Get-Module -ListAvailable) - - # Determine a query that will be used to decide if the module is already installed - if ($RequiredVersion) - { - [ScriptBlock] $Query = { - ($_.Name -eq $Name) -and ($_.Version -eq $RequiredVersion) - } - - $versionMessage = $RequiredVersion - } - elseif ($MinimumVersion) - { - [ScriptBlock] $Query = { - ($_.Name -eq $Name) -and ($_.Version -ge $MinimumVersion) - } - - $versionMessage = "min ${MinimumVersion}" - } - else - { - [ScriptBlock] $Query = { - $_.Name -eq $Name - } - - $versionMessage = 'any version' - } - - # Is the module installed? - if ($installedModules.Where($Query).Count -eq 0) - { - Write-LabMessage -Message ($LocalizedData.ModuleNotInstalledMessage ` - -f $Name, $versionMessage) - - # If a URL was specified, download this module via HTTP - if ($URL) - { - # The module is not installed - so download it - # This is usually for downloading modules directly from github - Write-LabMessage -Message ($LocalizedData.DownloadingLabResourceWebMessage ` - -f $Name, $versionMessage, $URL) - - $modulesFolder = "$($ENV:ProgramFiles)\WindowsPowerShell\Modules\" - - Invoke-LabDownloadAndUnzipFile ` - -URL $URL ` - -DestinationPath $modulesFolder ` - -ErrorAction Stop - - if ($Folder) - { - # This zip file contains a folder that is not the name of the module so it must be - # renamed. This is usually the case with source downloaded directly from GitHub - $modulePath = Join-Path -Path $modulesFolder -ChildPath $Name - - if (Test-Path -Path $modulePath) - { - Remove-Item -Path $modulePath -Recurse -Force - } - - Rename-Item ` - -Path (Join-Path -Path $modulesFolder -ChildPath $Folder) ` - -NewName $Name ` - -Force - } # if - - Write-LabMessage -Message ($LocalizedData.InstalledLabResourceWebMessage ` - -f $Name, $versionMessage, $modulePath) - } - else - { - # Install the package via PowerShellGet from the PowerShellGallery - # Make sure the Nuget Package provider is initialized. - $null = Get-PackageProvider ` - -name nuget ` - -ForceBootStrap ` - -Force - - # Make sure PSGallery is trusted - Set-PSRepository ` - -Name PSGallery ` - -InstallationPolicy Trusted - - # Install the module - $installModuleParameters = [PSObject] @{ Name = $Name } - - if ($RequiredVersion) - { - # Is a specific module version required? - $installModuleParameters += [PSObject] @{ - RequiredVersion = $RequiredVersion - } - } - elseif ($MinimumVersion) - { - # Is a specific module version minimum version? - $installModuleParameters += [PSObject] @{ - MinimumVersion = $MinimumVersion - } - } - - try - { - Install-Module @installModuleParameters -Force -ErrorAction Stop - } - catch - { - $exceptionParameters = @{ - errorId = 'ModuleNotAvailableError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ModuleNotAvailableError ` - -f $Name, $versionMessage, $_.Exception.Message) - } - New-LabException @exceptionParameters - } - } # If - } # If -} diff --git a/src/lib/private/New-LabCredential.ps1 b/src/lib/private/New-LabCredential.ps1 deleted file mode 100644 index d9af4574..00000000 --- a/src/lib/private/New-LabCredential.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -<# - .SYNOPSIS - Generates a credential object from a username and password. -#> -function New-LabCredential() -{ - [CmdletBinding()] - [OutputType([PSCredential])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Username, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Password - ) - - $credential = New-Object ` - -TypeName System.Management.Automation.PSCredential ` - -ArgumentList ($Username, (ConvertTo-SecureString $Password -AsPlainText -Force)) - - return $credential -} diff --git a/src/lib/private/New-LabException.ps1 b/src/lib/private/New-LabException.ps1 deleted file mode 100644 index 623462dd..00000000 --- a/src/lib/private/New-LabException.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -<# - .SYNOPSIS - Throws a custom exception. - - .DESCRIPTION - This cmdlet throws a terminating or non-terminating exception. - - .PARAMETER errorId - The Id of the exception. - - .PARAMETER errorCategory - The category of the exception. It must be a valid [System.Management.Automation.ErrorCategory] - value. - - .PARAMETER errorMessage - The exception message. - - .PARAMETER terminate - This switch will cause the exception to terminate the cmdlet. - - .EXAMPLE - $exceptionParameters = @{ - errorId = 'ConnectionFailure' - errorCategory = 'ConnectionError' - errorMessage = 'Could not connect' - } - New-LabException @exceptionParameters - Throw a ConnectionError exception with the message 'Could not connect'. - - .OUTPUTS - None -#> -function New-LabException -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $ErrorId, - - [Parameter(Mandatory = $true)] - [System.Management.Automation.ErrorCategory] - $ErrorCategory, - - [Parameter(Mandatory = $true)] - [System.String] - $ErrorMessage, - - [Switch] - $Terminate - ) - - $exception = New-Object -TypeName System.Exception ` - -ArgumentList $errorMessage - $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord ` - -ArgumentList $exception, $errorId, $errorCategory, $null - - if ($Terminate) - { - # This is a terminating exception. - throw $errorRecord - } - else - { - # Note: Although this method is called ThrowTerminatingError, it doesn't terminate. - $PSCmdlet.ThrowTerminatingError($errorRecord) - } -} diff --git a/src/lib/private/New-LabHostSelfSignedCertificate.ps1 b/src/lib/private/New-LabHostSelfSignedCertificate.ps1 deleted file mode 100644 index dd943c19..00000000 --- a/src/lib/private/New-LabHostSelfSignedCertificate.ps1 +++ /dev/null @@ -1,99 +0,0 @@ -<# - .SYNOPSIS - Generate a new credential encryption certificate on the Host for a VM. - - .DESCRIPTION - This function will create a new self-signed certificate on the host that can be uploaded - to the VM that it is created for. The certificate will be created in the LabBuilder files - folder for the specified VM. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - New-LabHostSelfSignedCertificate -Lab $Lab -VM $VMs[0] - Causes a new self-signed certificate for the VM and stores it to the Labbuilder files folder - of th VM. - - .OUTPUTS - The path to the certificate file that was created. -#> -function New-LabHostSelfSignedCertificate -{ - [CmdLetBinding()] - [OutputType([System.IO.FileInfo])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - $certificateFriendlyName = $Script:DSCCertificateFriendlyName - $certificateSubject = "CN=$($VM.ComputerName)" - - # Create the self-signed certificate for the destination VM - . $Script:SupportGertGenPath - New-SelfsignedCertificateEx ` - -Subject $certificateSubject ` - -EKU 'Document Encryption','Server Authentication','Client Authentication' ` - -KeyUsage 'DigitalSignature, KeyEncipherment, DataEncipherment' ` - -SAN $VM.ComputerName ` - -FriendlyName $certificateFriendlyName ` - -Exportable ` - -StoreLocation 'LocalMachine' ` - -StoreName 'My' ` - -KeyLength $Script:SelfSignedCertKeyLength ` - -ProviderName $Script:SelfSignedCertProviderName ` - -AlgorithmName $Script:SelfSignedCertAlgorithmName ` - -SignatureAlgorithm $Script:SelfSignedCertSignatureAlgorithm ` - -ErrorAction Stop - - # Locate the newly created certificate - $certificate = Get-ChildItem -Path cert:\LocalMachine\My ` - | Where-Object { - ($_.FriendlyName -eq $certificateFriendlyName) ` - -and ($_.Subject -eq $certificateSubject) - } | Select-Object -First 1 - - # Export the certificate with the Private key in - # preparation for upload to the VM - $certificatePassword = ConvertTo-SecureString ` - -String $Script:DSCCertificatePassword ` - -Force ` - -AsPlainText - $certificatePfxDestination = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionPfxCert - $null = Export-PfxCertificate ` - -FilePath $certificatePfxDestination ` - -Cert $certificate ` - -Password $certificatePassword ` - -ErrorAction Stop - - # Export the certificate without a private key - $certificateDestination = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionCert - $null = Export-Certificate ` - -Type CERT ` - -FilePath $certificateDestination ` - -Cert $certificate ` - -ErrorAction Stop - - # Remove the certificate from the Local Machine store - $certificate | Remove-Item - - return (Get-Item -Path $certificateDestination) -} diff --git a/src/lib/private/New-LabVMInitializationFile.ps1 b/src/lib/private/New-LabVMInitializationFile.ps1 deleted file mode 100644 index c3a25c19..00000000 --- a/src/lib/private/New-LabVMInitializationFile.ps1 +++ /dev/null @@ -1,200 +0,0 @@ -<# - .SYNOPSIS - Prepares the the files for initializing a new VM. - - .DESCRIPTION - This function creates the following files in the LabBuilder Files for the a VM in preparation - for them to be applied to the VM VHD before it is booted up for the first time: - 1. Unattend.xml - a Windows Unattend.xml file. - 2. SetupComplete.cmd - the command file that gets run after the Windows OOBE is complete. - 3. SetupComplete.ps1 - this PowerShell script file that is run at the the end of the - SetupComplete.cmd. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - New-LabVMInitializationFile -Lab $Lab -VM $VMs[0] - Prepare the first VM in the Lab c:\mylab\config.xml for initial boot. - - .OUTPUTS - None. -#> -function New-LabVMInitializationFile -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - # Generate an unattended setup file - $unattendFile = Get-LabUnattendFileContent ` - -Lab $Lab ` - -VM $VM - $null = Set-Content ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'Unattend.xml') ` - -Value $unattendFile -Force - - # Assemble the SetupComplete.* scripts. - $setupCompleteCmd = '' - - # Write out the PS1 Setup Complete File - if ($VM.OSType -eq [LabOSType]::Nano) - { - # For a Nano Server we also need to create the certificates - # to upload to it (because it Nano Server can't generate them) - $null = New-LabHostSelfSignedCertificate ` - -Lab $Lab ` - -VM $VM - - # PowerShell currently can't find any basic Cmdlets when executed by - # SetupComplete.cmd during the initialization phase, so create an empty - # a SetupComplete.ps1 - $setupCompletePs = '' - } - else - { - if ($VM.CertificateSource -eq [LabCertificateSource]::Host) - { - # Generate the PFX certificate on the host - $null = New-LabHostSelfSignedCertificate ` - -Lab $Lab ` - -VM $VM - } - - $getCertPs = Get-LabCertificatePsFileContent ` - -Lab $Lab ` - -VM $VM - $setupCompletePs = @" -Add-Content `` - -Path "C:\WINDOWS\Setup\Scripts\SetupComplete.log" `` - -Value 'SetupComplete.ps1 Script Started...' `` - -Encoding Ascii -Start-Sleep -Seconds 30 -$getCertPs -Add-Content `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'Certificate identified and saved to C:\Windows\$Script:DSCEncryptionCert ...' `` - -Encoding Ascii -Enable-PSRemoting -SkipNetworkProfileCheck -Force -Add-Content `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'Windows Remoting Enabled ...' `` - -Encoding Ascii -"@ - } # if - - if ($VM.SetupComplete) - { - $setupComplete = $VM.SetupComplete - - if (-not (Test-Path -Path $setupComplete)) - { - $exceptionParameters = @{ - errorId = 'SetupCompleteScriptMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.SetupCompleteScriptMissingError ` - -f $VM.name,$setupComplete) - } - New-LabException @exceptionParameters - } - - $extension = [System.IO.Path]::GetExtension($setupComplete) - - switch ($extension.ToLower()) - { - '.ps1' - { - $setupCompletePs += Get-Content -Path $setupComplete - Break - } # 'ps1' - - '.cmd' - { - $setupCompleteCmd += Get-Content -Path $setupComplete - Break - } # 'cmd' - } # Switch - } # If - - # Write out the CMD Setup Complete File - if ($VM.OSType -eq [LabOSType]::Nano) - { - $setupCompleteCmd = @" -@echo SetupComplete.cmd Script Started... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log -$setupCompleteCmd -certoc.exe -ImportPFX -p $Script:DSCCertificatePassword root $ENV:SystemRoot\$Script:DSCEncryptionPfxCert >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log -@echo SetupComplete.cmd Script Finished... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log -@echo Initial Setup Completed - this file indicates that setup has completed. >> %SYSTEMROOT%\Setup\Scripts\InitialSetupCompleted.txt -"@ - } - else - { - $setupCompleteCmd = @" -@echo SetupComplete.cmd Script Started... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log`r -$setupCompleteCmd -@echo SetupComplete.cmd Execute SetupComplete.ps1... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log`r -powerShell.exe -ExecutionPolicy Unrestricted -Command `"%SYSTEMROOT%\Setup\Scripts\SetupComplete.ps1`" `r -@echo SetupComplete.cmd Script Finished... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log -@echo Initial Setup Completed - this file indicates that setup has completed. >> %SYSTEMROOT%\Setup\Scripts\InitialSetupCompleted.txt -"@ - } - - $null = Set-Content ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'SetupComplete.cmd') ` - -Value $setupCompleteCmd -Force - - # Write out the PowerShell Setup Complete file - $setupCompletePs = @" -Add-Content `` - -Path `"$($ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'SetupComplete.ps1 Script Started...' `` - -Encoding Ascii -$setupCompletePs -Add-Content `` - -Path `"$($ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'SetupComplete.ps1 Script Finished...' `` - -Encoding Ascii -"@ - $null = Set-Content ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'SetupComplete.ps1') ` - -Value $setupCompletePs -Force - - # If ODJ file specified copy it to the labuilder path. - if ($VM.OSType -eq [LabOSType]::Nano ` - -and -not [System.String]::IsNullOrWhiteSpace($VM.NanoODJPath)) - { - if ([System.IO.Path]::IsPathRooted($VM.NanoODJPath)) - { - $nanoODJPath = $VM.NanoODJPath - } - else - { - $nanoODJPath = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $VM.NanoODJPath - } # if - - $null = Copy-Item ` - -Path (Join-Path -Path $nanoODJPath -ChildPath "$($VM.ComputerName).txt") ` - -Destination $vmLabBuilderFiles ` - -ErrorAction Stop - } # if - - Write-LabMessage -Message $($LocalizedData.CreatedVMInitializationFiles ` - -f $VM.Name) -} diff --git a/src/lib/private/Recieve-LabSelfSignedCertificate.ps1 b/src/lib/private/Recieve-LabSelfSignedCertificate.ps1 deleted file mode 100644 index 7e6dcfc3..00000000 --- a/src/lib/private/Recieve-LabSelfSignedCertificate.ps1 +++ /dev/null @@ -1,135 +0,0 @@ -<# - .SYNOPSIS - Download the existing self-signed certificate from a running VM. - - .DESCRIPTION - This function uses PS Remoting to connect to a running VM and download the an existing - Self-Signed certificate file that was written to the c:\windows folder of the guest operating - system by the SetupComplete.ps1 script on the. The certificate will be downloaded to the VM's - Labbuilder files folder. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .PARAMETER Timeout - The maximum amount of time that this function can take to download the certificate. - If the timeout is reached before the process is complete an error will be thrown. - The timeout defaults to 300 seconds. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Recieve-LabSelfSignedCertificate -Lab $Lab -VM $VMs[0] - Downloads the existing Self-signed certificate for the VM to the Labbuilder files folder of the - VM. - - .OUTPUTS - The path to the certificate file that was downloaded. -#> -function Recieve-LabSelfSignedCertificate -{ - [CmdLetBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [Parameter()] - [System.Int32] - $Timeout = 300 - ) - - $startTime = Get-Date - $session = $null - $complete = $false - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - $session = Connect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - # Failed to connnect to the VM - if (-not $session) - { - $exceptionParameters = @{ - errorId = 'CertificateDownloadError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.CertificateDownloadError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - return - } # if - - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $complete)) - { - # We connected OK - download the Certificate file - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - try - { - $null = Copy-Item ` - -Path "c:\windows\$Script:DSCEncryptionCert" ` - -Destination $vmLabBuilderFiles ` - -FromSession $session ` - -ErrorAction Stop - $complete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.WaitingForCertificateMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # try - } # while - } # if - - # If the copy didn't complete and we're out of time throw an exception - if ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $exceptionParameters = @{ - errorId = 'CertificateDownloadError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.CertificateDownloadError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } # if - - # Close the Session if it is opened and the download is complete - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and ($complete)) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - } # if - } # while - - return (Get-Item -Path "$vmLabBuilderFiles\$($Script:DSCEncryptionCert)") -} diff --git a/src/lib/private/Register-LabPackageSource.ps1 b/src/lib/private/Register-LabPackageSource.ps1 deleted file mode 100644 index af7fda23..00000000 --- a/src/lib/private/Register-LabPackageSource.ps1 +++ /dev/null @@ -1,112 +0,0 @@ -<# - .SYNOPSIS - Ensures the Package Sources required by LabBuilder are registered. - - .DESCRIPTION - This function will check that both the NuGet.org and the PSGallery package - sources are registered. - If either of them are missing the function will attempt to register them. - - .EXAMPLE - Register-LabPackageSource - Ensures the required Package Sources for LabBuilder are required. - - .OUTPUTS - None -#> -function Register-LabPackageSource -{ - [CmdLetBinding(SupportsShouldProcess = $true, - ConfirmImpact = 'High')] - param - ( - [Parameter()] - [Switch] - $Force - ) - - $requiredPackageSources = @( - @{ - Name = 'nuget.org' - ProviderName = 'NuGet' - Location = 'https://www.nuget.org/api/v2/' - }, - @{ - Name = 'PSGallery' - ProviderName = 'PowerShellGet' - Location = 'https://www.powershellgallery.com/api/v2/' - } - ) - - $currentPackageSources = Get-PackageSource -ErrorAction Stop - - foreach ($requiredPackageSource in $requiredPackageSources) - { - $packageSource = $currentPackageSources | - Where-Object -FilterScript { - $_.Name -eq $requiredPackageSource.Name - } - - if ($packageSource) - { - if (-not $packageSource.IsTrusted) - { - if ($Force -or $PSCmdlet.ShouldProcess( 'Localhost', ` - ($LocalizedData.ShouldTrustPackageSource ` - -f $requiredPackageSource.Name, $requiredPackageSource.Location ))) - { - # The Package source is not trusted so trust it - Write-LabMessage -Message ($LocalizedData.RegisterPackageSourceMessage ` - -f $requiredPackageSource.Name, $requiredPackageSource.Location) - - $null = Set-PackageSource ` - -Name $requiredPackageSource.Name ` - -Trusted ` - -Force ` - -ErrorAction Stop - } - else - { - # Can't continue if the package source is not trusted. - $exceptionParameters = @{ - errorId = 'PackageSourceNotTrustedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageSourceNotTrustedError ` - -f $requiredPackageSource.Name) - } - New-LabException @exceptionParameters - } # if - } # if - } - else - { - # The Package source is not registered so register it - if ($Force -or $PSCmdlet.ShouldProcess( 'Localhost', ` - ($LocalizedData.ShouldRegisterPackageSource ` - -f $requiredPackageSource.Name, $requiredPackageSource.Location ))) - { - Write-LabMessage -Message ($LocalizedData.RegisterPackageSourceMessage ` - -f $requiredPackageSource.Name, $requiredPackageSource.Location) - - $null = Register-PackageSource ` - -Name $requiredPackageSource.Name ` - -Location $requiredPackageSource.Location ` - -ProviderName $requiredPackageSource.ProviderName ` - -Trusted ` - -Force ` - -ErrorAction Stop - } - else - { - # Can't continue if the package source is not registered. - $exceptionParameters = @{ - errorId = 'PackageSourceNotRegisteredError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageSourceNotRegisteredError ` - -f $requiredPackageSource.Name) - } - New-LabException @exceptionParameters - } # if - } # if - } # foreach -} diff --git a/src/lib/private/Request-LabSelfSignedCertificate.ps1 b/src/lib/private/Request-LabSelfSignedCertificate.ps1 deleted file mode 100644 index ba61afb3..00000000 --- a/src/lib/private/Request-LabSelfSignedCertificate.ps1 +++ /dev/null @@ -1,203 +0,0 @@ -<# - .SYNOPSIS - Generate and download a new credential encryption certificate from a running VM. - - .DESCRIPTION - This function uses PS Remoting to connect to a running VM and upload the GetDSCEncryptionCert.ps1 - script and then run it. This wil create a new self-signed certificate that is written to the - c:\windows folder of the guest operating system. The certificate will be downloaded to the VM's - Labbuilder files folder. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .PARAMETER Timeout - The maximum amount of time that this function can take to download the certificate. - If the timeout is reached before the process is complete an error will be thrown. - The timeout defaults to 300 seconds. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Request-LabSelfSignedCertificate -Lab $Lab -VM $VMs[0] - Causes a new self-signed certificate on the VM and download it to the Labbuilder files folder - of th VM. - - .OUTPUTS - The path to the certificate file that was downloaded. -#> -function Request-LabSelfSignedCertificate -{ - [CmdLetBinding()] - [OutputType([System.IO.FileInfo])] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [Parameter()] - [System.Int32] - $Timeout = 300 - ) - - $startTime = Get-Date - $session = $null - $complete = $false - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - # Ensure the certificate generation script has been created - $getCertPs = Get-LabCertificatePsFileContent ` - -Lab $Lab ` - -VM $VM ` - -CertificateSource Guest - - $null = Set-Content ` - -Path "$VMLabBuilderFiles\GetDSCEncryptionCert.ps1" ` - -Value $getCertPs ` - -Force - - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - $session = Connect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - # Failed to connnect to the VM - if (-not $session) - { - $exceptionParameters = @{ - errorId = 'CertificateDownloadError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.CertificateDownloadError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - return - } # if - - $complete = $false - - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $complete)) - { - # We connected OK - Upload the script - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - try - { - Copy-Item ` - -Path "$VMLabBuilderFiles\GetDSCEncryptionCert.ps1" ` - -Destination 'c:\windows\setup\scripts\' ` - -ToSession $session ` - -Force ` - -ErrorAction Stop - $complete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.FailedToUploadCertificateCreateScriptMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # try - } # while - } # if - - $complete = $false - - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $complete)) - { - # Script uploaded, run it - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - try - { - Invoke-Command -Session $session -ScriptBlock { - C:\Windows\Setup\Scripts\GetDSCEncryptionCert.ps1 - } - - $complete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.FailedToExecuteCertificateCreateScriptMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # try - } # while - } # if - - $complete = $false - - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $complete)) - { - # Now download the Certificate - while ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - try { - $null = Copy-Item ` - -Path "c:\windows\$($Script:DSCEncryptionCert)" ` - -Destination $vmLabBuilderFiles ` - -FromSession $session ` - -ErrorAction Stop - - $complete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.FailedToDownloadCertificateMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # Try - } # While - } # If - - # If the process didn't complete and we're out of time throw an exception - if ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) - { - if ($session) - { - Remove-PSSession -Session $session - } - - $exceptionParameters = @{ - errorId = 'CertificateDownloadError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.CertificateDownloadError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } - - # Close the Session if it is opened and the download is complete - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and ($complete)) - { - Remove-PSSession -Session $session - } # If - } # While - - return (Get-Item -Path "$vmLabBuilderFiles\$($Script:DSCEncryptionCert)") -} diff --git a/src/lib/private/Set-LabDSC.ps1 b/src/lib/private/Set-LabDSC.ps1 deleted file mode 100644 index 7f3bd22a..00000000 --- a/src/lib/private/Set-LabDSC.ps1 +++ /dev/null @@ -1,158 +0,0 @@ -<# - .SYNOPSIS - This function prepares the PowerShell scripts used for starting up DSC on a VM. - - .DESCRIPTION - Two PowerShell scripts will be created by this function in the LabBuilder Files - folder of the VM: - 1. StartDSC.ps1 - the script that is called automatically to start up DSC. - 2. StartDSCDebug.ps1 - a debug script that will start up DSC in debug mode. - These scripts will contain code to perform the following operations: - 1. Configure the names of the Network Adapters so that they will match the - names in the DSC Configuration files. - 2. Enable/Disable DSC Event Logging. - 3. Apply Configuration to the Local Configuration Manager. - 4. Start DSC. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Set-LabDSC -Lab $Lab -VM $VMs[0] - Prepare the first VM in the Lab c:\mylab\config.xml for DSC start up. - - .OUTPUTS - None. -#> -function Set-LabDSC -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - $dscStartPs = '' - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - <# - Relabel the Network Adapters so that they match what the DSC Networking config will use - This is because unfortunately the Hyper-V Device Naming feature doesn't work. - #> - $managementSwitchName = Get-LabManagementSwitchName -Lab $Lab - $adapters = [System.String[]] ($VM.Adapters).Name - $adapters += @($managementSwitchName) - - foreach ($adapter in $adapters) - { - $netAdapter = Get-VMNetworkAdapter -VMName $($VM.Name) -Name $adapter - - if (-not $netAdapter) - { - $exceptionParameters = @{ - errorId = 'NetworkAdapterNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NetworkAdapterNotFoundError ` - -f $adapter, $VM.Name) - } - New-LabException @exceptionParameters - } # if - - $macAddress = $netAdapter.MacAddress - - if (-not $macAddress) - { - $exceptionParameters = @{ - errorId = 'NetworkAdapterBlankMacError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NetworkAdapterBlankMacError ` - -f $adapter, $VM.Name) - } - New-LabException @exceptionParameters - } # If - - $dscStartPs += @" -Get-NetAdapter `` - | Where-Object { `$_.MacAddress.Replace('-','') -eq '$macAddress' } `` - | Rename-NetAdapter -NewName '$($adapter)' - -"@ - } # Foreach - - <# - Enable DSC logging (as long as it hasn't been already) - Nano Server doesn't have the Microsoft-Windows-Dsc/Analytic channels so - Logging can't be enabled. - #> - if ($VM.OSType -ne [LabOSType]::Nano) - { - $logging = ($VM.DSC.Logging).ToString() - - $dscStartPs += @" -`$Result = & "wevtutil.exe" get-log "Microsoft-Windows-Dsc/Analytic" -if (-not (`$Result -like '*enabled: true*')) { - & "wevtutil.exe" set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:$logging -} -`$Result = & "wevtutil.exe" get-log "Microsoft-Windows-Dsc/Debug" -if (-not (`$Result -like '*enabled: true*')) { - & "wevtutil.exe" set-log "Microsoft-Windows-Dsc/Debug" /q:true /e:$logging -} - -"@ - } # if - - # Start the actual DSC Configuration - $dscStartPs += @" -Set-DscLocalConfigurationManager `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` - -Verbose *>> `"`$(`$ENV:SystemRoot)\Setup\Scripts\DSC.log`" -Start-DSCConfiguration `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` - -Force `` - -Verbose *>> `"`$(`$ENV:SystemRoot)\Setup\Scripts\DSC.log`" - -"@ - $null = Set-Content ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSC.ps1') ` - -Value $dscStartPs -Force - - $dscStartPsDebug = @" -param ( - [System.Boolean] `$WaitForDebugger -) -Set-DscLocalConfigurationManager `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` - -Verbose -if (`$WaitForDebugger) -{ - Enable-DscDebug `` - -BreakAll -} -Start-DSCConfiguration `` - -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\`" `` - -Force `` - -Debug `` - -Wait `` - -Verbose -if (`$WaitForDebugger) -{ - Disable-DscDebug -} -"@ - - $null = Set-Content ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSCDebug.ps1') ` - -Value $dscStartPsDebug -Force -} diff --git a/src/lib/private/Set-LabModulesInDSCConfig.ps1 b/src/lib/private/Set-LabModulesInDSCConfig.ps1 deleted file mode 100644 index 93025358..00000000 --- a/src/lib/private/Set-LabModulesInDSCConfig.ps1 +++ /dev/null @@ -1,129 +0,0 @@ -<# - .SYNOPSIS - Sets the Modules Resources that should be imported in a DSC Config. - - .DESCRIPTION - It will completely replace the list of Imported DSCResources with this new list. - - .PARAMETER DscConfigFile - Contains the path to the DSC Config file to set resource module names in. - - .PARAMETER DscConfigContent - Contains the content of the DSC Config to set resource module names in. - - .PARAMETER Modules - Contains an array of LabDSCModule objects to replace set in the Configuration. - - .EXAMPLE - Set-LabModulesInDSCConfig -DscConfigFile c:\mydsc\Server01.ps1 -Modules $Modules - Set the DSC Resource module in the content from file c:\mydsc\server01.ps1 - - .EXAMPLE - Set-LabModulesInDSCConfig -DscConfigContent $DSCConfig -Modules $Modules - Set the DSC Resource module in the content $DSCConfig - - .OUTPUTS - A string containing the content of the DSC Config file with the updated - module names in it. -#> -function Set-LabModulesInDSCConfig -{ - [CmdLetBinding(DefaultParameterSetName = "Content")] - [OutputType([System.String])] - param - ( - [parameter( - Position = 1, - ParameterSetName = "Content", - Mandatory = $true)] - [System.String] - $DscConfigContent, - - [parameter( - Position = 2, - ParameterSetName = "File", - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $DscConfigFile, - - [parameter( - Position = 3, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [LabDSCModule[]] - $Modules - ) - - if ($PSCmdlet.ParameterSetName -eq 'File') - { - $DscConfigContent = Get-Content -Path $DscConfigFile -Raw - } # if - - $regex = "[ \t]*?Import\-DscResource[ \t]+(?:\-ModuleName[ \t]+)?'?`"?([A-Za-z0-9._-]+)`"?'?([ \t]+(-ModuleVersion[ \t]+)?'?`"?([0-9.]+)`"?'?)?[ \t]*[\r\n]+" - $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') - - foreach ($module in $Modules) - { - $importCommand = "Import-DscResource -ModuleName '$($module.ModuleName)'" - if ($module.ModuleVersion) - { - $importCommand = "$importCommand -ModuleVersion '$($module.ModuleVersion)'" - } # if - - $importCommand = " $importCommand`r`n" - - # is this module already in there? - $found = $false - - foreach ($moduleMatch in $moduleMatches) - { - if ($moduleMatch.Groups[1].Value -eq $module.ModuleName) - { - # Found the module - so replace it - $DscConfigContent = ("{0}{1}{2}" -f ` - $DscConfigContent.Substring(0, $moduleMatch.Index), ` - $importCommand, ` - $DscConfigContent.Substring($moduleMatch.Index + $moduleMatch.Length)) - - $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') - $found = $true - break - } # if - } # foreach - - if (-not $found) - { - if ($moduleMatches.Count -gt 0) - { - # Add this to the end of the existing Import-DSCResource lines - $moduleMatch = $moduleMatches[$moduleMatches.count - 1] - } - else - { - # There are no existing DSC Resource lines, so add it after - # Configuration ... { line - $moduleMatch = [regex]::matches($DscConfigContent, "[ \t]*?Configuration[ \t]+?'?`"?[A-Za-z0-9._-]+`"?'?[ \t]*?[\r\n]*?{[\r\n]*?", 'IgnoreCase') - - if (-not $moduleMatch) - { - $exceptionParameters = @{ - errorId = 'DSCConfiguartionMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfiguartionMissingError) - } - New-LabException @exceptionParameters - } - } # if - - $DscConfigContent = ("{0}{1}{2}" -f ` - $DscConfigContent.Substring(0, $moduleMatch.Index + $moduleMatch.Length), ` - $importCommand, ` - $DscConfigContent.Substring($moduleMatch.Index + $moduleMatch.Length)) - - $moduleMatches = [regex]::matches($DscConfigContent, $regex, 'IgnoreCase') - } # Module not found so add it to the end - } # foreach - - return $DscConfigContent -} diff --git a/src/lib/private/Set-LabSwitchAdapter.ps1 b/src/lib/private/Set-LabSwitchAdapter.ps1 deleted file mode 100644 index 22ecaecc..00000000 --- a/src/lib/private/Set-LabSwitchAdapter.ps1 +++ /dev/null @@ -1,134 +0,0 @@ -<# - .SYNOPSIS - Ensures that the virtual adapter is attached to a Virtual Switch - and configured correctly. - - .DESCRIPTION - This function is used to add or update the specified virtual network adapter - that is used by the Management OS to connect to the specifed virtual switch. - - .PARAMETER Name - Contains the name of the virtual network adapter to add. - - .PARAMETER SwitchName - Contains the name of the virtual switch to connect this adapter to. - - .PARAMETER ManagementOS - Whether or not this adapter is attached to the Management OS. - - .PARAMETER StaticMacAddress - This optional parameter contains the static MAC address to assign to the virtual - network adapter. - - .PARAMETER VlanId - This optional parameter contains the VLan Id to assign to this network adapter. - - .EXAMPLE - Set-LabSwitchAdapter -Name 'Domain Nat SMB' -SwitchName 'Domain Nat' -VlanId 25 - - .OUTPUTS - None. -#> -function Set-LabSwitchAdapter -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Name, - - [Parameter(Mandatory = $true)] - [System.String] - $SwitchName, - - [Parameter()] - [Switch] - $ManagementOS, - - [Parameter()] - [System.String] - $StaticMacAddress, - - [Parameter()] - [AllowNull()] - [Nullable[System.Int32]] - $VlanId - ) - - # Determine if we should set the MAC address and VLan Id - $setVlanId = $PSBoundParameters.ContainsKey('VlanId') - $setMacAddress = $PSBoundParameters.ContainsKey('StaticMacAddress') - - # Remove VlanId Parameter so this can be splatted - $null = $PSBoundParameters.Remove('VlanId') - $null = $PSBoundParameters.Remove('StaticMacAddress') - - $existingManagementAdapter = Get-VMNetworkAdapter ` - @PSBoundParameters ` - -ErrorAction SilentlyContinue - - if (-not $existingManagementAdapter) - { - # Adapter does not exist so add it - if ($SetMacAddress) - { - # For a management adapter a Static MAC address can only be assigned at creation time. - if (-not ([System.String]::IsNullOrEmpty($StaticMacAddress))) - { - $PSBoundParameters.Add('StaticMacAddress', $StaticMacAddress) - } - } - - $existingManagementAdapter = Add-VMNetworkAdapter ` - @PSBoundParameters ` - -Passthru - } - else - { - <# - The MAC Address for an existing Management Adapter can not be changed - This shouldn't ever happen unless the configuration is changed. - Not sure of the solution to this problem. - #> - } - - # Set or clear the VlanId - if ($setVlanId) - { - $existingManagementAdapterVlan = Get-VMNetworkAdapterVlan ` - -VMNetworkAdapter $existingManagementAdapter - - $existingManagementVlan = $existingManagementAdapterVlan.AccessVlanId - - if ($null -eq $VlanId) - { - if ($null -eq $existingManagementVlan) - { - $setVMNetworkAdapterVlanParameters = @{ - VMNetworkAdapter = $existingManagementAdapter - Untagged = $true - } - - $null = Set-VMNetworkAdapterVlan ` - @setVMNetworkAdapterVlanParameters ` - -ErrorAction Stop - } - } - else - { - if ($VlanId -ne $existingManagementVlan) - { - $setVMNetworkAdapterVlanParameters = @{ - VMNetworkAdapter = $existingManagementAdapter - Access = $true - VlanId = $VlanId - } - - $null = Set-VMNetworkAdapterVlan ` - @setVMNetworkAdapterVlanParameters ` - -ErrorAction Stop - } - } - } -} diff --git a/src/lib/private/Start-LabDSC.ps1 b/src/lib/private/Start-LabDSC.ps1 deleted file mode 100644 index accfd136..00000000 --- a/src/lib/private/Start-LabDSC.ps1 +++ /dev/null @@ -1,239 +0,0 @@ -<# - .SYNOPSIS - Uploads prepared Modules and MOF files to a VM and starts up Desired State - Configuration (DSC) on it. - - .DESCRIPTION - This function will perform the following tasks: - 1. Connect to the VM via remoting. - 2. Upload the DSC and LCM MOF files to the c:\windows\setup\scripts folder of the VM. - 3. Upload DSC Start up scripts to the c:\windows\setup\scripts folder of the VM. - 4. Upload all required modules to the c:\program files\WindowsPowerShell\Modules\ folder - of the VM. - 5. Invoke the StartDSC.ps1 script on the VM to start DSC processing. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM. - - .PARAMETER Timeout - The maximum amount of time that this function can take to perform DSC start-up. - If the timeout is reached before the process is complete an error will be thrown. - The timeout defaults to 300 seconds. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Start-LabDSC -Lab $Lab -VM $VMs[0] - Starts up Desired State Configuration for the first VM in the Lab c:\mylab\config.xml. - - .OUTPUTS - None. -#> -function Start-LabDSC -{ - [CmdLetBinding()] - param ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [Parameter()] - [System.Int32] - $Timeout = 300 - ) - - $startTime = Get-Date - $session = $null - $complete = $false - $configCopyComplete = $false - $moduleCopyComplete = $false - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - While ((-not $complete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - # Connect to the VM - $session = Connect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - # Failed to connnect to the VM - if (-not $session) - { - $exceptionParameters = @{ - errorId = 'DSCInitializationError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.DSCInitializationError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - - return - } - - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $configCopyComplete)) - { - $copyParameters = @{ - Destination = 'c:\Windows\Setup\Scripts' - ToSession = $session - Force = $true - ErrorAction = 'Stop' - } - - # Connection has been made OK, upload the DSC files - While ((-not $configCopyComplete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $TimeOut) - { - Try - { - Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMMessage ` - -f $VM.Name, 'DSC') - - $null = Copy-Item ` - @copyParameters ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).mof") - - if (Test-Path ` - -Path "$vmLabBuilderFiles\$($VM.ComputerName).meta.mof") - { - $null = Copy-Item ` - @copyParameters ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).meta.mof") - } # If - - $null = Copy-Item ` - @copyParameters ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSC.ps1') - - $null = Copy-Item ` - @copyParameters ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath 'StartDSCDebug.ps1') - - $configCopyComplete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` - -f $VM.Name, 'DSC', $Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # try - } # while - } # if - - # If the copy didn't complete and we're out of time throw an exception - if ((-not $configCopyComplete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $exceptionParameters = @{ - errorId = 'DSCInitializationError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.DSCInitializationError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } # if - - # Upload any required modules to the VM - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and (-not $moduleCopyComplete)) - { - $dscContent = Get-Content ` - -Path $($VM.DSC.ConfigFile) ` - -Raw - [LabDSCModule[]] $dscModules = Get-LabModulesInDSCConfig -DSCConfigContent $dscContent - - # Add the NetworkingDsc DSC Resource because it is always used - $module = [LabDSCModule]::New('NetworkingDsc') - $dscModules += @( $module ) - - foreach ($dscModule in $dscModules) - { - $moduleName = $dscModule.ModuleName - - # Upload all but PSDesiredStateConfiguration because it - # should always exist on client node. - if ($moduleName -ne 'PSDesiredStateConfiguration') - { - try - { - Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMMessage ` - -f $VM.Name, "DSC Module $moduleName") - - $null = Copy-Item ` - -Path (Join-Path -Path $vmLabBuilderFiles -ChildPath "DSC Modules\$moduleName\") ` - -Destination "$($env:ProgramFiles)\WindowsPowerShell\Modules\" ` - -ToSession $session ` - -Force ` - -Recurse ` - -ErrorAction Stop - } - catch - { - Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` - -f $VM.Name, "DSC Module $moduleName", $Script:RetryConnectSeconds) - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # try - } # if - } # foreach - - $moduleCopyComplete = $true - } # if - - # If the copy didn't complete and we're out of time throw an exception - if ((-not $moduleCopyComplete) ` - -and (((Get-Date) - $startTime).TotalSeconds) -ge $TimeOut) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $exceptionParameters = @{ - errorId = 'DSCInitializationError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.DSCInitializationError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } # if - - # Finally, Start DSC up! - if (($session) ` - -and ($session.State -eq 'Opened') ` - -and ($configCopyComplete) ` - -and ($moduleCopyComplete)) - { - Write-LabMessage -Message $($LocalizedData.StartingDSCMessage ` - -f $VM.Name) - - Invoke-Command -Session $session { - c:\windows\setup\scripts\StartDSC.ps1 - } - - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $complete = $true - } # if - } # while -} diff --git a/src/lib/private/Update-LabDSC.ps1 b/src/lib/private/Update-LabDSC.ps1 deleted file mode 100644 index 803a283e..00000000 --- a/src/lib/private/Update-LabDSC.ps1 +++ /dev/null @@ -1,413 +0,0 @@ -<# - .SYNOPSIS - This function prepares all the files and modules necessary for a VM to be configured using - Desired State Configuration (DSC). - - .DESCRIPTION - This funcion performs the following tasks in preparation for starting Desired State - Configuration on a Virtual Machine: - 1. Ensures the folder structure for the Virtual Machine DSC files is available. - 2. Gets a list of all Modules required by the DSC configuration to be applied. - 3. Download and Install any missing DSC modules required for the DSC configuration. - 4. Copy all modules required for the DSC configuration to the VM folder. - 5. Cause a self-signed cetficiate to be created and downloaded on the Lab VM. - 6. Create a Networking DSC configuration file and ensure the DSC config file calss it. - 7. Create the MOF file from the config and an LCM config. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Update-LabDSC -Lab $Lab -VM $VMs[0] - Prepare the first VM in the Lab c:\mylab\config.xml for DSC configuration. - - .OUTPUTS - None. -#> -function Update-LabDSC -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - $Lab, - - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - $dscMOFFile = '' - $dscMOFMetaFile = '' - - # Get Path to LabBuilder files - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - if (-not $VM.DSC.ConfigFile) - { - # This VM doesn't have a DSC Configuration - return - } - - # Make sure all the modules required to create the MOF file are installed - $installedModules = Get-Module -ListAvailable - - Write-LabMessage -Message $($LocalizedData.DSCConfigIdentifyModulesMessage ` - -f $VM.DSC.ConfigFile, $VM.Name) - - $dscConfigContent = Get-Content ` - -Path $($VM.DSC.ConfigFile) ` - -Raw - - [LabDSCModule[]] $dscModules = Get-LabModulesInDSCConfig ` - -DSCConfigContent $dscConfigContent - - # Add the NetworkingDsc DSC Resource because it is always used - $module = [LabDSCModule]::New('NetworkingDsc') - - # It must be 7.0.0.0 or greater - $module.MinimumVersion = [Version] '7.0.0.0' - $dscModules += @( $module ) - - foreach ($dscModule in $dscModules) - { - $moduleName = $dscModule.ModuleName - $moduleParameters = @{ Name = $ModuleName } - $moduleVersion = $dscModule.ModuleVersion - $minimumVersion = $dscModule.MinimumVersion - - if ($moduleVersion) - { - $filterScript = { - ($_.Name -eq $ModuleName) -and ($moduleVersion -eq $_.Version) - } - - $moduleParameters += @{ - RequiredVersion = $moduleVersion - } - } - elseif ($minimumVersion) - { - $filterScript = { - ($_.Name -eq $ModuleName) -and ($_.Version -ge $minimumVersion) - } - - $moduleParameters += @{ - MinimumVersion = $minimumVersion - } - } - else - { - $filterScript = { - $_.Name -eq $ModuleName - } - } - - $module = ($installedModules | - Where-Object -FilterScript $filterScript | - Sort-Object -Property Version -Descending | - Select-Object -First 1) - - if ($module) - { - # The module already exists, load the version number into the Module - # to force the version number to be set in the DSC Config file - $dscModule.ModuleVersion = $module.Version - } - else - { - # The Module isn't available on this computer, so try and install it - Write-LabMessage -Message $($LocalizedData.DSCConfigSearchingForModuleMessage ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) - - $newModule = Find-Module ` - @moduleParameters - - if ($newModule) - { - Write-LabMessage -Message $($LocalizedData.DSCConfigInstallingModuleMessage ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) - - try - { - $newModule | Install-Module - } - catch - { - $exceptionParameters = @{ - errorId = 'DSCModuleDownloadError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCModuleDownloadError ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) - } - New-LabException @exceptionParameters - } - } - else - { - $exceptionParameters = @{ - errorId = 'DSCModuleDownloadError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCModuleDownloadError ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) - } - New-LabException @exceptionParameters - } - - $dscModule.ModuleVersion = $newModule.Version - } # if - - Write-LabMessage -Message $($LocalizedData.DSCConfigSavingModuleMessage ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) - - # Find where the module is actually stored - $modulePath = '' - - foreach ($Path in $ENV:PSModulePath.Split(';')) - { - if (-not [System.String]::IsNullOrEmpty($Path)) - { - $modulePath = Join-Path ` - -Path $Path ` - -ChildPath $ModuleName - - if (Test-Path -Path $modulePath) - { - break - } # If - } - } # Foreach - - if (-not (Test-Path -Path $modulePath)) - { - $exceptionParameters = @{ - errorId = 'DSCModuleNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCModuleNotFoundError ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName) - } - New-LabException @exceptionParameters - } - - $destinationPath = Join-Path -Path $vmLabBuilderFiles -ChildPath 'DSC Modules\' - - if (-not (Test-Path -Path $destinationPath)) - { - # Create the DSC Modules folder if it doesn't exist. - $null = New-Item -Path $destinationPath -ItemType Directory -Force - } # if - - Write-LabMessage -Message $($LocalizedData.DSCConfigCopyingModuleMessage ` - -f $VM.DSC.ConfigFile, $VM.Name, $ModuleName, $modulePath, $destinationPath) - Copy-Item ` - -Path $modulePath ` - -Destination $destinationPath ` - -Recurse ` - -Force ` - -ErrorAction Continue - } # Foreach - - if ($VM.CertificateSource -eq [LabCertificateSource]::Guest) - { - # Recreate the certificate if it the source is the Guest - if (-not (Request-LabSelfSignedCertificate -Lab $Lab -VM $VM)) - { - $exceptionParameters = @{ - errorId = 'CertificateCreateError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.CertificateCreateError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } - - # Remove any old self-signed certifcates for this VM - Get-ChildItem -Path cert:\LocalMachine\My | - Where-Object { $_.FriendlyName -eq $Script:DSCCertificateFriendlyName } | - Remove-Item - } # if - - # Add the VM Self-Signed Certificate to the Local Machine store and get the Thumbprint - $certificateFile = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionCert - $certificate = Import-Certificate ` - -FilePath $certificateFile ` - -CertStoreLocation 'Cert:LocalMachine\My' - $certificateThumbprint = $certificate.Thumbprint - - # Set the predicted MOF File name - $dscMOFFile = Join-Path ` - -Path $ENV:Temp ` - -ChildPath "$($VM.ComputerName).mof" - $dscMOFMetaFile = ([System.IO.Path]::ChangeExtension($dscMOFFile, 'meta.mof')) - - # Generate the LCM MOF File - Write-LabMessage -Message $($LocalizedData.DSCConfigCreatingLCMMOFMessage -f $dscMOFMetaFile, $VM.Name) - - $null = ConfigLCM ` - -OutputPath $ENV:Temp ` - -ComputerName $($VM.ComputerName) ` - -Thumbprint $certificateThumbprint - - if (-not (Test-Path -Path $dscMOFMetaFile)) - { - $exceptionParameters = @{ - errorId = 'DSCConfigMetaMOFCreateError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfigMetaMOFCreateError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } # If - - # A DSC Config File was provided so create a MOF File out of it. - Write-LabMessage -Message $($LocalizedData.DSCConfigCreatingMOFMessage -f $VM.DSC.ConfigFile, $VM.Name) - - # Now create the Networking DSC Config file - $dscNetworkingConfig = Get-LabDSCNetworkingConfig ` - -Lab $Lab -VM $VM - $NetworkingDscFile = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath 'DSCNetworking.ps1' - $null = Set-Content ` - -Path $NetworkingDscFile ` - -Value $dscNetworkingConfig - . $NetworkingDscFile - $dscFile = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath 'DSC.ps1' - - # Set the Modules List in the DSC Configuration - $dscConfigContent = Set-LabModulesInDSCConfig ` - -DSCConfigContent $dscConfigContent ` - -Modules $dscModules - - if (-not ($dscConfigContent -match 'Networking Network {}')) - { - # Add the Networking Configuration item to the base DSC Config File - # Find the location of the line containing "Node $AllNodes.NodeName {" - [System.String] $Regex = '\s*Node\s.*{.*' - $Matches = [regex]::matches($dscConfigContent, $Regex, 'IgnoreCase') - - if ($Matches.Count -eq 1) - { - $dscConfigContent = $dscConfigContent.` - Insert($Matches[0].Index + $Matches[0].Length, "`r`nNetworking Network {}`r`n") - } - else - { - $exceptionParameters = @{ - errorId = 'DSCConfigMoreThanOneNodeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfigMoreThanOneNodeError ` - -f $VM.DSC.ConfigFile, $VM.Name) - } - New-LabException @exceptionParameters - } # if - } # if - - # Save the DSC Content - $null = Set-Content ` - -Path $dscFile ` - -Value $dscConfigContent ` - -Force - - # Hook the Networking DSC File into the main DSC File - . $dscFile - - $dscConfigName = $VM.DSC.ConfigName - - Write-LabMessage -Message $($LocalizedData.DSCConfigPrepareMessage -f $dscConfigName, $VM.Name) - - # Generate the Configuration Nodes data that always gets passed to the DSC configuration. - $dscConfigData = @" -@{ - AllNodes = @( - @{ - NodeName = '$($VM.ComputerName)' - CertificateFile = '$certificateFile' - Thumbprint = '$certificateThumbprint' - LocalAdminPassword = '$($VM.administratorpassword)' - $($VM.DSC.Parameters) - } - ) -} -"@ - # Write it to a temp file - $dscConfigFile = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath 'DSCConfigData.psd1' - - if (Test-Path -Path $dscConfigFile) - { - $null = Remove-Item ` - -Path $dscConfigFile ` - -Force - } - - $null = Set-Content -Path $dscConfigFile -Value $dscConfigData - - # Read the config data into a Hash Table - $dscConfigData = Import-LocalizedData -BaseDirectory $vmLabBuilderFiles -FileName 'DSCConfigData.psd1' - - # Generate the MOF file from the configuration - $null = & $dscConfigName ` - -OutputPath $ENV:Temp ` - -ConfigurationData $dscConfigData ` - -ErrorAction Stop - - if (-not (Test-Path -Path $dscMOFFile)) - { - $exceptionParameters = @{ - errorId = 'DSCConfigMOFCreateError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfigMOFCreateError -f $VM.DSC.ConfigFile, $VM.Name) - } - New-LabException @exceptionParameters - } # If - - # Remove the VM Self-Signed Certificate from the Local Machine Store - $null = Remove-Item ` - -Path "Cert:LocalMachine\My\$certificateThumbprint" ` - -Force - - Write-LabMessage -Message $($LocalizedData.DSCConfigMOFCreatedMessage -f $VM.DSC.ConfigFile, $VM.Name) - - # Copy the files to the LabBuilder Files folder - $dscMOFDestinationFile = Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).mof" - $null = Copy-Item ` - -Path $dscMOFFile ` - -Destination $dscMOFDestinationFile ` - -Force - - if (-not $VM.DSC.MOFFile) - { - # Remove Temporary files created by DSC - $null = Remove-Item ` - -Path $dscMOFFile ` - -Force - } - - if (Test-Path -Path $dscMOFMetaFile) - { - $dscMOFMetaDestinationFile = Join-Path -Path $vmLabBuilderFiles -ChildPath "$($VM.ComputerName).meta.mof" - $null = Copy-Item ` - -Path $dscMOFMetaFile ` - -Destination $dscMOFMetaDestinationFile ` - -Force - - if (-not $VM.DSC.MOFFile) - { - # Remove Temporary files created by DSC - $null = Remove-Item ` - -Path $dscMOFMetaFile ` - -Force - } - } # if -} diff --git a/src/lib/private/Update-LabVMDataDisk.ps1 b/src/lib/private/Update-LabVMDataDisk.ps1 deleted file mode 100644 index a1c4d79f..00000000 --- a/src/lib/private/Update-LabVMDataDisk.ps1 +++ /dev/null @@ -1,397 +0,0 @@ -<# - .SYNOPSIS - Updates the VM Data Disks to match the VM Configuration. - - .DESCRIPTION - This cmdlet will take the VM configuration provided and ensure that that data disks that are - attached to the VM. - - The function will use the array of items in the DataVHDs property of the VM to create and - attach any data disk VHDs that are missing. - - If the data disk VHD file exists but is not attached it will be attached to the VM. If the - data disk VHD file does not exist then it will be created and attached. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Update-LabVMDataDisk -Lab $Lab -VM VM[0] - This will update the data disks for the first VM in the configuration file c:\mylab\config.xml. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .OUTPUTS - None. -#> -function Update-LabVMDataDisk -{ - [CmdLetBinding()] - param - ( - [Parameter( - Mandatory, - Position=0)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Mandatory, - Position=1)] - [ValidateNotNullOrEmpty()] - [LabVM] - $VM - ) - - # If there are no data VHDs just return - if (-not $VM.DataVHDs) - { - return - } - - # Get the root path of the VM - $vmRootPath = $VM.VMRootPath - - # Get the Virtual Hard Disk Path - $vhdPath = Join-Path ` - -Path $vmRootPath ` - -ChildPath 'Virtual Hard Disks' - - foreach ($dataVhd in @($VM.DataVHDs)) - { - $vhd = $dataVhd.Vhd - if (Test-Path -Path $vhd) - { - Write-LabMessage -Message $($LocalizedData.VMDiskAlreadyExistsMessage ` - -f $VM.Name,$vhd,'Data') - - # Check the parameters of the VHD match - $existingVhd = Get-VHD -Path $vhd - - # Check the VHD Type - if (($dataVhd.VhdType) ` - -and ($existingVhd.VhdType.ToString() -ne $dataVhd.VhdType.ToString())) - { - # The type of disk can't be changed. - $exceptionParameters = @{ - errorId = 'VMDataDiskVHDConvertError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskVHDConvertError ` - -f $VM.name,$vhd,$dataVhd.VhdType) - } - New-LabException @exceptionParameters - } - - # Check the size - if ($dataVhd.Size) - { - if ($existingVhd.Size -lt $dataVhd.Size) - { - # Expand the disk - Write-LabMessage -Message $($LocalizedData.ExpandingVMDiskMessage ` - -f $VM.Name,$vhd,'Data',$dataVhd.Size) - - $null = Resize-VHD ` - -Path $vhd ` - -SizeBytes $dataVhd.Size - } - elseif ($existingVhd.Size -gt $dataVhd.Size) - { - <# - The disk size can't be reduced. - This could be revisited later. - #> - $exceptionParameters = @{ - errorId = 'VMDataDiskVHDShrinkError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskVHDShrinkError ` - -f $VM.name,$vhd,$dataVhd.Size) - } - New-LabException @exceptionParameters - } # if - } # if - } - else - { - # The data disk VHD does not exist so create it - $SourceVhd = $dataVhd.SourceVhd - if ($SourceVhd) - { - # A source VHD was specified to create the new VHD using - if (! (Test-Path -Path $SourceVhd)) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskSourceVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskSourceVHDNotFoundError ` - -f $VM.name,$SourceVhd) - } - New-LabException @exceptionParameters - } # if - - # Should the Source VHD be copied or moved - if ($dataVhd.MoveSourceVHD) - { - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskByMovingSourceVHDMessage ` - -f $VM.Name,$vhd,$SourceVhd) - - $null = Move-Item ` - -Path $SourceVhd ` - -Destination $vhdPath ` - -Force ` - -ErrorAction Stop - } - else - { - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskByCopyingSourceVHDMessage ` - -f $VM.Name,$vhd,$SourceVhd) - - $null = Copy-Item ` - -Path $SourceVhd ` - -Destination $vhdPath ` - -Force ` - -ErrorAction Stop - } # if - } - else - { - $size = $dataVhd.size - - switch ($dataVhd.VhdType) - { - 'fixed' - { - # Create a new Fixed VHD - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` - -f $VM.Name,$vhd,'Fixed Data') - - $null = New-VHD ` - -Path $vhd ` - -SizeBytes $size ` - -Fixed ` - -ErrorAction Stop - break; - } # 'fixed' - - 'dynamic' - { - # Create a new Dynamic VHD - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` - -f $VM.Name,$vhd,'Dynamic Data') - - $null = New-VHD ` - -Path $vhd ` - -SizeBytes $size ` - -Dynamic ` - -ErrorAction Stop - break; - } # 'dynamic' - - 'differencing' - { - <# - A differencing disk is specified so check the Parent VHD - is specified and exists. - #> - $ParentVhd = $dataVhd.ParentVhd - if (-not $ParentVhd) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskParentVHDMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskParentVHDMissingError ` - -f $VM.name) - } - New-LabException @exceptionParameters - } # if - if (-not (Test-Path -Path $ParentVhd)) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskParentVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskParentVHDNotFoundError ` - -f $VM.name,$ParentVhd) - } - New-LabException @exceptionParameters - } # if - - # Create a new Differencing VHD - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` - -f $VM.Name,$vhd,"Differencing Data using Parent '$ParentVhd'") - - $null = New-VHD ` - -Path $vhd ` - -SizeBytes $size ` - -Differencing ` - -ParentPath $ParentVhd ` - -ErrorAction Stop - break; - } # 'differencing' - - default - { - $exceptionParameters = @{ - errorId = 'VMDataDiskUnknownTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskUnknownTypeError ` - -f $VM.Name,$vhd,$dataVhd.VhdType) - } - New-LabException @exceptionParameters - } # default - } # switch - } # if - - # Do folders need to be copied to this Data Disk? - if ($null -ne $dataVhd.CopyFolders) - { - <# - Files need to be copied to this Data VHD so - set up a mount folder for it to be mounted to. - Get Path to LabBuilder files - #> - $vmLabBuilderFiles = $VM.LabBuilderFilesPath - - $mountPoint = Join-Path ` - -Path $vmLabBuilderFiles ` - -ChildPath 'VHDMount' - - if (-not (Test-Path -Path $mountPoint -PathType Container)) - { - $null = New-Item ` - -Path $mountPoint ` - -ItemType Directory - } - - # Yes, initialize the disk (or check it is) - $initializeLabVHDParams = @{ - Path = $vhd - AccessPath = $mountPoint - } - - # Are we allowed to initialize/format the disk? - if ($dataVhd.PartitionStyle -and $dataVhd.FileSystem) - { - # Yes, initialize the disk - $initializeLabVHDParams += @{ - PartitionStyle = $dataVhd.PartitionStyle - FileSystem = $dataVhd.FileSystem - } - - # Set a FileSystemLabel too? - if ($dataVhd.FileSystemLabel) - { - $initializeLabVHDParams += @{ - FileSystemLabel = $dataVhd.FileSystemLabel - } - } - } - - Write-LabMessage -Message $($LocalizedData.InitializingVMDiskMessage ` - -f $VM.Name,$vhd) - - Initialize-LabVHD ` - @initializeLabVHDParams ` - -ErrorAction Stop - - # Copy each folder to the VM Data Disk - foreach ($copyFolder in @($dataVhd.CopyFolders)) - { - Write-LabMessage -Message $($LocalizedData.CopyingFoldersToVMDiskMessage ` - -f $VM.Name,$vhd,$copyFolder) - - Copy-item ` - -Path $copyFolder ` - -Destination $mountPoint ` - -Recurse ` - -Force - } - - # Dismount the VM Data Disk - Write-LabMessage -Message $($LocalizedData.DismountingVMDiskMessage ` - -f $VM.Name,$vhd) - - Dismount-VHD ` - -Path $vhd ` - -ErrorAction Stop - } - else - { - <# - No folders need to be copied but check if we - need to initialize the new disk. - #> - if ($dataVhd.PartitionStyle -and $dataVhd.FileSystem) - { - $InitializeVHDParams = @{ - Path = $vhd - PartitionStyle = $dataVhd.PartitionStyle - FileSystem = $dataVhd.FileSystem - } - - if ($dataVhd.FileSystemLabel) - { - $InitializeVHDParams += @{ - FileSystemLabel = $dataVhd.FileSystemLabel - } - } # if - - Write-LabMessage -Message $($LocalizedData.InitializingVMDiskMessage ` - -f $VM.Name,$vhd) - - Initialize-LabVHD ` - @InitializeVHDParams ` - -ErrorAction Stop - - # Dismount the VM Data Disk - Write-LabMessage -Message $($LocalizedData.DismountingVMDiskMessage ` - -f $VM.Name,$vhd) - - Dismount-VHD ` - -Path $vhd ` - -ErrorAction Stop - } # if - } # if - } # if - - # Get a list of disks attached to the VM - $VMHardDiskDrives = Get-VMHardDiskDrive ` - -VMName $VM.Name - - # The data disk VHD will now exist so ensure it is attached - if (($VMHardDiskDrives | Where-Object -Property Path -eq $vhd).Count -eq 0) - { - # The data disk is not yet attached - Write-LabMessage -Message $($LocalizedData.AddingVMDiskMessage ` - -f $VM.Name,$vhd,'Data') - - <# - Determine the ControllerLocation and ControllerNumber to - attach the VHD to. - #> - $controllerLocation = ($VMHardDiskDrives | - Measure-Object -Property ControllerLocation -Maximum).Maximum + 1 - - $newHardDiskParams = @{ - VMName = $VM.Name - Path = $vhd - ControllerType = 'SCSI' - ControllerLocation = $controllerLocation - ControllerNumber = 0 - ErrorAction = 'Stop' - } - if ($dataVhd.Shared -or $dataVhd.SupportPR) - { - $newHardDiskParams += @{ - SupportPersistentReservations = $true - } - } # if - - Write-Verbose -Message ($newHardDiskParams | Out-String | Fl *) -Verbose - - $null = Add-VMHardDiskDrive @newHardDiskParams - } # if - } # foreach -} diff --git a/src/lib/private/Update-LabVMDvdDrive.ps1 b/src/lib/private/Update-LabVMDvdDrive.ps1 deleted file mode 100644 index 53845192..00000000 --- a/src/lib/private/Update-LabVMDvdDrive.ps1 +++ /dev/null @@ -1,108 +0,0 @@ -<# - .SYNOPSIS - Updates the VM DVD Drives to match the VM Configuration. - - .DESCRIPTION - This cmdlet will take the VM configuration provided and ensure that the DVD Drives are - attached to the VM and with the specified ISO. - - The function will use the array of items in the DVDDrives property of the VM to create and - attach any DVD Drives that are missing. - - If an ISO File is specified in the DVD Drive then it will be mounted to the DVD Drive. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Update-LabVMDvdDrive -Lab $Lab -VM VM[0] - This will update the DVD Drives for the first VM in the configuration file c:\mylab\config.xml. - - .PARAMETER Lab - Contains the Lab object that was produced by the Get-Lab cmdlet. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .OUTPUTS - None. -#> -function Update-LabVMDvdDrive -{ - [CmdLetBinding()] - param - ( - [Parameter( - Mandatory, - Position=0)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Mandatory, - Position=1)] - [ValidateNotNullOrEmpty()] - [LabVM] - $VM - ) - - # If there are no DVD Drives just return - if (-not $VM.DVDDrives) - { - return - } - - [System.Int32] $DVDDriveCount = 0 - foreach ($DVDDrive in @($VM.DVDDrives)) - { - # Get a list of DVD Drives attached to the VM - $VMDVDDrives = @(Get-VMDVDDrive ` - -VMName $VM.Name) - - # The DVD Drive will now exist so ensure it is attached - if ($VMDVDDrives[$DVDDriveCount]) - { - # The DVD Drive is already attached then make sure the correct ISO - if ($VMDVDDrives[$DVDDriveCount].Path -ne $DVDDrive.Path) - { - if ($DVDDrive.Path) - { - Write-LabMessage -Message $($LocalizedData.MountingVMDVDDriveISOMessage ` - -f $VM.Name,$DVDDrive.Path) - } - else - { - Write-LabMessage -Message $($LocalizedData.DismountingVMDVDDriveISOMessage ` - -f $VM.Name,$VMDVDDrives[$DVDDriveCount].Path) - } # if - Set-VMDVDDrive ` - -VMName $VM.Name ` - -ControllerNumber $VMDVDDrives[$DVDDriveCount].ControllerNumber ` - -ControllerLocation $VMDVDDrives[$DVDDriveCount].ControllerLocation ` - -Path $DVDDrive.Path - } # if - } - else - { - # The DVD Drive does not exist - Write-LabMessage -Message $($LocalizedData.AddingVMDVDDriveMessage ` - -f $VM.Name) - - $NewDVDDriveParams = @{ - VMName = $VM.Name - ErrorAction = 'Stop' - } - - if ($DVDDrive.Path) - { - Write-LabMessage -Message $($LocalizedData.MountingVMDVDDriveISOMessage ` - -f $VM.Name,$DVDDrive.Path) - - $NewDVDDriveParams += @{ - Path = $DVDDrive.Path - } - } # if - $null = Add-VMDVDDrive @NewDVDDriveParams - } # if - $DVDDriveCount++ - } # foreach -} diff --git a/src/lib/private/Update-LabVMIntegrationService.ps1 b/src/lib/private/Update-LabVMIntegrationService.ps1 deleted file mode 100644 index 0c3cace5..00000000 --- a/src/lib/private/Update-LabVMIntegrationService.ps1 +++ /dev/null @@ -1,95 +0,0 @@ -<# - .SYNOPSIS - Updates the VM Integration Services to match the VM Configuration. - - .DESCRIPTION - This cmdlet will take the VM object provided and ensure the integration services specified - in it are enabled. - - The function will use comma delimited list of integration services in the VM object passed - and enable the integration services listed for this VM. - - If the IntegrationServices property of the VM is not set or set to null then ALL integration - services will be ENABLED. - - If the IntegrationServices property of the VM is set but is blank then ALL integration - services will be DISABLED. - - The IntegrationServices property should contain a comma delimited list of Integration Services - that should be enabled. - - The currently available Integration Services are: - - Guest Service Interface - - Heartbeat - - Key-Value Pair Exchange - - Shutdown - - Time Synchronization - - VSS - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Update-LabVMIntegrationService -VM VM[0] - This will update the Integration Services for the first VM in the configuration file c:\mylab\config.xml. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .OUTPUTS - None. -#> -function Update-LabVMIntegrationService -{ - [CmdLetBinding()] - param - ( - [Parameter( - Mandatory, - Position=1)] - [ValidateNotNullOrEmpty()] - [LabVM] - $VM - ) - - # Configure the Integration services - $integrationServices = $VM.IntegrationServices - if ($null -eq $integrationServices) - { - # Get the full list of Integration Service names localized - $integrationServices = ((Get-LabIntegrationServiceName) -Join ',') - } - - $enabledIntegrationServices = $integrationServices -split ',' - $existingIntegrationServices = Get-VMIntegrationService ` - -VMName $VM.Name ` - -ErrorAction Stop - - # Loop through listed integration services and enable them - foreach ($existingIntegrationService in $existingIntegrationServices) - { - if ($existingIntegrationService.Name -in $enabledIntegrationServices) - { - # This integration service should be enabled - if (-not $existingIntegrationService.Enabled) - { - # It is disabled so enable it - $existingIntegrationService | Enable-VMIntegrationService - - Write-LabMessage -Message $($LocalizedData.EnableVMIntegrationServiceMessage ` - -f $VM.Name,$existingIntegrationService.Name) - } # if - } - else - { - # This integration service should be disabled - if ($existingIntegrationService.Enabled) - { - # It is enabled so disable it - $existingIntegrationService | Disable-VMIntegrationService - - Write-LabMessage -Message $($LocalizedData.DisableVMIntegrationServiceMessage ` - -f $VM.Name,$existingIntegrationService.Name) - } # if - } # if - } # foreach -} diff --git a/src/lib/private/Wait-LabVMInitializationComplete.ps1 b/src/lib/private/Wait-LabVMInitializationComplete.ps1 deleted file mode 100644 index 10e593fd..00000000 --- a/src/lib/private/Wait-LabVMInitializationComplete.ps1 +++ /dev/null @@ -1,151 +0,0 @@ -<# - .SYNOPSIS - Waits for a VM to complete setup. - - .DESCRIPTION - When a VM starts up for the first time various scripts are run that prepare the Virtual Machine - to be managed as part of a Lab. This function will wait for these scripts to complete. - It determines if the setup has been completed by using PowerShell remoting to connect to the - VM and downloading the c:\windows\Setup\Scripts\InitialSetupCompleted.txt file. If this file - does not exist then the initial setup has not been completed. - - The cmdlet will wait for a maximum of 300 seconds for this process to be completed. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .PARAMETER Timeout - The maximum amount of time that this function will wait for the setup to complete. - If the timeout is reached before the process is complete an error will be thrown. - The timeout defaults to 300 seconds. - - .EXAMPLE - $Lab = Get-Lab -ConfigPath c:\mylab\config.xml - $VMs = Get-LabVM -Lab $Lab - Wait-LabVMInitializationComplete -VM $VMs[0] - Waits for the initial setup to complete on the first VM in the config.xml. - - .OUTPUTS - The path to the local copy of the Initial Setup complete file in the Labbuilder files folder - for this VM. -#> -function Wait-LabVMInitializationComplete -{ - [CmdLetBinding()] - [OutputType([System.String])] - param - ( - [Parameter(Mandatory = $true)] - [LabVM] - $VM, - - [Parameter()] - [System.Int32] - $Timeout = 300 - ) - - [DateTime] $StartTime = Get-Date - [System.Management.Automation.Runspaces.PSSession] $Session = $null - [System.Boolean] $Complete = $false - - # Get the root path of the VM - [System.String] $VMRootPath = $VM.VMRootPath - - # Get Path to LabBuilder files - [System.String] $VMLabBuilderFiles = $VM.LabBuilderFilesPath - - # Make sure the VM has started - Wait-LabVMStarted -VM $VM - - [System.String] $InitialSetupCompletePath = Join-Path ` - -Path $VMLabBuilderFiles ` - -ChildPath 'InitialSetupCompleted.txt' - - # Check the initial setup on this VM hasn't already completed - if (Test-Path -Path $InitialSetupCompletePath) - { - Write-LabMessage -Message $($LocalizedData.InitialSetupIsAlreadyCompleteMessaage ` - -f $VM.Name) - return $InitialSetupCompletePath - } - - while ((-not $Complete) ` - -and (((Get-Date) - $StartTime).TotalSeconds) -lt $TimeOut) - { - # Connect to the VM - $Session = Connect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - # Failed to connnect to the VM - if (-not $Session) - { - $exceptionParameters = @{ - errorId = 'InitialSetupCompleteError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.InitialSetupCompleteError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - return - } - - if (($Session) ` - -and ($Session.State -eq 'Opened') ` - -and (-not $Complete)) - { - # We connected OK - Download the script - while ((-not $Complete) ` - -and (((Get-Date) - $StartTime).TotalSeconds) -lt $TimeOut) - { - try - { - $null = Copy-Item ` - -Path "c:\windows\Setup\Scripts\InitialSetupCompleted.txt" ` - -Destination $VMLabBuilderFiles ` - -FromSession $Session ` - -Force ` - -ErrorAction Stop - $Complete = $true - } - catch - { - Write-LabMessage -Message $($LocalizedData.WaitingForInitialSetupCompleteMessage ` - -f $VM.Name, $Script:RetryConnectSeconds) - Start-Sleep ` - -Seconds $Script:RetryConnectSeconds - } # try - } # while - } # if - - # If the process didn't complete and we're out of time throw an exception - if ((-not $Complete) ` - -and (((Get-Date) - $StartTime).TotalSeconds) -ge $TimeOut) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - - $exceptionParameters = @{ - errorId = 'InitialSetupCompleteError' - errorCategory = 'OperationTimeout' - errorMessage = $($LocalizedData.InitialSetupCompleteError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } - - # Close the Session if it is opened - if (($Session) ` - -and ($Session.State -eq 'Opened')) - { - # Disconnect from the VM - Disconnect-LabVM ` - -VM $VM ` - -ErrorAction Continue - } # if - } # while - - return $InitialSetupCompletePath -} diff --git a/src/lib/private/Wait-LabVMOff.ps1 b/src/lib/private/Wait-LabVMOff.ps1 deleted file mode 100644 index 7e2286e7..00000000 --- a/src/lib/private/Wait-LabVMOff.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -<# - .SYNOPSIS - Wait for VM to enter the Off state. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM. - - .OUTPUTS - None. -#> -function Wait-LabVMOff -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - $runningVM = Get-VM -Name $VM.Name - while ($runningVM.State -ne 'Off') - { - $runningVM = Get-VM -Name $VM.Name - Start-Sleep -Seconds $Script:RetryHeartbeatSeconds - } # while -} diff --git a/src/lib/private/Wait-LabVMStarted.ps1 b/src/lib/private/Wait-LabVMStarted.ps1 deleted file mode 100644 index aea90963..00000000 --- a/src/lib/private/Wait-LabVMStarted.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -<# - .SYNOPSIS - Wait for the VM to enter the running state. - - .PARAMETER VM - A LabVM object pulled from the Lab Configuration file using Get-LabVM - - .OUTPUTS - None. -#> -function Wait-LabVMStarted -{ - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [LabVM] - $VM - ) - - # If the VM is not running then throw an exception - if ((Get-VM -VMName $VM.Name).State -ne 'Running') { - $exceptionParameters = @{ - errorId = 'VMNotRunningHeartbeatMessage' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMNotRunningHeartbeatMessage ` - -f $VM.name) - } - New-LabException @exceptionParameters - } # if - - # Names of IntegrationServices are not culture neutral, but have an ID - $heartbeatCultureNeutral = ( Get-VMIntegrationService -VMName $VM.Name | Where-Object { $_.ID -match "84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47" } ).Name - $heartbeat = Get-VMIntegrationService -VMName $VM.Name -Name $heartbeatCultureNeutral - - while (($heartbeat.PrimaryStatusDescription -ne 'OK') -and (-not [System.String]::IsNullOrEmpty($heartbeat.PrimaryStatusDescription))) - { - $heartbeat = Get-VMIntegrationService -VMName $VM.Name -Name $heartbeatCultureNeutral - - Write-LabMessage -Message $($LocalizedData.WaitingForVMHeartbeatMessage ` - -f $VM.Name,$Script:RetryHeartbeatSeconds) - - Start-Sleep -Seconds $Script:RetryHeartbeatSeconds - } # while -} diff --git a/src/lib/private/Write-LabMessage.ps1 b/src/lib/private/Write-LabMessage.ps1 deleted file mode 100644 index 09ff436f..00000000 --- a/src/lib/private/Write-LabMessage.ps1 +++ /dev/null @@ -1,93 +0,0 @@ -<# - .SYNOPSIS - Writes a Message of the specified Type. - - .DESCRIPTION - This cmdlet will write a message along with the time to the specified output stream. - - .PARAMETER Type - This can be one of the following: - Error - Writes to the Error Stream. - Warning - Writes to the Warning Stream. - Verbose - Writes to the Verbose Stream (default) - Debug - Writes to the Debug Stream. - Information - Writes to the Information Stream. - Output - Writes to the Output Stream (so should be used for a terminating message) - - .PARAMETER Message - The Message to output. - - .PARAMETER ForegroundColor - The foreground color of the message if being writen to the output stream. - - .EXAMPLE - Write-LabMessage -Type Verbose -Message 'Downloading file' - New-LabException @exceptionParameters - Outputs the message 'Downloading file' to the Verbose stream. - - .OUTPUTS - None -#> -function Write-LabMessage -{ - [CmdLetBinding()] - param - ( - [Parameter()] - [ValidateSet('Error', 'Warning', 'Verbose', 'Debug', 'Info', 'Alert')] - [System.String] - $Type = 'Verbose', - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Message, - - [Parameter()] - [System.String] - $ForegroundColor = 'Yellow' - ) - - $time = Get-Date -UFormat %T - - switch ($Type) - { - 'Error' - { - Write-Error -Message $Message - break - } - - 'Warning' - { - Write-Warning -Message ('[{0}]: {1}' -f $time, $Message) - break - } - - 'Verbose' - { - Write-Verbose -Message ('[{0}]: {1}' -f $time, $Message) - break - } - - 'Debug' - { - Write-Debug -Message ('[{0}]: {1}' -f $time, $Message) - break - } - - 'Info' - { - Write-Information -MessageData ('INFO: [{0}]: {1}' -f $time, $Message) - break - } - - 'Alert' - { - Write-Host ` - -ForegroundColor $ForegroundColor ` - -Object $Message - break - } - } # switch -} diff --git a/src/lib/public/Connect-LabVm.ps1 b/src/lib/public/Connect-LabVm.ps1 deleted file mode 100644 index 293b303b..00000000 --- a/src/lib/public/Connect-LabVm.ps1 +++ /dev/null @@ -1,118 +0,0 @@ -function Connect-LabVM -{ - [OutputType([System.Management.Automation.Runspaces.PSSession])] - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [LabVM] - $VM, - - [Parameter( - Position = 2)] - [System.Int32] - $ConnectTimeout = 300 - ) - - $startTime = Get-Date - $session = $null - $adminCredential = New-LabCredential ` - -Username '.\Administrator' ` - -Password $VM.AdministratorPassword - $fatalException = $false - - while (($null -eq $session) ` - -and (((Get-Date) - $startTime).TotalSeconds) -lt $ConnectTimeout ` - -and -not $fatalException) - { - try - { - <# - Get the Management IP Address of the VM - We repeat this because the IP Address will only be assiged - once the VM is fully booted. - #> - $ipAddress = Get-LabVMManagementIPAddress ` - -Lab $Lab ` - -VM $VM - - <# - Add the IP Address to trusted hosts if not already in it - This could be avoided if able to use SSL or if PS Direct is used. - Also, don't add if TrustedHosts is already * - #> - $trustedHosts = (Get-Item -Path WSMAN::localhost\Client\TrustedHosts).Value - - if (($trustedHosts -notlike "*$ipAddress*") -and ($trustedHosts -ne '*')) - { - if ([System.String]::IsNullOrWhitespace($trustedHosts)) - { - $trustedHosts = "$ipAddress" - } - else - { - $trustedHosts = "$trustedHosts,$ipAddress" - } - - Set-Item ` - -Path WSMAN::localhost\Client\TrustedHosts ` - -Value $trustedHosts ` - -Force - Write-LabMessage -Message $($LocalizedData.AddingIPAddressToTrustedHostsMessage ` - -f $VM.Name, $ipAddress) - } - - if (Test-WSMan -ComputerName $ipAddress -ErrorAction SilentlyContinue) - { - Write-LabMessage -Message $($LocalizedData.ConnectingVMMessage ` - -f $VM.Name, $ipAddress) - - $session = New-PSSession ` - -Name 'LabBuilder' ` - -ComputerName $ipAddress ` - -Credential $adminCredential ` - -ErrorAction Stop - } - else - { - Write-LabMessage -Message $($LocalizedData.WaitingForIPAddressAssignedMessage ` - -f $VM.Name, $Script:RetryConnectSeconds) - } - } - catch - { - if (-not $ipAddress) - { - Write-LabMessage -Message $($LocalizedData.WaitingForIPAddressAssignedMessage ` - -f $VM.Name, $Script:RetryConnectSeconds) - } - else - { - Write-LabMessage -Message $($LocalizedData.ConnectingVMFailedMessage ` - -f $VM.Name, $Script:RetryConnectSeconds, $_.Exception.Message) - } - - Start-Sleep -Seconds $Script:RetryConnectSeconds - } # Try - } # While - - <# - If a fatal exception occured or the connection just couldn't be established - then throw an exception so it can be caught by the calling code. - #> - if ($fatalException -or ($null -eq $session)) - { - # The connection failed so throw an error - $exceptionParameters = @{ - errorId = 'RemotingConnectionError' - errorCategory = 'ConnectionError' - errorMessage = $($LocalizedData.RemotingConnectionError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } - - return $session -} # Connect-LabVM diff --git a/src/lib/public/Disconnect-LabVm.ps1 b/src/lib/public/Disconnect-LabVm.ps1 deleted file mode 100644 index 01d72d4f..00000000 --- a/src/lib/public/Disconnect-LabVm.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -function Disconnect-LabVM -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position=1, - Mandatory=$true)] - [LabVM] $VM - ) - - $adminCredential = New-LabCredential ` - -Username '.\Administrator' ` - -Password $VM.AdministratorPassword - - # Get the Management IP Address of the VM - $ipAddress = Get-LabVMManagementIPAddress ` - -Lab $Lab ` - -VM $VM - - try - { - # Look for the session - $session = Get-PSSession ` - -Name 'LabBuilder' ` - -ComputerName $ipAddress ` - -Credential $adminCredential ` - -ErrorAction Stop - - if (-not $session) - { - # No session found to this machine so nothing to do. - Write-LabMessage -Message $($LocalizedData.VMSessionDoesNotExistMessage ` - -f $VM.Name) - } - else - { - if ($session.State -eq 'Opened') - { - # Disconnect the session - $null = $session | Disconnect-PSSession - Write-LabMessage -Message $($LocalizedData.DisconnectingVMMessage ` - -f $VM.Name,$IPAddress) - } - # Remove the session - $null = $session | Remove-PSSession -ErrorAction SilentlyContinue - } - } - catch - { - Throw $_ - } - finally - { - # Remove the entry from TrustedHosts - $trustedHosts = (Get-Item -Path WSMAN::localhost\Client\TrustedHosts).Value - - if (($trustedHosts -like "*$ipAddress*") -and ($trustedHosts -ne '*')) - { - $ipAddresses = @($trustedHosts -split ',') - $trustedHosts = ($ipAddresses | Where-Object -FilterScript { - $_ -ne $ipAddress - }) -join ',' - - Set-Item ` - -Path WSMAN::localhost\Client\TrustedHosts ` - -Value $trustedHosts ` - -Force - Write-LabMessage -Message $($LocalizedData.RemovingIPAddressFromTrustedHostsMessage ` - -f $VM.Name,$ipAddress) - } - } # try -} # Disconnect-LabVM diff --git a/src/lib/public/Get-Lab.ps1 b/src/lib/public/Get-Lab.ps1 deleted file mode 100644 index b03a048b..00000000 --- a/src/lib/public/Get-Lab.ps1 +++ /dev/null @@ -1,173 +0,0 @@ -function Get-Lab -{ - [CmdLetBinding()] - [OutputType([XML])] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ConfigPath, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String] - $labPath, - - [Parameter( - Position = 3)] - [Switch] - $SkipXMLValidation - ) - - <# - If a relative path to the config has been specified - then convert it to absolute path - #> - if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) - { - $ConfigPath = Join-Path ` - -Path (Get-Location).Path ` - -ChildPath $ConfigPath - } # if - - if (-not (Test-Path -Path $ConfigPath)) - { - $exceptionParameters = @{ - errorId = 'ConfigurationFileNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ConfigurationFileNotFoundError ` - -f $ConfigPath) - } - New-LabException @exceptionParameters - } # if - - $content = Get-Content -Path $ConfigPath -Raw - - if (-not $content) - { - $exceptionParameters = @{ - errorId = 'ConfigurationFileEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ConfigurationFileEmptyError ` - -f $ConfigPath) - } - New-LabException @exceptionParameters - } # if - - if (-not $SkipXMLValidation) - { - # Validate the XML - Assert-LabValidConfigurationXMLSchema ` - -ConfigPath $ConfigPath ` - -ErrorAction Stop - } - - # The XML passes the Schema check so load it. - $lab = New-Object -TypeName System.Xml.XmlDocument - $lab.PreserveWhitespace = $true - $lab.LoadXML($content) - - # Check the Required Windows Build - $requiredWindowsBuild = $lab.labbuilderconfig.settings.requiredwindowsbuild - - if ($requiredWindowsBuild -and ` - ($Script:CurrentBuild -lt $requiredWindowsBuild)) - { - $exceptionParameters = @{ - errorId = 'RequiredBuildNotMetError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.RequiredBuildNotMetError ` - -f $Script:CurrentBuild, $requiredWindowsBuild) - } - New-LabException @exceptionParameters - } # if - - <# - Figure out the Config path and load it into the XML object (if we can) - This path is used to find any additional configuration files that might - be provided with config - #> - [System.String] $ConfigPath = [System.IO.Path]::GetDirectoryName($ConfigPath) - [System.String] $xmlConfigPath = $lab.labbuilderconfig.settings.configpath - - if ($xmlConfigPath) - { - $xmlConfigPath = ConvertTo-LabAbsolutePath -Path $xmlConfigPath -BasePath $labPath - } - else - { - [System.String] $fullConfigPath = $ConfigPath - } - - $lab.labbuilderconfig.settings.setattribute('fullconfigpath', $fullConfigPath) - - # if the LabPath was passed as a parameter, set it in the config - if ($labPath) - { - $lab.labbuilderconfig.settings.SetAttribute('labpath', $labPath) - } - else - { - [System.String] $labPath = $lab.labbuilderconfig.settings.labpath - } - - # Get the VHDParentPathFull - if it isn't supplied default - [System.String] $vhdParentPath = $lab.labbuilderconfig.settings.vhdparentpath - - if (-not $vhdParentPath) - { - $vhdParentPath = 'Virtual Hard Disk Templates' - } - - # if the resulting parent path is not rooted make the root the Lab Path - $vhdParentPath = ConvertTo-LabAbsolutePath -Path $vhdParentPath -BasePath $labPath - $lab.labbuilderconfig.settings.setattribute('vhdparentpathfull', $vhdParentPath) - - # Get the DSCLibraryPathFull - if it isn't supplied default - [System.String] $dscLibraryPath = $lab.labbuilderconfig.settings.dsclibrarypath - - if (-not $dscLibraryPath) - { - $dscLibraryPath = Get-LabBuilderModulePath | Join-Path -ChildPath 'dsclibrary' - } # if - - # if the resulting parent path is not rooted make the root the Full config path - $dscLibraryPath = ConvertTo-LabAbsolutePath -Path $dscLibraryPath -BasePath $labPath - $lab.labbuilderconfig.settings.setattribute('dsclibrarypathfull', $dscLibraryPath) - - # Get the ResourcePathFull - if it isn't supplied default - [System.String] $resourcePath = $lab.labbuilderconfig.settings.resourcepath - - if (-not $resourcePath) - { - $resourcePath = 'Resource' - } # if - - # if the resulting Resource path is not rooted make the root the Lab Path - $resourcePath = ConvertTo-LabAbsolutePath -Path $resourcePath -BasePath $labPath - $lab.labbuilderconfig.settings.setattribute('resourcepathfull', $resourcePath) - - <# - Determine the ModulePath where alternate Lab PowerShell Modules can be found. - If a path is specified but it is relative, make it relative to the lab path. - Otherwise use it as is. - #> - [System.String] $modulePath = $lab.labbuilderconfig.settings.modulepath - - if ($modulePath) - { - $modulePath = ConvertTo-LabAbsolutePath -Path $modulePath -BasePath $labPath - - # If the path is not included in the PSModulePath add it - if (-not $env:PSModulePath.ToLower().Contains($modulePath.ToLower() + ';')) - { - $env:PSModulePath = "$modulePath;" + $env:PSModulePath - } # if - } # if - - return $lab -} # Get-Lab diff --git a/src/lib/public/Get-LabResourceIso.ps1 b/src/lib/public/Get-LabResourceIso.ps1 deleted file mode 100644 index 228b4cd3..00000000 --- a/src/lib/public/Get-LabResourceIso.ps1 +++ /dev/null @@ -1,92 +0,0 @@ -function Get-LabResourceISO -{ - [OutputType([LabResourceISO[]])] - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] - $Name - ) - - <# - Determine the ISORootPath where the ISO files should be found. - If no path is specified then look in the resource path. - If a path is specified but it is relative, make it relative to the resource path. - Otherwise use it as is. - #> - [System.String] $isoRootPath = $Lab.labbuilderconfig.Resources.ISOPath - - if ($isoRootPath) - { - $isoRootPath = ConvertTo-LabAbsolutePath -Path $isoRootPath ` - -BasePath $Lab.labbuilderconfig.settings.resourcepathfull - } - else - { - $isoRootPath = $Lab.labbuilderconfig.settings.resourcepathfull - } # if - - [LabResourceISO[]] $resourceISOs = @() - - if ($Lab.labbuilderconfig.resources) - { - foreach ($iso in $Lab.labbuilderconfig.resources.iso) - { - $isoName = $iso.Name - - if ($Name -and ($isoName -notin $Name)) - { - # A names list was passed but this ISO wasn't included - continue - } # if - - if ($isoName -eq 'iso') - { - $exceptionParameters = @{ - errorId = 'ResourceISONameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ResourceISONameIsEmptyError) - } - New-LabException @exceptionParameters - } # if - - $resourceISO = [LabResourceISO]::New($isoName) - $path = $iso.Path - - if ($path) - { - $path = ConvertTo-LabAbsolutePath -Path $path -BasePath $isoRootPath - } - else - { - # A Path is not provided - $exceptionParameters = @{ - errorId = 'ResourceISOPathIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ResourceISOPathIsEmptyError ` - -f $isoName) - } - New-LabException @exceptionParameters - } - - if ($iso.URL) - { - $resourceISO.URL = $iso.URL - } # if - - $resourceISO.Path = $path - $resourceISOs += @( $resourceISO ) - } # foreach - } # if - - return $resourceISOs -} # Get-LabResourceISO diff --git a/src/lib/public/Get-LabResourceModule.ps1 b/src/lib/public/Get-LabResourceModule.ps1 deleted file mode 100644 index 774d548f..00000000 --- a/src/lib/public/Get-LabResourceModule.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -function Get-LabResourceModule -{ - [OutputType([LabResourceModule[]])] - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name - ) - - [LabResourceModule[]] $ResourceModules = @() - if ($Lab.labbuilderconfig.resources) - { - foreach ($Module in $Lab.labbuilderconfig.resources.module) - { - $ModuleName = $Module.Name - if ($Name -and ($ModuleName -notin $Name)) - { - # A names list was passed but this Module wasn't included - continue - } # if - - if ($ModuleName -eq 'module') - { - $exceptionParameters = @{ - errorId = 'ResourceModuleNameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ResourceModuleNameIsEmptyError) - } - New-LabException @exceptionParameters - } # if - $ResourceModule = [LabResourceModule]::New($ModuleName) - $ResourceModule.URL = $Module.URL - $ResourceModule.Folder = $Module.Folder - $ResourceModule.MinimumVersion = $Module.MinimumVersion - $ResourceModule.RequiredVersion = $Module.RequiredVersion - $ResourceModules += @( $ResourceModule ) - } # foreach - } # if - return $ResourceModules -} # Get-LabResourceModule diff --git a/src/lib/public/Get-LabResourceMsu.ps1 b/src/lib/public/Get-LabResourceMsu.ps1 deleted file mode 100644 index f2073f05..00000000 --- a/src/lib/public/Get-LabResourceMsu.ps1 +++ /dev/null @@ -1,64 +0,0 @@ -function Get-LabResourceMSU -{ - [OutputType([LabResourceMSU[]])] - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name - ) - - [LabResourceMSU[]] $ResourceMSUs = @() - if ($Lab.labbuilderconfig.resources) - { - foreach ($MSU in $Lab.labbuilderconfig.resources.msu) - { - $MSUName = $MSU.Name - if ($Name -and ($MSUName -notin $Name)) - { - # A names list was passed but this MSU wasn't included - continue - } # if - - if ($MSUName -eq 'msu') - { - $exceptionParameters = @{ - errorId = 'ResourceMSUNameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ResourceMSUNameIsEmptyError) - } - New-LabException @exceptionParameters - } # if - $ResourceMSU = [LabResourceMSU]::New($MSUName, $MSU.URL) - $Path = $MSU.Path - if ($Path) - { - if (-not [System.IO.Path]::IsPathRooted($Path)) - { - $Path = Join-Path ` - -Path $Lab.labbuilderconfig.settings.resourcepathfull ` - -ChildPath $Path - } - } - else - { - $Path = $Lab.labbuilderconfig.settings.resourcepathfull - } - $FileName = Join-Path ` - -Path $Path ` - -ChildPath $MSU.URL.Substring($MSU.URL.LastIndexOf('/') + 1) - $ResourceMSU.Path = $Path - $ResourceMSU.Filename = $Filename - $ResourceMSUs += @( $ResourceMSU ) - } # foreach - } # if - return $ResourceMSUs -} # Get-LabResourceMSU diff --git a/src/lib/public/Get-LabSwitch.ps1 b/src/lib/public/Get-LabSwitch.ps1 deleted file mode 100644 index 966232f9..00000000 --- a/src/lib/public/Get-LabSwitch.ps1 +++ /dev/null @@ -1,116 +0,0 @@ -function Get-LabSwitch -{ - [OutputType([LabSwitch[]])] - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name - ) - - [System.String] $LabId = $Lab.labbuilderconfig.settings.labid - [LabSwitch[]] $Switches = @() - $ConfigSwitches = $Lab.labbuilderconfig.Switches.Switch - - foreach ($ConfigSwitch in $ConfigSwitches) - { - # It can't be switch because if the name attrib/node is missing the name property on the - # XML object defaults to the name of the parent. So we can't easily tell if no name was - # specified or if they actually specified 'switch' as the name. - $SwitchName = $ConfigSwitch.Name - if ($Name -and ($SwitchName -notin $Name)) - { - # A names list was passed but this swtich wasn't included - continue - } # if - - if ($SwitchName -eq 'switch') - { - $exceptionParameters = @{ - errorId = 'SwitchNameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.SwitchNameIsEmptyError) - } - New-LabException @exceptionParameters - } - - # Convert the switch type string to a LabSwitchType - $SwitchType = [LabSwitchType]::$($ConfigSwitch.Type) - - # If the SwitchType string doesn't match any enum value it will be - # set to null. - if (-not $SwitchType) - { - $exceptionParameters = @{ - errorId = 'UnknownSwitchTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.UnknownSwitchTypeError ` - -f $ConfigSwitch.Type, $SwitchName) - } - New-LabException @exceptionParameters - } # if - - # if a LabId is set for the lab, prepend it to the Switch name as long as it isn't - # an external switch. - if ($LabId -and ($SwitchType -ne [LabSwitchType]::External)) - { - $SwitchName = "$LabId$SwitchName" - } # if - - # Assemble the list of Mangement OS Adapters if any are specified for this switch - # Only Intenal and External switches are allowed Management OS adapters. - if ($ConfigSwitch.Adapters) - { - [LabSwitchAdapter[]] $ConfigAdapters = @() - foreach ($Adapter in $ConfigSwitch.Adapters.Adapter) - { - $AdapterName = $Adapter.Name - # if a LabId is set for the lab, prepend it to the adapter name. - # But only if it is not an External switch. - if ($LabId -and ($SwitchType -ne [LabSwitchType]::External)) - { - $AdapterName = "$LabId$AdapterName" - } - - $ConfigAdapter = [LabSwitchAdapter]::New($AdapterName) - $ConfigAdapter.MACAddress = $Adapter.MacAddress - $ConfigAdapters += @( $ConfigAdapter ) - } # foreach - if (($ConfigAdapters.Count -gt 0) ` - -and ($SwitchType -notin [LabSwitchType]::External, [LabSwitchType]::Internal)) - { - $exceptionParameters = @{ - errorId = 'AdapterSpecifiedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.AdapterSpecifiedError ` - -f $SwitchType, $SwitchName) - } - New-LabException @exceptionParameters - } # if - } - else - { - $ConfigAdapters = $null - } # if - - # Create the new Switch object - [LabSwitch] $NewSwitch = [LabSwitch]::New($SwitchName, $SwitchType) - $NewSwitch.VLAN = $ConfigSwitch.VLan - $NewSwitch.BindingAdapterName = $ConfigSwitch.BindingAdapterName - $NewSwitch.BindingAdapterMac = $ConfigSwitch.BindingAdapterMac - $NewSwitch.BindingAdapterMac = $ConfigSwitch.BindingAdapterMac - $NewSwitch.NatSubnet = $ConfigSwitch.NatSubnet - $NewSwitch.NatGatewayAddress = $ConfigSwitch.NatGatewayAddress - $NewSwitch.Adapters = $ConfigAdapters - $Switches += @( $NewSwitch ) - } # foreach - return $Switches -} # Get-LabSwitch diff --git a/src/lib/public/Get-LabVMTemplate.ps1 b/src/lib/public/Get-LabVMTemplate.ps1 deleted file mode 100644 index a16aae98..00000000 --- a/src/lib/public/Get-LabVMTemplate.ps1 +++ /dev/null @@ -1,296 +0,0 @@ -function Get-LabVMTemplate -{ - [OutputType([LabVMTemplate[]])] - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabVMTemplateVHD[]] $VMTemplateVHDs - ) - - # if VMTeplateVHDs array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMTemplateVHDs')) - { - [LabVMTemplateVHD[]] $VMTemplateVHDs = Get-LabVMTemplateVHD ` - -Lab $Lab - } # if - - [LabVMTemplate[]] $VMTemplates = @() - [System.String] $VHDParentPath = $Lab.labbuilderconfig.settings.vhdparentpathfull - - # Get a list of all templates in the Hyper-V system matching the phrase found in the fromvm - # config setting - [System.String] $FromVM = $Lab.labbuilderconfig.templates.fromvm - if ($FromVM) - { - $Templates = @(Get-VM -Name $FromVM) - foreach ($Template in $Templates) - { - if ($Name -and ($Template.Name -notin $Name)) - { - # A names list was passed but this VM Template wasn't included - continue - } # if - - [System.String] $VHDFilepath = (Get-VMHardDiskDrive -VMName $Template.Name).Path - [System.String] $VHDFilename = [System.IO.Path]::GetFileName($VHDFilepath) - [LabVMTemplate] $VMTemplate = [LabVMTemplate]::New($Template.Name) - $VMTemplate.Vhd = $VHDFilename - $VMTemplate.SourceVhd = $VHDFilepath - $VMTemplate.ParentVhd = (Join-Path -Path $VHDParentPath -ChildPath $VHDFilename) - $VMTemplates += @( $VMTemplate ) - } # foreach - } # if - - # Read the list of templates from the configuration file - $Templates = $Lab.labbuilderconfig.templates.template - foreach ($Template in $Templates) - { - # It can't be template because if the name attrib/node is missing the name property on - # the XML object defaults to the name of the parent. So we can't easily tell if no name - # was specified or if they actually specified 'template' as the name. - $TemplateName = $Template.Name - if ($Name -and ($TemplateName -notin $Name)) - { - # A names list was passed but this VM Template wasn't included - continue - } # if - - if ($TemplateName -eq 'template') - { - $exceptionParameters = @{ - errorId = 'EmptyTemplateNameError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.EmptyTemplateNameError) - } - New-LabException @exceptionParameters - } # if - - # Does the template already exist in the list? - [System.Boolean] $Found = $false - foreach ($VMTemplate in $VMTemplates) - { - if ($VMTemplate.Name -eq $TemplateName) - { - # The template already exists - so don't add it again - $Found = $true - Break - } # if - } # foreach - if (-not $Found) - { - # The template wasn't found in the list of templates so add it - $VMTemplate = [LabVMTemplate]::New($TemplateName) - # Add the new Template to the Templates Array - $VMTemplates += @( $VMTemplate ) - } # if - - # Determine the Source VHD, Template VHD and VHD - [System.String] $SourceVHD = $Template.SourceVHD - [System.String] $TemplateVHD = $Template.TemplateVHD - - # Throw an error if both a TemplateVHD and SourceVHD are provided - if ($TemplateVHD -and $SourceVHD) - { - $exceptionParameters = @{ - errorId = 'TemplateSourceVHDAndTemplateVHDConflictError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.TemplateSourceVHDAndTemplateVHDConflictError ` - -f $TemplateName) - } - New-LabException @exceptionParameters - } # if - - if ($TemplateVHD) - { - # A TemplateVHD was provided so look it up. - $VMTemplateVHD = ` - $VMTemplateVHDs | Where-Object -Property Name -EQ $TemplateVHD - if ($VMTemplateVHD) - { - # The TemplateVHD was found - $VMTemplate.Sourcevhd = $VMTemplateVHD.VHDPath - - # if a VHD filename wasn't specified in the TemplateVHD - # Just use the leaf of the SourceVHD - if ($VMTemplateVHD.VHD) - { - $VMTemplate.Vhd = $VMTemplateVHD.VHD - } - else - { - $VMTemplate.Vhd = Split-Path ` - -Path $VMTemplate.sourcevhd ` - -Leaf - } # if - } - else - { - # The TemplateVHD could not be found in the list - $exceptionParameters = @{ - errorId = 'TemplateTemplateVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.TemplateTemplateVHDNotFoundError ` - -f $TemplateName, $TemplateVHD) - } - New-LabException @exceptionParameters - } # if - } - elseif ($SourceVHD) - { - # A Source VHD was provided so use that. - # if this is a relative path, add it to the config path - if ([System.IO.Path]::IsPathRooted($SourceVHD)) - { - $VMTemplate.SourceVhd = $SourceVHD - } - else - { - $VMTemplate.SourceVhd = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $SourceVHD - } - - # A Source VHD file was specified - does it exist? - if (-not (Test-Path -Path $VMTemplate.sourcevhd)) - { - $exceptionParameters = @{ - errorId = 'TemplateSourceVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.TemplateSourceVHDNotFoundError ` - -f $TemplateName, $VMTemplate.sourcevhd) - } - New-LabException @exceptionParameters - } # if - - # if a VHD filename wasn't specified in the Template - # Just use the leaf of the SourceVHD - if ($Template.VHD) - { - $VMTemplate.vhd = $Template.VHD - } - else - { - $VMTemplate.vhd = Split-Path ` - -Path $VMTemplate.sourcevhd ` - -Leaf - } # if - } - elseif ($VMTemplate.SourceVHD) - { - # A SourceVHD is already set - # Usually because it was pulled From a Hyper-V VM template. - } - else - { - # Neither a SourceVHD or TemplateVHD was provided - # So throw an exception - $exceptionParameters = @{ - errorId = 'TemplateSourceVHDandTemplateVHDMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.TemplateSourceVHDandTemplateVHDMissingError ` - -f $TemplateName) - } - New-LabException @exceptionParameters - } # if - - # Ensure the ParentVHD is up-to-date - $VMTemplate.parentvhd = Join-Path ` - -Path $VHDParentPath ` - -ChildPath ([System.IO.Path]::GetFileName($VMTemplate.vhd)) - - # Write any template specific default VM attributes - [System.Int64] $MemoryStartupBytes = 1GB - if ($Template.MemoryStartupBytes) - { - $MemoryStartupBytes = (Invoke-Expression $Template.MemoryStartupBytes) - } # if - if ($MemoryStartupBytes -gt 0) - { - $VMTemplate.memorystartupbytes = $MemoryStartupBytes - } # if - if ($Template.DynamicMemoryEnabled) - { - $VMTemplate.DynamicMemoryEnabled = ($Template.DynamicMemoryEnabled -eq 'Y') - } - elseif (-not $VMTemplate.DynamicMemoryEnabled) - { - $VMTemplate.DynamicMemoryEnabled = $true - } # if - if ($Template.version) - { - $VMTemplate.version = $Template.version - } - elseif (-not $Template.version) - { - $VMTemplate.version = "8.0" - } # if - if ($Template.generation) - { - $VMTemplate.generation = $Template.generation - } - elseif (-not $Template.generation) - { - $VMTemplate.generation = 2 - } # if - - if ($Template.ProcessorCount) - { - $VMTemplate.ProcessorCount = $Template.ProcessorCount - } # if - if ($Template.ExposeVirtualizationExtensions) - { - $VMTemplate.ExposeVirtualizationExtensions = ($Template.ExposeVirtualizationExtensions -eq 'Y') - } # if - if ($Template.AdministratorPassword) - { - $VMTemplate.AdministratorPassword = $Template.AdministratorPassword - } # if - if ($Template.ProductKey) - { - $VMTemplate.ProductKey = $Template.ProductKey - } # if - if ($Template.TimeZone) - { - $VMTemplate.TimeZone = $Template.TimeZone - } # if - - if ($Template.OSType) - { - $VMTemplate.OSType = [LabOSType]::$($Template.OSType) - } - elseif (-not $VMTemplate.OSType) - { - $VMTemplate.OSType = [LabOStype]::Server - } # if - if ($Template.IntegrationServices) - { - $VMTemplate.IntegrationServices = $Template.IntegrationServices - } - else - { - $VMTemplate.IntegrationServices = $null - } # if - if ($Template.Packages) - { - $VMTemplate.Packages = $Template.Packages - } - else - { - $VMTemplate.Packages = $null - } # if - } # foreach - Return $VMTemplates -} # Get-LabVMTemplate diff --git a/src/lib/public/Get-LabVm.ps1 b/src/lib/public/Get-LabVm.ps1 deleted file mode 100644 index 2513b00c..00000000 --- a/src/lib/public/Get-LabVm.ps1 +++ /dev/null @@ -1,989 +0,0 @@ -function Get-LabVM -{ - [OutputType([LabVM[]])] - [CmdLetBinding()] - param ( - [Parameter( - Position=1, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position=2)] - [ValidateNotNullOrEmpty()] - [System.String[]] - $Name, - - [Parameter( - Position=3)] - [LabVMTemplate[]] - $vmTemplates, - - [Parameter( - Position=4)] - [LabSwitch[]] - $switches - ) - - # If VMTeplates array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMTemplates')) - { - [LabVMTemplate[]] $vmTemplates = Get-LabVMTemplate ` - -Lab $Lab - } - - # If Switches array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('Switches')) - { - [LabSwitch[]] $switches = Get-LabSwitch ` - -Lab $Lab - } - - [LabVM[]] $labVMs = @() - [System.String] $labPath = $Lab.labbuilderconfig.settings.labpath - [System.String] $labId = $Lab.labbuilderconfig.settings.labid - $vms = $Lab.labbuilderconfig.vms.vm - - foreach ($vm in $vms) - { - if ($vm.Name -eq 'VM') - { - $exceptionParameters = @{ - errorId = 'VMNameError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMNameError) - } - New-LabException @exceptionParameters - } # if - - # Get the Instance Count attribute - $instanceCount = $vm.InstanceCount - - if (-not $instanceCount) - { - $instanceCount = 1 - } - - foreach ($instance in 1..$instanceCount) - { - # If InstanceCount is 1 then don't increment the IP or MAC addresses or append count to the name - if ($instanceCount -eq 1) - { - $vmName = $vm.Name - $computerName = $vm.ComputerName - $incNetIds = 0 - } - else - { - $vmName = "$($vm.Name)$instance" - $computerName = "$($vm.ComputerName)$instance" - # This value is used to increment IP and MAC addresses - $incNetIds = $instance - 1 - } # if - - if ($Name -and ($vmName -notin $Name)) - { - # A names list was passed but this VM wasn't included - continue - } # if - - # If a LabId is set for the lab, prepend it to the VM name. - if ($labId) - { - $vmName = "$labId$vmName" - } - - if (-not $vm.Template) - { - $exceptionParameters = @{ - errorId = 'VMTemplateNameEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMTemplateNameEmptyError ` - -f $vmName) - } - New-LabException @exceptionParameters - } # if - - # Find the template that this VM uses and get the VHD Path - [System.String] $parentVHDPath = '' - [System.Boolean] $found = $false - - foreach ($vmTemplate in $vmTemplates) { - if ($vmTemplate.Name -eq $vm.Template) { - $parentVHDPath = $vmTemplate.ParentVHD - $found = $true - Break - } # if - } # foreach - - if (-not $found) - { - $exceptionParameters = @{ - errorId = 'VMTemplateNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMTemplateNotFoundError ` - -f $vmName,$vm.template) - } - New-LabException @exceptionParameters - } # if - - # Get path to Offline Domain Join file if it exists - [System.String] $nanoODJPath = $null - - if ($vm.NanoODJPath) - { - $nanoODJPath = $vm.NanoODJPath - } # if - - # Assemble the Network adapters that this VM will use - [LabVMAdapter[]] $vmAdapters = @() - [System.Int32] $adapterCount = 0 - - foreach ($vmAdapter in $vm.Adapters.Adapter) - { - $adapterCount++ - $adapterName = $vmAdapter.Name - $adapterSwitchName = $vmAdapter.SwitchName - - if ($adapterName -eq 'adapter') - { - $exceptionParameters = @{ - errorId = 'VMAdapterNameError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMAdapterNameError ` - -f $vmName) - } - New-LabException @exceptionParameters - } # if - - if (-not $adapterSwitchName) - { - $exceptionParameters = @{ - errorId = 'VMAdapterSwitchNameError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMAdapterSwitchNameError ` - -f $vmName,$adapterName) - } - New-LabException @exceptionParameters - } # if - - <# - If a LabId is set for the lab, prepend it to the adapter name - name and switch name. - #> - if ($labId) - { - $adapterName = "$labId$adapterName" - $adapterSwitchName = "$labId$adapterSwitchName" - } # if - - # Check the switch is in the switch list - $found = $false - - foreach ($switch in $switches) - { - <# - Match the switch name to the Adapter Switch Name or - the LabId and Adapter Switch Name - #> - if ($switch.Name -eq $adapterSwitchName) - { - # The switch is found in the switch list - record the VLAN (if there is one) - $found = $true - $switchVLan = $switch.Vlan - break - } # if - elseif ($switch.Name -eq $vmAdapter.SwitchName) - { - # The switch is found in the switch list - record the VLAN (if there is one) - $found = $true - $switchVLan = $switch.Vlan - - if ($switch.Type -eq [LabSwitchType]::External) - { - $adapterName = $vmAdapter.Name - $adapterSwitchName = $vmAdapter.SwitchName - } # if - - break - } - } # foreach - - if (-not $found) - { - $exceptionParameters = @{ - errorId = 'VMAdapterSwitchNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMAdapterSwitchNotFoundError ` - -f $vmName,$adapterName,$adapterSwitchName) - } - New-LabException @exceptionParameters - } # if - - # Figure out the VLan - If defined in the VM use it, otherwise use the one defined in the Switch, otherwise keep blank. - [System.String] $vLan = $vmAdapter.VLan - - if (-not $vLan) - { - $vLan = $switchVLan - } # if - - [System.Boolean] $MACAddressSpoofing = ($vmAdapter.macaddressspoofing -eq 'On') - - # Have we got any IPv4 settings? - Remove-Variable -Name IPv4 -ErrorAction SilentlyContinue - - if ($vmAdapter.IPv4) - { - if ($vmAdapter.IPv4.Address) - { - $ipv4 = [LabVMAdapterIPv4]::New(` - (Get-LabNextIpAddress ` - -IpAddress $vmAdapter.IPv4.Address` - -Step $incNetIds)` - ,$vmAdapter.IPv4.SubnetMask) - } # if - - $ipv4.defaultgateway = $vmAdapter.IPv4.DefaultGateway - $ipv4.dnsserver = $vmAdapter.IPv4.DNSServer - } # if - - # Have we got any IPv6 settings? - Remove-Variable -Name IPv6 -ErrorAction SilentlyContinue - - if ($vmAdapter.IPv6) - { - if ($vmAdapter.IPv6.Address) - { - $ipv6 = [LabVMAdapterIPv6]::New(` - (Get-LabNextIpAddress ` - -IpAddress $vmAdapter.IPv6.Address` - -Step $incNetIds)` - ,$vmAdapter.IPv6.SubnetMask) - } # if - - $ipv6.defaultgateway = $vmAdapter.IPv6.DefaultGateway - $ipv6.dnsserver = $vmAdapter.IPv6.DNSServer - } # if - - $newVMAdapter = [LabVMAdapter]::New($adapterName) - $newVMAdapter.SwitchName = $adapterSwitchName - - if ($vmAdapter.macaddress) - { - $newVMAdapter.MACAddress = Get-NextMacAddress ` - -MacAddress $vmAdapter.macaddress ` - -Step $incNetIds - } # if - - $newVMAdapter.MACAddressSpoofing = $MACAddressSpoofing - $newVMAdapter.VLan = $vLan - $newVMAdapter.IPv4 = $ipv4 - $newVMAdapter.IPv6 = $ipv6 - $vmAdapters += @( $newVMAdapter ) - } # foreach - - # Assemble the Data Disks this VM will use - [LabDataVHD[]] $dataVhds = @() - [System.Int32] $dataVhdCount = 0 - - foreach ($vmDataVhd in $vm.DataVhds.DataVhd) - { - $dataVhdCount++ - - # Load all the VHD properties and check they are valid - [System.String] $vhd = $vmDataVhd.Vhd - - if (-not $vmDataVhd.Vhd) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskVHDEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskVHDEmptyError ` - -f $vmName) - } - New-LabException @exceptionParameters - } # if - - <# - Adjust the path to be relative to the Virtual Hard Disks folder of the VM - if it doesn't contain a root (e.g. c:\) - #> - if (-not [System.IO.Path]::IsPathRooted($vhd)) - { - $vhd = Join-Path ` - -Path $labPath ` - -ChildPath "$($vmName)\Virtual Hard Disks\$vhd" - } # if - - # Does the VHD already exist? - $exists = Test-Path ` - -Path $vhd - - # Create the new Data VHD object - $newDataVHD = [LabDataVHD]::New($vhd) - - # Get the Parent VHD and check it exists if passed - if ($vmDataVhd.ParentVHD) - { - $newDataVHD.ParentVhd = $vmDataVhd.ParentVHD - <# - Adjust the path to be relative to the Virtual Hard Disks folder of the VM - if it doesn't contain a root (e.g. c:\) - #> - if (-not [System.IO.Path]::IsPathRooted($newDataVHD.ParentVhd)) - { - $newDataVHD.ParentVhd = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $newDataVHD.ParentVhd - } - - if (-not (Test-Path -Path $newDataVHD.ParentVhd)) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskParentVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskParentVHDNotFoundError ` - -f $vmName,$newDataVHD.ParentVhd) - } - New-LabException @exceptionParameters - } # if - } # if - - # Get the Source VHD and check it exists if passed - if ($vmDataVhd.SourceVHD) - { - $newDataVHD.SourceVhd = $vmDataVhd.SourceVHD - <# - Adjust the path to be relative to the Virtual Hard Disks folder of the VM - if it doesn't contain a root (e.g. c:\) - #> - if (-not [System.IO.Path]::IsPathRooted($newDataVHD.SourceVhd)) - { - $newDataVHD.SourceVhd = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $newDataVHD.SourceVhd - } # if - - if (-not (Test-Path -Path $newDataVHD.SourceVhd)) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskSourceVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskSourceVHDNotFoundError ` - -f $vmName,$newDataVHD.SourceVhd) - } - New-LabException @exceptionParameters - } # if - } # if - - # Get the disk size if provided - if ($vmDataVhd.Size) - { - $newDataVHD.Size = (Invoke-Expression -Command $vmDataVhd.Size) - } # if - - # Get the Shared flag - $newDataVHD.Shared = ($vmDataVhd.Shared -eq 'Y') - - # Get the Support Persistent Reservations - $newDataVHD.SupportPR = ($vmDataVhd.SupportPR -eq 'Y') - - # Validate the data disk type specified - if ($vmDataVhd.Type) - { - switch ($vmDataVhd.Type) - { - 'fixed' - { - break - } - - 'dynamic' - { - break - } - - 'differencing' - { - if (-not $newDataVHD.ParentVhd) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskParentVHDMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskParentVHDMissingError ` - -f $vmName) - } - New-LabException @exceptionParameters - } # if - - if ($newDataVHD.Shared) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskSharedDifferencingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskSharedDifferencingError ` - -f $vmName,$VHD) - } - New-LabException @exceptionParameters - } # if - - break - } - - default - { - $exceptionParameters = @{ - errorId = 'VMDataDiskUnknownTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskUnknownTypeError ` - -f $vmName,$VHD,$vmDataVhd.Type) - } - New-LabException @exceptionParameters - } - } # switch - - $newDataVHD.VHDType = [LabVHDType]::$($vmDataVhd.Type) - } # if - - # Get Partition Style for the new disk. - if ($vmDataVhd.PartitionStyle) - { - $PartitionStyle = [LabPartitionStyle]::$($vmDataVhd.PartitionStyle) - - if (-not $PartitionStyle) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskPartitionStyleError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskPartitionStyleError ` - -f $vmName,$VHD,$vmDataVhd.PartitionStyle) - } - New-LabException @exceptionParameters - } # if - $newDataVHD.PartitionStyle = $PartitionStyle - } # if - - # Get file system for the new disk. - if ($vmDataVhd.FileSystem) - { - $FileSystem = [LabFileSystem]::$($vmDataVhd.FileSystem) - - if (-not $FileSystem) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskFileSystemError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskFileSystemError ` - -f $vmName,$VHD,$vmDataVhd.FileSystem) - } - New-LabException @exceptionParameters - } # if - $newDataVHD.FileSystem = $FileSystem - } # if - - # Has a file system label been provided? - if ($vmDataVhd.FileSystemLabel) - { - $newDataVHD.FileSystemLabel = $vmDataVhd.FileSystemLabel - } # if - - <# - If the Partition Style, File System or File System Label has been - provided then ensure Partition Style and File System are set. - #> - if ($newDataVHD.PartitionStyle ` - -or $newDataVHD.FileSystem ` - -or $newDataVHD.FileSystemLabel) - { - if (-not $newDataVHD.PartitionStyle) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskPartitionStyleMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskPartitionStyleMissingError ` - -f $vmName,$VHD) - } - New-LabException @exceptionParameters - } # if - - if (-not $newDataVHD.FileSystem) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskFileSystemMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskFileSystemMissingError ` - -f $vmName,$VHD) - } - New-LabException @exceptionParameters - } # if - } # if - - # Get the Folder to copy and check it exists if passed - if ($vmDataVhd.CopyFolders) - { - foreach ($CopyFolder in ($vmDataVhd.CopyFolders -Split ',')) - { - <# - Adjust the path to be relative to the configuration folder - if it doesn't contain a root (e.g. c:\) - #> - if (-not [System.IO.Path]::IsPathRooted($CopyFolder)) - { - $CopyFolder = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $CopyFolder - } # if - - if (-not (Test-Path -Path $CopyFolder -Type Container)) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskCopyFolderMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskCopyFolderMissingError ` - -f $vmName,$VHD,$CopyFolder) - } - New-LabException @exceptionParameters - } - } # foreach - - $newDataVHD.CopyFolders = $vmDataVhd.CopyFolders - } # if - - # Should the Source VHD be moved rather than copied - if ($vmDataVhd.MoveSourceVHD) - { - $newDataVHD.MoveSourceVHD = ($vmDataVhd.MoveSourceVHD -eq 'Y') - if (-not $newDataVHD.SourceVHD) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskSourceVHDIfMoveError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskSourceVHDIfMoveError ` - -f $vmName,$VHD) - } - New-LabException @exceptionParameters - } # if - } # if - - # if the data disk file doesn't exist then some basic parameters MUST be provided - if (-not $exists ` - -and ( ( ( -not $newDataVHD.VhdType ) -or ( $newDataVHD.Size -eq 0) ) ` - -and -not $newDataVHD.SourceVhd ) ) - { - $exceptionParameters = @{ - errorId = 'VMDataDiskCantBeCreatedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDataDiskCantBeCreatedError ` - -f $vmName,$VHD) - } - New-LabException @exceptionParameters - } # if - - $dataVHDs += @( $newDataVHD ) - } # foreach - - # Assemble the DVD Drives this VM will use - [LabDVDDrive[]] $dvdDrives = @() - [System.Int32] $dvdDriveCount = 0 - - foreach ($vmDVDDrive in $vm.DVDDrives.DVDDrive) - { - $dvdDriveCount++ - - # Create the new DVD Drive object - $newDVDDrive = [LabDVDDRive]::New() - - # Load all the DVD Drive properties and check they are valid - if ($vmDVDDrive.ISO) - { - <# - Look the ISO up in the ISO Resources - Pull the list of Resource ISOs available if not already pulled from Lab. - #> - if (-not $resourceISOs) - { - $resourceISOs = Get-LabResourceISO ` - -Lab $Lab - } # if - - # Lookup the Resource ISO record - $resourceISO = $resourceISOs | Where-Object -Property Name -eq $vmDVDDrive.ISO - - if (-not $resourceISO) - { - # The ISO Resource was not found - $exceptionParameters = @{ - errorId = 'VMDVDDriveISOResourceNotFOundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDVDDriveISOResourceNotFOundError ` - -f $vmName,$vmDVDDrive.ISO) - } - New-LabException @exceptionParameters - } # if - - # The ISO resource was found so populate the ISO details - $newDVDDrive.ISO = $vmDVDDrive.ISO - $newDVDDrive.Path = $resourceISO.Path - } # if - - $dvdDrives += @( $newDVDDrive ) - } # foreach - - # Does the VM have an Unattend file specified? - $unattendFile = '' - - if ($vm.UnattendFile) - { - if ([System.IO.Path]::IsPathRooted($vm.UnattendFile)) - { - $unattendFile = $vm.UnattendFile - } - else - { - $unattendFile = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $vm.UnattendFile - } # if - - if (-not (Test-Path $unattendFile)) - { - $exceptionParameters = @{ - errorId = 'UnattendFileMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.UnattendFileMissingError ` - -f $vmName,$unattendFile) - } - New-LabException @exceptionParameters - } # if - } # if - - # Does the VM specify a Setup Complete Script? - $setupComplete = '' - - if ($vm.SetupComplete) - { - if ([System.IO.Path]::IsPathRooted($vm.SetupComplete)) - { - $setupComplete = $vm.SetupComplete - } - else - { - $setupComplete = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $vm.SetupComplete - } # if - - if ([System.IO.Path]::GetExtension($setupComplete).ToLower() -notin '.ps1','.cmd' ) - { - $exceptionParameters = @{ - errorId = 'SetupCompleteFileBadTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.SetupCompleteFileBadTypeError ` - -f $vmName,$setupComplete) - } - New-LabException @exceptionParameters - } # if - - if (-not (Test-Path $setupComplete)) - { - $exceptionParameters = @{ - errorId = 'SetupCompleteFileMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.SetupCompleteFileMissingError ` - -f $vmName,$setupComplete) - } - New-LabException @exceptionParameters - } # if - } # if - - # Create the Lab DSC object - $labDSC = [LabDSC]::New($vm.DSC.ConfigName) - - # Load the DSC Config File setting and check it - $labDSC.ConfigFile = '' - - if ($vm.DSC.ConfigFile) - { - if (-not [System.IO.Path]::IsPathRooted($vm.DSC.ConfigFile)) - { - $labDSC.ConfigFile = Join-Path ` - -Path $Lab.labbuilderconfig.settings.dsclibrarypathfull ` - -ChildPath $vm.DSC.ConfigFile - } - else - { - $labDSC.ConfigFile = $vm.DSC.ConfigFile - } # if - - if ([System.IO.Path]::GetExtension($labDSC.ConfigFile).ToLower() -ne '.ps1' ) - { - $exceptionParameters = @{ - errorId = 'DSCConfigFileBadTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfigFileBadTypeError ` - -f $vmName,$labDSC.ConfigFile) - } - New-LabException @exceptionParameters - } # if - - if (-not (Test-Path $labDSC.ConfigFile)) - { - $exceptionParameters = @{ - errorId = 'DSCConfigFileMissingError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfigFileMissingError ` - -f $vmName,$labDSC.ConfigFile) - } - New-LabException @exceptionParameters - } # if - - if (-not $vm.DSC.ConfigName) - { - $exceptionParameters = @{ - errorId = 'DSCConfigNameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DSCConfigNameIsEmptyError ` - -f $vmName) - } - New-LabException @exceptionParameters - } # if - } # if - - # Load the DSC Parameters - $labDSC.Parameters = '' - - if ($vm.DSC.Parameters) - { - <# - Correct any LFs into CRLFs to ensure the new line format is the same when - pulled from the XML. - #> - $labDSC.Parameters = ($vm.DSC.Parameters -replace "`r`n","`n") -replace "`n","`r`n" - } # if - - # Load the DSC Parameters - $labDSC.Logging = ($vm.DSC.Logging -eq 'Y') - - # Get the Memory Startup Bytes (from the template or VM) - [System.Int64] $memoryStartupBytes = 1GB - - if ($vm.memorystartupbytes) - { - $memoryStartupBytes = (Invoke-Expression -Command $vm.memorystartupbytes) - } - elseif ($vmTemplate.memorystartupbytes) - { - $memoryStartupBytes = $vmTemplate.memorystartupbytes - } # if - - # Get the Dynamic Memory Enabled flag - $dynamicMemoryEnabled = $true - - if ($vm.DynamicMemoryEnabled) - { - $dynamicMemoryEnabled = ($vm.DynamicMemoryEnabled -eq 'Y') - } - elseif ($vmTemplate.DynamicMemoryEnabled) - { - $dynamicMemoryEnabled = $vmTemplate.DynamicMemoryEnabled - } # if - - # Get the Number of vCPUs (from the template or VM) - [System.Int32] $processorCount = 1 - - if ($vm.processorcount) - { - $processorCount = (Invoke-Expression $vm.processorcount) - } - elseif ($vmTemplate.processorcount) - { - $processorCount = $vmTemplate.processorcount - } # if - - # Get the Expose Virtualization Extensions flag - if ($vm.ExposeVirtualizationExtensions) - { - $exposeVirtualizationExtensions = ($vm.ExposeVirtualizationExtensions -eq 'Y') - } - elseif ($vmTemplate.ExposeVirtualizationExtensions) - { - $exposeVirtualizationExtensions = $vmTemplate.ExposeVirtualizationExtensions - } # if - - <# - If VM requires ExposeVirtualizationExtensions but - it is not supported on Host then throw an exception. - #> - if ($exposeVirtualizationExtensions -and ($Script:CurrentBuild -lt 10565)) - { - $exceptionParameters = @{ - errorId = 'VMVirtualizationExtError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMVirtualizationExtError ` - -f $vmName) - } - New-LabException @exceptionParameters - } # if - - $useDifferencingDisk = $true - - if ($vm.UseDifferencingDisk -eq 'N') - { - $useDifferencingDisk = $false - } # if - - # Get the Integration Services flags - if ($null -ne $vm.IntegrationServices) - { - $integrationServices = $vm.IntegrationServices - } - elseif ($null -ne $vmTemplate.IntegrationServices) - { - $integrationServices = $vmTemplate.IntegrationServices - } # if - - # Get the Administrator password (from the template or VM) - $administratorPassword = '' - - if ($vm.administratorpassword) - { - $administratorPassword = $vm.administratorpassword - } - elseif ($vmTemplate.administratorpassword) - { - $administratorPassword = $vmTemplate.administratorpassword - } # if - - # Get the Product Key (from the template or VM) - $productKey = '' - - if ($vm.productkey) - { - $productKey = $vm.productkey - } - elseif ($vmTemplate.productkey) - { - $productKey = $vmTemplate.productkey - } # if - - # Get the Timezone (from the template or VM) - $timezone = 'Pacific Standard Time' - - if ($vm.timezone) - { - $timezone = $vm.timezone - } - elseif ($vmTemplate.timezone) - { - $timezone = $vmTemplate.timezone - } # if - - # Get the OS Type - $osType = [LabOStype]::Server - - if ($vm.OSType) - { - $osType = $vm.OSType - } - elseif ($vmTemplate.OSType) - { - $osType = $vmTemplate.OSType - } # if - - # Get the Bootorder - [Byte] $bootorder = [Byte]::MaxValue - - if ($vm.bootorder) - { - $bootorder = $vm.bootorder - } # if - - # Get the Packages - [System.String] $packages = $null - - if ($vm.packages) - { - $packages = $vm.packages - } - elseif ($vmTemplate.packages) - { - $packages = $vmTemplate.packages - } # if - - # Get the Version (from the template or VM) - $version = '8.0' - - if ($vm.version) - { - $version = $vm.version - } - elseif ($vmTemplate.version) - { - $version = $vmTemplate.version - } # if - - # Get the Generation (from the template or VM) - $generation = '2' - - if ($vm.generation) - { - $generation = $vm.generation - } - elseif ($vmTemplate.generation) - { - $generation = $vmTemplate.generation - } # if - - # Get the Certificate Source - $certificateSource = [LabCertificateSource]::Guest - - if ($osType -eq [LabOSType]::Nano) - { - # Nano Server can't generate certificates so must always be set to Host - $certificateSource = [LabCertificateSource]::Host - } - elseif ($vm.CertificateSource) - { - $certificateSource = $vm.CertificateSource - } # if - - - $labVM = [LabVM]::New($vmName,$computerName) - $labVM.Template = $vm.Template - $labVM.ParentVHD = $parentVHDPath - $labVM.UseDifferencingDisk = $useDifferencingDisk - $labVM.MemoryStartupBytes = $memoryStartupBytes - $labVM.DynamicMemoryEnabled = $dynamicMemoryEnabled - $labVM.ProcessorCount = $processorCount - $labVM.ExposeVirtualizationExtensions = $exposeVirtualizationExtensions - $labVM.IntegrationServices = $integrationServices - $labVM.AdministratorPassword = $administratorPassword - $labVM.ProductKey = $productKey - $labVM.TimeZone =$timezone - $labVM.UnattendFile = $unattendFile - $labVM.SetupComplete = $setupComplete - $labVM.OSType = $osType - $labVM.CertificateSource = $certificateSource - $labVM.Bootorder = $bootorder - $labVM.Packages = $packages - $labVM.Version = $version - $labVM.Generation = $generation - $labVM.Adapters = $vmAdapters - $labVM.DataVHDs = $dataVHDs - $labVM.DVDDrives = $dvdDrives - $labVM.DSC = $labDSC - $labVM.NanoODJPath = $nanoODJPath - $labVM.VMRootPath = Join-Path ` - -Path $labPath ` - -ChildPath $vmName - $labVM.LabBuilderFilesPath = Join-Path ` - -Path $labPath ` - -ChildPath "$vmName\LabBuilder Files" - $labVMs += @( $labVM ) - } # foreach - } # foreach - - return $labVMs -} # Get-LabVM diff --git a/src/lib/public/Get-LabVmTemplateVhd.ps1 b/src/lib/public/Get-LabVmTemplateVhd.ps1 deleted file mode 100644 index f2be7f57..00000000 --- a/src/lib/public/Get-LabVmTemplateVhd.ps1 +++ /dev/null @@ -1,290 +0,0 @@ -function Get-LabVMTemplateVHD -{ - [OutputType([LabVMTemplateVHD[]])] - [CmdLetBinding()] - param - ( - [Parameter ( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name - ) - - # return null if the TemplateVHDs node does not exist - if (-not $Lab.labbuilderconfig.TemplateVHDs) - { - return - } - - # Determine the ISORootPath where the ISO files should be found - # if no path is specified then look in the same path as the config - # if a path is specified but it is relative, make it relative to the - # config path. Otherwise use it as is. - [System.String] $ISORootPath = $Lab.labbuilderconfig.TemplateVHDs.ISOPath - if (-not $ISORootPath) - { - $ISORootPath = $Lab.labbuilderconfig.settings.fullconfigpath - } - else - { - if (-not [System.IO.Path]::IsPathRooted($ISORootPath)) - { - $ISORootPath = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $ISORootPath - } # if - } # if - if (-not (Test-Path -Path $ISORootPath -Type Container)) - { - $exceptionParameters = @{ - errorId = 'VMTemplateVHDISORootPathNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMTemplateVHDISORootPathNotFoundError ` - -f $ISORootPath) - } - New-LabException @exceptionParameters - } # if - - # Determine the VHDRootPath where the VHD files should be put - # if no path is specified then look in the same path as the config - # if a path is specified but it is relative, make it relative to the - # config path. Otherwise use it as is. - [System.String] $VHDRootPath = $Lab.labbuilderconfig.TemplateVHDs.VHDPath - if (-not $VHDRootPath) - { - $VHDRootPath = $Lab.labbuilderconfig.settings.fullconfigpath - } - else - { - if (-not [System.IO.Path]::IsPathRooted($VHDRootPath)) - { - $VHDRootPath = Join-Path ` - -Path $Lab.labbuilderconfig.settings.fullconfigpath ` - -ChildPath $VHDRootPath - } # if - } # if - if (-not (Test-Path -Path $VHDRootPath -Type Container)) - { - $exceptionParameters = @{ - errorId = 'VMTemplateVHDRootPathNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMTemplateVHDRootPathNotFoundError ` - -f $VHDRootPath) - } - New-LabException @exceptionParameters - } # if - - $TemplatePrefix = $Lab.labbuilderconfig.templatevhds.prefix - - # Read the list of templateVHD from the configuration file - $TemplateVHDs = $Lab.labbuilderconfig.templatevhds.templatevhd - [LabVMTemplateVHD[]] $VMTemplateVHDs = @() - foreach ($TemplateVHD in $TemplateVHDs) - { - # It can't be template because if the name attrib/node is missing the name property on - # the XML object defaults to the name of the parent. So we can't easily tell if no name - # was specified or if they actually specified 'templatevhd' as the name. - $TemplateVHDName = $TemplateVHD.Name - if ($Name -and ($TemplateVHDName -notin $Name)) - { - # A names list was passed but this VM Template VHD wasn't included - continue - } # if - - if (($TemplateVHDName -eq 'TemplateVHD') ` - -or ([System.String]::IsNullOrWhiteSpace($TemplateVHDName))) - { - $exceptionParameters = @{ - errorId = 'EmptyVMTemplateVHDNameError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.EmptyVMTemplateVHDNameError) - } - New-LabException @exceptionParameters - } # if - - # Get the ISO Path - [System.String] $ISOPath = $TemplateVHD.ISO - if (-not $ISOPath) - { - $exceptionParameters = @{ - errorId = 'EmptyVMTemplateVHDISOPathError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.EmptyVMTemplateVHDISOPathError ` - -f $TemplateVHD.Name) - } - New-LabException @exceptionParameters - } # if - - # Adjust the ISO Path if required - if (-not [System.IO.Path]::IsPathRooted($ISOPath)) - { - $ISOPath = Join-Path ` - -Path $ISORootPath ` - -ChildPath $ISOPath - } # if - - # Does the ISO Exist? - if (-not (Test-Path -Path $ISOPath)) - { - $URL = $TemplateVHD.URL - if ($URL) - { - Write-LabMessage ` - -Type Alert ` - -Message $($LocalizedData.ISONotFoundDownloadURLMessage ` - -f $TemplateVHD.Name, $ISOPath, $URL) - } # if - $exceptionParameters = @{ - errorId = 'VMTemplateVHDISOPathNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMTemplateVHDISOPathNotFoundError ` - -f $TemplateVHD.Name, $ISOPath) - } - New-LabException @exceptionParameters - } # if - - # Get the VHD Path - [System.String] $VHDPath = $TemplateVHD.VHD - if (-not $VHDPath) - { - $exceptionParameters = @{ - errorId = 'EmptyVMTemplateVHDPathError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.EmptyVMTemplateVHDPathError ` - -f $TemplateVHD.Name) - } - New-LabException @exceptionParameters - } # if - - # Adjust the VHD Path if required - if (-not [System.IO.Path]::IsPathRooted($VHDPath)) - { - $VHDPath = Join-Path ` - -Path $VHDRootPath ` - -ChildPath $VHDPath - } # if - - # Add the template prefix to the VHD name. - if (-not ([System.String]::IsNullOrWhitespace($TemplatePrefix))) - { - $VHDPath = Join-Path ` - -Path (Split-Path -Path $VHDPath)` - -ChildPath ("$TemplatePrefix$(Split-Path -Path $VHDPath -Leaf)") - } # if - - # Get the Template OS Type - $OSType = [LabOStype]::Server - if ($TemplateVHD.OSType) - { - $OSType = [LabOStype]::$($TemplateVHD.OSType) - } # if - if (-not $OSType) - { - $exceptionParameters = @{ - errorId = 'InvalidVMTemplateVHDOSTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InvalidVMTemplateVHDOSTypeError ` - -f $TemplateVHD.Name, $TemplateVHD.OSType) - } - New-LabException @exceptionParameters - } # if - - # Get the Template Wim Image to use - $Edition = $null - if ($TemplateVHD.Edition) - { - $Edition = $TemplateVHD.Edition - } # if - - # Get the Template VHD Format - $VHDFormat = [LabVHDFormat]::VHDx - if ($TemplateVHD.VHDFormat) - { - $VHDFormat = [LabVHDFormat]::$($TemplateVHD.VHDFormat) - } # if - if (-not $VHDFormat) - { - $exceptionParameters = @{ - errorId = 'InvalidVMTemplateVHDVHDFormatError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InvalidVMTemplateVHDVHDFormatError ` - -f $TemplateVHD.Name, $TemplateVHD.VHDFormat) - } - New-LabException @exceptionParameters - } - - # Get the Template VHD Type - $VHDType = [LabVHDType]::Dynamic - if ($TemplateVHD.VHDType) - { - $VHDType = [LabVHDType]::$($TemplateVHD.VHDType) - } # if - if (-not $VHDType) - { - $exceptionParameters = @{ - errorId = 'InvalidVMTemplateVHDVHDTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InvalidVMTemplateVHDVHDTypeError ` - -f $TemplateVHD.Name, $TemplateVHD.VHDType) - } - New-LabException @exceptionParameters - } # if - - # Get the disk size if provided - [System.Int64] $VHDSize = 25GB - if ($TemplateVHD.VHDSize) - { - $VHDSize = (Invoke-Expression $TemplateVHD.VHDSize) - } # if - - # Get the Template VM Generation - [System.Int32] $Generation = 2 - if ($TemplateVHD.Generation) - { - $Generation = $TemplateVHD.Generation - } # if - if ($Generation -notin @(1, 2) ) - { - $exceptionParameters = @{ - errorId = 'InvalidVMTemplateVHDGenerationError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InvalidVMTemplateVHDGenerationError ` - -f $TemplateVHD.Name, $Generation) - } - New-LabException @exceptionParameters - } - - # Get the Template Packages - if ($TemplateVHD.packages) - { - $Packages = $TemplateVHD.Packages - } # if - - # Get the Template Features - if ($TemplateVHD.features) - { - $Features = $TemplateVHD.Features - } # if - - # Add template VHD to the list - $NewVMTemplateVHD = [LabVMTemplateVHD]::New($TemplateVHDName) - $NewVMTemplateVHD.ISOPath = $ISOPath - $NewVMTemplateVHD.VHDPath = $VHDPath - $NewVMTemplateVHD.OSType = $OSType - $NewVMTemplateVHD.Edition = $Edition - $NewVMTemplateVHD.Generation = $Generation - $NewVMTemplateVHD.VHDFormat = $VHDFormat - $NewVMTemplateVHD.VHDType = $VHDType - $NewVMTemplateVHD.VHDSize = $VHDSize - $NewVMTemplateVHD.Packages = $Packages - $NewVMTemplateVHD.Features = $Features - $VMTemplateVHDs += @( $NewVMTemplateVHD ) - } # foreach - Return $VMTemplateVHDs -} # Get-LabVMTemplateVHD diff --git a/src/lib/public/Initialize-LabResourceIso.ps1 b/src/lib/public/Initialize-LabResourceIso.ps1 deleted file mode 100644 index 0867445c..00000000 --- a/src/lib/public/Initialize-LabResourceIso.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -function Initialize-LabResourceISO -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabResourceISO[]] $ResourceISOs - ) - - # if resource ISOs was not passed, pull it. - if (-not $PSBoundParameters.ContainsKey('resourceisos')) - { - $ResourceMSUs = Get-LabResourceISO ` - @PSBoundParameters - } # if - - if ($ResourceISOs) - { - foreach ($ResourceISO in $ResourceISOs) - { - if (-not (Test-Path -Path $ResourceISO.Path)) - { - # The Resource ISO does not exist - if (-not ($ResourceISO.URL)) - { - $exceptionParameters = @{ - errorId = 'ResourceISOFileNotFoundAndNoURLError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ResourceISOFileNotFoundAndNoURLError ` - -f $ISOName, $Path) - } - New-LabException @exceptionParameters - } # if - - $URLLeaf = [System.IO.Path]::GetFileName($ResourceISO.URL) - $URLExtension = [System.IO.Path]::GetExtension($URLLeaf) - if ($URLExtension -in @('.zip', '.iso')) - { - Write-LabMessage -Message $($LocalizedData.DownloadingResourceISOMessage ` - -f $ResourceISO.Name, $ResourceISO.URL) - - Invoke-LabDownloadAndUnzipFile ` - -URL $ResourceISO.URL ` - -DestinationPath (Split-Path -Path $ResourceISO.Path) - } - elseif ([System.String]::IsNullOrEmpty($URLExtension)) - { - Write-LabMessage ` - -Type Alert ` - -Message $($LocalizedData.ISONotFoundDownloadURLMessage ` - -f $ResourceISO.Name, $ResourceISO.Path, $ResourceISO.URL) - } # if - if (-not (Test-Path -Path $ResourceISO.Path)) - { - $exceptionParameters = @{ - errorId = 'ResourceISOFileNotDownloadedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ResourceISOFileNotDownloadedError ` - -f $ResourceISO.Name, $ResourceISO.Path, $ResourceISO.URL) - } - New-LabException @exceptionParameters - } # if - } # if - } # foreach - } # if -} # Initialize-LabResourceISO diff --git a/src/lib/public/Initialize-LabResourceModule.ps1 b/src/lib/public/Initialize-LabResourceModule.ps1 deleted file mode 100644 index f732fde5..00000000 --- a/src/lib/public/Initialize-LabResourceModule.ps1 +++ /dev/null @@ -1,57 +0,0 @@ -function Initialize-LabResourceModule -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabResourceModule[]] $ResourceModules - ) - - # if resource modules was not passed, pull it. - if (-not $PSBoundParameters.ContainsKey('resourcemodules')) - { - $ResourceModules = Get-LabResourceModule ` - @PSBoundParameters - } - - if ($ResourceModules) - { - foreach ($Module in $ResourceModules) - { - $Splat = [PSObject] @{ Name = $Module.Name } - if ($Module.URL) - { - $Splat += [PSObject] @{ URL = $Module.URL } - } - if ($Module.Folder) - { - $Splat += [PSObject] @{ Folder = $Module.Folder } - } - if ($Module.RequiredVersion) - { - $Splat += [PSObject] @{ RequiredVersion = $Module.RequiredVersion } - } - if ($Module.MiniumVersion) - { - $Splat += [PSObject] @{ MiniumVersion = $Module.MiniumVersion } - } - - Write-LabMessage -Message $($LocalizedData.DownloadingResourceModuleMessage ` - -f $Name, $URL) - - Invoke-LabDownloadResourceModule @Splat - } # foreach - } # if -} # Initialize-LabResourceModule diff --git a/src/lib/public/Initialize-LabResourceMsu.ps1 b/src/lib/public/Initialize-LabResourceMsu.ps1 deleted file mode 100644 index 36f3977b..00000000 --- a/src/lib/public/Initialize-LabResourceMsu.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -function Initialize-LabResourceMSU -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabResourceMSU[]] $ResourceMSUs - ) - - # if resource MSUs was not passed, pull it. - if (-not $PSBoundParameters.ContainsKey('resourcemsus')) - { - $ResourceMSUs = Get-LabResourceMSU ` - @PSBoundParameters - } - - if ($ResourceMSUs) - { - foreach ($MSU in $ResourceMSUs) - { - if (-not (Test-Path -Path $MSU.Filename)) - { - Write-LabMessage -Message $($LocalizedData.DownloadingResourceMSUMessage ` - -f $MSU.Name, $MSU.URL) - - Invoke-LabDownloadAndUnzipFile ` - -URL $MSU.URL ` - -DestinationPath (Split-Path -Path $MSU.Filename) - } # if - } # foreach - } # if -} # Initialize-LabResourceMSU diff --git a/src/lib/public/Initialize-LabSwitch.ps1 b/src/lib/public/Initialize-LabSwitch.ps1 deleted file mode 100644 index bc75d067..00000000 --- a/src/lib/public/Initialize-LabSwitch.ps1 +++ /dev/null @@ -1,320 +0,0 @@ -function Initialize-LabSwitch -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabSwitch[]] $Switches - ) - - # if switches was not passed, pull it. - if (-not $PSBoundParameters.ContainsKey('switches')) - { - [LabSwitch[]] $Switches = Get-LabSwitch ` - @PSBoundParameters - } - - # Create Hyper-V Switches - foreach ($VMSwitch in $Switches) - { - if ($Name -and ($VMSwitch.name -notin $Name)) - { - # A names list was passed but this swtich wasn't included - continue - } # if - - if ((Get-VMSwitch | Where-Object -Property Name -eq $($VMSwitch.Name)).Count -eq 0) - { - [System.String] $SwitchName = $VMSwitch.Name - if (-not $SwitchName) - { - $exceptionParameters = @{ - errorId = 'SwitchNameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.SwitchNameIsEmptyError) - } - New-LabException @exceptionParameters - } - [LabSwitchType] $SwitchType = $VMSwitch.Type - Write-LabMessage -Message $($LocalizedData.CreatingVirtualSwitchMessage ` - -f $SwitchType, $SwitchName) - Switch ($SwitchType) - { - 'External' - { - # Determine which Physical Adapter to bind this switch to - if ($VMSwitch.BindingAdapterMac) - { - $BindingAdapter = Get-NetAdapter | Where-Object { - ($_.MacAddress -replace '-', '') -eq $VMSwitch.BindingAdapterMac - } - $ErrorDetail = "with a MAC address '$($VMSwitch.BindingAdapterMac)' " - } - elseif ($VMSwitch.BindingAdapterName) - { - $BindingAdapter = Get-NetAdapter ` - -Name $VMSwitch.BindingAdapterName ` - -ErrorAction SilentlyContinue - $ErrorDetail = "with a name '$($VMSwitch.BindingAdapterName)' " - } - else - { - $BindingAdapter = Get-NetAdapter | ` - Where-Object { - ($_.Status -eq 'Up') ` - -and (-not $_.Virtual) ` - } | Select-Object -First 1 - $ErrorDetail = '' - } # if - - # Check that a Binding Adapter was found - if (-not $BindingAdapter) - { - $exceptionParameters = @{ - errorId = 'BindingAdapterNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.BindingAdapterNotFoundError ` - -f $SwitchName, $ErrorDetail) - } - New-LabException @exceptionParameters - } # if - - # Check this adapter is not already bound to a switch - $VMSwitchNames = (Get-VMSwitch | Where-Object { - $_.SwitchType -eq 'External' - }).Name - $MacAddress = @() - - foreach ($VmSwitchName in $VmSwitchNames) - { - $MacAddress += (Get-VMNetworkAdapter ` - -ManagementOS ` - -SwitchName $VmSwitchName ` - -Name $VmSwitchName ` - -ErrorAction SilentlyContinue).MacAddress - } # foreach - - $UsedAdapters = @((Get-NetAdapter | Where-Object { - ($_.MacAddress -replace '-', '') -in $MacAddress - }).Name) - if ($BindingAdapter.Name -in $UsedAdapters) - { - $exceptionParameters = @{ - errorId = 'BindingAdapterUsedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.BindingAdapterUsedError ` - -f $SwitchName, $BindingAdapter.Name) - } - New-LabException @exceptionParameters - } # if - - # Create the swtich - $null = New-VMSwitch ` - -Name $SwitchName ` - -NetAdapterName $BindingAdapter.Name - break - } # 'External' - - 'Private' - { - $null = New-VMSwitch ` - -Name $SwitchName ` - -SwitchType Private - Break - } # 'Private' - - 'Internal' - { - $null = New-VMSwitch ` - -Name $SwitchName ` - -SwitchType Internal - Break - } # 'Internal' - - 'NAT' - { - if ($Script:CurrentBuild -lt 14295) - { - $exceptionParameters = @{ - errorId = 'NatSwitchNotSupportedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSwitchNotSupportedError -f $SwitchName) - } - New-LabException @exceptionParameters - } - - $NatSubnet = $VMSwitch.NatSubnet - # Check Nat Subnet is set - if (-not $NatSubnet) - { - $exceptionParameters = @{ - errorId = 'NatSubnetEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSubnetEmptyError ` - -f $SwitchName) - } - New-LabException @exceptionParameters - } # if - # Ensure Nat Subnet looks valid - if ($NatSubnet -notmatch '[0-9]+.[0-9]+.[0-9]+.[0-9]+/[0-9]+') - { - $exceptionParameters = @{ - errorId = 'NatSubnetInvalidError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSubnetInvalidError ` - -f $SwitchName, $NatSubnet) - } - New-LabException @exceptionParameters - } # if - $NatSubnetComponents = ($NatSubnet -split '/') - $NatSubnetAddress = $NatSubnetComponents[0] - # Validate the Nat Subnet Address - if (-not ([System.Net.Ipaddress]::TryParse($NatSubnetAddress, [ref]0))) - { - $exceptionParameters = @{ - errorId = 'NatSubnetAddressInvalidError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSubnetAddressInvalidError ` - -f $SwitchName, $NatSubnetAddress) - } - New-LabException @exceptionParameters - } # if - # Validate the Nat Subnet Prefix Length - [System.Int32] $NatSubnetPrefixLength = $NatSubnetComponents[1] - if (($NatSubnetPrefixLength -lt 1) -or ($NatSubnetPrefixLength -gt 31)) - { - $exceptionParameters = @{ - errorId = 'NatSubnetPrefixLengthInvalidError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSubnetPrefixLengthInvalidError ` - -f $SwitchName, $NatSubnetPrefixLength) - } - New-LabException @exceptionParameters - } # if - $NatGatewayAddress = $VMSwitch.NatGatewayAddress - - # Create the Internal Switch - $null = New-VMSwitch ` - -Name $SwitchName ` - -SwitchType Internal ` - -ErrorAction Stop - # Set the IP Address on the default adapter connected to the NAT switch - $MacAddress = (Get-VMNetworkAdapter ` - -ManagementOS ` - -SwitchName $SwitchName ` - -Name $SwitchName ` - -ErrorAction Stop).MacAddress - if ([System.String]::IsNullOrEmpty($MacAddress)) - { - $exceptionParameters = @{ - errorId = 'NatSwitchDefaultAdapterMacEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSwitchDefaultAdapterMacEmptyError ` - -f $SwitchName) - } - New-LabException @exceptionParameters - } # if - $Adapter = Get-NetAdapter | - Where-Object { ($_.MacAddress -replace '-', '') -eq $MacAddress } - if (-not $Adapter) - { - $exceptionParameters = @{ - errorId = 'NatSwitchDefaultAdapterNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NatSwitchDefaultAdapterNotFoundError ` - -f $SwitchName) - } - New-LabException @exceptionParameters - } - $null = $Adapter | New-NetIPAddress ` - -IPAddress $NatGatewayAddress ` - -PrefixLength $NatSubnetPrefixLength ` - -ErrorAction Stop - # Does the NAT already exist? - $NetNat = Get-NetNat ` - -Name $SwitchName ` - -ErrorAction SilentlyContinue - if ($NetNat) - { - # If the NAT already exists, remove it so it can be recreated - $null = $NetNat | Remove-NetNat -Confirm:$false - } - # Create the new NAT - $null = New-NetNat ` - -Name $SwitchName ` - -InternalIPInterfaceAddressPrefix $NatSubnet ` - -ErrorAction Stop - Break - } # 'NAT' - Default - { - $exceptionParameters = @{ - errorId = 'UnknownSwitchTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.UnknownSwitchTypeError ` - -f $SwitchType, $SwitchName) - } - New-LabException @exceptionParameters - } - } # switch - - if ($SwitchType -ne 'Private') - { - # Configure the VLan on the default Management Adapter - $setLabSwitchAdapterParameters = @{ - Name = $SwitchName - SwitchName = $SwitchName - } - - if ($VMSwitch.VLan) - { - $setLabSwitchAdapterParameters += @{ - VlanId = $VMSwitch.Vlan - } - } # if - - Set-LabSwitchAdapter @setLabSwitchAdapterParameters - - # Add any management OS adapters to the switch - if ($VMSwitch.Adapters) - { - foreach ($Adapter in $VMSwitch.Adapters) - { - $setLabSwitchAdapterParameters = @{ - Name = $Adapter.Name - SwitchName = $SwitchName - } - - if ($Adapter.MacAddress) - { - $setLabSwitchAdapterParameters += @{ - StaticMacAddress = $Adapter.MacAddress - } - } # if - - if ($VMSwitch.VLan) - { - $setLabSwitchAdapterParameters += @{ - VlanId = $VMSwitch.Vlan - } - } # if - - Set-LabSwitchAdapter @setLabSwitchAdapterParameters - } # foreach - } # if - } # if - } # if - } # foreach -} # Initialize-LabSwitch diff --git a/src/lib/public/Initialize-LabVm.ps1 b/src/lib/public/Initialize-LabVm.ps1 deleted file mode 100644 index f4072bed..00000000 --- a/src/lib/public/Initialize-LabVm.ps1 +++ /dev/null @@ -1,318 +0,0 @@ -function Initialize-LabVM -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position=1, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position=2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position=3)] - [LabVM[]] $VMs - ) - - # if VMs array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMs')) - { - [LabVM[]] $VMs = Get-LabVM ` - @PSBoundParameters - } # if - - # if there are not VMs just return - if (-not $VMs) - { - return - } # if - - $CurrentVMs = Get-VM - - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - # Figure out the name of the LabBuilder control switch - $ManagementSwitchName = Get-LabManagementSwitchName ` - -Lab $Lab - if ($Lab.labbuilderconfig.switches.ManagementVlan) - { - [Int32] $ManagementVlan = $Lab.labbuilderconfig.switches.ManagementVlan - } - else - { - [Int32] $ManagementVlan = $Script:DefaultManagementVLan - } # if - - foreach ($VM in $VMs) - { - if ($Name -and ($VM.Name -notin $Name)) - { - # A names list was passed but this VM wasn't included - continue - } # if - - # Get the root path of the VM - [System.String] $VMRootPath = $VM.VMRootPath - - # Get the Virtual Machine Path - [System.String] $VMPath = Join-Path ` - -Path $VMRootPath ` - -ChildPath 'Virtual Machines' - - # Get the Virtual Hard Disk Path - [System.String] $VHDPath = Join-Path ` - -Path $VMRootPath ` - -ChildPath 'Virtual Hard Disks' - - # Get Path to LabBuilder files - [System.String] $VMLabBuilderFiles = $VM.LabBuilderFilesPath - - if (($CurrentVMs | Where-Object -Property Name -eq $VM.Name).Count -eq 0) - { - Write-LabMessage -Message $($LocalizedData.CreatingVMMessage ` - -f $VM.Name) - - # Make sure the appropriate folders exist - Initialize-LabVMPath ` - -VMPath $VMRootPath - - # Create the boot disk - $VMBootDiskPath = "$VHDPath\$($VM.Name) Boot Disk.vhdx" - if (-not (Test-Path -Path $VMBootDiskPath)) - { - if ($VM.UseDifferencingDisk) - { - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` - -f $VM.Name,$VMBootDiskPath,'Differencing Boot') - - $null = New-VHD ` - -Differencing ` - -Path $VMBootDiskPath ` - -ParentPath $VM.ParentVHD - } - else - { - Write-LabMessage -Message $($LocalizedData.CreatingVMDiskMessage ` - -f $VM.Name,$VMBootDiskPath,'Boot') - - $null = Copy-Item ` - -Path $VM.ParentVHD ` - -Destination $VMBootDiskPath - } - - # Create all the required initialization files for this VM - New-LabVMInitializationFile ` - -Lab $Lab ` - -VM $VM - - # Because this is a new boot disk apply any required initialization - Initialize-LabBootVHD ` - -Lab $Lab ` - -VM $VM ` - -VMBootDiskPath $VMBootDiskPath - } - else - { - Write-LabMessage -Message $($LocalizedData.VMDiskAlreadyExistsMessage ` - -f $VM.Name,$VMBootDiskPath,'Boot') - } # if - - # Create New VM from settings - if ($VM.Version -and ($Script:CurrentBuild -ge 14352)) - { - $null = New-VM ` - -Name $VM.Name ` - -MemoryStartupBytes $VM.MemoryStartupBytes ` - -Generation $VM.Generation ` - -Path $LabPath ` - -VHDPath $VMBootDiskPath ` - -Version $VM.Version - } - - else - { - $null = New-VM ` - -Name $VM.Name ` - -MemoryStartupBytes $VM.MemoryStartupBytes ` - -Generation $VM.Generation ` - -Path $LabPath ` - -VHDPath $VMBootDiskPath ` - - - } - - # Remove the default network adapter created with the VM because we don't need it - Remove-VMNetworkAdapter ` - -VMName $VM.Name ` - -Name 'Network Adapter' - } - - # Set the processor count if different to default and if specified in config file - if ($VM.ProcessorCount) - { - if ($VM.ProcessorCount -ne (Get-VM -Name $VM.Name).ProcessorCount) - { - Set-VM ` - -Name $VM.Name ` - -ProcessorCount $VM.ProcessorCount - } # if - } # if - - # Enable/Disable Dynamic Memory - Write-Verbose -Message "Checking Dynamic Memory: $($VM.DynamicMemoryEnabled) = $((Get-VMMemory -VMName $VM.Name).DynamicMemoryEnabled)" -Verbose - if ($VM.DynamicMemoryEnabled -ne (Get-VMMemory -VMName $VM.Name).DynamicMemoryEnabled) - { - Write-Verbose -Message "Checking Dynamic Memory: $($VM.DynamicMemoryEnabled)" -Verbose - Set-VMMemory ` - -VMName $VM.Name ` - -DynamicMemoryEnabled:$($VM.DynamicMemoryEnabled) - } # if - - # Is ExposeVirtualizationExtensions supported? - if ($Script:CurrentBuild -lt 10565) - { - # No, it is not supported - is it required by VM? - if ($VM.ExposeVirtualizationExtensions) - { - # ExposeVirtualizationExtensions is required for this VM - $exceptionParameters = @{ - errorId = 'VMVirtualizationExtError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMVirtualizationExtError ` - -f $VM.Name) - } - New-LabException @exceptionParameters - } # if - } - else - { - # Yes, it is - is the setting different? - if ($VM.ExposeVirtualizationExtensions ` - -ne (Get-VMProcessor -VMName $VM.Name).ExposeVirtualizationExtensions) - { - if ($Script:CurrentBuild -ge 14352 -and ($VM.Version -eq "8.0")) - { - Set-VMSecurity ` - -VMName $VM.Name ` - -VirtualizationBasedSecurityOptOut $true - } # if - # Try and update it - Set-VMProcessor ` - -VMName $VM.Name ` - -ExposeVirtualizationExtensions:$VM.ExposeVirtualizationExtensions ` - -ErrorAction Stop - } # if - } # if - - # Enable/Disable the Integration Services - Update-LabVMIntegrationService ` - -VM $VM - - # Update the data disks for the VM - Update-LabVMDataDisk ` - -Lab $Lab ` - -VM $VM - - # Update the DVD Drives for the VM - Update-LabVMDvdDrive ` - -Lab $Lab ` - -VM $VM - - # Create/Update the Management Network Adapter - if ((Get-VMNetworkAdapter -VMName $VM.Name | Where-Object -Property Name -EQ $ManagementSwitchName).Count -eq 0) - { - Write-LabMessage -Message $($LocalizedData.AddingVMNetworkAdapterMessage ` - -f $VM.Name,$ManagementSwitchName,'Management') - - Add-VMNetworkAdapter ` - -VMName $VM.Name ` - -SwitchName $ManagementSwitchName ` - -Name $ManagementSwitchName - } - $VMNetworkAdapter = Get-VMNetworkAdapter ` - -VMName $VM.Name ` - -Name $ManagementSwitchName - $null = $VMNetworkAdapter | - Set-VMNetworkAdapterVlan ` - -Access ` - -VlanId $ManagementVlan - - Write-LabMessage -Message $($LocalizedData.SettingVMNetworkAdapterVlanMessage ` - -f $VM.Name,$ManagementSwitchName,'Management',$ManagementVlan) - - # Create any network adapters - foreach ($VMAdapter in $VM.Adapters) - { - if ((Get-VMNetworkAdapter -VMName $VM.Name | Where-Object -Property Name -EQ $VMAdapter.Name).Count -eq 0) - { - Write-LabMessage -Message $($LocalizedData.AddingVMNetworkAdapterMessage ` - -f $VM.Name,$VMAdapter.SwitchName,$VMAdapter.Name) - - Add-VMNetworkAdapter ` - -VMName $VM.Name ` - -SwitchName $VMAdapter.SwitchName ` - -Name $VMAdapter.Name - } # if - - $VMNetworkAdapter = Get-VMNetworkAdapter ` - -VMName $VM.Name ` - -Name $VMAdapter.Name - if ($VMAdapter.VLan) - { - $null = $VMNetworkAdapter | - Set-VMNetworkAdapterVlan ` - -Access ` - -VlanId $VMAdapter.VLan - - Write-LabMessage -Message $($LocalizedData.SettingVMNetworkAdapterVlanMessage ` - -f $VM.Name,$VMAdapter.Name,'',$VMAdapter.VLan) - } - else - { - $null = $VMNetworkAdapter | - Set-VMNetworkAdapterVlan ` - -Untagged - - Write-LabMessage -Message $($LocalizedData.ClearingVMNetworkAdapterVlanMessage ` - -f $VM.Name,$VMAdapter.Name,'') - } # if - - if ([System.String]::IsNullOrWhitespace($VMAdapter.MACAddress)) - { - $null = $VMNetworkAdapter | - Set-VMNetworkAdapter ` - -DynamicMacAddress - } - else - { - $null = $VMNetworkAdapter | - Set-VMNetworkAdapter ` - -StaticMacAddress $VMAdapter.MACAddress - } # if - - # Enable Device Naming if supported by VM version and generation - if (((Get-Command -Name Set-VMNetworkAdapter).Parameters.ContainsKey('DeviceNaming')) -and (($VM.Version -ge "6.2") -and ($VM.Generation -eq 2))) - { - $null = $VMNetworkAdapter | - Set-VMNetworkAdapter ` - -DeviceNaming On - } # if - if ($VMAdapter.MACAddressSpoofing -ne $VMNetworkAdapter.MACAddressSpoofing) - { - $MACAddressSpoofing = if ($VMAdapter.MACAddressSpoofing) {'On'} else {'Off'} - $null = $VMNetworkAdapter | - Set-VMNetworkAdapter ` - -MacAddressSpoofing $MACAddressSpoofing - } # if - } # foreach - - Install-LabVM ` - -Lab $Lab ` - -VM $VM - } # foreach -} # Initialize-LabVM diff --git a/src/lib/public/Initialize-LabVmTemplate.ps1 b/src/lib/public/Initialize-LabVmTemplate.ps1 deleted file mode 100644 index 6051ee6e..00000000 --- a/src/lib/public/Initialize-LabVmTemplate.ps1 +++ /dev/null @@ -1,228 +0,0 @@ -function Initialize-LabVMTemplate -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabVMTemplate[]] $VMTemplates, - - [Parameter( - Position = 4)] - [LabVMTemplateVHD[]] $VMTemplateVHDs - ) - - # if VMTeplates array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMTemplates')) - { - [LabVMTemplate[]] $VMTemplates = Get-LabVMTemplate ` - @PSBoundParameters - } - - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - # Check each Parent VHD exists in the Parent VHDs folder for the - # Lab. If it isn't, try and copy it from the SourceVHD - # Location. - foreach ($VMTemplate in $VMTemplates) - { - if ($Name -and ($VMTemplate.Name -notin $Name)) - { - # A names list was passed but this VM Template wasn't included - continue - } # if - - if (-not (Test-Path $VMTemplate.ParentVhd)) - { - # The Parent VHD isn't in the VHD Parent folder - # so copy it there, optimize it and mark it read-only. - if (-not (Test-Path $VMTemplate.SourceVhd)) - { - # The source VHD could not be found. - $exceptionParameters = @{ - errorId = 'TemplateSourceVHDNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.TemplateSourceVHDNotFoundError ` - -f $VMTemplate.Name, $VMTemplate.sourcevhd) - } - New-LabException @exceptionParameters - } - - Write-LabMessage -Message $($LocalizedData.CopyingTemplateSourceVHDMessage ` - -f $VMTemplate.SourceVhd, $VMTemplate.ParentVhd) - Copy-Item ` - -Path $VMTemplate.SourceVhd ` - -Destination $VMTemplate.ParentVhd - - # Add any packages to the template if required - if (-not [System.String]::IsNullOrWhitespace($VMTemplate.Packages)) - { - if ($VMTemplate.OSType -ne [LabOStype]::Nano) - { - # Mount the Template Boot VHD so that files can be loaded into it - Write-LabMessage -Message $($LocalizedData.MountingTemplateBootDiskMessage ` - -f $VMTemplate.Name, $VMTemplate.ParentVhd) - - # Create a mount point for mounting the Boot VHD - [System.String] $MountPoint = Join-Path ` - -Path (Split-Path -Path $VMTemplate.ParentVHD) ` - -ChildPath 'Mount' - - if (-not (Test-Path -Path $MountPoint -PathType Container)) - { - $null = New-Item ` - -Path $MountPoint ` - -ItemType Directory - } - - # Mount the VHD to the Mount point - $null = Mount-WindowsImage ` - -ImagePath $VMTemplate.parentvhd ` - -Path $MountPoint ` - -Index 1 - - # Get the list of Packages to apply - $ApplyPackages = @($VMTemplate.Packages -split ',') - - # Get the list of Lab Resource MSUs - $ResourceMSUs = Get-LabResourceMSU ` - -Lab $Lab - - foreach ($Package in $ApplyPackages) - { - # Find the package in the Resources - [System.Boolean] $Found = $false - foreach ($ResourceMSU in $ResourceMSUs) - { - if ($ResourceMSU.Name -eq $Package) - { - # Found the package - $Found = $true - break - } # if - } # foreach - if (-not $Found) - { - # Dismount before throwing the error - Write-LabMessage -Message $($LocalizedData.DismountingTemplateBootDiskMessage ` - -f $VMTemplate.Name, $VMTemplate.parentvhd) - $null = Dismount-WindowsImage ` - -Path $MountPoint ` - -Save - $null = Remove-Item ` - -Path $MountPoint ` - -Recurse ` - -Force - - $exceptionParameters = @{ - errorId = 'PackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageNotFoundError ` - -f $Package) - } - New-LabException @exceptionParameters - } # if - - $PackagePath = $ResourceMSU.Filename - if (-not (Test-Path -Path $PackagePath)) - { - # Dismount before throwing the error - Write-LabMessage -Message $($LocalizedData.DismountingTemplateBootDiskMessage ` - -f $VMTemplate.Name, $VMTemplate.ParentVhd) - $null = Dismount-WindowsImage ` - -Path $MountPoint ` - -Save - $null = Remove-Item ` - -Path $MountPoint ` - -Recurse ` - -Force - - $exceptionParameters = @{ - errorId = 'PackageMSUNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageMSUNotFoundError ` - -f $Package, $PackagePath) - } - New-LabException @exceptionParameters - } # if - - # Apply a Pacakge - Write-LabMessage -Message $($LocalizedData.ApplyingTemplateBootDiskFileMessage ` - -f $VMTemplate.Name, $Package, $PackagePath) - - $null = Add-WindowsPackage ` - -PackagePath $PackagePath ` - -Path $MountPoint - } # foreach - - # Dismount the VHD - Write-LabMessage -Message $($LocalizedData.DismountingTemplateBootDiskMessage ` - -f $VMTemplate.Name, $VMTemplate.parentvhd) - $null = Dismount-WindowsImage ` - -Path $MountPoint ` - -Save - $null = Remove-Item ` - -Path $MountPoint ` - -Recurse ` - -Force - } # if - } # if - - Write-LabMessage -Message $($LocalizedData.OptimizingParentVHDMessage ` - -f $VMTemplate.parentvhd) - Set-ItemProperty ` - -Path $VMTemplate.parentvhd ` - -Name IsReadOnly ` - -Value $false - Optimize-VHD ` - -Path $VMTemplate.parentvhd ` - -Mode Full - Write-LabMessage -Message $($LocalizedData.SettingParentVHDReadonlyMessage ` - -f $VMTemplate.parentvhd) - Set-ItemProperty ` - -Path $VMTemplate.parentvhd ` - -Name IsReadOnly ` - -Value $true - } - Else - { - Write-LabMessage -Message $($LocalizedData.SkipParentVHDFileMessage ` - -f $VMTemplate.Name, $VMTemplate.parentvhd) - } - - # if this is a Nano Server template, we need to ensure that the - # NanoServerPackages folder is copied to our Lab folder - if ($VMTemplate.OSType -eq [LabOStype]::Nano) - { - [System.String] $VHDPackagesFolder = Join-Path ` - -Path (Split-Path -Path $VMTemplate.SourceVhd -Parent)` - -ChildPath 'NanoServerPackages' - - [System.String] $NanoPackagesFolder = Join-Path ` - -Path $LabPath ` - -ChildPath 'NanoServerPackages' - - if (-not (Test-Path -Path $NanoPackagesFolder -Type Container)) - { - Write-LabMessage -Message $($LocalizedData.CachingNanoServerPackagesMessage ` - -f $VHDPackagesFolder, $NanoPackagesFolder) - Copy-Item ` - -Path $VHDPackagesFolder ` - -Destination $LabPath ` - -Recurse ` - -Force - } - } - } -} diff --git a/src/lib/public/Initialize-LabVmTemplateVhd.ps1 b/src/lib/public/Initialize-LabVmTemplateVhd.ps1 deleted file mode 100644 index c8eacfdc..00000000 --- a/src/lib/public/Initialize-LabVmTemplateVhd.ps1 +++ /dev/null @@ -1,364 +0,0 @@ -function Initialize-LabVMTemplateVHD -{ - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabVMTemplateVHD[]] $VMTemplateVHDs - ) - - # if VMTeplateVHDs array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMTemplateVHDs')) - { - [LabVMTemplateVHD[]] $VMTemplateVHDs = Get-LabVMTemplateVHD ` - @PSBoundParameters - } # if - - # if there are no VMTemplateVHDs just return - if ($null -eq $VMTemplateVHDs) - { - return - } # if - - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - # Is an alternate path to DISM specified? - if ($Lab.labbuilderconfig.settings.DismPath) - { - $DismPath = Join-Path ` - -Path $Lab.labbuilderconfig.settings.DismPath ` - -ChildPath 'dism.exe' - if (-not (Test-Path -Path $DismPath)) - { - $exceptionParameters = @{ - errorId = 'FileNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.FileNotFoundError ` - -f 'alternate DISM.EXE', $DismPath) - } - New-LabException @exceptionParameters - } - } - - foreach ($VMTemplateVHD in $VMTemplateVHDs) - { - [System.String] $TemplateVHDName = $VMTemplateVHD.Name - if ($Name -and ($TemplateVHDName -notin $Name)) - { - # A names list was passed but this VM Template VHD wasn't included - continue - } # if - - [System.String] $VHDPath = $VMTemplateVHD.VHDPath - - if (Test-Path -Path ($VHDPath)) - { - # The SourceVHD already exists - Write-LabMessage -Message $($LocalizedData.SkipVMTemplateVHDFileMessage ` - -f $TemplateVHDName, $VHDPath) - - continue - } # if - - # Create the VHD - Write-LabMessage -Message $($LocalizedData.CreatingVMTemplateVHDMessage ` - -f $TemplateVHDName, $VHDPath) - - # Check the ISO exists. - [System.String] $ISOPath = $VMTemplateVHD.ISOPath - if (-not (Test-Path -Path $ISOPath)) - { - $exceptionParameters = @{ - errorId = 'VMTemplateVHDISOPathNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMTemplateVHDISOPathNotFoundError ` - -f $TemplateVHDName, $ISOPath) - } - New-LabException @exceptionParameters - } # if - - # Mount the ISO so we can read the files. - Write-LabMessage -Message $($LocalizedData.MountingVMTemplateVHDISOMessage ` - -f $TemplateVHDName, $ISOPath) - - $null = Mount-DiskImage ` - -ImagePath $ISOPath ` - -StorageType ISO ` - -Access Readonly - - # Refresh the PS Drive list to make sure the new drive can be detected - Get-PSDrive ` - -PSProvider FileSystem - - $DiskImage = Get-DiskImage -ImagePath $ISOPath - $Volume = Get-Volume -DiskImage $DiskImage - if (-not $Volume) - { - $exceptionParameters = @{ - errorId = 'VolumeNotAvailableAfterMountError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VolumeNotAvailableAfterMountError ` - -f $ISOPath) - } - New-LabException @exceptionParameters - } - [System.String] $DriveLetter = $Volume.DriveLetter - if (-not $DriveLetter) - { - $exceptionParameters = @{ - errorId = 'DriveLetterNotAssignedError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.DriveLetterNotAssignedError ` - -f $ISOPath) - } - New-LabException @exceptionParameters - } - [System.String] $ISODrive = "$([System.String]$DriveLetter):" - - # Determine the path to the WIM - [System.String] $SourcePath = "$ISODrive\Sources\Install.WIM" - if ($VMTemplateVHD.OSType -eq [LabOStype]::Nano) - { - $SourcePath = "$ISODrive\Nanoserver\NanoServer.WIM" - } # if - - # This will have to change depending on the version - # of Convert-WindowsImage being used. - [System.String] $VHDFormat = $VMTemplateVHD.VHDFormat - [System.String] $VHDType = $VMTemplateVHD.VHDType - [System.String] $VHDDiskLayout = 'UEFI' - if ($VMTemplateVHD.Generation -eq 1) - { - $VHDDiskLayout = 'BIOS' - } # if - - [System.String] $Edition = $VMTemplateVHD.Edition - # if edition is not set then use Get-WindowsImage to get the name - # of the first image in the WIM. - if ([System.String]::IsNullOrWhiteSpace($Edition)) - { - $Edition = (Get-WindowsImage ` - -ImagePath $SourcePath ` - -Index 1).ImageName - } # if - - $ConvertParams = @{ - sourcepath = $SourcePath - vhdpath = $VHDpath - vhdformat = $VHDFormat - # Convert-WindowsImage doesn't support creating different VHDTypes - # vhdtype = $VHDType - edition = $Edition - disklayout = $VHDDiskLayout - erroraction = 'Stop' - } - - # Set the size - if ($null -ne $VMTemplateVHD.VHDSize) - { - $ConvertParams += @{ - sizebytes = $VMTemplateVHD.VHDSize - } - } # if - - # Are any features specified? - if (-not [System.String]::IsNullOrWhitespace($VMTemplateVHD.Features)) - { - $Features = @($VMTemplateVHD.Features -split ',') - $ConvertParams += @{ - feature = $Features - } - } # if - - # Is an alternate path to DISM specified? - if ($DismPath) - { - $ConvertParams += @{ - DismPath = $DismPath - } - } - - # Perform Nano Server package prep - if ($VMTemplateVHD.OSType -eq [LabOStype]::Nano) - { - # Make a copy of the all the Nano packages in the VHD root folder - # So that if any VMs need to add more packages they are accessible - # once the ISO has been dismounted. - [System.String] $VHDFolder = Split-Path ` - -Path $VHDPath ` - -Parent - - [System.String] $NanoPackagesFolder = Join-Path ` - -Path $VHDFolder ` - -ChildPath 'NanoServerPackages' - - if (-not (Test-Path -Path $NanoPackagesFolder -Type Container)) - { - Write-LabMessage -Message $($LocalizedData.CachingNanoServerPackagesMessage ` - -f "$ISODrive\Nanoserver\Packages", $NanoPackagesFolder) - Copy-Item ` - -Path "$ISODrive\Nanoserver\Packages" ` - -Destination $VHDFolder ` - -Recurse ` - -Force - Rename-Item ` - -Path "$VHDFolder\Packages" ` - -NewName 'NanoServerPackages' - } # if - } # if - - # Do we need to add any packages? - if (-not [System.String]::IsNullOrWhitespace($VMTemplateVHD.Packages)) - { - $Packages = @() - - # Get the list of Lab Resource MSUs - $ResourceMSUs = Get-LabResourceMSU ` - -Lab $Lab - - try - { - foreach ($Package in @($VMTemplateVHD.Packages -split ',')) - { - if (([System.IO.Path]::GetExtension($Package) -eq '.cab') ` - -and ($VMTemplateVHD.OSType -eq [LabOSType]::Nano)) - { - # This is a Nano Server .CAB pacakge - # Generate the path to the Nano Package - $PackagePath = Join-Path ` - -Path $NanoPackagesFolder ` - -ChildPath $Package - # Does it exist? - if (-not (Test-Path -Path $PackagePath)) - { - $exceptionParameters = @{ - errorId = 'NanoPackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NanoPackageNotFoundError ` - -f $PackagePath) - } - New-LabException @exceptionParameters - } - $Packages += @( $PackagePath ) - - # Generate the path to the Nano Language Package - $PackageLangFile = $Package -replace '.cab', "_$($Script:NanoPackageCulture).cab" - $PackageLangPath = Join-Path ` - -Path $NanoPackagesFolder ` - -ChildPath "$($Script:NanoPackageCulture)\$PackageLangFile" - # Does it exist? - if (-not (Test-Path -Path $PackageLangPath)) - { - $exceptionParameters = @{ - errorId = 'NanoPackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.NanoPackageNotFoundError ` - -f $PackageLangPath) - } - New-LabException @exceptionParameters - } - $Packages += @( $PackageLangPath ) - } - else - { - # Tihs is a ResourceMSU type package - [System.Boolean] $Found = $false - foreach ($ResourceMSU in $ResourceMSUs) - { - if ($ResourceMSU.Name -eq $Package) - { - # Found the package - $Found = $true - break - } # if - } # foreach - if (-not $Found) - { - $exceptionParameters = @{ - errorId = 'PackageNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageNotFoundError ` - -f $Package) - } - New-LabException @exceptionParameters - } # if - - $PackagePath = $ResourceMSU.Filename - if (-not (Test-Path -Path $PackagePath)) - { - $exceptionParameters = @{ - errorId = 'PackageMSUNotFoundError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.PackageMSUNotFoundError ` - -f $Package, $PackagePath) - } - New-LabException @exceptionParameters - } # if - $Packages += @( $PackagePath ) - } - } # foreach - $ConvertParams += @{ - Package = $Packages - } - } - catch - { - # Dismount Disk Image before throwing exception - $null = Dismount-DiskImage ` - -ImagePath $ISOPath - - Throw $_ - } # try - } # if - - Write-LabMessage -Message ($LocalizedData.ConvertingWIMtoVHDMessage ` - -f $SourcePath, $VHDPath, $VHDFormat, $Edition, $VHDPartitionStyle, $VHDType) - - # Work around an issue with Convert-WindowsImage not seeing the drive - Get-PSDrive ` - -PSProvider FileSystem - - # Dot source the Convert-WindowsImage script - # Should only be done once - if (-not (Test-Path -Path Function:Convert-WindowsImage)) - { - . $Script:SupportConvertWindowsImagePath - } # if - - try - { - # Call the Convert-WindowsImage script - Convert-WindowsImage @ConvertParams - } # try - catch - { - $exceptionParameters = @{ - errorId = 'ConvertWindowsImageError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ConvertWindowsImageError ` - -f $ISOPath, $SourcePath, $Edition, $VHDFormat, $_.Exception.Message) - } - New-LabException @exceptionParameters - } # catch - finally - { - # Dismount the ISO. - Write-LabMessage -Message $($LocalizedData.DismountingVMTemplateVHDISOMessage ` - -f $TemplateVHDName, $ISOPath) - - $null = Dismount-DiskImage ` - -ImagePath $ISOPath - } # finally - } # endfor -} # Initialize-LabVMTemplateVHD diff --git a/src/lib/public/Install-Lab.ps1 b/src/lib/public/Install-Lab.ps1 deleted file mode 100644 index 6d9c8bc4..00000000 --- a/src/lib/public/Install-Lab.ps1 +++ /dev/null @@ -1,196 +0,0 @@ -function Install-Lab -{ - [CmdLetBinding(DefaultParameterSetName="Lab")] - param - ( - [parameter( - Position=1, - ParameterSetName="File", - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $ConfigPath, - - [parameter( - Position=2, - ParameterSetName="File")] - [ValidateNotNullOrEmpty()] - [System.String] $LabPath, - - [Parameter( - Position=3, - ParameterSetName="Lab", - Mandatory=$true, - ValueFromPipeline=$true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position=4)] - [Switch] $CheckEnvironment, - - [Parameter( - Position=5)] - [Switch] $Force, - - [Parameter( - Position=6)] - [Switch] $OffLine - - ) # Param - - begin - { - # Create a splat array containing force if it is set - $ForceSplat = @{} - - if ($PSBoundParameters.ContainsKey('Force')) - { - $ForceSplat = @{ Force = $true } - } # if - - # Remove some PSBoundParameters so we can Splat - $null = $PSBoundParameters.Remove('CheckEnvironment') - $null = $PSBoundParameters.Remove('Force') - - if ($CheckEnvironment) - { - # Check Hyper-V - Install-LabHyperV ` - -ErrorAction Stop - } # if - - # Ensure WS-Man is enabled - Enable-LabWSMan ` - @ForceSplat ` - -ErrorAction Stop - - if (!($PSBoundParameters.ContainsKey('OffLine'))) - { - # Install Package Providers - Install-LabPackageProvider ` - @ForceSplat ` - -ErrorAction Stop - - # Register Package Sources - Register-LabPackageSource ` - @ForceSplat ` - -ErrorAction Stop - } - - $null = $PSBoundParameters.Remove('Offline') - - if ($PSCmdlet.ParameterSetName -eq 'File') - { - # Read the configuration - $Lab = Get-Lab ` - @PSBoundParameters ` - -ErrorAction Stop - } # if - } # begin - - process - { - # Initialize the core Lab components - # Check Lab Folder structure - Write-LabMessage -Message $($LocalizedData.InitializingLabFoldersMesage) - - # Check folders are defined - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - if (-not (Test-Path -Path $LabPath)) - { - Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` - -f 'LabPath',$LabPath) - - $null = New-Item ` - -Path $LabPath ` - -Type Directory - } - - [System.String] $VHDParentPath = $Lab.labbuilderconfig.settings.vhdparentpathfull - - if (-not (Test-Path -Path $VHDParentPath)) - { - Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` - -f 'VHDParentPath',$VHDParentPath) - - $null = New-Item ` - -Path $VHDParentPath ` - -Type Directory - } - - [System.String] $ResourcePath = $Lab.labbuilderconfig.settings.resourcepathfull - - if (-not (Test-Path -Path $ResourcePath)) - { - Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` - -f 'ResourcePath',$ResourcePath) - - $null = New-Item ` - -Path $ResourcePath ` - -Type Directory - } - - # Initialize the Lab Management Switch - Initialize-LabManagementSwitch ` - -Lab $Lab ` - -ErrorAction Stop - - # Download any Resource Modules required by this Lab - $ResourceModules = Get-LabResourceModule ` - -Lab $Lab - Initialize-LabResourceModule ` - -Lab $Lab ` - -ResourceModules $ResourceModules ` - -ErrorAction Stop - - # Download any Resource MSUs required by this Lab - $ResourceMSUs = Get-LabResourceMSU ` - -Lab $Lab - Initialize-LabResourceMSU ` - -Lab $Lab ` - -ResourceMSUs $ResourceMSUs ` - -ErrorAction Stop - - # Initialize the Switches - $Switches = Get-LabSwitch ` - -Lab $Lab - Initialize-LabSwitch ` - -Lab $Lab ` - -Switches $Switches ` - -ErrorAction Stop - - # Initialize the VM Template VHDs - $VMTemplateVHDs = Get-LabVMTemplateVHD ` - -Lab $Lab - Initialize-LabVMTemplateVHD ` - -Lab $Lab ` - -VMTemplateVHDs $VMTemplateVHDs ` - -ErrorAction Stop - - # Initialize the VM Templates - $VMTemplates = Get-LabVMTemplate ` - -Lab $Lab - Initialize-LabVMTemplate ` - -Lab $Lab ` - -VMTemplates $VMTemplates ` - -ErrorAction Stop - - # Initialize the VMs - $VMs = Get-LabVM ` - -Lab $Lab ` - -VMTemplates $VMTemplates ` - -Switches $Switches - Initialize-LabVM ` - -Lab $Lab ` - -VMs $VMs ` - -ErrorAction Stop - - Write-LabMessage -Message $($LocalizedData.LabInstallCompleteMessage ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath) - } # process - - end - { - } # end -} # Install-Lab diff --git a/src/lib/public/Install-LabVm.ps1 b/src/lib/public/Install-LabVm.ps1 deleted file mode 100644 index 46173164..00000000 --- a/src/lib/public/Install-LabVm.ps1 +++ /dev/null @@ -1,90 +0,0 @@ -function Install-LabVM -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position=1, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position=2)] - [ValidateNotNullOrEmpty()] - [LabVM] $VM - ) - - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - # The VM is now ready to be started - if ((Get-VM -Name $VM.Name).State -eq 'Off') - { - Write-LabMessage -Message $($LocalizedData.StartingVMMessage ` - -f $VM.Name) - - Start-VM -VMName $VM.Name - } # if - - # We only perform this section of VM Initialization (DSC, Cert, etc) with Server OS - if ($VM.DSC.ConfigFile) - { - # Has this VM been initialized before (do we have a cert for it) - if (-not (Test-Path "$LabPath\$($VM.Name)\LabBuilder Files\$Script:DSCEncryptionCert")) - { - # No, so check it is initialized and download the cert if required - if (Wait-LabVMInitializationComplete -VM $VM -ErrorAction Continue) - { - Write-LabMessage -Message $($LocalizedData.CertificateDownloadStartedMessage ` - -f $VM.Name) - - if ($VM.CertificateSource -eq [LabCertificateSource]::Guest) - { - if (Recieve-LabSelfSignedCertificate -Lab $Lab -VM $VM) - { - Write-LabMessage -Message $($LocalizedData.CertificateDownloadCompleteMessage ` - -f $VM.Name) - } - else - { - $exceptionParameters = @{ - errorId = 'CertificateDownloadError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.CertificateDownloadError ` - -f $VM.name) - } - New-LabException @exceptionParameters - } # if - } # if - } - else - { - $exceptionParameters = @{ - errorId = 'InitializationDidNotCompleteError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.InitializationDidNotCompleteError ` - -f $VM.name) - } - New-LabException @exceptionParameters - } # if - } # if - - if ($VM.OSType -in ([LabOStype]::Nano)) - { - # Copy ODJ Files if it Exists - Copy-LabOdjFile ` - -Lab $Lab ` - -VM $VM - } # if - - # Create any DSC Files for the VM - Initialize-LabDSC ` - -Lab $Lab ` - -VM $VM - - # Attempt to start DSC on the VM - Start-LabDSC ` - -Lab $Lab ` - -VM $VM - } # if -} # Install-LabVM diff --git a/src/lib/public/New-Lab.ps1 b/src/lib/public/New-Lab.ps1 deleted file mode 100644 index 64990739..00000000 --- a/src/lib/public/New-Lab.ps1 +++ /dev/null @@ -1,157 +0,0 @@ -function New-Lab -{ - [CmdLetBinding( - SupportsShouldProcess = $true)] - [OutputType([XML])] - param - ( - [Parameter( - Position=1, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $ConfigPath, - - [Parameter( - Position=2, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $LabPath, - - [Parameter( - Position=3, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $Name, - - [Parameter( - Position=4)] - [ValidateNotNullOrEmpty()] - [System.String] $Version = '1.0', - - [Parameter( - Position=5)] - [ValidateNotNullOrEmpty()] - [System.String] $Id, - - [Parameter( - Position=6)] - [ValidateNotNullOrEmpty()] - [System.String] $Description, - - [Parameter( - Position=7)] - [ValidateNotNullOrEmpty()] - [System.String] $DomainName, - - [Parameter( - Position=8)] - [ValidateNotNullOrEmpty()] - [System.String] $Email - ) # Param - - # Determine the full Lab Path - if (-not [System.IO.Path]::IsPathRooted($LabPath)) - { - $LabPath = Join-Path ` - -Path Get-Location ` - -ChildPath $LabPath - } # if - - # Does the Lab Path exist? - if (Test-Path -Path $LabPath -Type Container) - { - # It does - exit if the user declines - if (-not $PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldOverwriteLab ` - -f $LabPath ))) - { - return - } - } - else - { - Write-LabMessage -Message $($LocalizedData.CreatingLabFolderMessage ` - -f 'LabPath',$LabPath) - - $null = New-Item ` - -Path $LabPath ` - -Type Directory - } # if - - # Determine the full Lab configuration Path - if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) - { - $ConfigPath = Join-Path ` - -Path $LabPath ` - -ChildPath $ConfigPath - } # if - - # Does the lab configuration path already exist? - if (Test-Path -Path $ConfigPath) - { - # It does - exit if the user declines - if (-not $PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldOverwriteLabConfig ` - -f $ConfigPath ))) - { - return - } - } # if - - # Get the Config Template into a variable - $Content = Get-Content ` - -Path $Script:ConfigurationXMLTemplate - - # The XML passes the Schema check so load it. - [XML] $Lab = New-Object System.Xml.XmlDocument - $Lab.PreserveWhitespace = $true - $Lab.LoadXML($Content) - - # Populate the Lab Entries - $Lab.labbuilderconfig.name = $Name - $Lab.labbuilderconfig.version = $Version - $Lab.labbuilderconfig.settings.labpath = $LabPath - if ($PSBoundParameters.ContainsKey('Id')) - { - $Lab.labbuilderconfig.settings.SetAttribute('Id',$Id) - } # if - if ($PSBoundParameters.ContainsKey('Description')) - { - $Lab.labbuilderconfig.description = $Description - } # if - if ($PSBoundParameters.ContainsKey('DomainName')) - { - $Lab.labbuilderconfig.settings.SetAttribute('DomainName',$DomainName) - } # if - if ($PSBoundParameters.ContainsKey('Email')) - { - $Lab.labbuilderconfig.settings.SetAttribute('Email',$Email) - } # if - - # Save Configiration XML - $Lab.Save($ConfigPath) - - # Create ISOFiles folder - $null = New-Item ` - -Path (Join-Path -Path $LabPath -ChildPath 'ISOFiles')` - -Type Directory ` - -ErrorAction SilentlyContinue - - # Create VDFFiles folder - $null = New-Item ` - -Path (Join-Path -Path $LabPath -ChildPath 'VHDFiles')` - -Type Directory ` - -ErrorAction SilentlyContinue - - # Copy the DSCLibrary - $null = Copy-Item ` - -Path $Script:DSCLibraryPath ` - -Destination $LabPath ` - -Recurse ` - -Force ` - -ErrorAction SilentlyContinue - - Return (Get-Lab ` - -ConfigPath $ConfigPath ` - -LabPath $LabPath) -} # New-Lab diff --git a/src/lib/public/Remove-LabSwitch.ps1 b/src/lib/public/Remove-LabSwitch.ps1 deleted file mode 100644 index 38cc5366..00000000 --- a/src/lib/public/Remove-LabSwitch.ps1 +++ /dev/null @@ -1,138 +0,0 @@ -function Remove-LabSwitch -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] - $Name, - - [Parameter( - Position = 3)] - [LabSwitch[]] - $Switches, - - [Parameter( - Position = 4)] - [Switch] - $RemoveExternal - ) - - $PSBoundParameters.Remove('RemoveExternal') - - # if switches were not passed so pull them - if (-not $PSBoundParameters.ContainsKey('switches')) - { - [LabSwitch[]] $Switches = Get-LabSwitch ` - @PSBoundParameters - } - - # Delete Hyper-V Switches - foreach ($VMSwitch in $Switches) - { - if ($Name -and ($VMSwitch.name -notin $Name)) - { - # A names list was passed but this swtich wasn't included - continue - } # if - - $existingVMSwitch = Get-VMSwitch | Where-Object -Property Name -eq $VMSwitch.Name - - if ($existingVMSwitch.Count -ne 0) - { - $SwitchName = $VMSwitch.Name - - if (-not $SwitchName) - { - $exceptionParameters = @{ - errorId = 'SwitchNameIsEmptyError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.SwitchNameIsEmptyError) - } - New-LabException @exceptionParameters - } - - [LabSwitchType] $SwitchType = $VMSwitch.Type - - Write-LabMessage -Message $($LocalizedData.DeleteingVirtualSwitchMessage ` - -f $SwitchType, $SwitchName) - - Switch ($SwitchType) - { - 'External' - { - if ($VMSwitch.Adapters) - { - $VMSwitch.Adapters.foreach( { - $null = Remove-VMNetworkAdapter ` - -SwitchName $SwitchName ` - -Name $_.Name ` - -ManagementOS ` - -ErrorAction SilentlyContinue - } ) - } # if - - if ($RemoveExternal) - { - Remove-VMSwitch ` - -Name $SwitchName - } - break - } # 'External' - - 'Private' - { - Remove-VMSwitch ` - -Name $SwitchName - break - } # 'Private' - - 'Internal' - { - if ($VMSwitch.Adapters) - { - $VMSwitch.Adapters.foreach( { - $null = Remove-VMNetworkAdapter ` - -SwitchName $SwitchName ` - -Name $_.Name ` - -ManagementOS ` - -ErrorAction SilentlyContinue - } ) - } # if - - Remove-VMSwitch ` - -Name $SwitchName - break - } # 'Internal' - - 'NAT' - { - Remove-NetNat ` - -Name $SwitchName - Remove-VMSwitch ` - -Name $SwitchName - break - } # 'Internal' - - Default - { - $exceptionParameters = @{ - errorId = 'UnknownSwitchTypeError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.UnknownSwitchTypeError ` - -f $SwitchType, $SwitchName) - } - New-LabException @exceptionParameters - } - } # Switch - } # if - } # foreach -} diff --git a/src/lib/public/Remove-LabVMTemplate.ps1 b/src/lib/public/Remove-LabVMTemplate.ps1 deleted file mode 100644 index 9046bbc7..00000000 --- a/src/lib/public/Remove-LabVMTemplate.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -function Remove-LabVMTemplate -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabVMTemplate[]] $VMTemplates - ) - - # if VMTeplates array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMTemplates')) - { - $VMTemplates = Get-LabVMTemplate ` - @PSBoundParameters - } # if - foreach ($VMTemplate in $VMTemplates) - { - if ($Name -and ($VMTemplate.Name -notin $Name)) - { - # A names list was passed but this VM Template wasn't included - continue - } # if - - if (Test-Path $VMTemplate.ParentVhd) - { - Set-ItemProperty ` - -Path $VMTemplate.parentvhd ` - -Name IsReadOnly ` - -Value $false - Write-LabMessage -Message $($LocalizedData.DeletingParentVHDMessage ` - -f $VMTemplate.ParentVhd) - Remove-Item ` - -Path $VMTemplate.ParentVhd ` - -Confirm:$false ` - -Force - } # if - } # foreach -} diff --git a/src/lib/public/Remove-LabVm.ps1 b/src/lib/public/Remove-LabVm.ps1 deleted file mode 100644 index 3ad7328b..00000000 --- a/src/lib/public/Remove-LabVm.ps1 +++ /dev/null @@ -1,92 +0,0 @@ -function Remove-LabVM -{ - [CmdLetBinding()] - param - ( - [Parameter( - Position=1, - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position=2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position=3)] - [LabVM[]] $VMs, - - [Parameter( - Position=4)] - [Switch] $RemoveVMFolder - ) - - # if VMs array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMs')) - { - $null = $PSBoundParameters.Remove('RemoveVMFolder') - [LabVM[]] $VMs = Get-LabVM ` - @PSBoundParameters - } # if - - $CurrentVMs = Get-VM - - # Get the LabPath - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - foreach ($VM in $VMs) - { - if ($Name -and ($VM.Name -notin $Name)) - { - # A names list was passed but this VM wasn't included - continue - } # if - - if (($CurrentVMs | Where-Object -Property Name -eq $VM.Name).Count -ne 0) - { - # if the VM is running we need to shut it down. - if ((Get-VM -Name $VM.Name).State -eq 'Running') - { - Write-LabMessage -Message $($LocalizedData.StoppingVMMessage ` - -f $VM.Name) - - Stop-VM ` - -Name $VM.Name - # Wait for it to completely shut down and report that it is off. - Wait-LabVMOff ` - -VM $VM - } - - Write-LabMessage -Message $($LocalizedData.RemovingVMMessage ` - -f $VM.Name) - - # Now delete the actual VM - Get-VM ` - -Name $VM.Name | Remove-VM -Force -Confirm:$false - - Write-LabMessage -Message $($LocalizedData.RemovedVMMessage ` - -f $VM.Name) - } - else - { - Write-LabMessage -Message $($LocalizedData.VMNotFoundMessage ` - -f $VM.Name) - } - } - # Should we remove the VM Folder? - if ($RemoveVMFolder) - { - if (Test-Path -Path $VM.VMRootPath) - { - Write-LabMessage -Message $($LocalizedData.DeletingVMFolderMessage ` - -f $VM.Name) - - Remove-Item ` - -Path $VM.VMRootPath ` - -Recurse ` - -Force - } - } -} # Remove-LabVM diff --git a/src/lib/public/Remove-LabVmTemplateVhd.ps1 b/src/lib/public/Remove-LabVmTemplateVhd.ps1 deleted file mode 100644 index 8722cbd2..00000000 --- a/src/lib/public/Remove-LabVmTemplateVhd.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -function Remove-LabVMTemplateVHD -{ - param - ( - [Parameter( - Position = 1, - Mandatory = $true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position = 2)] - [ValidateNotNullOrEmpty()] - [System.String[]] $Name, - - [Parameter( - Position = 3)] - [LabVMTemplateVHD[]] $VMTemplateVHDs - ) - - # if VMTeplateVHDs array not passed, pull it from config. - if (-not $PSBoundParameters.ContainsKey('VMTemplateVHDs')) - { - [LabVMTemplateVHD[]] $VMTemplateVHDs = Get-LabVMTemplateVHD ` - @PSBoundParameters - } # if - - # if there are no VMTemplateVHDs just return - if ($null -eq $VMTemplateVHDs) - { - return - } # if - - [System.String] $LabPath = $Lab.labbuilderconfig.settings.labpath - - foreach ($VMTemplateVHD in $VMTemplateVHDs) - { - [System.String] $TemplateVHDName = $VMTemplateVHD.Name - if ($Name -and ($TemplateVHDName -notin $Name)) - { - # A names list was passed but this VM Template VHD wasn't included - continue - } # if - - [System.String] $VHDPath = $VMTemplateVHD.VHDPath - - if (Test-Path -Path ($VHDPath)) - { - Remove-Item ` - -Path $VHDPath ` - -Force - Write-LabMessage -Message $($LocalizedData.DeletingVMTemplateVHDFileMessage ` - -f $TemplateVHDName, $VHDPath) - } # if - } # endfor -} # Remove-LabVMTemplateVHD diff --git a/src/lib/public/Start-Lab.ps1 b/src/lib/public/Start-Lab.ps1 deleted file mode 100644 index 60e07b59..00000000 --- a/src/lib/public/Start-Lab.ps1 +++ /dev/null @@ -1,174 +0,0 @@ -function Start-Lab -{ - [CmdLetBinding(DefaultParameterSetName="Lab")] - param - ( - [parameter( - Position=1, - ParameterSetName="File", - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $ConfigPath, - - [parameter( - Position=2, - ParameterSetName="File")] - [ValidateNotNullOrEmpty()] - [System.String] $labPath, - - [Parameter( - Position=3, - ParameterSetName="Lab", - Mandatory=$true, - ValueFromPipeline=$true)] - [ValidateNotNullOrEmpty()] - $lab, - - [Parameter( - Position=4)] - [System.Int32] $StartupTimeout = $Script:StartupTimeout - ) # Param - - begin - { - # Remove some PSBoundParameters so we can Splat - $null = $PSBoundParameters.Remove('StartupTimeout') - - if ($PSCmdlet.ParameterSetName -eq 'File') - { - # Read the configuration - $lab = Get-Lab @PSBoundParameters - } # if - } # begin - - process - { - # Get the VMs - $vms = Get-LabVM ` - -Lab $lab - - # Get the bootorders by lowest first and ignoring 0 and call - $bootOrders = @( ($vms | - Where-Object -FilterScript { ($_.Bootorder -gt 0) } ).Bootorder ) - $bootPhases = @( ($bootOrders | Sort-Object -Unique) ) - - # Step through each of these "Bootphases" waiting for them to complete - foreach ($bootPhase in $bootPhases) - { - # Process this "Bootphase" - Write-LabMessage -Message $($LocalizedData.StartingBootPhaseVMsMessage ` - -f $bootPhase) - - # Get all VMs in this "Bootphase" - $bootVMs = @( $vms | Where-Object -FilterScript { ($_.BootOrder -eq $bootPhase) } ) - - $startPhase = Get-Date - $phaseComplete = $false - $phaseAllBooted = $true - $vmCount = $bootVMs.Count - $vmNumber = 0 - - <# - Loop through all the VMs in this "Bootphase" repeatedly - until timeout occurs or PhaseComplete is marked as complete - #> - while (-not $phaseComplete ` - -and ((Get-Date) -lt $startPhase.AddSeconds($StartupTimeout))) - { - # Get the VM to boot/check - $vm = $bootVMs[$vmNumber] - $vmName = $vm.Name - - # Get the actual Hyper-V VM object - $vmObject = Get-VM ` - -Name $vmName ` - -ErrorAction SilentlyContinue - - if ($vmObject) - { - # Start the VM if it is off - if ($vmObject.State -eq 'Off') - { - Write-LabMessage -Message $($LocalizedData.StartingVMMessage -f $vmName) - Start-VM -VM $vmObject - } # if - - <# - Use the allocation of a Management IP Address as an indicator - the machine has booted - #> - $managementIP = Get-LabVMManagementIPAddress ` - -Lab $lab ` - -VM $vm ` - -ErrorAction SilentlyContinue - - if (-not ($managementIP)) - { - # It has not booted - $phaseAllBooted = $false - } # if - } - else - { - # if the VM does not exist then throw a non-terminating exception - $exceptionParameters = @{ - errorId = 'VMDoesNotExistError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDoesNotExistError ` - -f $vmName) - - } - New-LabException @exceptionParameters - } # if - - $vmNumber++ - - if ($vmNumber -eq $vmCount) - { - <# - We have stepped through all VMs in this Phase so check - if all have booted, otherwise reset the loop. - #> - if ($phaseAllBooted) - { - <# - If we have gone through all VMs in this "Bootphase" - and they're all marked as booted then we can mark - this phase as complete and allow moving on to the next one - #> - Write-LabMessage -Message $($LocalizedData.AllBootPhaseVMsStartedMessage -f $bootPhase) - $phaseComplete = $true - } - else - { - $phaseAllBooted = $true - } # if - - # Reset the VM Loop - $vmNumber = 0 - } # if - } # while - - # Did we timeout? - if (-not ($phaseComplete)) - { - # Yes, throw an exception - $exceptionParameters = @{ - errorId = 'BootPhaseVMsTimeoutError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.BootPhaseStartVMsTimeoutError ` - -f $bootPhase) - - } - New-LabException @exceptionParameters - } # if - } # foreach - - Write-LabMessage -Message $($LocalizedData.LabStartCompleteMessage ` - -f $lab.labbuilderconfig.name,$lab.labbuilderconfig.settings.fullconfigpath) - } # process - - end - { - } # end -} # Start-Lab diff --git a/src/lib/public/Stop-Lab.ps1 b/src/lib/public/Stop-Lab.ps1 deleted file mode 100644 index cb3f75d2..00000000 --- a/src/lib/public/Stop-Lab.ps1 +++ /dev/null @@ -1,146 +0,0 @@ -function Stop-Lab -{ - [CmdLetBinding(DefaultParameterSetName="Lab")] - param - ( - [parameter( - Position=1, - ParameterSetName="File", - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $ConfigPath, - - [parameter( - Position=2, - ParameterSetName="File")] - [ValidateNotNullOrEmpty()] - [System.String] $LabPath, - - [Parameter( - Position=3, - ParameterSetName="Lab", - Mandatory=$true, - ValueFromPipeline=$true)] - [ValidateNotNullOrEmpty()] - $Lab - ) # Param - - begin - { - # Remove some PSBoundParameters so we can Splat - if ($PSCmdlet.ParameterSetName -eq 'File') - { - # Read the configuration - $Lab = Get-Lab ` - @PSBoundParameters - } # if - } # begin - - process - { - # Get the VMs - $vms = Get-LabVM ` - -Lab $Lab - - # Get the bootorders by highest first and ignoring 0 - $bootOrders = @( ($vms | - Where-Object -FilterScript { ($_.Bootorder -gt 0) } ).Bootorder ) - $bootPhases = @( ($bootOrders | Sort-Object -Unique -Descending) ) - - # Step through each of these "Bootphases" waiting for them to complete - foreach ($bootPhase in $bootPhases) - { - # Process this "Bootphase" - Write-LabMessage -Message $($LocalizedData.StoppingBootPhaseVMsMessage ` - -f $bootPhase) - - # Get all VMs in this "Bootphase" - $bootVMs = @( $vms | - Where-Object -FilterScript { ($_.BootOrder -eq $bootPhase) } ) - - $phaseComplete = $false - $phaseAllStopped = $true - $vmCount = $bootVMs.Count - $vmNumber = 0 - - # Loop through all the VMs in this "Bootphase" repeatedly - while (-not $phaseComplete) - { - # Get the VM to boot/check - $VM = $bootVMs[$vmNumber] - $vmName = $VM.Name - - # Get the actual Hyper-V VM object - $vmObject = Get-VM ` - -Name $vmName ` - -ErrorAction SilentlyContinue - - if (-not $vmObject) - { - # if the VM does not exist then throw a non-terminating exception - $exceptionParameters = @{ - errorId = 'VMDoesNotExistError' - errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.VMDoesNotExistError ` - -f $vmName) - - } - New-LabException @exceptionParameters - } # if - - # Shutodwn the VM if it is off - if ($vmObject.State -eq 'Running') - { - Write-LabMessage -Message $($LocalizedData.StoppingVMMessage ` - -f $VMName) - $null = Stop-VM ` - -VM $vmObject ` - -Force ` - -ErrorAction Continue - } # if - - # Determine if the VM has stopped. - if ($vmObject -and (Get-VM -VMName $vmName).State -ne 'Off') - { - # It has not stopped - $phaseAllStopped = $false - } # if - - $vmNumber++ - - if ($vmNumber -eq $vmCount) - { - <# - We have stepped through all VMs in this Phase so check - if all have stopped, otherwise reset the loop. - #> - if ($phaseAllStopped) - { - <# - if we have gone through all VMs in this "Bootphase" - and they're all marked as stopped then we can mark - this phase as complete and allow moving on to the next one - #> - Write-LabMessage -Message $($LocalizedData.AllBootPhaseVMsStoppedMessage ` - -f $bootPhase) - $phaseComplete = $true - } - else - { - $phaseAllStopped = $true - } # if - - # Reset the VM Loop - $vmNumber = 0 - } # if - } # while - } # foreach - - Write-LabMessage -Message $($LocalizedData.LabStopCompleteMessage ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.fullconfigpath) - } # process - - end - { - } # end -} # Stop-Lab diff --git a/src/lib/public/Uninstall-Lab.ps1 b/src/lib/public/Uninstall-Lab.ps1 deleted file mode 100644 index f90b279b..00000000 --- a/src/lib/public/Uninstall-Lab.ps1 +++ /dev/null @@ -1,156 +0,0 @@ -function Uninstall-Lab -{ - [CmdLetBinding(DefaultParameterSetName="Lab", - SupportsShouldProcess = $true, - ConfirmImpact = 'High')] - param - ( - [parameter( - Position=1, - ParameterSetName="File", - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $ConfigPath, - - [parameter( - Position=2, - ParameterSetName="File")] - [ValidateNotNullOrEmpty()] - [System.String] $LabPath, - - [Parameter( - Position=3, - ParameterSetName="Lab", - Mandatory=$true, - ValueFromPipeline=$true)] - [ValidateNotNullOrEmpty()] - $Lab, - - [Parameter( - Position=4)] - [Switch] $RemoveSwitch, - - [Parameter( - Position=5)] - [Switch] $RemoveVMTemplate, - - [Parameter( - Position=6)] - [Switch] $RemoveVMFolder, - - [Parameter( - Position=7)] - [Switch] $RemoveVMTemplateVHD, - - [Parameter( - Position=8)] - [Switch] $RemoveLabFolder - ) # Param - - begin - { - # Remove some PSBoundParameters so we can Splat - $null = $PSBoundParameters.Remove('RemoveSwitch') - $null = $PSBoundParameters.Remove('RemoveVMTemplate') - $null = $PSBoundParameters.Remove('RemoveVMFolder') - $null = $PSBoundParameters.Remove('RemoveVMTemplateVHD') - $null = $PSBoundParameters.Remove('RemoveLabFolder') - - if ($PSCmdlet.ParameterSetName -eq 'File') - { - # Read the configuration - $Lab = Get-Lab ` - @PSBoundParameters - } # if - } # begin - - process - { - if ($PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldUninstallLab ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) - { - # Remove the VMs - $VMSplat = @{} - if ($RemoveVMFolder) - { - $VMSplat += @{ RemoveVMFolder = $true } - } # if - $null = Remove-LabVM ` - -Lab $Lab ` - @VMSplat - - # Remove the VM Templates - if ($RemoveVMTemplate) - { - if ($PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldRemoveVMTemplate ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) - { - $null = Remove-LabVMTemplate ` - -Lab $Lab - } # if - } # if - - # Remove the VM Switches - if ($RemoveSwitch) - { - if ($PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldRemoveSwitch ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) - { - $null = Remove-LabSwitch ` - -Lab $Lab - } # if - } # if - - # Remove the VM Template VHDs - if ($RemoveVMTemplateVHD) - { - if ($PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldRemoveVMTemplateVHD ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) - { - $null = Remove-LabVMTemplateVHD ` - -Lab $Lab - } # if - } # if - - # Remove the Lab Folder - if ($RemoveLabFolder) - { - if (Test-Path -Path $Lab.labbuilderconfig.settings.labpath) - { - if ($PSCmdlet.ShouldProcess( 'LocalHost', ` - ($LocalizedData.ShouldRemoveLabFolder ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ))) - { - Remove-Item ` - -Path $Lab.labbuilderconfig.settings.labpath ` - -Recurse ` - -Force - } # if - } # if - } # if - - # Remove the LabBuilder Management Network switch - [System.String] $ManagementSwitchName = Get-LabManagementSwitchName ` - -Lab $Lab - if ((Get-VMSwitch | Where-Object -Property Name -eq $ManagementSwitchName).Count -ne 0) - { - $null = Remove-VMSwitch ` - -Name $ManagementSwitchName - - Write-LabMessage -Message $($LocalizedData.RemovingLabManagementSwitchMessage ` - -f $ManagementSwitchName) - } - - Write-LabMessage -Message $($LocalizedData.LabUninstallCompleteMessage ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.labpath ) - } # if - } # process - - end - { - } # end -} # Uninstall-Lab diff --git a/src/lib/public/Update-Lab.ps1 b/src/lib/public/Update-Lab.ps1 deleted file mode 100644 index 16a8c1a4..00000000 --- a/src/lib/public/Update-Lab.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -function Update-Lab -{ - [CmdLetBinding(DefaultParameterSetName="Lab")] - param - ( - [parameter( - Position=1, - ParameterSetName="File", - Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] $ConfigPath, - - [parameter( - Position=2, - ParameterSetName="File")] - [ValidateNotNullOrEmpty()] - [System.String] $LabPath, - - [Parameter( - Position=3, - ParameterSetName="Lab", - Mandatory=$true, - ValueFromPipeline=$true)] - [ValidateNotNullOrEmpty()] - $Lab - ) # Param - - begin - { - if ($PSCmdlet.ParameterSetName -eq 'File') - { - # Read the configuration - $Lab = Get-Lab ` - @PSBoundParameters - } # if - } # begin - - process - { - Install-Lab ` - @PSBoundParameters - - Write-LabMessage -Message $($LocalizedData.LabUpdateCompleteMessage ` - -f $Lab.labbuilderconfig.name,$Lab.labbuilderconfig.settings.fullconfigpath) - } # process - - end - { - } # end -} # Update-Lab diff --git a/src/samples/Sample_WS2012R2_DCandDHCPOnly.xml b/src/samples/Sample_WS2012R2_DCandDHCPOnly.xml deleted file mode 100644 index 348180b5..00000000 --- a/src/samples/Sample_WS2012R2_DCandDHCPOnly.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_DCandDHCPOnly" - version="1.0"> - <description>Sample Windows Server 2012 R2 Lab Configuration DC and DHCP Only</description> - - <settings labid="LABBUILDER-DCANDDHCPONLY.COM " - domainname="LABBUILDER-DCANDDHCPONLY.COM" - email="admina@LABBUILDER-DCANDDHCPONLY.COM" - labpath="c:\vm\LABBUILDER-DCANDDHCPONLY.COM /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = ''; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml b/src/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml deleted file mode 100644 index b17816e7..00000000 --- a/src/samples/Sample_WS2012R2_DCandDHCPOnly_NAT.xml +++ /dev/null @@ -1,149 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_DCandDHCPOnly_NAT" - version="1.0"> - <description>Sample Windows Server 2012 R2 Lab Configuration DC and DHCP Only and using a NAT virtual switch.</description> - - <settings labid="LABBUILDER-NAT.COM " - domainname="LABBUILDER-NAT.COM" - email="admina@LABBUILDER-NAT.COM" - labpath="c:\vm\LABBUILDER-NAT.COM" - requiredwindowsbuild="14295" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="Domain NAT" - type="NAT" - vlan="5" - natsubnet="192.168.140.0/24" - natgatewayaddress="192.168.140.1" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Nat" - switchname="Domain Nat"> - <ipv4 address="192.168.140.10" - defaultgateway="192.168.140.1" - subnetmask="24" - dnsserver="192.168.140.10"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.140.50'; - End = '192.168.140.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.140.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.140.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.140.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.140.16'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.140.0'; - DNServerIPAddress = @('192.168.140.10'); - Router = '192.168.140.1'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Nat" - switchname="Domain Nat"> - <ipv4 address="192.168.140.16" - defaultgateway="192.168.140.1" - subnetmask="24" - dnsserver="192.168.140.10"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_DCandDHCPandEdge.xml b/src/samples/Sample_WS2012R2_DCandDHCPandEdge.xml deleted file mode 100644 index 34812101..00000000 --- a/src/samples/Sample_WS2012R2_DCandDHCPandEdge.xml +++ /dev/null @@ -1,203 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_DCandDHCPandEdge" - version="1.0"> - <description>Sample Windows Server 2012 R2 Lab Configuration DC, DHCP and Edge.</description> - - <settings labid="LABBUILDER-DCDHCPEDGE.COM " - domainname="LABBUILDER-DCDHCPEDGE.COM" - email="admina@LABBUILDER-DCDHCPEDGE.COM" - labpath="c:\vm\LABBUILDER-DCDHCPEDGE.COM" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_DomainClustering.xml b/src/samples/Sample_WS2012R2_DomainClustering.xml deleted file mode 100644 index fb1479bb..00000000 --- a/src/samples/Sample_WS2012R2_DomainClustering.xml +++ /dev/null @@ -1,719 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_DomainClustering" - version="1.0"> - <description>Sample Windows Server 2012 R2 Lab Configuration Domain with Cluster Server and NLB nodes</description> - - <settings labid="LABBUILDER-DOMAINCLUSTERING.COM " - domainname="LABBUILDER-DOMAINCLUSTERING.COM" - email="admin@LABBUILDER-DOMAINCLUSTERING.COM" - labpath="c:\vm\LABBUILDER-DOMAINCLUSTERING.COM" - requiredwindowsbuild="10586" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site A iSCSI" type="Private" vlan="3" /> - <switch name="Domain Private Site A LM" type="Private" vlan="4" /> - <switch name="Domain Private Site A SMB" type="Private" vlan="5" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DC2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC2" - bootorder="1"> - <dsc configname="DC_SECONDARY" - configfile="DC_SECONDARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCName = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.11" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::b" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.24'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000B'; - IPAddress = '192.168.128.25'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB1-CLS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000D'; - IPAddress = '192.168.128.35'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000C'; - IPAddress = '192.168.128.26'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB2-CLS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000E'; - IPAddress = '192.168.128.36'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000F'; - IPAddress = '192.168.128.28'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000010'; - IPAddress = '192.168.128.29'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOC3'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000011'; - IPAddress = '192.168.128.30'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOHV1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000F'; - IPAddress = '192.168.128.31'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOHV2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000010'; - IPAddress = '192.168.128.32'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LABBUILDER.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt\n32:http://pki.labbuilder.com/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FS1" - bootorder="4"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="40GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER_ISCSI" - configfile="MEMBER_FILESERVER_ISCSI.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - TargetName = 'sa-foc-target' - VirtualDisks = @( - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-witness.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 500MB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk1.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk2.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk3.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - } - ) - ClusterInitiatorIds = @( - 'Iqn:iqn.1991-05.com.microsoft:sa-foc1.labbuilder.com' - 'Iqn:iqn.1991-05.com.microsoft:sa-foc2.labbuilder.com' - 'Iqn:iqn.1991-05.com.microsoft:sa-foc3.labbuilder.com' - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.24" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::18" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.24" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::18" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-NLB1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-NLB1" - bootorder="5"> - <dsc configname="MEMBER_NLB" - configfile="MEMBER_NLB.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DCName = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.25" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::19" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A Cluster" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.35" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::35" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-NLB2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-NLB2" - bootorder="5"> - <dsc configname="MEMBER_NLB" - configfile="MEMBER_NLB.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DCName = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.26" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::1a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A Cluster" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.36" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::36" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FOC1" - bootorder="6"> - <dsc configname="MEMBER_FAILOVERCLUSTER_FS" - configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - ServerName = 'sa-fs1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.28' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.28" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::28" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.28" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::28" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.28" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::28" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOC2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FOC2" - bootorder="6"> - <dsc configname="MEMBER_FAILOVERCLUSTER_FS" - configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - ServerName = 'sa-fs1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.29' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.29" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::29" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.29" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::29" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.29" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::29" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOC3" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FOC3" - bootorder="6"> - <dsc configname="MEMBER_FAILOVERCLUSTER_FS" - configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - ServerName = 'sa-fs1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.30' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.30" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::30" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.30" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::30" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.30" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::30" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOHV1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FOHV1" - memorystartupbytes="4GB" - dynamicmemoryenabled="N" - exposevirtualizationextensions="Y" - bootorder="0"> - <dsc configname="MEMBER_FAILOVERCLUSTER_HV" - configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.31" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::31" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A LM" - switchname="Domain Private Site A LM"> - <ipv4 address="192.168.130.31" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc02::31" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.31" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::31" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOHV2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FOHV2" - memorystartupbytes="4GB" - dynamicmemoryenabled="N" - exposevirtualizationextensions="Y" - bootorder="0"> - <dsc configname="MEMBER_FAILOVERCLUSTER_HV" - configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.32" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::32" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A LM" - switchname="Domain Private Site A LM"> - <ipv4 address="192.168.130.32" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc02::32" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.32" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::32" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_DomainComplete.xml b/src/samples/Sample_WS2012R2_DomainComplete.xml deleted file mode 100644 index 8dc258c8..00000000 --- a/src/samples/Sample_WS2012R2_DomainComplete.xml +++ /dev/null @@ -1,691 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_DomainComplete" - version="1.0"> - <description> - Sample Windows Server 2012 R2 Lab Configuration Domain with multiple DCs, DHCP Servers, WSUS, WDS, Edge, File Servers and two-tier PKI with offline CA. - - Useful for general experimentation and testing of Windows Server 2012 R2 features. - </description> - - <settings labid="LABBUILDER-DOMAINCOMPLETE.COM " - domainname="LABBUILDER-DOMAINCOMPLETE.COM" - email="admin@LABBUILDER-DOMAINCOMPLETE.COM" - labpath="c:\vm\LABBUILDER-DOMAINCOMPLETE.COM" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - <msu name="RSAT-W10" - url="http://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-KB2693643-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - <switch name="Internet" type="Private" vlan="9" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - <templatevhd name="Windows 10 Enterprise" - iso="10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise" - vhd="Windows 10 Enterprise.vhdx" - edition="Windows 10 Enterprise" - ostype="Client" - packages="RSAT-W10" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter Core" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows 10 Enterprise" - templatevhd="Windows 10 Enterprise" - memorystartupbytes="2GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Client" - packages="RSAT-W10" /> - </templates> - - <vms> - <vm name="SS-ROOTCA" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SS-ROOTCA"> - <dsc configname="STANDALONE_ROOTCA" - configfile="STANDALONE_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - CACommonName = "LABBUILDER.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - SubCAs = @('SA-SUBCA') - </parameters> - </dsc> - </vm> - - <vm name="SS-INTERNET" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="SS-INTERNET"> - <dsc configname="STANDALONE_INTERNET" - configfile="STANDALONE_INTERNET.DSC.ps1" - logging="Y"> - <parameters> - Scopes = @( - @{ Name = 'Internet'; - Start = '131.107.0.50'; - End = '131.107.0.250'; - SubnetMask = '255.255.0.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-EDGE1'; - ScopeID = '131.107.0.0'; - ClientMACAddress = '000000000000'; - IPAddress = '131.107.0.50'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE2'; - ScopeID = '131.107.0.0'; - ClientMACAddress = '000000000001'; - IPAddress = '131.107.0.51'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '131.107.0.0'; - DNServerIPAddress = @('131.107.0.1'); - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Internet" - switchname="Internet"> - <ipv4 address="131.107.0.1" - defaultgateway="" - subnetmask="8" - dnsserver="131.107.0.1"/> - <ipv6 address="2001:54b9:f782:ae41::10" - defaultgateway="" - subnetmask="64" - dnsserver="2001:54b9:f782:ae41::10"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DC2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC2"> - <dsc configname="DC_SECONDARY" - configfile="DC_SECONDARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - Forwarders = @('8.8.8.8','8.8.4.4') - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.11" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::b" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-RODC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-RODC1"> - <dsc configname="RODC_SECONDARY" - configfile="RODC_SECONDARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - Forwarders = @('8.8.8.8','8.8.4.4') - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.30" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.30,192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::20" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::20,fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-RODC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.30'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000003'; - IPAddress = '192.168.128.17'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NPS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000004'; - IPAddress = '192.168.128.18'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000006'; - IPAddress = '192.168.128.20'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-WDS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.21'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-WSUS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000008'; - IPAddress = '192.168.128.22'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-SUBCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000009'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.24'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000B'; - IPAddress = '192.168.128.25'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Remediation Site A'; - Start = '192.168.129.50'; - End = '192.168.129.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.129.0'; - DNServerIPAddress = ''; - Router = ''; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.17" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::11" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-NPS" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="SA-NPS"> - <dsc configname="MEMBER_NPS_DFSTEST" - configfile="MEMBER_NPS_DFSTEST.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.18" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::12" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-EDGE1"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Internet" - switchname="Internet" > - <ipv4 address="131.107.0.50" - defaultgateway="" - subnetmask="16" - dnsserver="131.107.0.1"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-EDGE2"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.20" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::14" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Internet" - switchname="Internet" > - <ipv4 address="131.107.0.51" - defaultgateway="" - subnetmask="16" - dnsserver="131.107.0.1"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-WDS" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="SA-WDS"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-WDS Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_WDS" - configfile="MEMBER_WDS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.21" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::15" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-WSUS" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-WSUS"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-WSUS Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_WSUS" - configfile="MEMBER_WSUS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.22" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::16" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-SUBCA" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-SUBCA"> - <dsc configname="MEMBER_SUBCA" - configfile="MEMBER_SUBCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LABBUILDER.COM Issuing CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - RootCAName = "SS_ROOTCA" - RootCACommonName = "LABBUILDER.COM Root CA" - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FS1"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER" - configfile="MEMBER_FILESERVER.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.24" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::18" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS2" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-FS2"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS2 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER" - configfile="MEMBER_FILESERVER.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.25" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::19" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="DA-IT01" - template="Template Windows 10 Enterprise" - computername="DA-IT01"> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A" /> - <adapter name="Internet" - switchname="Internet" > - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_DomainSQL2014.xml b/src/samples/Sample_WS2012R2_DomainSQL2014.xml deleted file mode 100644 index 05a51920..00000000 --- a/src/samples/Sample_WS2012R2_DomainSQL2014.xml +++ /dev/null @@ -1,263 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_DomainSQL2014" - version="1.0"> - <description> - Sample Windows Server 2012 R2 Lab Configuration DC, DHCP and Edge. - - A SQL Server 2014 node will also be installed from a SQL Server 2014 ISO with data stored on a local data disk. - - Download the SQL Server 2014 ISO from here: - https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2014 - - **Update the Resource ISO path below with the location of the SQL Server 2014 ISO.** - - Please Note: This sample could be updated to Windows Server 2016 TP5, but there is a problen with installing the .NET 3.5 Framework on that version. - This problem can be worked around by manually installing this feature using the Source set to E:\Sources\Sxs. - </description> - - <settings labid="DOMAINSQL2014.COM " - domainname="DOMAINSQL2014.COM" - email="admina@DOMAINSQL2014.COM" - labpath="c:\vm\DOMAINSQL2014.COM" /> - - <resources isopath="ISOFiles"> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - <iso name="SQL2014SP1_FULL_ENU" - path="SQLServer2014SP1-FullSlipstream-x64-ENU.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2014" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2014.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2014.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - @{ Name = 'SA-SQL1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.20'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2012 R2 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2014.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="SA-SQL1" - template="Template Windows Server 2012 R2 Datacenter FULL" - computername="SA-SQL1" - bootorder="4"> - <dsc configname="MEMBER_SQLSERVER2014" - configfile="MEMBER_SQLSERVER2014.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2014.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallerUsername = 'Administrator' - InstallerPassword = 'P@ssword!1' - SQLAdminAccount = 'Administrator' - SQLDataDrive = 'E' - SourcePath = 'D:\' - Instances = @( - @{ - Name = 'MSSQLSERVER' - Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } - ) - InstallManagementTools = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.21" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::15" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - <datavhds> - <datavhd vhd="SQLData.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="SQLData" /> - </datavhds> - <dvddrives> - <dvddrive iso="SQL2014SP1_FULL_ENU" /> - </dvddrives> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_MultiForest.xml b/src/samples/Sample_WS2012R2_MultiForest.xml deleted file mode 100644 index 8e62d37b..00000000 --- a/src/samples/Sample_WS2012R2_MultiForest.xml +++ /dev/null @@ -1,416 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_MultiForest" - version="1.0"> - <description> - Sample Windows Server 2012 R2 Lab Configuration containing two forests: - - ALPHA.LOCAL - - BRAVO.LOCAL - Each forest contains a child domain DEV.*.LOCAL. - Each forest is on an isolated subnet and contains a DC server, DHCP server and an Edge Server. - The edge servers are also on a shared subnet (Domain Internal) enabling routing between the isolated subnets (Domain Private Alpha/Bravo). - A forest trust is not established between the forests. - </description> - - <settings labid="LABBUILDER-TWOFORESTS.COM " - domainname="LABBUILDER-TWOFORESTS.COM" - email="admina@LABBUILDER-TWOFORESTS.COM" - labpath="c:\vm\LABBUILDER-TWOFORESTS.COM" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Alpha" type="Private" vlan="2" /> - <switch name="Domain Private Bravo" type="Private" vlan="3" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="ALPHA-DC1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-DC2" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-DC2"> - <dsc configname="DC_FORESTCHILDDOMAIN" - configfile="DC_FORESTCHILDDOMAIN.DSC.ps1"> - <parameters> - ParentDomainName = "ALPHA.LOCAL" - DomainName = "DEV" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.11" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.11,192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::b" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::b,fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Alpha Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'ALPHA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-EDGE1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="Domain Internal" - switchname="Domain Internal"> - <ipv4 address="192.168.131.10" - defaultgateway="" - subnetmask="24" - dnsserver=""/> - <ipv6 address="fd53:ccc5:895d:bc00::10" - defaultgateway="" - subnetmask="64" - dnsserver=""/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="BRAVO-DC1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.10" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::a" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-DC2" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-DC2"> - <dsc configname="DC_FORESTCHILDDOMAIN" - configfile="DC_FORESTCHILDDOMAIN.DSC.ps1"> - <parameters> - ParentDomainName = "BRAVO.LOCAL" - DomainName = "SALES" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.11" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.11,192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::b" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::b,fd53:ccc5:895c:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-DHCP1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Bravo Primary'; - Start = '192.168.130.50'; - End = '192.168.130.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'BRAVO-DC1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.130.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-DC2'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.130.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-DHCP1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.130.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-EDGE1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.130.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.130.0'; - DNServerIPAddress = @('192.168.130.10','192.168.130.11'); - Router = '192.168.130.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.16" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::10" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-EDGE1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a"/> - </adapter> - <adapter name="Domain Internal" - switchname="Domain Internal"> - <ipv4 address="192.168.131.20" - defaultgateway="" - subnetmask="24" - dnsserver=""/> - <ipv6 address="fd53:ccc5:895d:bc00::20" - defaultgateway="" - subnetmask="64" - dnsserver=""/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_MultiForest_ADFS.xml b/src/samples/Sample_WS2012R2_MultiForest_ADFS.xml deleted file mode 100644 index a25ffbc4..00000000 --- a/src/samples/Sample_WS2012R2_MultiForest_ADFS.xml +++ /dev/null @@ -1,586 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_MultiForest_ADFS" - version="1.0"> - <description> - Sample Windows Server 2012 R2 Lab Configuration containing two forests: - - ALPHA.LOCAL - - BRAVO.LOCAL - Each forest is on an isolated subnet and contains a DC server, DHCP server, ADFS Server, ADCS Server and an Edge Server. - The edge servers are also on a shared subnet (Domain Internal) enabling routing between the isolated subnets (Domain Private Alpha/Bravo). - ADFS Trusts are not established between the two forests/domains. - The two forests are not configured to trust each others Root certificates. - </description> - - <settings labid="LABBUILDER-ADFS.COM " - domainname="LABBUILDER-ADFS.COM" - email="admina@LABBUILDER-ADFS.COM" - labpath="c:\vm\LABBUILDER-ADFS.COM" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Alpha" type="Private" vlan="2" /> - <switch name="Domain Private Bravo" type="Private" vlan="3" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - <template name="Template Windows Server 2012 R2 Datacenter Core" - templatevhd="Windows Server 2012 R2 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="ALPHA-DC1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-DHCP1" - template="Template Windows Server 2012 R2 Datacenter Core" - computername="ALPHA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Alpha Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'ALPHA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-ROOTCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000003'; - IPAddress = '192.168.128.17'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-ADFS1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000004'; - IPAddress = '192.168.128.18'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'ALPHA-WEBAPP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000006'; - IPAddress = '192.168.128.20'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-ROOTCA" - template="Template Windows Server 2012 R2 Datacenter Core" - computername="ALPHA-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "ALPHA.LOCAL Root CA" - CADistinguishedNameSuffix = "DC=ALPHA,DC=LOCAL" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.alpha.local/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.alpha.local/CertEnroll/%1_%3%4.crt\n32:http://pki.alpha.local/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.17" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-ADFS1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-ADFS1" - bootorder="3"> - <dsc configname="MEMBER_ADFS" - configfile="MEMBER_ADFS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.18" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::18" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="ALPHA-EDGE1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS_WAP" - configfile="MEMBER_REMOTEACCESS_WAP.DSC.ps1"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="Domain Internal" - switchname="Domain Internal"> - <ipv4 address="192.168.131.10" - defaultgateway="" - subnetmask="24" - dnsserver=""/> - <ipv6 address="fd53:ccc5:895d:bc00::10" - defaultgateway="" - subnetmask="64" - dnsserver=""/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="ALPHA-WEBAPP1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="ALPHA-WEBAPP1" - bootorder="3"> - <dsc configname="MEMBER_WEBSERVER" - configfile="MEMBER_WEBSERVER.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "ALPHA.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "ALPHA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Alpha" - switchname="Domain Private Alpha"> - <ipv4 address="192.168.128.20" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::20" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-DC1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.10" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::a" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-DHCP1" - template="Template Windows Server 2012 R2 Datacenter Core" - computername="BRAVO-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Bravo Primary'; - Start = '192.168.130.50'; - End = '192.168.130.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'BRAVO-DC1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.130.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-DHCP1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.130.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-ROOTCA'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000003'; - IPAddress = '192.168.130.17'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-ADFS1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000004'; - IPAddress = '192.168.130.18'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-EDGE1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.130.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'BRAVO-WEBAP1'; - ScopeID = '192.168.130.0'; - ClientMACAddress = '000000000006'; - IPAddress = '192.168.130.20'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.130.0'; - DNServerIPAddress = @('192.168.130.10','192.168.130.11'); - Router = '192.168.130.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.16" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::10" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-ROOTCA" - template="Template Windows Server 2012 R2 Datacenter Core" - computername="BRAVO-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "BRAVO.LOCAL Root CA" - CADistinguishedNameSuffix = "DC=BRAVO,DC=LOCAL" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.bravo.local/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.bravo.local/CertEnroll/%1_%3%4.crt\n32:http://pki.bravo.local/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.17" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10,192.168.130.11"/> - <ipv6 address="fd53:ccc5:895c:bc00::17" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a,fd53:ccc5:895c:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-ADFS1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-ADFS1" - bootorder="3"> - <dsc configname="MEMBER_ADFS" - configfile="MEMBER_ADFS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.18" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10,192.168.130.11"/> - <ipv6 address="fd53:ccc5:895c:bc00::18" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a,fd53:ccc5:895c:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="BRAVO-EDGE1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS_WAP" - configfile="MEMBER_REMOTEACCESS_WAP.DSC.ps1"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.130.10"/> - <ipv6 address="fd53:ccc5:895c:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a"/> - </adapter> - <adapter name="Domain Internal" - switchname="Domain Internal"> - <ipv4 address="192.168.131.20" - defaultgateway="" - subnetmask="24" - dnsserver=""/> - <ipv6 address="fd53:ccc5:895d:bc00::20" - defaultgateway="" - subnetmask="64" - dnsserver=""/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="BRAVO-WEBAPP1" - template="Template Windows Server 2012 R2 Datacenter Full" - computername="BRAVO-WEBAPP1" - bootorder="3"> - <dsc configname="MEMBER_WEBSERVER" - configfile="MEMBER_WEBSERVER.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "BRAVO.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "BRAVO-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Bravo" - switchname="Domain Private Bravo"> - <ipv4 address="192.168.130.20" - defaultgateway="192.168.130.19" - subnetmask="24" - dnsserver="192.168.130.10,192.168.130.11"/> - <ipv6 address="fd53:ccc5:895c:bc00::20" - defaultgateway="fd53:ccc5:895c:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895c:bc00::a,fd53:ccc5:895c:bc00::b"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2012R2_Simple.xml b/src/samples/Sample_WS2012R2_Simple.xml deleted file mode 100644 index 753076cb..00000000 --- a/src/samples/Sample_WS2012R2_Simple.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2012R2_Simple" - version="1.0"> - <description>Sample Windows Server 2012 R2 Lab Configuration Simple</description> - - <settings labid="LABBUILDER-SIMPLE.COM " - domainname="LABBUILDER-SIMPLE.COM" - email="admin@LABBUILDER-SIMPLE.COM" - labpath="c:\vm\LABBUILDER-SIMPLE.COM" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="General Purpose Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2012 R2 Datacenter Full" - templatevhd="Windows Server 2012 R2 Datacenter FULL" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" /> - </templates> - - <vms> - <vm name="SS-DEFAULT" - template="Template Windows Server 2012 R2 Datacenter FULL" - computername="SS-DEFAULT"> - <dsc configname="STANDALONE_DEFAULT" - configfile="STANDALONE_DEFAULT.DSC.ps1"> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.10.2" - defaultgateway="192.168.10.1" - subnetmask="24" - dnsserver="192.168.10.2"/> - <ipv6 address="fd53:ccc5:895a:ba00::2" - defaultgateway="fd53:ccc5:895a:ba00::1" - subnetmask="64" - dnsserver="fd53:ccc5:895a:ba00::2"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DCandDHCPOnly.xml b/src/samples/Sample_WS2016_DCandDHCPOnly.xml deleted file mode 100644 index 3ed4a5c3..00000000 --- a/src/samples/Sample_WS2016_DCandDHCPOnly.xml +++ /dev/null @@ -1,132 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DCandDHCPOnly" - version="1.0"> - <description>Sample Windows Server 2016 Lab Configuration DC and DHCP Only</description> - - <settings labid="LABBUILDER-WS2016.COM " - domainname="LABBUILDER-WS2016.COM" - email="admin@LABBUILDER-WS2016.COM" - labpath="c:\vm\LABBUILDER-WS2016.COM" /> - - <resources> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="General Purpose Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1" > - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DCandDHCPandCA.xml b/src/samples/Sample_WS2016_DCandDHCPandCA.xml deleted file mode 100644 index a32581ef..00000000 --- a/src/samples/Sample_WS2016_DCandDHCPandCA.xml +++ /dev/null @@ -1,201 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DcandDHCPandCA" - version="1.0"> - <description> - Sample Windows Server 2016 Lab Configuration Domain with multiple DCs, DHCP Servers, PKI with enterprise CA. - - Useful for general experimentation and testing of Windows Server 2016 features. - </description> - - <settings labid="LABBUILDER-DCANDDHCPANDCA.COM " - domainname="LABBUILDER-DCANDDHCPANDCA.COM" - email="admin@LABBUILDER-DCANDDHCPANDCA.COM" - labpath="c:\vm\LABBUILDER-DCANDDHCPANDCA.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-ROOTCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000009'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-ROOTCA"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LABBUILDER.COM Issuing CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DCandDHCPandEdge.xml b/src/samples/Sample_WS2016_DCandDHCPandEdge.xml deleted file mode 100644 index 6bfeba08..00000000 --- a/src/samples/Sample_WS2016_DCandDHCPandEdge.xml +++ /dev/null @@ -1,194 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DCandDHCPandEdge" - version="1.0"> - <description>Sample Windows Server 2016 Lab Configuration DC, DHCP and Edge.</description> - - <settings labid="LABBUILDER-DCDHCPEDGE.COM " - domainname="LABBUILDER-DCDHCPEDGE.COM" - email="admina@LABBUILDER-DCDHCPEDGE.COM" - labpath="c:\vm\LABBUILDER-DCDHCPEDGE.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DFSHubAndSpoke.xml b/src/samples/Sample_WS2016_DFSHubAndSpoke.xml deleted file mode 100644 index 2393561f..00000000 --- a/src/samples/Sample_WS2016_DFSHubAndSpoke.xml +++ /dev/null @@ -1,311 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DFSHubAndSpoke" - version="1.0"> - <description>Sample Windows Server 2016 Lab Configuration DC, DHCP, EDGE and Hub and Spoke DFS replication group.</description> - - <settings labid="LABBUILDER-DFSHUBANDSPOKE.COM " - domainname="LABBUILDER-DFSHUBANDSPOKE.COM" - email="admina@LABBUILDER-DFSHUBANDSPOKE.COM" - labpath="c:\vm\LABBUILDER-DFSHUBANDSPOKE.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-SPOKE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000006'; - IPAddress = '192.168.128.20'; - AddressFamily = 'IPv4' - } - @{ Name = 'SA-SPOKE2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.21'; - AddressFamily = 'IPv4' - } - @{ Name = 'SA-HUB'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000008'; - IPAddress = '192.168.128.22'; - AddressFamily = 'IPv4' - } - - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="SA-SPOKE1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-SPOKE1" - bootorder="4"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_DFSSPOKE" - configfile="MEMBER_DFSSPOKE.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.20" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::14" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-SPOKE2" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-SPOKE2" - bootorder="4"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_DFSSPOKE" - configfile="MEMBER_DFSSPOKE.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.21" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::15" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-HUB" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-HUB" - bootorder="5"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_DFSHUB" - configfile="MEMBER_DFSHUB.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - SpokeComputerName = @('SA-SPOKE1','SA-SPOKE2') - ResourceGroupName = 'WebSite' - ResourceGroupDescription = 'Files for web server' - ResourceGroupFolderName = 'WebSiteFiles' - ResourceGroupContentPath = 'd:\inetpub\wwwroot\WebSiteFiles' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.22" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::16" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DomainClustering.xml b/src/samples/Sample_WS2016_DomainClustering.xml deleted file mode 100644 index f9729683..00000000 --- a/src/samples/Sample_WS2016_DomainClustering.xml +++ /dev/null @@ -1,710 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DomainClustering" - version="1.0"> - <description>Sample Windows Server 2016 Lab Configuration Domain with Cluster Server and NLB nodes</description> - - <settings labid="LABBUILDER-DOMAINCLUSTERING.COM " - domainname="LABBUILDER-DOMAINCLUSTERING.COM" - email="admin@LABBUILDER-DOMAINCLUSTERING.COM" - labpath="c:\vm\LABBUILDER-DOMAINCLUSTERING.COM" - requiredwindowsbuild="10586" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site A iSCSI" type="Private" vlan="3" /> - <switch name="Domain Private Site A LM" type="Private" vlan="4" /> - <switch name="Domain Private Site A SMB" type="Private" vlan="5" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DC2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC2" - bootorder="1"> - <dsc configname="DC_SECONDARY" - configfile="DC_SECONDARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCName = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.11" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::b" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.24'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000B'; - IPAddress = '192.168.128.25'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB1-CLS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000D'; - IPAddress = '192.168.128.35'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000C'; - IPAddress = '192.168.128.26'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NLB2-CLS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000E'; - IPAddress = '192.168.128.36'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000F'; - IPAddress = '192.168.128.28'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000010'; - IPAddress = '192.168.128.29'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOC3'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000011'; - IPAddress = '192.168.128.30'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOHV1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000F'; - IPAddress = '192.168.128.31'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FOHV2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000010'; - IPAddress = '192.168.128.32'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LABBUILDER.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt\n32:http://pki.labbuilder.com/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FS1" - bootorder="4"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="40GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER_ISCSI" - configfile="MEMBER_FILESERVER_ISCSI.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - TargetName = 'sa-foc-target' - VirtualDisks = @( - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-witness.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 500MB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk1.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk2.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - }, - @{ Path = 'D:\iSCSIVirtualDisks\sa-foc-disk3.vhdx'; - DiskType = 'Dynamic'; - SizeBytes = 10GB; - } - ) - ClusterInitiatorIds = @( - 'Iqn:iqn.1991-05.com.microsoft:sa-foc1.labbuilder.com' - 'Iqn:iqn.1991-05.com.microsoft:sa-foc2.labbuilder.com' - 'Iqn:iqn.1991-05.com.microsoft:sa-foc3.labbuilder.com' - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.24" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::18" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.24" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::18" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-NLB1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-NLB1" - bootorder="5"> - <dsc configname="MEMBER_NLB" - configfile="MEMBER_NLB.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DCName = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.25" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::19" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A Cluster" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.35" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::35" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-NLB2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-NLB2" - bootorder="5"> - <dsc configname="MEMBER_NLB" - configfile="MEMBER_NLB.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DCName = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.26" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::1a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A Cluster" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.36" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::36" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FOC1" - bootorder="6"> - <dsc configname="MEMBER_FAILOVERCLUSTER_FS" - configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - ServerName = 'sa-fs1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.28' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.28" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::28" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.28" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::28" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.28" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::28" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOC2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FOC2" - bootorder="6"> - <dsc configname="MEMBER_FAILOVERCLUSTER_FS" - configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - ServerName = 'sa-fs1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.29' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.29" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::29" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.29" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::29" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.29" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::29" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOC3" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FOC3" - bootorder="6"> - <dsc configname="MEMBER_FAILOVERCLUSTER_FS" - configfile="MEMBER_FAILOVERCLUSTER_FS.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - ServerName = 'sa-fs1' - ServerTargetName = 'sa-fs1-sa-foc-target-target' - TargetPortalAddress = '192.168.129.24' - InitiatorPortalAddress = '192.168.129.30' - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.30" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::30" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A iSCSI" - switchname="Domain Private Site A iSCSI"> - <ipv4 address="192.168.129.30" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc01::30" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.30" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::30" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOHV1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FOHV1" - memorystartupbytes="4GB" - dynamicmemoryenabled="N" - exposevirtualizationextensions="Y" - bootorder="0"> - <dsc configname="MEMBER_FAILOVERCLUSTER_HV" - configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.31" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::31" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A LM" - switchname="Domain Private Site A LM"> - <ipv4 address="192.168.130.31" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc02::31" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.31" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::31" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - - <vm name="SA-FOHV2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FOHV2" - memorystartupbytes="4GB" - dynamicmemoryenabled="N" - exposevirtualizationextensions="Y" - bootorder="0"> - <dsc configname="MEMBER_FAILOVERCLUSTER_HV" - configfile="MEMBER_FAILOVERCLUSTER_HV.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A" - macaddressspoofing="On"> - <ipv4 address="192.168.128.32" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::32" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Domain Private Site A LM" - switchname="Domain Private Site A LM"> - <ipv4 address="192.168.130.32" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc02::32" - subnetmask="64" /> - </adapter> - <adapter name="Domain Private Site A SMB" - switchname="Domain Private Site A SMB"> - <ipv4 address="192.168.131.32" - subnetmask="24" /> - <ipv6 address="fd53:ccc5:895a:bc03::32" - subnetmask="64" /> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DomainComplete.xml b/src/samples/Sample_WS2016_DomainComplete.xml deleted file mode 100644 index a8702fee..00000000 --- a/src/samples/Sample_WS2016_DomainComplete.xml +++ /dev/null @@ -1,687 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DomainComplete" - version="1.0"> - <description> - Sample Windows Server 2016 Lab Configuration Domain with multiple DCs, DHCP Servers, WSUS, WDS, Edge, File Servers and two-tier PKI with offline CA. - - Useful for general experimentation and testing of Windows Server 2016 features. - </description> - - <settings labid="LABBUILDER-DOMAINCOMPLETE.COM " - domainname="LABBUILDER-DOMAINCOMPLETE.COM" - email="admin@LABBUILDER-DOMAINCOMPLETE.COM" - labpath="c:\vm\LABBUILDER-DOMAINCOMPLETE.COM" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - <msu name="RSAT-W10" - url="http://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-KB2693643-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - <switch name="Internet" type="Private" vlan="9" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - <templatevhd name="Windows 10 Enterprise" - iso="10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise" - vhd="Windows 10 Enterprise.vhdx" - edition="Windows 10 Enterprise" - ostype="Client" - packages="RSAT-W10" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows 10 Enterprise" - templatevhd="Windows 10 Enterprise" - memorystartupbytes="2GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Client" - packages="RSAT-W10" /> - </templates> - - <vms> - <vm name="SS-ROOTCA" - template="Template Windows Server 2016 Datacenter CORE" - computername="SS-ROOTCA"> - <dsc configname="STANDALONE_ROOTCA" - configfile="STANDALONE_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - CACommonName = "LABBUILDER.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - SubCAs = @('SA-SUBCA') - </parameters> - </dsc> - </vm> - - <vm name="SS-INTERNET" - template="Template Windows Server 2016 Datacenter Full" - computername="SS-INTERNET"> - <dsc configname="STANDALONE_INTERNET" - configfile="STANDALONE_INTERNET.DSC.ps1" - logging="Y"> - <parameters> - Scopes = @( - @{ Name = 'Internet'; - Start = '131.107.0.50'; - End = '131.107.0.250'; - SubnetMask = '255.255.0.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-EDGE1'; - ScopeID = '131.107.0.0'; - ClientMACAddress = '000000000000'; - IPAddress = '131.107.0.50'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE2'; - ScopeID = '131.107.0.0'; - ClientMACAddress = '000000000001'; - IPAddress = '131.107.0.51'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '131.107.0.0'; - DNServerIPAddress = @('131.107.0.1'); - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Internet" - switchname="Internet"> - <ipv4 address="131.107.0.1" - defaultgateway="" - subnetmask="8" - dnsserver="131.107.0.1"/> - <ipv6 address="2001:54b9:f782:ae41::10" - defaultgateway="" - subnetmask="64" - dnsserver="2001:54b9:f782:ae41::10"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DC2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC2"> - <dsc configname="DC_SECONDARY" - configfile="DC_SECONDARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - Forwarders = @('8.8.8.8','8.8.4.4') - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.11" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::b" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-RODC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-RODC1"> - <dsc configname="RODC_SECONDARY" - configfile="RODC_SECONDARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - Forwarders = @('8.8.8.8','8.8.4.4') - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.30" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.30,192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::20" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::20,fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DC2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.11'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-RODC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000001'; - IPAddress = '192.168.128.30'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000003'; - IPAddress = '192.168.128.17'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NPS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000004'; - IPAddress = '192.168.128.18'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000006'; - IPAddress = '192.168.128.20'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-WDS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.21'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-WSUS'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000008'; - IPAddress = '192.168.128.22'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-SUBCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000009'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.24'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000B'; - IPAddress = '192.168.128.25'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10','192.168.128.11'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Remediation Site A'; - Start = '192.168.129.50'; - End = '192.168.129.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.129.0'; - DNServerIPAddress = ''; - Router = ''; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.17" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::11" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-NPS" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-NPS"> - <dsc configname="MEMBER_NPS_DFSTEST" - configfile="MEMBER_NPS_DFSTEST.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.18" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::12" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-EDGE1"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Internet" - switchname="Internet" > - <ipv4 address="131.107.0.50" - defaultgateway="" - subnetmask="16" - dnsserver="131.107.0.1"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-EDGE2"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.20" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::14" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - <adapter name="Internet" - switchname="Internet" > - <ipv4 address="131.107.0.51" - defaultgateway="" - subnetmask="16" - dnsserver="131.107.0.1"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-WDS" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-WDS"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-WDS Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_WDS" - configfile="MEMBER_WDS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.21" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::15" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-WSUS" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-WSUS"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-WSUS Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_WSUS" - configfile="MEMBER_WSUS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.22" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::16" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-SUBCA" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-SUBCA"> - <dsc configname="MEMBER_SUBCA" - configfile="MEMBER_SUBCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LABBUILDER.COM Issuing CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - RootCAName = "SS_ROOTCA" - RootCACommonName = "LABBUILDER.COM Root CA" - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FS1"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER" - configfile="MEMBER_FILESERVER.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.24" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::18" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS2" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FS2"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS2 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER" - configfile="MEMBER_FILESERVER.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.25" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::19" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="DA-IT01" - template="Template Windows 10 Enterprise" - computername="DA-IT01"> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A" /> - <adapter name="Internet" - switchname="Internet" > - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DomainFunctions.xml b/src/samples/Sample_WS2016_DomainFunctions.xml deleted file mode 100644 index b0afd0c3..00000000 --- a/src/samples/Sample_WS2016_DomainFunctions.xml +++ /dev/null @@ -1,330 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="LBFUNCTIONS.LOCAL" - version="1.0" > - <description>Simple Windows Server 2016 Lab Configuration creating an AD DC, DHCP Server, Edge Server, Root CA, SQL Server 2016 and File Server.</description> - - <settings labid="LBFUNCTIONS.LOCAL " - domainname="LBFUNCTIONS.LOCAL" - email="daniel@LBFUNCTIONS.LOCAL" - labpath="c:\vm\LBFUNCTIONS.LOCAL" /> - - <resources isopath="ISOFiles"> - <iso name="SQL2016_Full_ENU" - path="SQLServer2016-x64-ENU.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016" /> - </resources> - - <switches managementvlan="97"> - <switch name="General Purpose External" type="External" /> - <switch name="Domain Private" type="Private" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LBFUNCTIONS.LOCAL" - DomainAdminPassword = "P@ssword!1" - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LBFUNCTIONS.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'LBFUNCTIONS.LOCAL Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-ROOTCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-FS1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.24'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-SQL1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.50'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LBFUNCTIONS.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="General Purpose External" - switchname="General Purpose External" /> - </adapters> - <datavhds> - <datavhd vhd="ToolsDisk.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="ToolsDisk" /> - </datavhds> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LBFUNCTIONS.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LBFUNCTIONS.LOCAL Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n74:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.LBFUNCTIONS.LOCAL/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.LBFUNCTIONS.LOCAL/CertEnroll/%1_%3%4.crt\n32:http://pki.LBFUNCTIONS.LOCAL/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-FS1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-FS1"> - <datavhds> - <datavhd vhd="LABBUILDER.COM SA-FS1 Data Disk.vhdx" size="10GB" type="dynamic" /> - </datavhds> - <dsc configname="MEMBER_FILESERVER" - configfile="MEMBER_FILESERVER.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LBFUNCTIONS.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.24" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::18" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a,fd53:ccc5:895a:bc00::b"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-SQL1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-SQL1" - bootorder="4"> - <dsc configname="MEMBER_SQLSERVER2016" - configfile="MEMBER_SQLSERVER2016.DSC.ps1"> - <parameters> - DomainName = "LBFUNCTIONS.LOCAL" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallerUsername = 'Administrator' - InstallerPassword = 'P@ssword!1' - SQLAdminAccount = 'Administrator' - SQLDataDrive = 'E' - SourcePath = 'D:\' - SourceFolder = '' - Instances = @( - @{ - Name = 'MSSQLSERVER' - Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.50" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::50" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - <datavhds> - <datavhd vhd="SQLData.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="SQLData" /> - </datavhds> - <dvddrives> - <dvddrive iso="SQL2016_Full_ENU" /> - </dvddrives> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_DomainSQL2016.xml b/src/samples/Sample_WS2016_DomainSQL2016.xml deleted file mode 100644 index fd078e1f..00000000 --- a/src/samples/Sample_WS2016_DomainSQL2016.xml +++ /dev/null @@ -1,257 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_DomainSQL2016" - version="1.0"> - <description> - Sample Windows Server 2016 Lab Configuration DC, DHCP and Edge. - - A SQL Server 2016 node will also be installed from a SQL Server 2016 ISO with data stored on a local data disk. - - Download the SQL Server 2016 ISO from here: - https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016 - - Use the evaluation installer to download media (as an ISO). - - **Update the Resource ISO path below with the location of the SQL Server 2016 ISO.** - - Management Tools will not be automatically installed as they do not appear on the SQL Server 2016 ISO and must be downloaded and installed separately. - </description> - - <settings labid="DOMAINSQL2016.COM " - domainname="DOMAINSQL2016.COM" - email="admina@DOMAINSQL2016.COM" - labpath="c:\vm\DOMAINSQL2016.COM" /> - - <resources isopath="ISOFiles"> - <iso name="SQL2016_Full_ENU" - path="SQLServer2016-x64-ENU.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016" /> - </resources> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Core.vhdx" - edition="Windows Server 2016 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2016 Datacenter CORE" - templatevhd="Windows Server 2016 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2016.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2016.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - @{ Name = 'SA-SQL1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.20'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2016.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - - <vm name="SA-SQL1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-SQL1" - bootorder="4"> - <dsc configname="MEMBER_SQLSERVER2016" - configfile="MEMBER_SQLSERVER2016.DSC.ps1"> - <parameters> - DomainName = "DOMAINSQL2016.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallerUsername = 'Administrator' - InstallerPassword = 'P@ssword!1' - SQLAdminAccount = 'Administrator' - SQLDataDrive = 'E' - SourcePath = 'D:\' - Instances = @( - @{ - Name = 'MSSQLSERVER' - Features = 'SQLENGINE,FULLTEXT,RS,AS,IS' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.21" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::15" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - <datavhds> - <datavhd vhd="SQLData.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="SQLData" /> - </datavhds> - <dvddrives> - <dvddrive iso="SQL2016_Full_ENU" /> - </dvddrives> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_NanoDomain.xml b/src/samples/Sample_WS2016_NanoDomain.xml deleted file mode 100644 index 955bcca5..00000000 --- a/src/samples/Sample_WS2016_NanoDomain.xml +++ /dev/null @@ -1,328 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="NANOTEST.COM" - version="1.0" > - <description>Simple Windows Server 2016 Lab Configuration creating an AD DC, DHCP Server, Edge Server, Root CA and eight Nano Servers</description> - - <settings labid="NANOTEST.COM " - domainname="NANOTEST.COM" - email="daniel@NANOTEST.COM" - labpath="c:\vm\NANOTEST.COM" /> - - <switches managementvlan="97"> - <switch name="General Purpose External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private" type="Private" vlan="30" /> - <switch name="Domain Private iSCSI" type="Private" vlan="31" /> - <switch name="Domain Private LM" type="Private" vlan="32" /> - <switch name="Domain Private SMB" type="Private" vlan="33" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Nano Server 2016 Datacenter" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Nano Server 2016 Datacenter.vhdx" - edition="Windows Server 2016 SERVERDATACENTERNANO" - ostype="Nano" - packages="Microsoft-NanoServer-Guest-Package.cab" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Nano Server 2016 Datacenter" - templatevhd="Nano Server 2016 Datacenter" - memorystartupbytes="500MB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Nano" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'NANOTEST.COM Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-ROOTCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000008'; - IPAddress = '192.168.128.30'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000009'; - IPAddress = '192.168.128.31'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO3'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.32'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO4'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000B'; - IPAddress = '192.168.128.33'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO5'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000C'; - IPAddress = '192.168.128.34'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO6'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000D'; - IPAddress = '192.168.128.35'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO7'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000E'; - IPAddress = '192.168.128.36'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO8'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000F'; - IPAddress = '192.168.128.37'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="General Purpose External" - switchname="General Purpose External" /> - </adapters> - <datavhds> - <datavhd vhd="ToolsDisk.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="ToolsDisk" /> - </datavhds> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2016 Datacenter Full" - computername="SA-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "NANOTEST.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n74:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.NANOTEST.COM/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.NANOTEST.COM/CertEnroll/%1_%3%4.crt\n32:http://pki.NANOTEST.COM/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-NANO" - template="Template Nano Server 2016 Datacenter" - computername="SA-NANO" - bootorder="3" - instancecount="8" - packages="Microsoft-NanoServer-DSC-Package.cab,Microsoft-NanoServer-Containers-Package.cab,Microsoft-NanoServer-Guest-Package.cab" > - <dsc configname="MEMBER_NANO" - configfile="MEMBER_NANO.DSC.ps1" - logging="Y"> - <parameters> - ODJRequestFile = "c:\ODJRequest.txt" - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.30" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::30" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="General Purpose External" - switchname="General Purpose External" /> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2016_Simple.xml b/src/samples/Sample_WS2016_Simple.xml deleted file mode 100644 index 5c19481c..00000000 --- a/src/samples/Sample_WS2016_Simple.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2016_Simple" - version="1.0"> - <description>Sample Windows Server 2016 Lab Configuration Simple</description> - - <settings labid="LABBUILDER-SIMPLE.COM " - domainname="LABBUILDER-SIMPLE.COM" - email="admin@LABBUILDER-SIMPLE.COM" - labpath="c:\vm\LABBUILDER-SIMPLE.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="General Purpose Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2016 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016" - vhd="Windows Server 2016 Datacenter Full.vhdx" - edition="Windows Server 2016 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2016 Datacenter Full" - templatevhd="Windows Server 2016 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SS-DEFAULT" - template="Template Windows Server 2016 Datacenter Full" - computername="SS-DEFAULT"> - <dsc configname="STANDALONE_DEFAULT" - configfile="STANDALONE_DEFAULT.DSC.ps1"> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.10.2" - defaultgateway="192.168.10.1" - subnetmask="24" - dnsserver="192.168.10.2"/> - <ipv6 address="fd53:ccc5:895a:ba00::2" - defaultgateway="fd53:ccc5:895a:ba00::1" - subnetmask="64" - dnsserver="fd53:ccc5:895a:ba00::2"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2019_AzureADConnect.xml b/src/samples/Sample_WS2019_AzureADConnect.xml deleted file mode 100644 index ac91c3e0..00000000 --- a/src/samples/Sample_WS2019_AzureADConnect.xml +++ /dev/null @@ -1,225 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2019_AzureADConnect" - version="1.0"> - <description>Sample Windows Server 2019 Lab Configuration DC, DHCP, Edge and a Member for testing Azure AD Connect.</description> - - <settings labid="LABBUILDER-AZUREADCONNECT.COM " - domainname="LABBUILDER-AZUREADCONNECT.COM" - email="admina@LABBUILDER-AZUREADCONNECT.COM" - labpath="c:\vm\LABBUILDER-AZUREADCONNECT.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2019 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Full.vhdx" - edition="Windows Server 2019 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2019 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Core.vhdx" - edition="Windows Server 2019 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2019 Datacenter Full" - templatevhd="Windows Server 2019 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2019 Datacenter CORE" - templatevhd="Windows Server 2019 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-AADC'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.17'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-AADC" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-AADC" - bootorder="3"> - <dsc configname="MEMBER_DEFAULT" - configfile="MEMBER_DEFAULT.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.17" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::b" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - </vms> -</labbuilderconfig> diff --git a/src/samples/Sample_WS2019_DCandDHCPandCA.xml b/src/samples/Sample_WS2019_DCandDHCPandCA.xml deleted file mode 100644 index 00dab3dd..00000000 --- a/src/samples/Sample_WS2019_DCandDHCPandCA.xml +++ /dev/null @@ -1,201 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2019_DcandDHCPandCA" - version="1.0"> - <description> - Sample Windows Server 2019 Lab Configuration Domain with multiple DCs, DHCP Servers, PKI with enterprise CA. - - Useful for general experimentation and testing of Windows Server 2019 features. - </description> - - <settings labid="LABBUILDER-DCANDDHCPANDCA.COM " - domainname="LABBUILDER-DCANDDHCPANDCA.COM" - email="admin@LABBUILDER-DCANDDHCPANDCA.COM" - labpath="c:\vm\LABBUILDER-DCANDDHCPANDCA.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2019 Datacenter Full" - iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Full.vhdx" - edition="Windows Server 2019 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2019 Datacenter CORE" - iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Core.vhdx" - edition="Windows Server 2019 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2019 Datacenter Full" - templatevhd="Windows Server 2019 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2019 Datacenter CORE" - templatevhd="Windows Server 2019 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-ROOTCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000009'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-ROOTCA"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "LABBUILDER.COM Issuing CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt" - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10,192.168.128.11"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2019_DCandDHCPandEdge.xml b/src/samples/Sample_WS2019_DCandDHCPandEdge.xml deleted file mode 100644 index 7ece5182..00000000 --- a/src/samples/Sample_WS2019_DCandDHCPandEdge.xml +++ /dev/null @@ -1,194 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2019_DCandDHCPandEdge" - version="1.0"> - <description>Sample Windows Server 2019 Lab Configuration DC, DHCP and Edge.</description> - - <settings labid="LABBUILDER-DCDHCPEDGE.COM " - domainname="LABBUILDER-DCDHCPEDGE.COM" - email="admina@LABBUILDER-DCDHCPEDGE.COM" - labpath="c:\vm\LABBUILDER-DCDHCPEDGE.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2019 Datacenter Full" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Full.vhdx" - edition="Windows Server 2019 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2019 Datacenter CORE" - iso="14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Core.vhdx" - edition="Windows Server 2019 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2019 Datacenter Full" - templatevhd="Windows Server 2019 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Windows Server 2019 Datacenter CORE" - templatevhd="Windows Server 2019 Datacenter CORE" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-DC1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-DHCP1"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DCname = "SA-DC1" - DomainAdminPassword = "P@ssword!1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'Site A Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2019 Datacenter CORE" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "LABBUILDER.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="External" - switchname="External" /> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2019_NanoDomain.xml b/src/samples/Sample_WS2019_NanoDomain.xml deleted file mode 100644 index 23939124..00000000 --- a/src/samples/Sample_WS2019_NanoDomain.xml +++ /dev/null @@ -1,328 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="NANOTEST.COM" - version="1.0" > - <description>Simple Windows Server 2019 Lab Configuration creating an AD DC, DHCP Server, Edge Server, Root CA and eight Nano Servers</description> - - <settings labid="NANOTEST.COM " - domainname="NANOTEST.COM" - email="daniel@NANOTEST.COM" - labpath="c:\vm\NANOTEST.COM" /> - - <switches managementvlan="97"> - <switch name="General Purpose External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="Domain Private" type="Private" vlan="30" /> - <switch name="Domain Private iSCSI" type="Private" vlan="31" /> - <switch name="Domain Private LM" type="Private" vlan="32" /> - <switch name="Domain Private SMB" type="Private" vlan="33" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2019 Datacenter Full" - iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Full.vhdx" - edition="Windows Server 2019 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Nano Server 2019 Datacenter" - iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" - url="https://software-download.microsoft.com/download/sg/" - vhd="Nano Server 2019 Datacenter.vhdx" - edition="Windows Server 2019 SERVERDATACENTERNANO" - ostype="Nano" - packages="Microsoft-NanoServer-Guest-Package.cab" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2019 Datacenter Full" - templatevhd="Windows Server 2019 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - <template name="Template Nano Server 2019 Datacenter" - templatevhd="Nano Server 2019 Datacenter" - memorystartupbytes="500MB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Nano" /> - </templates> - - <vms> - <vm name="SA-DC1" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-DC1" - bootorder="1"> - <dsc configname="DC_FORESTPRIMARY" - configfile="DC_FORESTPRIMARY.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - InstallRSATTools = $true - Forwarders = @('8.8.8.8','8.8.4.4') - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.10" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::a" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-DHCP1" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-DHCP1" - bootorder="2"> - <dsc configname="MEMBER_DHCP" - configfile="MEMBER_DHCP.DSC.ps1"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - Scopes = @( - @{ Name = 'NANOTEST.COM Primary'; - Start = '192.168.128.50'; - End = '192.168.128.254'; - SubnetMask = '255.255.255.0'; - AddressFamily = 'IPv4' - } - ) - Reservations = @( - @{ Name = 'SA-DC1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000000'; - IPAddress = '192.168.128.10'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-DHCP1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000002'; - IPAddress = '192.168.128.16'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-EDGE1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000005'; - IPAddress = '192.168.128.19'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-ROOTCA'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000007'; - IPAddress = '192.168.128.23'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO1'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000008'; - IPAddress = '192.168.128.30'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO2'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '000000000009'; - IPAddress = '192.168.128.31'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO3'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000A'; - IPAddress = '192.168.128.32'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO4'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000B'; - IPAddress = '192.168.128.33'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO5'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000C'; - IPAddress = '192.168.128.34'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO6'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000D'; - IPAddress = '192.168.128.35'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO7'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000E'; - IPAddress = '192.168.128.36'; - AddressFamily = 'IPv4' - }, - @{ Name = 'SA-NANO8'; - ScopeID = '192.168.128.0'; - ClientMACAddress = '00000000000F'; - IPAddress = '192.168.128.37'; - AddressFamily = 'IPv4' - } - ) - ScopeOptions = @( - @{ ScopeID = '192.168.128.0'; - DNServerIPAddress = @('192.168.128.10'); - Router = '192.168.128.19'; - AddressFamily = 'IPv4' - } - ) - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.16" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::10" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-EDGE1" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-EDGE1" - bootorder="3"> - <dsc configname="MEMBER_REMOTEACCESS" - configfile="MEMBER_REMOTEACCESS.DSC.ps1"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.19" - defaultgateway="" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::13" - defaultgateway="" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="General Purpose External" - switchname="General Purpose External" /> - </adapters> - <datavhds> - <datavhd vhd="ToolsDisk.vhdx" type="dynamic" size="10GB" partitionstyle="GPT" filesystem="NTFS" filesystemlabel="ToolsDisk" /> - </datavhds> - </vm> - - <vm name="SA-ROOTCA" - template="Template Windows Server 2019 Datacenter Full" - computername="SA-ROOTCA" - bootorder="3"> - <dsc configname="MEMBER_ROOTCA" - configfile="MEMBER_ROOTCA.DSC.ps1" - logging="Y"> - <parameters> - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - InstallOnlineResponder = $true - InstallEnrollmentWebService = $true - CACommonName = "NANOTEST.COM Root CA" - CADistinguishedNameSuffix = "DC=LABBUILDER,DC=COM" - CRLPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n74:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n2:http://pki.NANOTEST.COM/CertEnroll/%3%8%9.crl" - CACertPublicationURLs = "1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.NANOTEST.COM/CertEnroll/%1_%3%4.crt\n32:http://pki.NANOTEST.COM/ocsp" - CRLPeriodUnits = 52 - CRLPeriod = 'Weeks' - CRLOverlapUnits = 12 - CRLOverlapPeriod = 'Hours' - ValidityPeriodUnits = 10 - ValidityPeriod = 'Years' - AuditFilter = 127 - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.23" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::17" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - </adapters> - </vm> - - <vm name="SA-NANO" - template="Template Nano Server 2019 Datacenter" - computername="SA-NANO" - bootorder="3" - instancecount="8" - packages="Microsoft-NanoServer-DSC-Package.cab,Microsoft-NanoServer-Containers-Package.cab,Microsoft-NanoServer-Guest-Package.cab" > - <dsc configname="MEMBER_NANO" - configfile="MEMBER_NANO.DSC.ps1" - logging="Y"> - <parameters> - ODJRequestFile = "c:\ODJRequest.txt" - DomainName = "NANOTEST.COM" - DomainAdminPassword = "P@ssword!1" - DCName = "SA-DC1" - PSDscAllowDomainUser = $true - </parameters> - </dsc> - <adapters> - <adapter name="Domain Private" - switchname="Domain Private"> - <ipv4 address="192.168.128.30" - defaultgateway="192.168.128.19" - subnetmask="24" - dnsserver="192.168.128.10"/> - <ipv6 address="fd53:ccc5:895a:bc00::30" - defaultgateway="fd53:ccc5:895a:bc00::13" - subnetmask="64" - dnsserver="fd53:ccc5:895a:bc00::a"/> - </adapter> - <adapter name="General Purpose External" - switchname="General Purpose External" /> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/Sample_WS2019_Simple.xml b/src/samples/Sample_WS2019_Simple.xml deleted file mode 100644 index 999ed64e..00000000 --- a/src/samples/Sample_WS2019_Simple.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="Sample_WS2019_Simple" - version="1.0"> - <description>Sample Windows Server 2019 Lab Configuration Simple</description> - - <settings labid="LABBUILDER-SIMPLE.COM " - domainname="LABBUILDER-SIMPLE.COM" - email="admin@LABBUILDER-SIMPLE.COM" - labpath="c:\vm\LABBUILDER-SIMPLE.COM" /> - - <switches> - <switch name="External" type="External"> - <adapters> - <adapter name="Cluster" macaddress="00155D010701" /> - <adapter name="Management" macaddress="00155D010702" /> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - <switch name="General Purpose Internal" type="Internal" /> - <switch name="Domain Private Site A" type="Private" vlan="2" /> - <switch name="Domain Private Site B" type="Private" vlan="3" /> - <switch name="Domain Private Site C" type="Private" vlan="4" /> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" - prefix="" > - <templatevhd name="Windows Server 2019 Datacenter Full" - iso="17763.379.190312-0539.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us.iso" - url="https://software-download.microsoft.com/download/sg/" - vhd="Windows Server 2019 Datacenter Full.vhdx" - edition="Windows Server 2019 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - </templatevhds> - - <templates> - <template name="Template Windows Server 2019 Datacenter Full" - templatevhd="Windows Server 2019 Datacenter Full" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="P@ssword!1" - timezone="New Zealand Standard Time" - ostype="Server" /> - </templates> - - <vms> - <vm name="SS-DEFAULT" - template="Template Windows Server 2019 Datacenter Full" - computername="SS-DEFAULT"> - <dsc configname="STANDALONE_DEFAULT" - configfile="STANDALONE_DEFAULT.DSC.ps1"> - </dsc> - <adapters> - <adapter name="External" - switchname="External" /> - <adapter name="Domain Private Site A" - switchname="Domain Private Site A"> - <ipv4 address="192.168.10.2" - defaultgateway="192.168.10.1" - subnetmask="24" - dnsserver="192.168.10.2"/> - <ipv6 address="fd53:ccc5:895a:ba00::2" - defaultgateway="fd53:ccc5:895a:ba00::1" - subnetmask="64" - dnsserver="fd53:ccc5:895a:ba00::2"/> - </adapter> - </adapters> - </vm> - </vms> - -</labbuilderconfig> diff --git a/src/samples/isofiles/Put Windows installation ISO files here.txt b/src/samples/isofiles/Put Windows installation ISO files here.txt deleted file mode 100644 index 351f4e66..00000000 --- a/src/samples/isofiles/Put Windows installation ISO files here.txt +++ /dev/null @@ -1 +0,0 @@ -Place any Windows Installation ISO Files required for this Lab in this folder. \ No newline at end of file diff --git a/src/schema/labbuilderconfig-schema.xsd b/src/schema/labbuilderconfig-schema.xsd deleted file mode 100644 index 4338ee60..00000000 --- a/src/schema/labbuilderconfig-schema.xsd +++ /dev/null @@ -1,1569 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="labbuilderconfig"> - <xs:complexType> - <xs:all> - <xs:element minOccurs="0" maxOccurs="1" name="description" type="xs:string"> - <xs:annotation> - <xs:documentation> -This optional element should contain a brief description of this Lab. - </xs:documentation> - <xs:appinfo>&lt;description&gt;This Lab builds two Domain Controllers and two DHCP Servers.&lt;/description&gt;</xs:appinfo> - </xs:annotation> - </xs:element> - <xs:element minOccurs="1" maxOccurs="1" name="settings"> - <xs:annotation> - <xs:documentation> -This required element contains settings attributes controlling general settings of this Lab. - </xs:documentation> - <xs:appinfo>&lt;settings /&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="labid" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains a Lab Identifier for the Lab. -This identifier will be pre-pended to the names of any Virtual Machines, Switches and Network Adapter names created for this Lab. - </xs:documentation> - <xs:appinfo>labid="WS2012R2-CLUSTER-TEST"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="domainname" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the Domain Name identifier used by Virtual Machines created in this Lab. It may be used by DSC to configure the Virtual Machines in the Lab. - </xs:documentation> - <xs:appinfo>domainname="CONTOSO.COM"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="email" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains an E-mail address of the Administrator of this Lab. It may be used by DSC to configure the Virtual Machines in the Lab. - </xs:documentation> - <xs:appinfo>email="dev@contoso.com"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="labpath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the full path to the folder that this Lab should be created in. It can be overridden when the Lab is installed. -The folder will be created when the Lab is installed if it doesn't already exist. -The Virtual Machines, Virtual Hard Disk drives and other Lab related files will be created in this folder. - </xs:documentation> - <xs:appinfo>labpath="f:\Labs\WS2012R2-CLUSTER-TEST-01"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhdparentpath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the path to the folder that will contain the Parent VHD files used by the Virtual Machines in this Lab. -If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. -The Parent VHD files are used as Parent VHD's to any Lab VM boot disks or cloned to each Virtual Machine folder depending on the 'usedifferencingdisk' setting for each Lab VM. - -- Default Value: ParentVHDs - </xs:documentation> - <xs:appinfo>vhdparentpath="f:\Labs\WS2012R2-CLUSTER-TEST-01\ParentVHDs"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="dsclibrarypath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the path to the folder that will contain the DSC Library files used by the Virtual Machines in this Lab. -If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. -If this setting is not set it will default to the 'dsclibrary' folder within this module and will not be a subfolder of the 'labpath'. -Usually the content of this folder will either be provided with the Lab or created by copying the DSCLibrary folder provided with the LabBuilder module. - -Each Virtual Machine that is set to be configured by DSC requires a DSC configuration file that must be found in this folder. - -- Default Value: DSCLibrary - </xs:documentation> - <xs:appinfo>dsclibrarypath="C:\DSC\MyLibrary"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="resourcepath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the path to the folder that will contain any Resource files required by this Lab. -This includes the MSU packages that may need to be installed into the TemplateVHD or VM BootVHD files by specifing the MSU package names in the Packages attribute of the VMTemplateVHD, TemplateVHD or VM elements. -If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. - -- Default Value: Resource - </xs:documentation> - <xs:appinfo>resourcepath="f:\SharedResources\"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="modulepath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be used to add a path to the PowerShell Module Search path. -It can be used to specify an alternate path for DSC Resource Modules for the use in this Lab. -If specified, LabBuilder will search for DSC Resource Modules in this path before searching all other default PowerShell Module Paths. -If this folder is not rooted, it will be assumed to be a subfolder of the 'labpath'. - </xs:documentation> - <xs:appinfo>modulepath="f:\SharedModules\"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="dismpath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the path to the copy of DISM.EXE that should be used to convert any Windows Install Media ISOs to VHD files. -This is usually only required if the Lab Host is running Windows Server 2012 R2 or earlier or Windows 8.1 or earlier and the Windows Install Media ISO being converted is Windows Server 2016. -The latest version of DISM can be found in the Windows ADK here https://msdn.microsoft.com/en-us/library/hh825494.aspx. -Once the ADK is installed this setting can be configured to tell LabBuilder where to find the appropriate version (x86 or amd64) of DISM. -You should not include the DISM.EXE application name in the path. - </xs:documentation> - <xs:appinfo>resourcepath="C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="requiredwindowsbuild" type="xs:integer" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the minimum build required on the Lab host to install or use this Lab. -If the Lab Host does not meet this build number an error will be thrown when loading the Lab configuration. -This ensures that all features required to install a Lab are available on the Lab Host before installation will proceed. -If this attribute is not set then the Lab Configuration will be able to installed on any Windows build version Lab Host. - </xs:documentation> - <xs:appinfo>requiredwindowsbuild="14295"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="resources"> - <xs:annotation> - <xs:documentation> -This optional element can contain one or more resources that will be required for this Lab to be installed. -These resources may be downloaded from the Internet automatically depending on the resource type. - -There can be different types of Resources that can be contained in the Resources element. - -Currently the Resource types that are supported are: - - Module: A PowerShell (DSC) Module that is downloaded via URL or using PowerShell Get. This can be a DSC or non-DSC PowerShell module. - - MSU: A Microsoft Update package that will be downloaded to the lab Resources folder and can be installed into the Boot VHD when it is created from an ISO, when it is copied to the Parent VHD folder or when the VM is prepared for first boot. - </xs:documentation> - <xs:appinfo>&lt;resources&gt;...&lt;/resources&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="module"> - <xs:annotation> - <xs:documentation> -A PowerShell (DSC) Module that will be downloaded and installed to the Lab Host when this Lab is installed. - -Note: This is not required for any PowerShell DSC Modules that are referenced in a DSC configuration used by a Virtual Machine if the version required is available in the PowerShell Gallery and is just the latest version. -This is usually only required if the Lab requires the use of development resources or versions that are either not available on PowerShell Gallery or a specific version is requred. - </xs:documentation> - <xs:appinfo>&lt;module /&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -The Name of the PowerShell (DSC) Module that this Lab requires. -If a URL attribute is not specified, the PowerShell Gallery will be searched for a module with this name and downloaded. - </xs:documentation> - <xs:appinfo>name="NetworkingDsc"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="url" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -An optional URL that will be used to download the PowerShell (DSC) Module from. Setting this attribute prevent LabBuilder from using PowerShell Get to download the Module if it is missing. -This is commonly used to download PowerShell (DSC) Modules directly from GitHub or other repositories. - </xs:documentation> - <xs:appinfo>url="https://github.com/PowerShell/NetworkingDsc/archive/dev.zip"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="folder" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute only needs to be set if the zip file downloaded by the URL in the URL attribute contains a folder that the PowerShell (DSC) Module is in. -This is usually used when the URL specifies a GitHub repository branch, which will cause the downloaded zip file to contain a folder named 'name-branch' (e.g. NetworkingDsc-dev). - </xs:documentation> - <xs:appinfo>folder="NetworkingDsc-dev"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="minimumversion" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the minimum PowerShell module version that is required by this Lab. -If a version of the Module is not found that is at least this version then a newer version will be downloaded using PowerShell Get. -This attribute should only be used if URL is not set. - </xs:documentation> - <xs:appinfo>minimumversion="2.0.0.0"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="requiredversion" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the specific PowerShell module version that is required by this Lab. -If a version of the Module is not found that is exactly this version then this version will be downloaded using PowerShell Get. -This attribute should only be used if URL is not set. - </xs:documentation> - <xs:appinfo>requiredversion="2.1.0.0"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="unbounded" name="msu"> - <xs:annotation> - <xs:documentation> -An Microsoft Update (MSU) package file to be installed into a Boot VM. - </xs:documentation> - <xs:appinfo>&lt;msu /&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -A descriptive name for this MSU that will be used to identify this package. -Any Lab build process that installs MSU packages will need to refer to this name, not the file name of the package. - </xs:documentation> - <xs:appinfo>name="WMF5.1-WS2012R2-W81"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="url" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -The URL to download this MSU file from. -If this file already exists in the Resources folder for this Lab when the Lab is installed, it will not be downloaded again. - -Note: If the Lab contains Windows Server 2012 R2, Windows Server 2012 or Windows Server 2008 R2 machines, the WMF 5.0 MSU packages MUST be installed on these machines before first boot or they will not be able to be configured. - -To download these packages: - - Windows Server 2012 R2 - https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu - - Windows Server 2012 - https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/W2K12-KB3134759-x64.msu - - Windows Server 2008 R2 - https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win7AndW2K8R2-KB3134760-x64.msu' - </xs:documentation> - <xs:appinfo>url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="path" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be used to set an optional path this package will be stored and/or downloaded to. - </xs:documentation> - <xs:appinfo>path="f:\LabBuilder\sharedpackages\"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="unbounded" name="iso"> - <xs:annotation> - <xs:documentation> -An ISO file that can be mounted into one or more Lab Virtual Machines. - </xs:documentation> - <xs:appinfo>&lt;iso /&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -A descriptive name for this ISO that will be used to identify this disk. -Any Lab build process that mounts ISO files will need to refer to this name, not the file name of the ISO. - </xs:documentation> - <xs:appinfo>name="SQL2012_FULL_ENU"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="url" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -The optional URL to download this ISO file from. -If this file already exists in the Resources folder for this Lab when the Lab is installed, it will not be downloaded again. -This attribute should not be used if the path attribute is also set. - </xs:documentation> - <xs:appinfo>url="https://download.microsoft.com/download/4/C/7/4C7D40B9-BCF8-4F8A-9E76-06E9B92FE5AE/ENU/SQLFULL_ENU.iso"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="path" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to set the filename (and optionally path) of the source ISO. -The ISO will be used from that location and not copied into the Resources folder of the Lab. -If this path does not contain a root it will be appended onto the _ISOFiles_ attribute on the _Resources_ node or the path set in the _ResourcePath_ attribute on the _Settings_ node. -If the ISO file does not exist but a URL is provided that contains a filename with an extension of ISO or ZIP it will be downloaded to this location and optionally unzipped. -If the ISO file does not exist but a URL is provided that does not contain an ISO or ZIP filename the user will be requested to manually download the file from the URL and Lab installation will terminate. - </xs:documentation> - <xs:appinfo>path="f:\isos\SQLFULL_ENU.iso"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - <xs:attribute name="isopath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be used to set the path to the folder that LabBuilder will look for the Resource ISO files. -If not set this will default to the ResourcePath specified in the Lab configuration file. -If a ResourcePath is not set then this will be the Resource folder within the Lab folder. -This can be a relative or full path. -If a relative path is set, it will be relative to the full path of the Lab Resource folder. - </xs:documentation> - <xs:appinfo>isopath="d:\LabShared\ISOs"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="switches"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more Switch nodes representing the Hyper-V Virtual Switches that are required for this Lab. -Any missing switches in this list will be created on the Lab Host when this Lab is installed. - -Note: A Private Management Virtual Switch will always be created for each installed Lab for LabBuilder to install and configure the Virtual Machines in a Lab. This Management Virtual Switch will not appear in this list but will always be created. - </xs:documentation> - <xs:appinfo>&lt;switches&gt;...&lt;/switches&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="switch"> - <xs:annotation> - <xs:documentation> -This optional element represents a Hyper-V Virtual Switch that is required for this Lab. -A Lab may contain one or more Internal, Private, External or NAT Virtual Switches. - </xs:documentation> - <xs:appinfo>&lt;switch&gt;...&lt;/switch&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:all> - <xs:element minOccurs="0" maxOccurs="1" name="adapters"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more Adapter nodes representing Hyper-V Virtual Network Adapters that are used by the Host Operating System to connect to this Hyper-V Virtual Switch. -This should element should only be added for External or Private switches. - </xs:documentation> - <xs:appinfo>&lt;adapters&gt;...&lt;/adapters&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="adapter"> - <xs:annotation> - <xs:documentation> -This optional element represents a Hyper-V Virtual Network Adapter that will be used as a Management Adapter for the Host Operating system to connect to this Virtual Switch. -These Management Adapters are usually used to allow access to the Internet by the Virtual Machines in a Lab. - </xs:documentation> - <xs:appinfo>&lt;switch&gt;...&lt;/switch&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to set the Name of the Management Virtual Adapter connected to this Hyper-V Virtual Switch. - -Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Switch is created if the Switch type is a Private, Internal or NAT. - </xs:documentation> - <xs:appinfo>name="Cluster Network"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="macaddress" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to set the MAC Address of the Management Virtual Adapter connected to this Hyper-V Virtual Switch. - </xs:documentation> - <xs:appinfo>macaddress="00155D010703"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vlan" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to set a VLAN ID of the Management Virtual Adapter connected to this Hyper-V Virtual Switch. - </xs:documentation> - <xs:appinfo>vlan="10"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - </xs:all> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to configure the Name of the Hyper-V Virtual Switch to be created for this Lab. - -Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Switch is created if the Switch type is a Private, Internal or NAT. - </xs:documentation> - <xs:appinfo>name="Domain Cluster"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="type" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to set the Type of Hyper-V Virtual Switch to create. -It can be set to: - - Internal - - Private - - External - - NAT (only available on Windows 10 and Windows Server 2016 build 14295 and above) - </xs:documentation> - <xs:appinfo>type="Internal"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vlan" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to configure the VLAN ID of all Virtual Network Adapters that will connect to this Virtual Switch. - </xs:documentation> - <xs:appinfo>vlan="43"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="bindingadaptername" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to configure which physical network adapter an External switch will be bound to. -This attribute should only be set if the switch type is External. -If the bindingadaptermac attribute is set then this attribute should not be set. - </xs:documentation> - <xs:appinfo>bindingadaptername="Ethernet 1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="bindingadaptermac" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to configure which physical network adapter an External switch will be bound to. -This attribute should only be set if the switch type is External. -If the bindingadaptername attribute is set then this attribute should not be set. - </xs:documentation> - <xs:appinfo>bindingadaptermac="C86000A1A895"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="natsubnet" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to configure the subnet that will be assigned to this NAT switch. -It must contain an IP address (192.168.10.0) followed by a slash (/) then the subnet prefix length (e.g. 24). -This attribute should only be set if the switch type is NAT. -If this attribute is set the natgatewayaddress attribute must also be set. - </xs:documentation> - <xs:appinfo>natsubnet="192.168.10.0/24"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="natgatewayaddress" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to configure the IP address that will be used as a gateway address on this NAT switch. -This IP Address must be within the defined NAT subnet set in the natsubnet attribute. -This attribute should only be set if the switch type is NAT. -If this attribute is set the natsubnet attribute must also be set. - </xs:documentation> - <xs:appinfo>natgatewayaddress="192.168.10.1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - <xs:attribute name="managementvlan" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to change the VLAN ID used by the Private Hyper-V Management Switch created to manage this Lab. -If not set the default VLAN ID value of 99 will be used. -All Virtual Network Adapters automatically created and attached to the Management Switch for this Lab will be set to use this VLAN ID. - </xs:documentation> - <xs:appinfo>managementvlan="55"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="templatevhds"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more TemplateVHD nodes representing the Template Virtual Hard Disk files that are required by the Templates and/or Virtual Machines in this Lab. -These Template VHD files will be created from Windows Install Media ISO files if they can't be found in the specified VHDPath folder during the Lab Install process. - </xs:documentation> - <xs:appinfo>&lt;templatevhds&gt;...&lt;/templatevhds&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="templatevhd"> - <xs:annotation> - <xs:documentation> -This optional element represents a Template VHD (Virtual Hard Disk) that will be created and used by a Virtual Machine Template to create boot disks for Virtual Machines. -If the VHD/VHDx file for this Template VHD can not be found it will be created using the specified Windows Install Media ISO file. -If the Windows Media ISO file can not be found the Lab Install will not be able to proceed. - -Note: It is common for more than one Lab to use the same Template VHD files, therefore it is common to set the VHDPath and ISOPath attributes of this element to be a folder that can be accessed by multiple Labs. - </xs:documentation> - <xs:appinfo>&lt;templatevhd&gt;...&lt;/templatevhd&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute will be the name of this Template VHD. -It will be used by Template elements to refer to this Template VHD. - </xs:documentation> - <xs:appinfo>name="Windows Server 2012 R2 Datacenter Full"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="iso" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute should contain the relative or full path to the Windows Install Media ISO file required to build this Template VHD. -If this filename is not a rooted path it will be appended to the path found in the ISOFiles attribute. - </xs:documentation> - <xs:appinfo>iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="url" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be set to a URL that will be reported to a user if the ISO file required to build this Template VHD is not found. - -Note: This URL will not be automatically downloaded, a user will need to open this URL in a browser. - </xs:documentation> - <xs:appinfo>url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhd" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute should contain the relative or full path to the VHD file that this Template VHD will use and/or create. -If this filename is not a rooted path it will be appended to the path found in the VHDFiles attribute. - </xs:documentation> - <xs:appinfo>vhd="Windows Server 2012 R2 Datacenter Full.vhdx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="edition" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute will be the Edition that is installed from the Windows Install Media ISO if the Template VHD needs to be created. -If this is not provided and the VHD file needs to be created from the Windows Install Media ISO then the first edition in this Install Media will be created. - </xs:documentation> - <xs:appinfo>edition="Windows Server 2012 R2 SERVERDATACENTER"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="ostype" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute sets defines the type of Operating System that this Template VHD contains. -It is used by LabBuilder to determine how to configure Virtual Machines based on this template VHD. -It is also used to determine if Nano Server packages should be applied. - - - Default Value: Server. - - Valid Values: Server | Client | Nano - </xs:documentation> - <xs:appinfo>ostype="Server"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="features" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can contain a comma delimited list of Windows Server Features that should be installed into this Virtual Machine Template VHD. -Normally, additional Windows Server Features are installed via DSC Library Configurations, so this attribute should not normally be used. - </xs:documentation> - <xs:appinfo>features="Web-Application-Proxy,Routing"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhdformat" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the type of VHD format to create if the VHD does not already exist. - - - Default Value: VHDX - - Valid Values: VHDX | VHD - </xs:documentation> - <xs:appinfo>vhdformat="VHDx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhdtype" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the type of VHD file to create if the VHD does not already exist. - - - Default Value: Dynamic - - Valid Values: Fixed | Dynamic - </xs:documentation> - <xs:appinfo>vhdtype="Fixed"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="generation" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the Virtual Machine generation of VHD file to create if the VHD does not already exist. - - - Default Value: 2 - - Valid Values: 1 | 2 - </xs:documentation> - <xs:appinfo>generation="1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhdsize" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the size of Boot VHD file to create if the VHD does not already exist. - -Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). - </xs:documentation> - <xs:appinfo>size="25GB"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="packages" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can contain a comma delimited list of packages that should be installed into this Virtual Machine Template VHD. - -If the Template VHD is a Nano Server then the packages can be .cab files, which will install the Nano Server package from the ISO or a Resource MSU file. - -If the Template VHD is not a Nano Server then the packages must be Resource MSU files. - -Valid Values: - - Resource MSU names that are can be found in the ResourceMSU list. - -Valid Values for Nano Server: - - Filename including the .cab extension of a valid Nano Server package found on the Windows Install Media ISO. - - Resource MSU names that are can be found in the ResourceMSU list. - </xs:documentation> - <xs:appinfo>packages="Microsoft-NanoServer-DNS-Package.cab,SomePackage.msu"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - <xs:attribute name="isopath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be used to set the path to the folder that LabBuilder will look for the Windows Install Media ISO files for any missing Template VHD files. -If not set this will default to the same path as the Lab configuration file. -This can be a relative or full path. -If a relative path is set, it will be relative to the full path of the Lab configuration file. - </xs:documentation> - <xs:appinfo>isopath="d:\LabShared\ISOs"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhdpath" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be used to set the path to the folder that LabBuilder will create or look for any Template VHD files in. -If not set this will default to the same path as the Lab configuration file. -This can be a relative or full path. -If a relative path is set, it will be relative to the full path of the Lab configuration file. - </xs:documentation> - <xs:appinfo>isopath="d:\LabShared\VHDs"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="prefix" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can be used to pre-pend a string to the VHD Template file that is created. - </xs:documentation> - <xs:appinfo>prefix="Templates "</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="templates"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more Template nodes representing the Virtual Machine Templates used to build the Virtual Machines in this Lab. -The Virtual Machine Templates in this list may refer to a TemplateVHD or define a direct path to a Source VHD file. -If a TemplateVHD is specified, this TemplateVHD must be found in the TemplateVHDs collection. -Every Virtual Machine defined in this Lab must refer to a Template in this collection. - </xs:documentation> - <xs:appinfo>&lt;templates&gt;...&lt;/templates&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="template"> - <xs:annotation> - <xs:documentation> -This optional element represents a Virtual Machine Template that will be created when this Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;template&gt;...&lt;/template&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute contains the name of this Virtual Machine Template. -Virtual Machines will refer to this value if they are going to use this Virtual Machine Template. - </xs:documentation> - <xs:appinfo>name="Windows Server 2012 R2 Datacenter CORE"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vhd" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the file name of the VHD Boot file that will be used for any Virtual Machines using this Template. -If this attribute is not set it will default to the filename specified in the SourceVHD attribute or the filename of the Template VHD linked to by the TemplateVHD attribute. - </xs:documentation> - <xs:appinfo>vhd="Windows Server 2012 R2 Datacenter CORE.vhdx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="sourcevhd" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the relative or full path to the VHD file that will be cloned for use as the Boot VHD for any Virtual Machines using this template. - -Note: This attribute should not be set if the TemplateVHD attribute is set. - </xs:documentation> - <xs:appinfo>sourcevhd="VhdFiles\Windows Server 2012 R2 Datacenter Full.vhdx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="memorystartupbytes" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the amount of startup memory to assign to Virtual Machines based on this Template. - - - Default Value: 1GB. - - Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). - </xs:documentation> - <xs:appinfo>memorystartupbytes="8GB"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="dynamicmemoryenabled" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains a flag to enable or disable Dynamic Memory for Virtual Machines based on this Template. -Note: Disabling this value is usually only used when Nested Virtualization is required. - - - Default Value: Y. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>dynamicmemoryenabled="N"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="generation" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the Virtual Machine generation to create. - - - Default Value: 2 - - Valid Values: 1 | 2 - </xs:documentation> - <xs:appinfo>generation="1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="Version" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the Virtual Machine Version to create, this is only applicable to Windows 10 build 14352 or higher/Server 2016 post TP5. - - - Default Value: 8.0 - - Valid Values: 5.0, 6.2, 7.0, 7.1, 8.0, 254.0, and 255.0 - </xs:documentation> - <xs:appinfo>version="5.0"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="processorcount" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute determines the number of virtual processors assigned to Virtual Machines based on this Template. - - - Default Value: 1. - - Valid Values: 1-n. - </xs:documentation> - <xs:appinfo>processorcount="2"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="administratorpassword" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute specifies the local Administrator password to assign when Virtual Machines based on this Template are installed. -If this is not defined for the Template it should be defined in the Virtual Machine definition. - </xs:documentation> - <xs:appinfo>administratorpassword="MyP@ssw0rd!1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="productkey" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute specifies the Windows product key to set on any Virtual Machines based on this Template. - </xs:documentation> - <xs:appinfo>productkey="AAAAA-AAAAA-AAAAA-AAAAA-AAAAA"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="timezone" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute sets the timezone assigned to Virtual Machines based on this Template. - - - Default Value: PST. - </xs:documentation> - <xs:appinfo>timezone="CST"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="ostype" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute sets defines the type of Operating System that this Template contains. -It is used by LabBuilder to determine how to configure Virtual Machines based on this template. - - - Default Value: Server. - - Valid Values: Server | Client | Nano - </xs:documentation> - <xs:appinfo>ostype="Server"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="integrationservices" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls which Integration Services are enabled on any Virtual Machines created using this Template. -It should contain a comma delimited list of Integration Service names that should be enabled. -If this attribute is defined but left empty, all Integration Services will be disabled. -If this attribute is not defined all Integration Services will be enabled. - - - Default Value: Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS - - Valid Values: Guest Service Interface | Heartbeat | Key-Value Pair Exchange | Shutdown | Time Synchronization | VSS - </xs:documentation> - <xs:appinfo>ostype="Server"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="templatevhd" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute defines a Template VHD that this Virtual Machine Template will use to determine the Boot VHD file. -If a TemplateVHD with a name matching the value of this attribute can not be found then an error will occur when this Lab is installed. -If this attribute is defined, the SourceVHD attribute should not be defined. - </xs:documentation> - <xs:appinfo>templatevhd="Windows Server 2012 R2 Datacenter Core"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="exposevirtualizationextensions" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls whether or not Virtualization Extensions are exposed for Virtual Machines based on this Template. -This attribute should only be enabled if the Host system this Lab is to be installed on is able to support Nested Virtualization. -Currently this is only supported on Windows 10 built 10586 or above and Windows Server 2016 TP4 or above. - - - Default Value: N. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>exposevirtualizationextensions="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="packages" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can contain a comma delimited list of packages that should be installed onto any Virtual Machines using this Template. - -If the Template is a Nano Server then the packages can be .cab files, which will install the Nano Server package from the ISO or a Resource MSU file. - -If the Template is not a Nano Server then the packages must be Resource MSU files. - -Valid Values: - - Resource MSU names that are can be found in the ResourceMSU list. - -Valid Values for Nano Server: - - Filename including the .cab extension of a valid Nano Server package found on the Windows Install Media ISO. - - Resource MSU names that are can be found in the ResourceMSU list. - </xs:documentation> - <xs:appinfo>packages="Microsoft-NanoServer-DNS-Package.cab,SomePackage.msu"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - <xs:attribute name="fromvm" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute enables the list of Template Virtual Machines to be pulled from the Virtual Machines defined in Hyper-V. -The list of Hyper-V Virtual Machines to use as templates can be specified using a wild card value. E.g. 'Template *' -If specified, when the Lab is installed the list of available Virtual Machine Templates will be pulled from Hyper-V by matching the names against the FromVM attribute. -After the list of Hyper-V Virtual Machines to use a templates is pulled in, any Templates defined in this container will be merged into this list. - </xs:documentation> - <xs:appinfo>fromvm="Template *"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="vms"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more VM nodes, each representing a Virtual Machine that will be created when this Lab is installed. -Each Virtual Machine will refer back to a Template that is found in the Templates collection. -If the Template used by this Virtual Machine can not be found, an error will occur when this Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;vms&gt;...&lt;/vms&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="vm"> - <xs:annotation> - <xs:documentation> -This optional element represents a Virtual Machine that will be created when this Lab is installed. - -All Lab configurations should include at least one Virtual Machine, although an error will not be thrown if no Virtual Machines are defined in a Lab. - </xs:documentation> - <xs:appinfo>&lt;vm&gt;...&lt;/vm&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:all> - <xs:element minOccurs="0" maxOccurs="1" name="datavhds"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more DataVHD nodes, each representing a Data Virtual Hard Drive that will be created and attached to this Virtual Machine when this Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;datavhds&gt;...&lt;/datavhds&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="datavhd"> - <xs:annotation> - <xs:documentation> -This optional element represents a Data Virtual Hard Drive that will be created and attached to the Virtual Machine when the Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;datavhd&gt;...&lt;/datavhd&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="vhd" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to specify the path and filename of the Virtual Machine Data VHD to create and attach to the Virtual Machine. -If a relative path or just a filename is provided to the VHD file then it will be set as relative to the 'Virtual Hard Disks' folder in the Virtual Machine folder. -If this VHD does not exist then it may be created using the additional attributes provided. - </xs:documentation> - <xs:appinfo>vhd="DataDisks/DataDisk1.vhdx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="sourcevhd" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the file path to the VHD file that will be cloned to create the new Data VHD if it does not exist. -This attribute is only used when the Data VHD does not exist and need to be created. -This attribute should not be defined if the Size or Type attributes are defined. -If the MoveSourceVHD attribute is set to 'Y' then this file will be moved to Data VHD location instead of being copied. - </xs:documentation> - <xs:appinfo>vhd="DataDisks/DataDisk1.vhdx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="copyfolders" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute allows specified folders to be copied onto a new Data VHD when it is first created. -This attribute is only used when the Data VHD does not exist and need to be created. -When a new DataVHD is created the folders specified in this attribute are copied recursively to the first formatted partition on the new Data VHD. -This attribute should only be set if both the partitionstyle and filesystem attributes are defined, or if the new Data VHD is cloned from a VHD that contains a formatted volume. - </xs:documentation> - <xs:appinfo>copyfolders="f:\data\tools"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="type" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the type of Data VHD file to create if the VHD does not already exist. -This attribute is only used when the Data VHD does not exist and need to be created. -This attribute should not be defined if the SourceVHD attribute is defined. -If the value of this attribute is Differencing then the ParentVHD attribute must also be set. - - - Valid Values: Fixed | Dynamic | Differencing - </xs:documentation> - <xs:appinfo>type="Dynamic"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="size" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the size of Data VHD file to create if the VHD does not already exist. -If the VHD already exists and this value is larger than the current size of the VHD, then it will be expanded, otherwise an error will occur. -This attribute should not be defined if the SourceVHD attribute is defined. - - - Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). - </xs:documentation> - <xs:appinfo>size="100GB"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="supportpr" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute enables support persistent reservation on this Data VHD. -This attribute is only used when the Data VHD does not exist and need to be created. -This attribute is only used when the Data VHD has the Shared attribute set to 'Y'. - - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>supportpr="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="partitionstyle" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute causes a newly created Data VHD to be partitioned and formatted so that files and folders can be copied to it. -This attribute is only used when the Data VHD does not exist and need to be created. -Normally, this attribute would be set if the CopyFolders attribute was also set, enabling the folders to be copied onto the Data VHD before the Virtual Machine has been provisioned. -If this is not defined for then the Data VHD will need to be partitioned and allocated within the host operating system via DSC or some other mechanism. -If this attribute is defined then the FileSystem attribute must also be defined. - - - Valid Values: MBR | GPT - </xs:documentation> - <xs:appinfo>partitionstyle="GPT"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="filesystem" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute causes a newly created Data VHD to be partitioned and formatted so that files and folders can be copied to it. -This attribute is only used when the Data VHD does not exist and need to be created. -Normally, this attribute would be set if the CopyFolders attribute was also set, enabling the folders to be copied onto the Data VHD before the Virtual Machine has been provisioned. -If this is not defined for then the Data VHD will need to be partitioned and allocated within the host operating system via DSC or some other mechanism. -If this attribute is defined then the PartitionStyle attribute must also be defined. - - - Valid Values: FAT32 | EXFAT | NTFS | REFS -Note: REFS can only be used if the host is Windows Server 2012 or above. However, REFS can still be formatted within the guest operating system even if the Host is Windows 10. - </xs:documentation> - <xs:appinfo>filesystem="NTFS"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="filesystemlabel" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute causes a newly created Data VHD to be set with a label if it has been partitioned and formatted. -This attribute is only used when the Data VHD does not exist and need to be created. -Normally, this attribute would be set if the CopyFolders attribute was also set, enabling the folders to be copied onto the Data VHD before the Virtual Machine has been provisioned. -If this attribute is defined then both the PartitionStyle and FileSystem attributes must also be defined. - </xs:documentation> - <xs:appinfo>filesystemlabel="ToolsDisk"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="parentvhd" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute specifies the Parent VHD to use for a Differencing VHD. -It should be a full path or a path relative to the Virtual Hard Disk folder in the Virtual Machine. -This attribute is only used when the Data VHD does not exist and need to be created. -If this attribute is defined then the Type attribute must be set to Differencing. - </xs:documentation> - <xs:appinfo>parentvhd="..\..\ToolsDiskParent.vhdx"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="movesourcevhd" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute causes the Source VHD that is used to create the new Data VHD to be moved instead of copied. -This attribute should only be set to 'Y' if this SourceVHD attribute is defined. - - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>movesourcevhd="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="shared" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute enables the Data VHD to be attached as a Shared VHD to the Virtual Machine. -This attribute should only be set to 'Y' if this DataVHD is being stored on a Cluster Shared Volume and the share supports SMB 3.02. - - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>shared="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="dvddrives"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more DVDDrive nodes, each representing a Virtual DVD Drive that will be created and attached to this Virtual Machine when this Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;dvddrives&gt;...&lt;/dvedrives&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="dvddrive"> - <xs:annotation> - <xs:documentation> -This optional element represents a Virtual DVD Drive that will be created and attached to the Virtual Machine when the Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;dvddrive&gt;...&lt;/dvddrive&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="iso" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to specify the name of the ISO Resource to mount to this Virtual DVD Drive. - </xs:documentation> - <xs:appinfo>iso=""</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="adapters"> - <xs:annotation> - <xs:documentation> -This optional element contains a collection of zero or more Adapter nodes, each representing a Virtual Network Adapter that will be created, attached to the Virtual Machine and connected to a Virtual Switch when this Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;adapters&gt;...&lt;/adapters&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:sequence> - <xs:element minOccurs="0" maxOccurs="unbounded" name="adapter"> - <xs:annotation> - <xs:documentation> -This optional element represents a Virtual Network Adapter that will be created, attached to the Virtual Machine and connected to a Virtual Switch when the Lab is installed. - </xs:documentation> - <xs:appinfo>&lt;adapter&gt;...&lt;/adapter&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:all> - <xs:element minOccurs="0" maxOccurs="1" name="ipv4"> - <xs:annotation> - <xs:documentation> -This optional element represents the IPv4 settings that will be assigned to this Virtual Network Adapter from within the guest operating system. -If this element is not defined, then the IPv4 settings for the adapter will not be set or changed. - </xs:documentation> - <xs:appinfo>&lt;ipv4&gt;...&lt;/ipv4&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="address" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute configures this Virtual Adapter to use a static IPv4 address. -If this attribute is defined then the IPv4 subnetmask attribute should also be defined. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>address="10.0.1.19"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="defaultgateway" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute configures the IPv4 default gateway of the Virtual Adapter. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>defaultgateway="10.0.1.0"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="subnetmask" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This required attribute configures the subnet mask of the IPv4 Address assigned to this Virtual Network Adapter. -If this attribute is defined then the IPv4 address attribute should also be defined. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>subnetmask="8"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="dnsserver" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This required attribute configures the IPv4 DNS Server addresses assigned to this Virtual Network Adapter. -This can be a comma delimited list of addresses. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>dnsserver="10.0.1.10,10.0.1.11"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="ipv6"> - <xs:annotation> - <xs:documentation> -This optional element represents the IPv6 settings that will be assigned to this Virtual Network Adapter from within the guest operating system. -If this element is not defined, then the IPv6 settings for the adapter will not be set or changed. - </xs:documentation> - <xs:appinfo>&lt;ipv4&gt;...&lt;/ipv4&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:attribute name="address" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute configures this Virtual Adapter to use a static IPv6 address. -If this attribute is defined then the IPv6 subnetmask attribute should also be defined. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>address="fd53:ccc5:895a:0000::1a"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="defaultgateway" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute configures the IPv6 default gateway of the Virtual Adapter. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>defaultgateway="fd53:ccc5:895a:0000::0"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="subnetmask" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This required attribute configures the subnet mask of the IPv6 Address assigned to this Virtual Network Adapter. -If this attribute is defined then the IPv6 address attribute should also be defined. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>subnetmask="64"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="dnsserver" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This required attribute configures the IPv6 DNS Server addresses assigned to this Virtual Network Adapter. -This can be a comma delimited list of addresses. -This property of the Virtual Adapter is configured using Desired State Configuration. - </xs:documentation> - <xs:appinfo>dnsserver="fd53:ccc5:895a:0000::a,fd53:ccc5:895a:0000::b"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:all> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute controls the name of this Virtual Network Adapter within the Host Operating System and the Guest Operating System of this Virtual Machine. -The Virtual Network Adapter name within the Host will be changed immediately, but the Adapter name within the Guest Operating System will only be configured when the Guest is first installed. - -Changing the Name of the Adapter after the Guest Operating System has been installed is not possible, by changing this Name value. - -Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Virtual Machine is created. - </xs:documentation> - <xs:appinfo>name="Cluster Comms"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="switchname" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute controls the which Virtual Switch this Virtual Network Adapter should connect to. - -Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Virtual Machine is created as long as the switch being connected to is an Internal, Private or NAT switch. - </xs:documentation> - <xs:appinfo>switchname="Cluster"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="macaddress" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to set a static MAC Address on the Virtual Network Adapter. -Care should be taken to ensure that this MAC Address is unique on the Virtual Switch that is being connected to. - </xs:documentation> - <xs:appinfo>macaddress="00155D010801"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="vlan" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute is used to configure a VLAN ID on the Virtual Network Adapter. -If this attribute is set it will override any VLAN ID that is set on the Virtual Switch that this Virtual Network Adapter connects to. -If this attribute is not set but the VLAN ID is set on the Virtual Switch that this Virtual Network Adapter connects to is set, then the Virtual Network Adapter VLAN ID will be set to the Virtual Switch VLAN ID. - </xs:documentation> - <xs:appinfo>vlan="80"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="macaddressspoofing" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute enables MAC Address spoofing by the Guest Operating System. -This is usually required when Network Virtualization is being implemented. - - - Default Value: N. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>macaddressspoofing="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:element minOccurs="0" maxOccurs="1" name="dsc"> - <xs:annotation> - <xs:documentation> -This optional element contains the settings related to configuring Desired State Configuration on the Lab Virtual Machine. - </xs:documentation> - <xs:appinfo>&lt;dsc&gt;...&lt;/dsc&gt;</xs:appinfo> - </xs:annotation> - <xs:complexType> - <xs:all> - <xs:element minOccurs="0" maxOccurs="1" name="parameters" type="xs:string"> - <xs:annotation> - <xs:documentation> -This optional element contains any parameters that should be passed to the DSC Library Configuration script being used to configure this Virtual Machine. -These parameters get loaded into the ConfigData object and can be then used by the DSC Library Configuration script. -The parameters that are available to be set depends on the DSC Library Configuration script that is assigned to this Virtual Machine. -Review the documentation within the DSC Library Configuration script to see what parameters are available. - </xs:documentation> - <xs:appinfo>&lt;parameters&gt;...&lt;/parameters&gt;</xs:appinfo> - </xs:annotation> - </xs:element> - </xs:all> - <xs:attribute name="configname" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute contains the configuration name that is set in the DSC Library Configuration file that is used to configure this Virtual Machine. - </xs:documentation> - <xs:appinfo>configname="DC_FORESTPRIMARY"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="configfile" type="xs:string" use="required" > - <xs:annotation> - <xs:documentation> -This required attribute contains the filename for the DSC Library Configuration file to use to configure this Virtual Machine. -If a relative path is used for this attribute then it will be appended onto the DSCLibrary path specified in the Lab settings, otherwise the full rooted path to the file will be used. - </xs:documentation> - <xs:appinfo>configfile="DC_FORESTPRIMARY.DSC.ps1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="logging" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute enables DSC Logging on the Virtual Machine in the DSC Event Logs. - - - Default Value: N. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>logging="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:all> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute contains the name of the Lab Virtual Machine. -This is the name that will appear in the Hyper-V manager for this Virtual Machine when this Lab is installed. - -Note: If this Lab configuration has got a LabId setting defined, it will be pre-pended to this value when the Virtual Machines is created. - </xs:documentation> - <xs:appinfo>name="SA-DC1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="template" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute is used to specify the template from the templates collection that will be used to create this Virtual Machine from. -The Template must match the name of one of the Templates in the Template collection, otherwise an error will occur when the Lab is installed. - -Note: many of the attributes defined in for the Virtual Machine may also be defined in the Template. -If they are defined in the template but not in the Virtual Machine then the template setting will be used. -If the setting is defined in the Virtual Machine and also in the Template, the Virtual Machine value will be used. - </xs:documentation> - <xs:appinfo>template="Windows Server 2012 R2 Datacenter Core"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="computername" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute contains the Computer Name of the Virtual Machine. -This is the name that will be set on the Virtual Machine once it has been first booted. - </xs:documentation> - <xs:appinfo>computername="SA-DC1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="memorystartupbytes" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains the amount of startup memory to assign to Virtual Machine. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. - - - Default Value: 1GB. - - Valid Values: PowerShell numeric values (e.g. 2GB, 1TB, 300MB, 160000000). - </xs:documentation> - <xs:appinfo>memorystartupbytes="8GB"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="generation" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the Virtual Machine generation to create. - - - Default Value: 2 - - Valid Values: 1 | 2 - </xs:documentation> - <xs:appinfo>generation="1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="Version" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the Virtual Machine Version to create, this is only applicable to Windows 10 build 14352 or higher/Server 2016 post TP5. - - - Default Value: 8.0 - - Valid Values: 5.0, 6.2, 7.0, 7.1, 8.0, 254.0, and 255.0 - </xs:documentation> - <xs:appinfo>version="5.0"</xs:appinfo> - </xs:annotation> - </xs:attribute> <xs:attribute name="dynamicmemoryenabled" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute contains a flag to enable or disable Dynamic Memory for Virtual Machine. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. -Note: Disabling this value is usually only used when Nested Virtualization is required. - - - Default Value: Y. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>dynamicmemoryenabled="N"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="exposevirtualizationextensions" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls whether or not Virtualization Extensions are exposed for Virtual Machine. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. -This attribute should only be enabled if the Host system this Lab is to be installed on is able to support Nested Virtualziation. -Currently this is only supported on Windows 10 built 10586 or above and Windows Server 2016 TP4 or above. - - - Default Value: N. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>exposevirtualizationextensions="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="usedifferencingdisk" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls whether or not the Boot VHD created for this Virtual Machine will be a Differencing disk or a copy of the Template VHD. -Using a Differencing Disk for the Boot VHD will conserve disk space. - - - Default Value: Y. - - Valid Values: Y | N - </xs:documentation> - <xs:appinfo>usedifferencingdisk="Y"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="administratorpassword" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute specifies the local Administrator password to assign when this Virtual Machine is installed. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. - </xs:documentation> - <xs:appinfo>administratorpassword="MyP@ssw0rd!1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="productkey" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute specifies the Windows product key to set on this Virtual Machine. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. - </xs:documentation> - <xs:appinfo>productkey="AAAAA-AAAAA-AAAAA-AAAAA-AAAAA"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="timezone" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute sets the timezone assigned to this Virtual Machine. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. - - - Default Value: PST. - </xs:documentation> - <xs:appinfo>timezone="CST"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="unattendfile" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute allows a specific unattend XML file to be used instead of the default XML file. -If a relative path is used for this attribute then it will be appended onto the path of this the Lab config file, otherwise the full rooted path to the file will be used. - </xs:documentation> - <xs:appinfo>unattendfile="Unattend\SpecialUnattend.xml"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="setupcomplete" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute allows a specific Setup Complete script file to be used instead of the default Setup Complete script. -If a relative path is used for this attribute then it will be appended onto the path of this the Lab config file, otherwise the full rooted path to the file will be used. - </xs:documentation> - <xs:appinfo>setupcomplete="Scripts\SetupScompleteDebug.cmd"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="integrationservices" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls which Integration Services are enabled on this Virtual Machine. -It should contain a comma delimited list of Integration Service names that should be enabled. -If this attribute is defined but left empty, all Integration Services will be disabled. -If this attribute is not defined all Integration Services will be enabled. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. - - - Default Value: Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS - - Valid Values: Guest Service Interface | Heartbeat | Key-Value Pair Exchange | Shutdown | Time Synchronization | VSS - </xs:documentation> - <xs:appinfo>integrationservices="Guest Service Interface,Heartbeat"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="packages" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute can contain a comma delimited list of packages that should be installed onto this Virtual Machine. -If this attribute is not defined, but it is defined in the Template then the template value will be used, otherwise the default value will be used. - -If the Virtual Machine is a Nano Server then the packages can be .cab files, which will install the Nano Server package from the ISO or a Resource MSU file. - -If the Virtual Machine is not a Nano Server then the packages must be Resource MSU files. - -Valid Values: - - Resource MSU names that are can be found in the ResourceMSU list. - -Valid Values for Nano Server: - - Filename including the .cab extension of a valid Nano Server package found on the Windows Install Media ISO. - - Resource MSU names that are can be found in the ResourceMSU list. - </xs:documentation> - <xs:appinfo>packages="Microsoft-NanoServer-DNS-Package.cab,SomePackage.msu"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="bootorder" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls the boot and shutdown order of the Virtual Machine when Start-Lab or Stop-Lab is called repsectively. -Multiple Lab Virtual Machines in the same Lab can share the same boot order. -Any Lab Virtual Machines without a boot order will be started last or shutdown first. - </xs:documentation> - <xs:appinfo>bootorder="4"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="certificatesource" type="xs:string" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute controls where the Certificates for the Lab Virtual Machine is generated from. -This attribute should not need to be changed in most Lab Virtual Machines. -The attribute is ignored for Nano Servers because certificate generation can not be performed by Nano Servers (currently). - - - Default Value: Guest (or Host for Nano Servers). - - Valid Values: Guest | Host - </xs:documentation> - <xs:appinfo>certificatesource="Host"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="instancecount" type="xs:unsignedByte" use="optional"> - <xs:annotation> - <xs:documentation> -This optional attribute causes more than one copy of the Virtual Machine to be generated. -If set to a value more than one, it will cause this Virtual Machine to be replicated this number of times, with the machine number appended onto the end of the Virtual Machine and folder. -Any IP addresses and MAC addresses statically assigned to the network adapters in this machine will also be adjusted by increasing by one each time. - -**Care should be taken to ensure that IP addresses and MAC addresses do not overlap or stretch outside of subnet boundaries.** -**It is strongly recommended that the adapter MAC addresses on Lab VMs that have an instance count of more than one is not set, but allowed to be managed by the Hyper-V Host.** -**DHCP address assignment is also recommneded on all adapters connected to Lab VMs with an instance count of more than one.** -**If DHCP address assignement is not used then extreme care must be taken to ensure that all adapters are assigned to different subnets and will not overlap any other Lab Virtual Machine IP address assignments.** - - - Default Value: 1 - - Valid Values: 1 - 255 - </xs:documentation> - <xs:appinfo>instancecount="5"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> - </xs:sequence> - </xs:complexType> - </xs:element> - </xs:all> - <xs:attribute name="name" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute should be used to set a descriptive name for this Lab configuration. - </xs:documentation> - <xs:appinfo>name="WS2012R2-DOMAIN-CLUSTER"</xs:appinfo> - </xs:annotation> - </xs:attribute> - <xs:attribute name="version" type="xs:decimal" use="required"> - <xs:annotation> - <xs:documentation> -This required attribute should be used to set a version number for this Lab configuration in the format #.#. -It should be updated each time the Lab configuration is changed. - </xs:documentation> - <xs:appinfo>version="2.1"</xs:appinfo> - </xs:annotation> - </xs:attribute> - </xs:complexType> - </xs:element> -</xs:schema> diff --git a/src/support/Convert-LabBuilderConfigSchemaToMD.ps1 b/src/support/Convert-LabBuilderConfigSchemaToMD.ps1 deleted file mode 100644 index 241dc11c..00000000 --- a/src/support/Convert-LabBuilderConfigSchemaToMD.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -<# -.Synopsis - Creates the ..\schema\labbuilderconfig-schema.md from the ..\schema\labbuilderconfig-schema.xsd - using the transform\labbuilderconfig-schema-transformtomd.xsl transformation file. -#> -$XMLFile = Join-Path -Path $PSScriptRoot -ChildPath '..\schema\labbuilderconfig-schema.xsd' -$XSLFile = Join-Path -Path $PSScriptRoot -ChildPath 'transform\labbuilderconfig-schema-transformtomd.xsl' -$OutputFile = Join-Path -Path $PSScriptRoot -ChildPath '..\docs\labbuilderconfig-schema.md' -Write-Verbose -Verbose "Conversion '..\schema\labbuilderconfig-schema.xsd' to '..\docs\labbuilderconfig-schema.md' started" -& "$PSScriptRoot\Convert-XSDToMD.ps1" ` - -XmlFile $XMLFile ` - -XslFile $XSLFile ` - -OutputFile $OutputFile -Write-Verbose -Verbose "'..\schema\labbuilderconfig-schema.xsd' has been converted to '..\docs\labbuilderconfig-schema.md' successfully" \ No newline at end of file diff --git a/src/support/Convert-WindowsImage.ps1 b/src/support/Convert-WindowsImage.ps1 deleted file mode 100644 index 1c6c9907..00000000 --- a/src/support/Convert-WindowsImage.ps1 +++ /dev/null @@ -1,4053 +0,0 @@ -function -Convert-WindowsImage -{ - <# - .NOTES - Copyright (c) Microsoft Corporation. All rights reserved. - - Use of this sample source code is subject to the terms of the Microsoft - license agreement under which you licensed this sample source code. If - you did not accept the terms of the license agreement, you are not - authorized to use this sample source code. For the terms of the license, - please see the license agreement between you and Microsoft or, if applicable, - see the LICENSE.RTF on your install media or the root of your tools installation. - THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES. - - .SYNOPSIS - Creates a bootable VHD(X) based on Windows 7 or Windows 8 installation media. - - .DESCRIPTION - Creates a bootable VHD(X) based on Windows 7 or Windows 8 installation media. - - .PARAMETER SourcePath - The complete path to the WIM or ISO file that will be converted to a Virtual Hard Disk. - The ISO file must be valid Windows installation media to be recognized successfully. - - .PARAMETER CacheSource - If the source WIM/ISO was copied locally, we delete it by default. - Pass $true to cache the source image from the temp directory. - - .PARAMETER VHDPath - The name and path of the Virtual Hard Disk to create. - Omitting this parameter will create the Virtual Hard Disk is the current directory, (or, - if specified by the -WorkingDirectory parameter, the working directory) and will automatically - name the file in the following format: - - <build>.<revision>.<architecture>.<branch>.<timestamp>_<skufamily>_<sku>_<language>.<extension> - i.e.: - 9200.0.amd64fre.winmain_win8rtm.120725-1247_client_professional_en-us.vhd(x) - - .PARAMETER WorkingDirectory - Specifies the directory where the VHD(X) file should be generated. - If specified along with -VHDPath, the -WorkingDirectory value is ignored. - The default value is the current directory ($pwd). - - .PARAMETER TempDirectory - Specifies the directory where the logs and ISO files should be placed. - The default value is the temp directory ($env:Temp). - - .PARAMETER SizeBytes - The size of the Virtual Hard Disk to create. - For fixed disks, the VHD(X) file will be allocated all of this space immediately. - For dynamic disks, this will be the maximum size that the VHD(X) can grow to. - The default value is 40GB. - - .PARAMETER VHDFormat - Specifies whether to create a VHD or VHDX formatted Virtual Hard Disk. - The default is AUTO, which will create a VHD if using the BIOS disk layout or - VHDX if using UEFI or WindowsToGo layouts. - - .PARAMETER DiskLayout - Specifies whether to build the image for BIOS (MBR), UEFI (GPT), or WindowsToGo (MBR). - Generation 1 VMs require BIOS (MBR) images. Generation 2 VMs require UEFI (GPT) images. - Windows To Go images will boot in UEFI or BIOS but are not technically supported (upgrade - doesn't work) - - .PARAMETER UnattendPath - The complete path to an unattend.xml file that can be injected into the VHD(X). - - .PARAMETER Edition - The name or image index of the image to apply from the WIM. - - .PARAMETER Passthru - Specifies that the full path to the VHD(X) that is created should be - returned on the pipeline. - - .PARAMETER BCDBoot - By default, the version of BCDBOOT.EXE that is present in \Windows\System32 - is used by Convert-WindowsImage. If you need to specify an alternate version, - use this parameter to do so. - - .PARAMETER MergeFolder - Specifies additional MergeFolder path to be added to the root of the VHD(X) - - .PARAMETER BCDinVHD - Specifies the purpose of the VHD(x). Use NativeBoot to skip cration of BCD store - inside the VHD(x). Use VirtualMachine (or do not specify this option) to ensure - the BCD store is created inside the VHD(x). - - .PARAMETER Driver - Full path to driver(s) (.inf files) to inject to the OS inside the VHD(x). - - .PARAMETER ExpandOnNativeBoot - Specifies whether to expand the VHD(x) to its maximum suze upon native boot. - The default is True. Set to False to disable expansion. - - .PARAMETER RemoteDesktopEnable - Enable Remote Desktop to connect to the OS inside the VHD(x) upon provisioning. - Does not include Windows Firewall rules (firewall exceptions). The default is False. - - .PARAMETER Feature - Enables specified Windows Feature(s). Note that you need to specify the Internal names - understood by DISM and DISM CMDLets (e.g. NetFx3) instead of the "Friendly" names - from Server Manager CMDLets (e.g. NET-Framework-Core). - - .PARAMETER Package - Injects specified Windows Package(s). Accepts path to either a directory or individual - CAB or MSU file. - - .PARAMETER ShowUI - Specifies that the Graphical User Interface should be displayed. - - .PARAMETER EnableDebugger - Configures kernel debugging for the VHD(X) being created. - EnableDebugger takes a single argument which specifies the debugging transport to use. - Valid transports are: None, Serial, 1394, USB, Network, Local. - - Depending on the type of transport selected, additional configuration parameters will become - available. - - Serial: - -ComPort - The COM port number to use while communicating with the debugger. - The default value is 1 (indicating COM1). - -BaudRate - The baud rate (in bps) to use while communicating with the debugger. - The default value is 115200, valid values are: - 9600, 19200, 38400, 56700, 115200 - - 1394: - -Channel - The 1394 channel used to communicate with the debugger. - The default value is 10. - - USB: - -Target - The target name used for USB debugging. - The default value is "debugging". - - Network: - -IPAddress - The IP address of the debugging host computer. - -Port - The port on which to connect to the debugging host. - The default value is 50000, with a minimum value of 49152. - -Key - The key used to encrypt the connection. Only [0-9] and [a-z] are allowed. - -nodhcp - Prevents the use of DHCP to obtain the target IP address. - -newkey - Specifies that a new encryption key should be generated for the connection. - - .PARAMETER DismPath - Full Path to an alternative version of the Dism.exe tool. The default is the current OS version. - - .PARAMETER ApplyEA - Specifies that any EAs captured in the WIM should be applied to the VHD. - The default is False. - - .EXAMPLE - .\Convert-WindowsImage.ps1 -SourcePath D:\foo\install.wim -Edition Professional -WorkingDirectory D:\foo - - This command will create a 40GB dynamically expanding VHD in the D:\foo folder. - The VHD will be based on the Professional edition from D:\foo\install.wim, - and will be named automatically. - - .EXAMPLE - .\Convert-WindowsImage.ps1 -SourcePath D:\foo\Win7SP1.iso -Edition Ultimate -VHDPath D:\foo\Win7_Ultimate_SP1.vhd - - This command will parse the ISO file D:\foo\Win7SP1.iso and try to locate - \sources\install.wim. If that file is found, it will be used to create a - dynamically-expanding 40GB VHD containing the Ultimate SKU, and will be - named D:\foo\Win7_Ultimate_SP1.vhd - - .EXAMPLE - .\Convert-WindowsImage.ps1 -SourcePath D:\foo\install.wim -Edition Professional -EnableDebugger Serial -ComPort 2 -BaudRate 38400 - - This command will create a VHD from D:\foo\install.wim of the Professional SKU. - Serial debugging will be enabled in the VHD via COM2 at a baud rate of 38400bps. - - .OUTPUTS - System.IO.FileInfo - #> - #Requires -Version 3.0 - [CmdletBinding(DefaultParameterSetName="SRC", - HelpURI="https://github.com/Microsoft/Virtualization-Documentation/tree/master/hyperv-tools/Convert-WindowsImage")] - - param( - [Parameter(ParameterSetName="SRC", Mandatory=$true, ValueFromPipeline=$true)] - [Alias("WIM")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path $(Resolve-Path $_) })] - $SourcePath, - - [Parameter(ParameterSetName="SRC")] - [switch] - $CacheSource = $false, - - [Parameter(ParameterSetName="SRC")] - [Alias("SKU")] - [System.String[]] - [ValidateNotNullOrEmpty()] - $Edition, - - [Parameter(ParameterSetName="SRC")] - [Alias("WorkDir")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path $_ })] - $WorkingDirectory = $pwd, - - [Parameter(ParameterSetName="SRC")] - [Alias("TempDir")] - [System.String] - [ValidateNotNullOrEmpty()] - $TempDirectory = $env:Temp, - - [Parameter(ParameterSetName="SRC")] - [Alias("VHD")] - [System.String] - [ValidateNotNullOrEmpty()] - $VHDPath, - - [Parameter(ParameterSetName="SRC")] - [Alias("Size")] - [UInt64] - [ValidateNotNullOrEmpty()] - [ValidateRange(512MB, 64TB)] - $SizeBytes = 25GB, - - [Parameter(ParameterSetName="SRC")] - [Alias("Format")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateSet("VHD", "VHDX", "AUTO")] - $VHDFormat = "AUTO", - - [Parameter(ParameterSetName="SRC")] - [Alias("MergeFolder")] - [System.String] - [ValidateNotNullOrEmpty()] - $MergeFolderPath = "", - - [Parameter(ParameterSetName="SRC", Mandatory=$true)] - [Alias("Layout")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateSet("BIOS", "UEFI", "WindowsToGo")] - $DiskLayout, - - [Parameter(ParameterSetName="SRC")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateSet("NativeBoot", "VirtualMachine")] - $BCDinVHD = "VirtualMachine", - - [Parameter(ParameterSetName="SRC")] - [Parameter(ParameterSetName="UI")] - [System.String] - $BCDBoot = "bcdboot.exe", - - [Parameter(ParameterSetName="SRC")] - [Parameter(ParameterSetName="UI")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateSet("None", "Serial", "1394", "USB", "Local", "Network")] - $EnableDebugger = "None", - - [Parameter(ParameterSetName="SRC")] - [System.String[]] - [ValidateNotNullOrEmpty()] - $Feature, - - [Parameter(ParameterSetName="SRC")] - [System.String[]] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path $(Resolve-Path $_) })] - $Driver, - - [Parameter(ParameterSetName="SRC")] - [System.String[]] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path $(Resolve-Path $_) })] - $Package, - - [Parameter(ParameterSetName="SRC")] - [switch] - $ExpandOnNativeBoot = $true, - - [Parameter(ParameterSetName="SRC")] - [switch] - $RemoteDesktopEnable = $false, - - [Parameter(ParameterSetName="SRC")] - [Alias("Unattend")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path $(Resolve-Path $_) })] - $UnattendPath, - - [Parameter(ParameterSetName="SRC")] - [Parameter(ParameterSetName="UI")] - [switch] - $Passthru, - - [Parameter(ParameterSetName="SRC")] - [System.String] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path $(Resolve-Path $_) })] - $DismPath, - - [Parameter(ParameterSetName="SRC")] - [switch] - $ApplyEA = $false, - - [Parameter(ParameterSetName="UI")] - [switch] - $ShowUI - ) - #region Code - - # Begin Dynamic Parameters - # Create the parameters for the various types of debugging. - DynamicParam - { - Set-StrictMode -version 3 - - # Set up the dynamic parameters. - # Dynamic parameters are only available if certain conditions are met, so they'll only show up - # as valid parameters when those conditions apply. Here, the conditions are based on the value of - # the EnableDebugger parameter. Depending on which of a set of values is the specified argument - # for EnableDebugger, different parameters will light up, as outlined below. - - $parameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary - - if (!(Test-Path Variable:Private:EnableDebugger)) - { - return $parameterDictionary - } - - switch ($EnableDebugger) - { - "Serial" - { - #region ComPort - - $ComPortAttr = New-Object System.Management.Automation.ParameterAttribute - $ComPortAttr.ParameterSetName = "__AllParameterSets" - $ComPortAttr.Mandatory = $false - - $ComPortValidator = New-Object System.Management.Automation.ValidateRangeAttribute( - 1, - 10 # Is that a good maximum? - ) - - $ComPortNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $ComPortAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $ComPortAttrCollection.Add($ComPortAttr) - $ComPortAttrCollection.Add($ComPortValidator) - $ComPortAttrCollection.Add($ComPortNotNull) - - $ComPort = New-Object System.Management.Automation.RuntimeDefinedParameter( - "ComPort", - [UInt16], - $ComPortAttrCollection - ) - - # By default, use COM1 - $ComPort.Value = 1 - $parameterDictionary.Add("ComPort", $ComPort) - #endregion ComPort - - #region BaudRate - $BaudRateAttr = New-Object System.Management.Automation.ParameterAttribute - $BaudRateAttr.ParameterSetName = "__AllParameterSets" - $BaudRateAttr.Mandatory = $false - - $BaudRateValidator = New-Object System.Management.Automation.ValidateSetAttribute( - 9600, 19200,38400, 57600, 115200 - ) - - $BaudRateNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $BaudRateAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $BaudRateAttrCollection.Add($BaudRateAttr) - $BaudRateAttrCollection.Add($BaudRateValidator) - $BaudRateAttrCollection.Add($BaudRateNotNull) - - $BaudRate = New-Object System.Management.Automation.RuntimeDefinedParameter( - "BaudRate", - [UInt32], - $BaudRateAttrCollection - ) - - # By default, use 115,200. - $BaudRate.Value = 115200 - $parameterDictionary.Add("BaudRate", $BaudRate) - #endregion BaudRate - - break - } - - "1394" - { - $ChannelAttr = New-Object System.Management.Automation.ParameterAttribute - $ChannelAttr.ParameterSetName = "__AllParameterSets" - $ChannelAttr.Mandatory = $false - - $ChannelValidator = New-Object System.Management.Automation.ValidateRangeAttribute( - 0, - 62 - ) - - $ChannelNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $ChannelAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $ChannelAttrCollection.Add($ChannelAttr) - $ChannelAttrCollection.Add($ChannelValidator) - $ChannelAttrCollection.Add($ChannelNotNull) - - $Channel = New-Object System.Management.Automation.RuntimeDefinedParameter( - "Channel", - [UInt16], - $ChannelAttrCollection - ) - - # By default, use channel 10 - $Channel.Value = 10 - $parameterDictionary.Add("Channel", $Channel) - break - } - - "USB" - { - $TargetAttr = New-Object System.Management.Automation.ParameterAttribute - $TargetAttr.ParameterSetName = "__AllParameterSets" - $TargetAttr.Mandatory = $false - - $TargetNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $TargetAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $TargetAttrCollection.Add($TargetAttr) - $TargetAttrCollection.Add($TargetNotNull) - - $Target = New-Object System.Management.Automation.RuntimeDefinedParameter( - "Target", - [System.String], - $TargetAttrCollection - ) - - # By default, use target = "debugging" - $Target.Value = "Debugging" - $parameterDictionary.Add("Target", $Target) - break - } - - "Network" - { - #region IP - $IpAttr = New-Object System.Management.Automation.ParameterAttribute - $IpAttr.ParameterSetName = "__AllParameterSets" - $IpAttr.Mandatory = $true - - $IpValidator = New-Object System.Management.Automation.ValidatePatternAttribute( - "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b" - ) - $IpNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $IpAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $IpAttrCollection.Add($IpAttr) - $IpAttrCollection.Add($IpValidator) - $IpAttrCollection.Add($IpNotNull) - - $IP = New-Object System.Management.Automation.RuntimeDefinedParameter( - "IPAddress", - [System.String], - $IpAttrCollection - ) - - # There's no good way to set a default value for this. - $parameterDictionary.Add("IPAddress", $IP) - #endregion IP - - #region Port - $PortAttr = New-Object System.Management.Automation.ParameterAttribute - $PortAttr.ParameterSetName = "__AllParameterSets" - $PortAttr.Mandatory = $false - - $PortValidator = New-Object System.Management.Automation.ValidateRangeAttribute( - 49152, - 50039 - ) - - $PortNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $PortAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $PortAttrCollection.Add($PortAttr) - $PortAttrCollection.Add($PortValidator) - $PortAttrCollection.Add($PortNotNull) - - - $Port = New-Object System.Management.Automation.RuntimeDefinedParameter( - "Port", - [UInt16], - $PortAttrCollection - ) - - # By default, use port 50000 - $Port.Value = 50000 - $parameterDictionary.Add("Port", $Port) - #endregion Port - - #region Key - $KeyAttr = New-Object System.Management.Automation.ParameterAttribute - $KeyAttr.ParameterSetName = "__AllParameterSets" - $KeyAttr.Mandatory = $true - - $KeyValidator = New-Object System.Management.Automation.ValidatePatternAttribute( - "\b([A-Z0-9]+).([A-Z0-9]+).([A-Z0-9]+).([A-Z0-9]+)\b" - ) - - $KeyNotNull = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute - - $KeyAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $KeyAttrCollection.Add($KeyAttr) - $KeyAttrCollection.Add($KeyValidator) - $KeyAttrCollection.Add($KeyNotNull) - - $Key = New-Object System.Management.Automation.RuntimeDefinedParameter( - "Key", - [System.String], - $KeyAttrCollection - ) - - # Don't set a default key. - $parameterDictionary.Add("Key", $Key) - #endregion Key - - #region NoDHCP - $NoDHCPAttr = New-Object System.Management.Automation.ParameterAttribute - $NoDHCPAttr.ParameterSetName = "__AllParameterSets" - $NoDHCPAttr.Mandatory = $false - - $NoDHCPAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $NoDHCPAttrCollection.Add($NoDHCPAttr) - - $NoDHCP = New-Object System.Management.Automation.RuntimeDefinedParameter( - "NoDHCP", - [switch], - $NoDHCPAttrCollection - ) - - $parameterDictionary.Add("NoDHCP", $NoDHCP) - #endregion NoDHCP - - #region NewKey - $NewKeyAttr = New-Object System.Management.Automation.ParameterAttribute - $NewKeyAttr.ParameterSetName = "__AllParameterSets" - $NewKeyAttr.Mandatory = $false - - $NewKeyAttrCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] - $NewKeyAttrCollection.Add($NewKeyAttr) - - $NewKey = New-Object System.Management.Automation.RuntimeDefinedParameter( - "NewKey", - [switch], - $NewKeyAttrCollection - ) - - # Don't set a default key. - $parameterDictionary.Add("NewKey", $NewKey) - #endregion NewKey - - break - } - - # There's nothing to do for local debugging. - # Synthetic debugging is not yet implemented. - - default - { - break - } - } - - return $parameterDictionary - } - - Begin - { - ########################################################################################## - # Constants and Pseudo-Constants - ########################################################################################## - $PARTITION_STYLE_MBR = 0x00000000 # The default value - $PARTITION_STYLE_GPT = 0x00000001 # Just in case... - - # Version information that can be populated by timebuild. - $ScriptVersion = DATA - { - ConvertFrom-StringData -StringData @" - Major = 10 - Minor = 0 - Build = 14278 - Qfe = 1000 - Branch = rs1_es_media - Timestamp = 160201-1707 - Flavor = amd64fre -"@ -} - - $myVersion = "$($ScriptVersion.Major).$($ScriptVersion.Minor).$($ScriptVersion.Build).$($ScriptVersion.QFE).$($ScriptVersion.Flavor).$($ScriptVersion.Branch).$($ScriptVersion.Timestamp)" - $scriptName = "Convert-WindowsImage" # Name of the script, obviously. - $sessionKey = [Guid]::NewGuid().ToString() # Session key, used for keeping records unique between multiple runs. - $logFolder = "$($TempDirectory)\$($scriptName)\$($sessionKey)" # Log folder path. - $vhdMaxSize = 2040GB # Maximum size for VHD is ~2040GB. - $vhdxMaxSize = 64TB # Maximum size for VHDX is ~64TB. - $lowestSupportedVersion = New-Object Version "6.1" # The lowest supported *image* version; making sure we don't run against Vista/2k8. - $lowestSupportedBuild = 9200 # The lowest supported *host* build. Set to Win8 CP. - $transcripting = $false - - # Since we use the VHDFormat in output, make it uppercase. - # We'll make it lowercase again when we use it as a file extension. - $VHDFormat = $VHDFormat.ToUpper() - ########################################################################################## - # Here Strings - ########################################################################################## - - # Banner text displayed during each run. - $header = @" - -Windows(R) Image to Virtual Hard Disk Converter for Windows(R) 10 -Copyright (C) Microsoft Corporation. All rights reserved. -Version $myVersion - -"@ - - # Text used as the banner in the UI. - $uiHeader = @" -You can use the fields below to configure the VHD or VHDX that you want to create! -"@ - - #region Helper Functions - - ########################################################################################## - # Helper Functions - ########################################################################################## - - <# - Functions to mount and dismount registry hives. - These hives will automatically be accessible via the HKLM:\ registry PSDrive. - - It should be noted that I have more confidence in using the RegLoadKey and - RegUnloadKey Win32 APIs than I do using REG.EXE - it just seems like we should - do things ourselves if we can, instead of using yet another binary. - - Consider this a TODO for future versions. - #> - Function Mount-RegistryHive - { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] - [System.IO.FileInfo] - [ValidateNotNullOrEmpty()] - [ValidateScript({ $_.Exists })] - $Hive - ) - - $mountKey = [System.Guid]::NewGuid().ToString() - $regPath = "REG.EXE" - - if (Test-Path HKLM:\$mountKey) - { - throw "The registry path already exists. I should just regenerate it, but I'm lazy." - } - - $regArgs = ( - "LOAD", - "HKLM\$mountKey", - $Hive.Fullname - ) - try - { - - Run-Executable -Executable $regPath -Arguments $regArgs - - } - catch - { - throw - } - - # Set a global variable containing the name of the mounted registry key - # so we can unmount it if there's an error. - $global:mountedHive = $mountKey - - return $mountKey - } - - ########################################################################################## - - Function Dismount-RegistryHive - { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] - [System.String] - [ValidateNotNullOrEmpty()] - $HiveMountPoint - ) - - $regPath = "REG.EXE" - - $regArgs = ( - "UNLOAD", - "HKLM\$($HiveMountPoint)" - ) - - Run-Executable -Executable $regPath -Arguments $regArgs - - $global:mountedHive = $null - } - - ########################################################################################## - - function - Test-Admin - { - <# - .SYNOPSIS - Short function to determine whether the logged-on user is an administrator. - - .EXAMPLE - Do you honestly need one? There are no parameters! - - .OUTPUTS - $true if user is admin. - $false if user is not an admin. - #> - [CmdletBinding()] - param() - - $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) - $isAdmin = $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) - Write-W2VTrace "isUserAdmin? $isAdmin" - - return $isAdmin - } - - ########################################################################################## - - function - Get-WindowsBuildNumber - { - $os = Get-WmiObject -Class Win32_OperatingSystem - return [System.Int32]($os.BuildNumber) - } - - ########################################################################################## - - function - Test-WindowsVersion - { - $isWin8 = ((Get-WindowsBuildNumber) -ge [System.Int32]$lowestSupportedBuild) - - Write-W2VTrace "is Windows 8 or Higher? $isWin8" - return $isWin8 - } - - ########################################################################################## - - function - Write-W2VInfo - { - # Function to make the Write-Host output a bit prettier. - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - [System.String] - [ValidateNotNullOrEmpty()] - $text - ) - Write-Host "INFO : $($text)" - } - - ########################################################################################## - - function - Write-W2VTrace - { - # Function to make the Write-Verbose output... well... exactly the same as it was before. - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - [System.String] - [ValidateNotNullOrEmpty()] - $text - ) - Write-Verbose $text - } - - ########################################################################################## - - function - Write-W2VError - { - # Function to make the Write-Host (NOT Write-Error) output prettier in the case of an error. - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - [System.String] - [ValidateNotNullOrEmpty()] - $text - ) - Write-Host "ERROR : $($text)" -ForegroundColor (Get-Host).PrivateData.ErrorForegroundColor - } - - ########################################################################################## - - function - Write-W2VWarn - { - # Function to make the Write-Host (NOT Write-Warning) output prettier. - [CmdletBinding()] - param( - [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - [System.String] - [ValidateNotNullOrEmpty()] - $text - ) - Write-Host "WARN : $($text)" -ForegroundColor (Get-Host).PrivateData.WarningForegroundColor - } - - ########################################################################################## - - function - Run-Executable - { - <# - .SYNOPSIS - Runs an external executable file, and validates the error level. - - .PARAMETER Executable - The path to the executable to run and monitor. - - .PARAMETER Arguments - An array of arguments to pass to the executable when it's executed. - - .PARAMETER SuccessfulErrorCode - The error code that means the executable ran successfully. - The default value is 0. - #> - - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [System.String] - [ValidateNotNullOrEmpty()] - $Executable, - - [Parameter(Mandatory=$true)] - [System.String[]] - [ValidateNotNullOrEmpty()] - $Arguments, - - [Parameter()] - [System.Int32] - [ValidateNotNullOrEmpty()] - $SuccessfulErrorCode = 0 - - ) - - Write-W2VTrace "Running $Executable $Arguments" - $ret = Start-Process ` - -FilePath $Executable ` - -ArgumentList $Arguments ` - -NoNewWindow ` - -Wait ` - -RedirectStandardOutput "$($TempDirectory)\$($scriptName)\$($sessionKey)\$($Executable)-StandardOutput.txt" ` - -RedirectStandardError "$($TempDirectory)\$($scriptName)\$($sessionKey)\$($Executable)-StandardError.txt" ` - -Passthru - - Write-W2VTrace "Return code was $($ret.ExitCode)." - - if ($ret.ExitCode -ne $SuccessfulErrorCode) - { - throw "$Executable failed with code $($ret.ExitCode)!" - } - } - - ########################################################################################## - Function Test-IsNetworkLocation - { - <# - .SYNOPSIS - Determines whether or not a given path is a network location or a local drive. - - .DESCRIPTION - Function to determine whether or not a specified path is a local path, a UNC path, - or a mapped network drive. - - .PARAMETER Path - The path that we need to figure stuff out about, - #> - - [CmdletBinding()] - param( - [Parameter(ValueFromPipeLine = $true)] - [System.String] - [ValidateNotNullOrEmpty()] - $Path - ) - - $result = $false - - if ([bool]([URI]$Path).IsUNC) - { - $result = $true - } - else - { - $driveInfo = [IO.DriveInfo]((Resolve-Path $Path).Path) - - if ($driveInfo.DriveType -eq "Network") - { - $result = $true - } - } - - return $result - } - ########################################################################################## - - #endregion Helper Functions - } - - Process - { - Write-Host $header - - $disk = $null - $openWim = $null - $openIso = $null - $openImage = $null - $vhdFinalName = $null - $vhdFinalPath = $null - $mountedHive = $null - $isoPath = $null - $tempSource = $null - - if (Get-Command Get-WindowsOptionalFeature -ErrorAction SilentlyContinue) - { - try - { - $hyperVEnabled = $((Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V).State -eq "Enabled") - } - catch - { - # WinPE DISM does not support online queries. This will throw on non-WinPE machines - $winpeVersion = (Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\WinPE').Version - - Write-W2VInfo "Running WinPE version $winpeVersion" - - $hyperVEnabled = $false - } - } - else - { - $hyperVEnabled = $false - } - - $vhd = @() - - try - { - # Create log folder - if (Test-Path $logFolder) - { - $null = rd $logFolder -Force -Recurse - } - - $null = md $logFolder -Force - - # Try to start transcripting. If it's already running, we'll get an exception and swallow it. - try - { - $null = Start-Transcript -Path (Join-Path $logFolder "Convert-WindowsImageTranscript.txt") -Force -ErrorAction SilentlyContinue - $transcripting = $true - } - catch - { - Write-W2VWarn "Transcription is already running. No Convert-WindowsImage-specific transcript will be created." - $transcripting = $false - } - - # - # Add types - # - Add-WindowsImageTypes - - # Check to make sure we're running as Admin. - if (!(Test-Admin)) - { - throw "Images can only be applied by an administrator. Please launch PowerShell elevated and run this script again." - } - - # Check to make sure we're running on Win8. - if (!(Test-WindowsVersion)) - { - throw "$scriptName requires Windows 8 Consumer Preview or higher. Please use WIM2VHD.WSF (http://code.msdn.microsoft.com/wim2vhd) if you need to create VHDs from Windows 7." - } - - # Resolve the path for the unattend file. - if (![System.String]::IsNullOrEmpty($UnattendPath)) - { - $UnattendPath = (Resolve-Path $UnattendPath).Path - } - - if ($ShowUI) - { - - Write-W2VInfo "Launching UI..." - Add-Type -AssemblyName System.Drawing,System.Windows.Forms - - #region Form Objects - $frmMain = New-Object System.Windows.Forms.Form - $groupBox4 = New-Object System.Windows.Forms.GroupBox - $btnGo = New-Object System.Windows.Forms.Button - $groupBox3 = New-Object System.Windows.Forms.GroupBox - $txtVhdName = New-Object System.Windows.Forms.TextBox - $label6 = New-Object System.Windows.Forms.Label - $btnWrkBrowse = New-Object System.Windows.Forms.Button - $cmbVhdSizeUnit = New-Object System.Windows.Forms.ComboBox - $numVhdSize = New-Object System.Windows.Forms.NumericUpDown - $cmbVhdFormat = New-Object System.Windows.Forms.ComboBox - $label5 = New-Object System.Windows.Forms.Label - $txtWorkingDirectory = New-Object System.Windows.Forms.TextBox - $label4 = New-Object System.Windows.Forms.Label - $label3 = New-Object System.Windows.Forms.Label - $label2 = New-Object System.Windows.Forms.Label - $label7 = New-Object System.Windows.Forms.Label - $txtUnattendFile = New-Object System.Windows.Forms.TextBox - $btnUnattendBrowse = New-Object System.Windows.Forms.Button - $groupBox2 = New-Object System.Windows.Forms.GroupBox - $cmbSkuList = New-Object System.Windows.Forms.ComboBox - $label1 = New-Object System.Windows.Forms.Label - $groupBox1 = New-Object System.Windows.Forms.GroupBox - $txtSourcePath = New-Object System.Windows.Forms.TextBox - $btnBrowseWim = New-Object System.Windows.Forms.Button - $openFileDialog1 = New-Object System.Windows.Forms.OpenFileDialog - $openFolderDialog1 = New-Object System.Windows.Forms.FolderBrowserDialog - $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState - - #endregion Form Objects - - #region Event scriptblocks. - - $btnGo_OnClick = { - $frmMain.Close() - } - - $btnWrkBrowse_OnClick = { - $openFolderDialog1.RootFolder = "Desktop" - $openFolderDialog1.Description = "Select the folder you'd like your VHD(X) to be created in." - $openFolderDialog1.SelectedPath = $WorkingDirectory - - $ret = $openFolderDialog1.ShowDialog() - - if ($ret -ilike "ok") - { - $WorkingDirectory = $txtWorkingDirectory = $openFolderDialog1.SelectedPath - Write-W2VInfo "Selected Working Directory is $WorkingDirectory..." - } - } - - $btnUnattendBrowse_OnClick = { - $openFileDialog1.InitialDirectory = $pwd - $openFileDialog1.Filter = "XML files (*.xml)|*.XML|All files (*.*)|*.*" - $openFileDialog1.FilterIndex = 1 - $openFileDialog1.CheckFileExists = $true - $openFileDialog1.CheckPathExists = $true - $openFileDialog1.FileName = $null - $openFileDialog1.ShowHelp = $false - $openFileDialog1.Title = "Select an unattend file..." - - $ret = $openFileDialog1.ShowDialog() - - if ($ret -ilike "ok") - { - $UnattendPath = $txtUnattendFile.Text = $openFileDialog1.FileName - } - } - - $btnBrowseWim_OnClick = { - $openFileDialog1.InitialDirectory = $pwd - $openFileDialog1.Filter = "All compatible files (*.ISO, *.WIM)|*.ISO;*.WIM|All files (*.*)|*.*" - $openFileDialog1.FilterIndex = 1 - $openFileDialog1.CheckFileExists = $true - $openFileDialog1.CheckPathExists = $true - $openFileDialog1.FileName = $null - $openFileDialog1.ShowHelp = $false - $openFileDialog1.Title = "Select a source file..." - - $ret = $openFileDialog1.ShowDialog() - - if ($ret -ilike "ok") - { - - if (([IO.FileInfo]$openFileDialog1.FileName).Extension -ilike ".iso") - { - - if (Test-IsNetworkLocation $openFileDialog1.FileName) - { - Write-W2VInfo "Copying ISO $(Split-Path $openFileDialog1.FileName -Leaf) to temp folder..." - Write-W2VWarn "The UI may become non-responsive while this copy takes place..." - Copy-Item -Path $openFileDialog1.FileName -Destination $TempDirectory -Force - $openFileDialog1.FileName = "$($TempDirectory)\$(Split-Path $openFileDialog1.FileName -Leaf)" - } - - $txtSourcePath.Text = $isoPath = (Resolve-Path $openFileDialog1.FileName).Path - Write-W2VInfo "Opening ISO $(Split-Path $isoPath -Leaf)..." - - $openIso = Mount-DiskImage -ImagePath $isoPath -StorageType ISO -PassThru - - # Refresh the DiskImage object so we can get the real information about it. I assume this is a bug. - $openIso = Get-DiskImage -ImagePath $isoPath - $driveLetter = ($openIso | Get-Volume).DriveLetter - - $script:SourcePath = "$($driveLetter):\sources\install.wim" - - # Check to see if there's a WIM file we can muck about with. - Write-W2VInfo "Looking for $($SourcePath)..." - if (!(Test-Path $SourcePath)) - { - throw "The specified ISO does not appear to be valid Windows installation media." - } - } - else - { - $txtSourcePath.Text = $script:SourcePath = $openFileDialog1.FileName - } - - # Check to see if the WIM is local, or on a network location. If the latter, copy it locally. - if (Test-IsNetworkLocation $SourcePath) - { - Write-W2VInfo "Copying WIM $(Split-Path $SourcePath -Leaf) to temp folder..." - Write-W2VWarn "The UI may become non-responsive while this copy takes place..." - Copy-Item -Path $SourcePath -Destination $TempDirectory -Force - $txtSourcePath.Text = $script:SourcePath = "$($TempDirectory)\$(Split-Path $SourcePath -Leaf)" - } - - $script:SourcePath = (Resolve-Path $SourcePath).Path - - Write-W2VInfo "Scanning WIM metadata..." - - $tempOpenWim = $null - - try - { - $tempOpenWim = New-Object WIM2VHD.WimFile $SourcePath - - # Let's see if we're running against an unstaged build. If we are, we need to blow up. - if ($tempOpenWim.ImageNames.Contains("Windows Longhorn Client") -or - $tempOpenWim.ImageNames.Contains("Windows Longhorn Server") -or - $tempOpenWim.ImageNames.Contains("Windows Longhorn Server Core")) - { - [Windows.Forms.MessageBox]::Show( - "Convert-WindowsImage cannot run against unstaged builds. Please try again with a staged build.", - "WIM is incompatible!", - "OK", - "Error" - ) - - return - } - else - { - $tempOpenWim.Images | %{ $cmbSkuList.Items.Add($_.ImageFlags) } - $cmbSkuList.SelectedIndex = 0 - } - - } - catch - { - throw "Unable to load WIM metadata!" - } - finally - { - $tempOpenWim.Close() - Write-W2VTrace "Closing WIM metadata..." - } - } - } - - $OnLoadForm_StateCorrection = { - - # Correct the initial state of the form to prevent the .Net maximized form issue - $frmMain.WindowState = $InitialFormWindowState - } - - #endregion Event scriptblocks - - # Figure out VHD size and size unit. - $unit = $null - switch ([Math]::Round($SizeBytes.ToString().Length / 3)) - { - 3 { $unit = "MB"; break } - 4 { $unit = "GB"; break } - 5 { $unit = "TB"; break } - default { $unit = ""; break } - } - - $quantity = Invoke-Expression -Command "$($SizeBytes) / 1$($unit)" - - #region Form Code - #region frmMain - $frmMain.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 579 - $System_Drawing_Size.Width = 512 - $frmMain.ClientSize = $System_Drawing_Size - $frmMain.Font = New-Object System.Drawing.Font("Segoe UI",10,0,3,1) - $frmMain.FormBorderStyle = 1 - $frmMain.MaximizeBox = $false - $frmMain.MinimizeBox = $false - $frmMain.Name = "frmMain" - $frmMain.StartPosition = 1 - $frmMain.Text = "Convert-WindowsImage UI" - #endregion frmMain - - #region groupBox4 - $groupBox4.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 10 - $System_Drawing_Point.Y = 498 - $groupBox4.Location = $System_Drawing_Point - $groupBox4.Name = "groupBox4" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 69 - $System_Drawing_Size.Width = 489 - $groupBox4.Size = $System_Drawing_Size - $groupBox4.TabIndex = 8 - $groupBox4.TabStop = $false - $groupBox4.Text = "4. Make the VHD!" - - $frmMain.Controls.Add($groupBox4) - #endregion groupBox4 - - #region btnGo - $btnGo.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 39 - $System_Drawing_Point.Y = 24 - $btnGo.Location = $System_Drawing_Point - $btnGo.Name = "btnGo" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 33 - $System_Drawing_Size.Width = 415 - $btnGo.Size = $System_Drawing_Size - $btnGo.TabIndex = 0 - $btnGo.Text = "&Make my VHD" - $btnGo.UseVisualStyleBackColor = $true - $btnGo.DialogResult = "OK" - $btnGo.add_Click($btnGo_OnClick) - - $groupBox4.Controls.Add($btnGo) - $frmMain.AcceptButton = $btnGo - #endregion btnGo - - #region groupBox3 - $groupBox3.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 10 - $System_Drawing_Point.Y = 243 - $groupBox3.Location = $System_Drawing_Point - $groupBox3.Name = "groupBox3" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 245 - $System_Drawing_Size.Width = 489 - $groupBox3.Size = $System_Drawing_Size - $groupBox3.TabIndex = 7 - $groupBox3.TabStop = $false - $groupBox3.Text = "3. Choose configuration options" - - $frmMain.Controls.Add($groupBox3) - #endregion groupBox3 - - #region txtVhdName - $txtVhdName.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 150 - $txtVhdName.Location = $System_Drawing_Point - $txtVhdName.Name = "txtVhdName" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 418 - $txtVhdName.Size = $System_Drawing_Size - $txtVhdName.TabIndex = 10 - - $groupBox3.Controls.Add($txtVhdName) - #endregion txtVhdName - - #region txtUnattendFile - $txtUnattendFile.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 198 - $txtUnattendFile.Location = $System_Drawing_Point - $txtUnattendFile.Name = "txtUnattendFile" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 418 - $txtUnattendFile.Size = $System_Drawing_Size - $txtUnattendFile.TabIndex = 11 - - $groupBox3.Controls.Add($txtUnattendFile) - #endregion txtUnattendFile - - #region label7 - $label7.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 23 - $System_Drawing_Point.Y = 180 - $label7.Location = $System_Drawing_Point - $label7.Name = "label7" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 23 - $System_Drawing_Size.Width = 175 - $label7.Size = $System_Drawing_Size - $label7.Text = "Unattend File (Optional)" - - $groupBox3.Controls.Add($label7) - #endregion label7 - - #region label6 - $label6.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 23 - $System_Drawing_Point.Y = 132 - $label6.Location = $System_Drawing_Point - $label6.Name = "label6" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 23 - $System_Drawing_Size.Width = 175 - $label6.Size = $System_Drawing_Size - $label6.Text = "VHD Name (Optional)" - - $groupBox3.Controls.Add($label6) - #endregion label6 - - #region btnUnattendBrowse - $btnUnattendBrowse.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 449 - $System_Drawing_Point.Y = 199 - $btnUnattendBrowse.Location = $System_Drawing_Point - $btnUnattendBrowse.Name = "btnUnattendBrowse" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 27 - $btnUnattendBrowse.Size = $System_Drawing_Size - $btnUnattendBrowse.TabIndex = 9 - $btnUnattendBrowse.Text = "..." - $btnUnattendBrowse.UseVisualStyleBackColor = $true - $btnUnattendBrowse.add_Click($btnUnattendBrowse_OnClick) - - $groupBox3.Controls.Add($btnUnattendBrowse) - #endregion btnUnattendBrowse - - #region btnWrkBrowse - $btnWrkBrowse.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 449 - $System_Drawing_Point.Y = 98 - $btnWrkBrowse.Location = $System_Drawing_Point - $btnWrkBrowse.Name = "btnWrkBrowse" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 27 - $btnWrkBrowse.Size = $System_Drawing_Size - $btnWrkBrowse.TabIndex = 9 - $btnWrkBrowse.Text = "..." - $btnWrkBrowse.UseVisualStyleBackColor = $true - $btnWrkBrowse.add_Click($btnWrkBrowse_OnClick) - - $groupBox3.Controls.Add($btnWrkBrowse) - #endregion btnWrkBrowse - - #region cmbVhdSizeUnit - $cmbVhdSizeUnit.DataBindings.DefaultDataSourceUpdateMode = 0 - $cmbVhdSizeUnit.FormattingEnabled = $true - $cmbVhdSizeUnit.Items.Add("MB") | Out-Null - $cmbVhdSizeUnit.Items.Add("GB") | Out-Null - $cmbVhdSizeUnit.Items.Add("TB") | Out-Null - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 409 - $System_Drawing_Point.Y = 42 - $cmbVhdSizeUnit.Location = $System_Drawing_Point - $cmbVhdSizeUnit.Name = "cmbVhdSizeUnit" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 67 - $cmbVhdSizeUnit.Size = $System_Drawing_Size - $cmbVhdSizeUnit.TabIndex = 5 - $cmbVhdSizeUnit.Text = $unit - - $groupBox3.Controls.Add($cmbVhdSizeUnit) - #endregion cmbVhdSizeUnit - - #region numVhdSize - $numVhdSize.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 340 - $System_Drawing_Point.Y = 42 - $numVhdSize.Location = $System_Drawing_Point - $numVhdSize.Name = "numVhdSize" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 63 - $numVhdSize.Size = $System_Drawing_Size - $numVhdSize.TabIndex = 4 - $numVhdSize.Value = $quantity - - $groupBox3.Controls.Add($numVhdSize) - #endregion numVhdSize - - #region cmbVhdFormat - $cmbVhdFormat.DataBindings.DefaultDataSourceUpdateMode = 0 - $cmbVhdFormat.FormattingEnabled = $true - $cmbVhdFormat.Items.Add("VHD") | Out-Null - $cmbVhdFormat.Items.Add("VHDX") | Out-Null - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 42 - $cmbVhdFormat.Location = $System_Drawing_Point - $cmbVhdFormat.Name = "cmbVhdFormat" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 136 - $cmbVhdFormat.Size = $System_Drawing_Size - $cmbVhdFormat.TabIndex = 0 - $cmbVhdFormat.Text = $VHDFormat - - $groupBox3.Controls.Add($cmbVhdFormat) - #endregion cmbVhdFormat - - #region label5 - $label5.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 23 - $System_Drawing_Point.Y = 76 - $label5.Location = $System_Drawing_Point - $label5.Name = "label5" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 23 - $System_Drawing_Size.Width = 264 - $label5.Size = $System_Drawing_Size - $label5.TabIndex = 8 - $label5.Text = "Working Directory" - - $groupBox3.Controls.Add($label5) - #endregion label5 - - #region txtWorkingDirectory - $txtWorkingDirectory.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 99 - $txtWorkingDirectory.Location = $System_Drawing_Point - $txtWorkingDirectory.Name = "txtWorkingDirectory" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 418 - $txtWorkingDirectory.Size = $System_Drawing_Size - $txtWorkingDirectory.TabIndex = 7 - $txtWorkingDirectory.Text = $WorkingDirectory - - $groupBox3.Controls.Add($txtWorkingDirectory) - #endregion txtWorkingDirectory - - #region label4 - $label4.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 340 - $System_Drawing_Point.Y = 21 - $label4.Location = $System_Drawing_Point - $label4.Name = "label4" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 27 - $System_Drawing_Size.Width = 86 - $label4.Size = $System_Drawing_Size - $label4.TabIndex = 6 - $label4.Text = "VHD Size" - - $groupBox3.Controls.Add($label4) - #endregion label4 - - #region label3 - $label3.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 176 - $System_Drawing_Point.Y = 21 - $label3.Location = $System_Drawing_Point - $label3.Name = "label3" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 27 - $System_Drawing_Size.Width = 92 - $label3.Size = $System_Drawing_Size - $label3.TabIndex = 3 - $label3.Text = "VHD Type" - - $groupBox3.Controls.Add($label3) - #endregion label3 - - #region label2 - $label2.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 21 - $label2.Location = $System_Drawing_Point - $label2.Name = "label2" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 30 - $System_Drawing_Size.Width = 118 - $label2.Size = $System_Drawing_Size - $label2.TabIndex = 1 - $label2.Text = "VHD Format" - - $groupBox3.Controls.Add($label2) - #endregion label2 - - #region groupBox2 - $groupBox2.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 10 - $System_Drawing_Point.Y = 169 - $groupBox2.Location = $System_Drawing_Point - $groupBox2.Name = "groupBox2" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 68 - $System_Drawing_Size.Width = 490 - $groupBox2.Size = $System_Drawing_Size - $groupBox2.TabIndex = 6 - $groupBox2.TabStop = $false - $groupBox2.Text = "2. Choose a SKU from the list" - - $frmMain.Controls.Add($groupBox2) - #endregion groupBox2 - - #region cmbSkuList - $cmbSkuList.DataBindings.DefaultDataSourceUpdateMode = 0 - $cmbSkuList.FormattingEnabled = $true - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 24 - $cmbSkuList.Location = $System_Drawing_Point - $cmbSkuList.Name = "cmbSkuList" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 452 - $cmbSkuList.Size = $System_Drawing_Size - $cmbSkuList.TabIndex = 2 - - $groupBox2.Controls.Add($cmbSkuList) - #endregion cmbSkuList - - #region label1 - $label1.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 23 - $System_Drawing_Point.Y = 21 - $label1.Location = $System_Drawing_Point - $label1.Name = "label1" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 71 - $System_Drawing_Size.Width = 464 - $label1.Size = $System_Drawing_Size - $label1.TabIndex = 5 - $label1.Text = $uiHeader - - $frmMain.Controls.Add($label1) - #endregion label1 - - #region groupBox1 - $groupBox1.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 10 - $System_Drawing_Point.Y = 95 - $groupBox1.Location = $System_Drawing_Point - $groupBox1.Name = "groupBox1" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 68 - $System_Drawing_Size.Width = 490 - $groupBox1.Size = $System_Drawing_Size - $groupBox1.TabIndex = 4 - $groupBox1.TabStop = $false - $groupBox1.Text = "1. Choose a source" - - $frmMain.Controls.Add($groupBox1) - #endregion groupBox1 - - #region txtSourcePath - $txtSourcePath.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 25 - $System_Drawing_Point.Y = 24 - $txtSourcePath.Location = $System_Drawing_Point - $txtSourcePath.Name = "txtSourcePath" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 418 - $txtSourcePath.Size = $System_Drawing_Size - $txtSourcePath.TabIndex = 0 - - $groupBox1.Controls.Add($txtSourcePath) - #endregion txtSourcePath - - #region btnBrowseWim - $btnBrowseWim.DataBindings.DefaultDataSourceUpdateMode = 0 - $System_Drawing_Point = New-Object System.Drawing.Point - $System_Drawing_Point.X = 449 - $System_Drawing_Point.Y = 24 - $btnBrowseWim.Location = $System_Drawing_Point - $btnBrowseWim.Name = "btnBrowseWim" - $System_Drawing_Size = New-Object System.Drawing.Size - $System_Drawing_Size.Height = 25 - $System_Drawing_Size.Width = 28 - $btnBrowseWim.Size = $System_Drawing_Size - $btnBrowseWim.TabIndex = 1 - $btnBrowseWim.Text = "..." - $btnBrowseWim.UseVisualStyleBackColor = $true - $btnBrowseWim.add_Click($btnBrowseWim_OnClick) - - $groupBox1.Controls.Add($btnBrowseWim) - #endregion btnBrowseWim - - $openFileDialog1.FileName = "openFileDialog1" - $openFileDialog1.ShowHelp = $true - - #endregion Form Code - - # Save the initial state of the form - $InitialFormWindowState = $frmMain.WindowState - - # Init the OnLoad event to correct the initial state of the form - $frmMain.add_Load($OnLoadForm_StateCorrection) - - # Return the constructed form. - $ret = $frmMain.ShowDialog() - - if (!($ret -ilike "OK")) - { - throw "Form session has been cancelled." - } - - if ([System.String]::IsNullOrEmpty($SourcePath)) - { - throw "No source path specified." - } - - # VHD Format - $VHDFormat = $cmbVhdFormat.SelectedItem - - # VHD Size - $SizeBytes = Invoke-Expression "$($numVhdSize.Value)$($cmbVhdSizeUnit.SelectedItem)" - - # Working Directory - $WorkingDirectory = $txtWorkingDirectory.Text - - # VHDPath - if (![System.String]::IsNullOrEmpty($txtVhdName.Text)) - { - $VHDPath = "$($WorkingDirectory)\$($txtVhdName.Text)" - } - - # Edition - if (![System.String]::IsNullOrEmpty($cmbSkuList.SelectedItem)) - { - $Edition = $cmbSkuList.SelectedItem - } - - # Because we used ShowDialog, we need to manually dispose of the form. - # This probably won't make much of a difference, but let's free up all of the resources we can - # before we start the conversion process. - - $frmMain.Dispose() - } - - if ($VHDFormat -ilike "AUTO") - { - if ($DiskLayout -eq "BIOS") - { - $VHDFormat = "VHD" - } - else - { - $VHDFormat = "VHDX" - } - } - - # - # Choose smallest supported block size for dynamic VHD(X) - # - $BlockSizeBytes = 1MB - - # There's a difference between the maximum sizes for VHDs and VHDXs. Make sure we follow it. - if ("VHD" -ilike $VHDFormat) - { - if ($SizeBytes -gt $vhdMaxSize) - { - Write-W2VWarn "For the VHD file format, the maximum file size is ~2040GB. We're automatically setting the size to 2040GB for you." - $SizeBytes = 2040GB - } - - $BlockSizeBytes = 512KB - } - - # Check if -VHDPath and -WorkingDirectory were both specified. - if ((![System.String]::IsNullOrEmpty($VHDPath)) -and (![System.String]::IsNullOrEmpty($WorkingDirectory))) - { - if ($WorkingDirectory -ne $pwd) - { - # If the WorkingDirectory is anything besides $pwd, tell people that the WorkingDirectory is being ignored. - Write-W2VWarn "Specifying -VHDPath and -WorkingDirectory at the same time is contradictory." - Write-W2VWarn "Ignoring the WorkingDirectory specification." - $WorkingDirectory = Split-Path $VHDPath -Parent - } - } - - if ($VHDPath) - { - # Check to see if there's a conflict between the specified file extension and the VHDFormat being used. - $ext = ([IO.FileInfo]$VHDPath).Extension - - if (!($ext -ilike ".$($VHDFormat)")) - { - throw "There is a mismatch between the VHDPath file extension ($($ext.ToUpper())), and the VHDFormat (.$($VHDFormat)). Please ensure that these match and try again." - } - } - - # Create a temporary name for the VHD(x). We'll name it properly at the end of the script. - if ([System.String]::IsNullOrEmpty($VHDPath)) - { - $VHDPath = Join-Path $WorkingDirectory "$($sessionKey).$($VHDFormat.ToLower())" - } - else - { - # Since we can't do Resolve-Path against a file that doesn't exist, we need to get creative in determining - # the full path that the user specified (or meant to specify if they gave us a relative path). - # Check to see if the path has a root specified. If it doesn't, use the working directory. - if (![IO.Path]::IsPathRooted($VHDPath)) - { - $VHDPath = Join-Path $WorkingDirectory $VHDPath - } - - $vhdFinalName = Split-Path $VHDPath -Leaf - $VHDPath = Join-Path (Split-Path $VHDPath -Parent) "$($sessionKey).$($VHDFormat.ToLower())" - } - - Write-W2VTrace "Temporary $VHDFormat path is : $VHDPath" - - # If we're using an ISO, mount it and get the path to the WIM file. - if (([IO.FileInfo]$SourcePath).Extension -ilike ".ISO") - { - # If the ISO isn't local, copy it down so we don't have to worry about resource contention - # or about network latency. - if (Test-IsNetworkLocation $SourcePath) - { - Write-W2VInfo "Copying ISO $(Split-Path $SourcePath -Leaf) to temp folder..." - robocopy $(Split-Path $SourcePath -Parent) $TempDirectory $(Split-Path $SourcePath -Leaf) | Out-Null - $SourcePath = "$($TempDirectory)\$(Split-Path $SourcePath -Leaf)" - - $tempSource = $SourcePath - } - - $isoPath = (Resolve-Path $SourcePath).Path - - Write-W2VInfo "Opening ISO $(Split-Path $isoPath -Leaf)..." - $openIso = Mount-DiskImage -ImagePath $isoPath -StorageType ISO -PassThru - # Refresh the DiskImage object so we can get the real information about it. I assume this is a bug. - $openIso = Get-DiskImage -ImagePath $isoPath - $driveLetter = ($openIso | Get-Volume).DriveLetter - - $SourcePath = "$($driveLetter):\sources\install.wim" - - # Check to see if there's a WIM file we can muck about with. - Write-W2VInfo "Looking for $($SourcePath)..." - if (!(Test-Path $SourcePath)) - { - throw "The specified ISO does not appear to be valid Windows installation media." - } - } - - # Check to see if the WIM is local, or on a network location. If the latter, copy it locally. - if (Test-IsNetworkLocation $SourcePath) - { - Write-W2VInfo "Copying WIM $(Split-Path $SourcePath -Leaf) to temp folder..." - robocopy $(Split-Path $SourcePath -Parent) $TempDirectory $(Split-Path $SourcePath -Leaf) | Out-Null - $SourcePath = "$($TempDirectory)\$(Split-Path $SourcePath -Leaf)" - - $tempSource = $SourcePath - } - - $SourcePath = (Resolve-Path $SourcePath).Path - - #################################################################################################### - # QUERY WIM INFORMATION AND EXTRACT THE INDEX OF TARGETED IMAGE - #################################################################################################### - - Write-W2VInfo "Looking for the requested Windows image in the WIM file" - $WindowsImage = Get-WindowsImage -ImagePath $SourcePath - - if (-not $WindowsImage -or ($WindowsImage -is [System.Array])) - { - # - # WIM may have multiple images. Filter on Edition (can be index or name) and try to find a unique image - # - $EditionIndex = 0; - if ([Int32]::TryParse($Edition, [ref]$EditionIndex)) - { - $WindowsImage = Get-WindowsImage -ImagePath $SourcePath -Index $EditionIndex - } - else - { - $WindowsImage = Get-WindowsImage -ImagePath $SourcePath | Where-Object {$_.ImageName -ilike "*$($Edition)"} - } - - if (-not $WindowsImage) - { - throw "Requested windows Image was not found on the WIM file!" - } - if ($WindowsImage -is [System.Array]) - { - Write-W2VInfo "WIM file has the following $($WindowsImage.Count) images that match filter *$($Edition)" - Get-WindowsImage -ImagePath $SourcePath - - Write-W2VError "You must specify an Edition or SKU index, since the WIM has more than one image." - throw "There are more than one images that match ImageName filter *$($Edition)" - } - } - - $ImageIndex = $WindowsImage[0].ImageIndex - - # We're good. Open the WIM container. - # NOTE: this is only required because we want to get the XML-based meta-data at the end. Is there a better way? - # If we can get this information from DISM cmdlets, we can remove the openWim constructs - $openWim = New-Object WIM2VHD.WimFile $SourcePath - - $openImage = $openWim[[Int32]$ImageIndex] - - if ($null -eq $openImage) - { - Write-W2VError "The specified edition does not appear to exist in the specified WIM." - Write-W2VError "Valid edition names are:" - $openWim.Images | %{ Write-W2VError " $($_.ImageFlags)" } - throw - } - - Write-W2VInfo "Image $($openImage.ImageIndex) selected ($($openImage.ImageFlags))..." - - # Check to make sure that the image we're applying is Windows 7 or greater. - if ($openImage.ImageVersion -lt $lowestSupportedVersion) - { - if ($openImage.ImageVersion -eq "0.0.0.0") - { - Write-W2VWarn "The specified WIM does not encode the Windows version." - } - else - { - throw "Convert-WindowsImage only supports Windows 7 and Windows 8 WIM files. The specified image (version $($openImage.ImageVersion)) does not appear to contain one of those operating systems." - } - } - - if ($hyperVEnabled) - { - Write-W2VInfo "Creating sparse disk..." - $newVhd = New-VHD -Path $VHDPath -SizeBytes $SizeBytes -BlockSizeBytes $BlockSizeBytes -Dynamic - - Write-W2VInfo "Mounting $VHDFormat..." - $disk = $newVhd | Mount-VHD -PassThru | Get-Disk - } - else - { - <# - Create the VHD using the VirtDisk Win32 API. - So, why not use the New-VHD cmdlet here? - - New-VHD depends on the Hyper-V Cmdlets, which aren't installed by default. - Installing those cmdlets isn't a big deal, but they depend on the Hyper-V WMI - APIs, which in turn depend on Hyper-V. In order to prevent Convert-WindowsImage - from being dependent on Hyper-V (and thus, x64 systems only), we're using the - VirtDisk APIs directly. - #> - - Write-W2VInfo "Creating sparse disk..." - [WIM2VHD.VirtualHardDisk]::CreateSparseDisk( - $VHDFormat, - $VHDPath, - $SizeBytes, - $true - ) - - # Attach the VHD.\ - Write-W2VInfo "Attaching $VHDFormat..." - $disk = Mount-DiskImage -ImagePath $VHDPath -PassThru | Get-DiskImage | Get-Disk - } - - switch ($DiskLayout) - { - "BIOS" - { - Write-W2VInfo "Initializing disk..." - Initialize-Disk -Number $disk.Number -PartitionStyle MBR - - # - # Create the Windows/system partition - # - Write-W2VInfo "Creating single partition..." - $systemPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -MbrType IFS -IsActive - $windowsPartition = $systemPartition - - Write-W2VInfo "Formatting windows volume..." - $systemVolume = Format-Volume -Partition $systemPartition -FileSystem NTFS -Force -Confirm:$false - $windowsVolume = $systemVolume - } - - "UEFI" - { - Write-W2VInfo "Initializing disk..." - Initialize-Disk -Number $disk.Number -PartitionStyle GPT - - if ((Get-WindowsBuildNumber) -ge 10240) - { - # - # Create the system partition. Create a data partition so we can format it, then change to ESP - # - Write-W2VInfo "Creating EFI system partition..." - $systemPartition = New-Partition -DiskNumber $disk.Number -Size 200MB -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' - - Write-W2VInfo "Formatting system volume..." - $systemVolume = Format-Volume -Partition $systemPartition -FileSystem FAT32 -Force -Confirm:$false - - Write-W2VInfo "Setting system partition as ESP..." - $systemPartition | Set-Partition -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' - $systemPartition | Add-PartitionAccessPath -AssignDriveLetter - } - else - { - # - # Create the system partition - # - Write-W2VInfo "Creating EFI system partition (ESP)..." - $systemPartition = New-Partition -DiskNumber $disk.Number -Size 200MB -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}' -AssignDriveLetter - - Write-W2VInfo "Formatting ESP..." - $formatArgs = @( - "$($systemPartition.DriveLetter):", # Partition drive letter - "/FS:FAT32", # File system - "/Q", # Quick format - "/Y" # Suppress prompt - ) - - Run-Executable -Executable format -Arguments $formatArgs - } - - # - # Create the reserved partition - # - Write-W2VInfo "Creating MSR partition..." - $reservedPartition = New-Partition -DiskNumber $disk.Number -Size 128MB -GptType '{e3c9e316-0b5c-4db8-817d-f92df00215ae}' - - # - # Create the Windows partition - # - Write-W2VInfo "Creating windows partition..." - $windowsPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' - - Write-W2VInfo "Formatting windows volume..." - $windowsVolume = Format-Volume -Partition $windowsPartition -FileSystem NTFS -Force -Confirm:$false - } - - "WindowsToGo" - { - Write-W2VInfo "Initializing disk..." - Initialize-Disk -Number $disk.Number -PartitionStyle MBR - - # - # Create the system partition - # - Write-W2VInfo "Creating system partition..." - $systemPartition = New-Partition -DiskNumber $disk.Number -Size 350MB -MbrType FAT32 -IsActive - - Write-W2VInfo "Formatting system volume..." - $systemVolume = Format-Volume -Partition $systemPartition -FileSystem FAT32 -Force -Confirm:$false - - # - # Create the Windows partition - # - Write-W2VInfo "Creating windows partition..." - $windowsPartition = New-Partition -DiskNumber $disk.Number -UseMaximumSize -MbrType IFS - - Write-W2VInfo "Formatting windows volume..." - $windowsVolume = Format-Volume -Partition $windowsPartition -FileSystem NTFS -Force -Confirm:$false - } - } - - # - # Assign drive letter to Windows partition. This is required for bcdboot - # - $windowsPartition | Add-PartitionAccessPath -AssignDriveLetter - $windowsDrive = $(Get-Partition -Volume $windowsVolume).AccessPaths[0].substring(0,2) - Write-W2VInfo "Windows path ($windowsDrive) has been assigned." - - # - # Refresh access paths (we have now formatted the volume) - # - $systemPartition = $systemPartition | Get-Partition - $systemDrive = $systemPartition.AccessPaths[0].trimend("\").replace("\?", "??") - Write-W2VInfo "System volume location: $systemDrive" - - #################################################################################################### - # APPLY IMAGE FROM WIM TO THE NEW VHD - #################################################################################################### - - Write-W2VInfo "Applying image to $VHDFormat. This could take a while..." - if ((Get-Command Expand-WindowsImage -ErrorAction SilentlyContinue) -and ((-not $ApplyEA) -and ([System.String]::IsNullOrEmpty($DismPath)))) - { - Expand-WindowsImage -ApplyPath $windowsDrive -ImagePath $SourcePath -Index $ImageIndex -LogPath "$($logFolder)\DismLogs.log" | Out-Null - } - else - { - if (![System.String]::IsNullOrEmpty($DismPath)) - { - $dismPath = $DismPath - } - else - { - $dismPath = $(Join-Path (get-item env:\windir).value "system32\dism.exe") - } - - $applyImage = "/Apply-Image" - if ($ApplyEA) - { - $applyImage = $applyImage + " /EA" - } - - $dismArgs = @("$applyImage /ImageFile:`"$SourcePath`" /Index:$ImageIndex /ApplyDir:$windowsDrive /LogPath:`"$($logFolder)\DismLogs.log`"") - Write-W2VInfo "Applying image: $dismPath $dismArgs" - $process = Start-Process -Passthru -Wait -NoNewWindow -FilePath $dismPath ` - -ArgumentList $dismArgs ` - - if ($process.ExitCode -ne 0) - { - throw "Image Apply failed! See DismImageApply logs for details" - } - } - Write-W2VInfo "Image was applied successfully. " - - # - # Here we copy in the unattend file (if specified by the command line) - # - if (![System.String]::IsNullOrEmpty($UnattendPath)) - { - Write-W2VInfo "Applying unattend file ($(Split-Path $UnattendPath -Leaf))..." - Copy-Item -Path $UnattendPath -Destination (Join-Path $windowsDrive "unattend.xml") -Force - } - - if (![System.String]::IsNullOrEmpty($MergeFolderPath)) - { - Write-W2VInfo "Applying merge folder ($MergeFolderPath)..." - Copy-Item -Recurse -Path (Join-Path $MergeFolderPath "*") -Destination $windowsDrive -Force #added to handle merge folders - } - - if (($openImage.ImageArchitecture -ne "ARM") -and # No virtualization platform for ARM images - ($openImage.ImageArchitecture -ne "ARM64") -and # No virtualization platform for ARM64 images - ($BCDinVHD -ne "NativeBoot")) # User asked for a non-bootable image - { - if (Test-Path "$($systemDrive)\boot\bcd") - { - Write-W2VInfo "Image already has BIOS BCD store..." - } - elseif (Test-Path "$($systemDrive)\efi\microsoft\boot\bcd") - { - Write-W2VInfo "Image already has EFI BCD store..." - } - else - { - Write-W2VInfo "Making image bootable..." - $bcdBootArgs = @( - "$($windowsDrive)\Windows", # Path to the \Windows on the VHD - "/s $systemDrive", # Specifies the volume letter of the drive to create the \BOOT folder on. - "/v" # Enabled verbose logging. - ) - - switch ($DiskLayout) - { - "BIOS" - { - $bcdBootArgs += "/f BIOS" # Specifies the firmware type of the target system partition - } - - "UEFI" - { - $bcdBootArgs += "/f UEFI" # Specifies the firmware type of the target system partition - } - - "WindowsToGo" - { - # Create entries for both UEFI and BIOS if possible - if (Test-Path "$($windowsDrive)\Windows\boot\EFI\bootmgfw.efi") - { - $bcdBootArgs += "/f ALL" - } - } - } - - Run-Executable -Executable $BCDBoot -Arguments $bcdBootArgs - - # The following is added to mitigate the VMM diff disk handling - # We're going to change from MBRBootOption to LocateBootOption. - - if ($DiskLayout -eq "BIOS") - { - Write-W2VInfo "Fixing the Device ID in the BCD store on $($VHDFormat)..." - Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( - "/store $($systemDrive)\boot\bcd", - "/set `{bootmgr`} device locate" - ) - Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( - "/store $($systemDrive)\boot\bcd", - "/set `{default`} device locate" - ) - Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( - "/store $($systemDrive)\boot\bcd", - "/set `{default`} osdevice locate" - ) - } - } - - Write-W2VInfo "Drive is bootable. Cleaning up..." - - # Are we turning the debugger on? - if ($EnableDebugger -inotlike "None") - { - $bcdEditArgs = $null; - - # Configure the specified debugging transport and other settings. - switch ($EnableDebugger) - { - "Serial" - { - $bcdEditArgs = @( - "/dbgsettings SERIAL", - "DEBUGPORT:$($ComPort.Value)", - "BAUDRATE:$($BaudRate.Value)" - ) - } - - "1394" - { - $bcdEditArgs = @( - "/dbgsettings 1394", - "CHANNEL:$($Channel.Value)" - ) - } - - "USB" - { - $bcdEditArgs = @( - "/dbgsettings USB", - "TARGETNAME:$($Target.Value)" - ) - } - - "Local" - { - $bcdEditArgs = @( - "/dbgsettings LOCAL" - ) - } - - "Network" - { - $bcdEditArgs = @( - "/dbgsettings NET", - "HOSTIP:$($IP.Value)", - "PORT:$($Port.Value)", - "KEY:$($Key.Value)" - ) - } - } - - $bcdStores = @( - "$($systemDrive)\boot\bcd", - "$($systemDrive)\efi\microsoft\boot\bcd" - ) - - foreach ($bcdStore in $bcdStores) - { - if (Test-Path $bcdStore) - { - Write-W2VInfo "Turning kernel debugging on in the $($VHDFormat) for $($bcdStore)..." - Run-Executable -Executable "BCDEDIT.EXE" -Arguments ( - "/store $($bcdStore)", - "/set `{default`} debug on" - ) - - $bcdEditArguments = @("/store $($bcdStore)") + $bcdEditArgs - - Run-Executable -Executable "BCDEDIT.EXE" -Arguments $bcdEditArguments - } - } - } - } - else - { - # Don't bother to check on debugging. We can't boot WoA VHDs in VMs, and - # if we're native booting, the changes need to be made to the BCD store on the - # physical computer's boot volume. - - Write-W2VInfo "Image applied. It is not bootable." - } - - if ($RemoteDesktopEnable -or (-not $ExpandOnNativeBoot)) - { - $hive = Mount-RegistryHive -Hive (Join-Path $windowsDrive "Windows\System32\Config\System") - - if ($RemoteDesktopEnable) - { - Write-W2VInfo -text "Enabling Remote Desktop" - Set-ItemProperty -Path "HKLM:\$($hive)\ControlSet001\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 - } - - if (-not $ExpandOnNativeBoot) - { - Write-W2VInfo -text "Disabling automatic $VHDFormat expansion for Native Boot" - Set-ItemProperty -Path "HKLM:\$($hive)\ControlSet001\Services\FsDepends\Parameters" -Name "VirtualDiskExpandOnMount" -Value 4 - } - - Dismount-RegistryHive -HiveMountPoint $hive - } - - if ($Driver) - { - Write-W2VInfo -text "Adding Windows Drivers to the Image" - $Driver | ForEach-Object -Process { - Write-W2VInfo -text "Driver path: $PSItem" - Add-WindowsDriver -Path $windowsDrive -Recurse -Driver $PSItem -Verbose | Out-Null - } - } - - if ($Feature) - { - Write-W2VInfo -text "Installing Windows Feature(s) $Feature to the Image" - $FeatureSourcePath = Join-Path -Path "$($driveLetter):" -ChildPath "sources\sxs" - Write-W2VInfo -text "From $FeatureSourcePath" - Enable-WindowsOptionalFeature -FeatureName $Feature -Source $FeatureSourcePath -Path $windowsDrive -All | Out-Null - } - - if ($Package) - { - Write-W2VInfo -text "Adding Windows Packages to the Image" - - $Package | ForEach-Object -Process { - Write-W2VInfo -text "Package path: $PSItem" - Add-WindowsPackage -Path $windowsDrive -PackagePath $PSItem | Out-Null - } - } - - # - # Remove system partition access path, if necessary - # - if ($DiskLayout -eq "UEFI") - { - $systemPartition | Remove-PartitionAccessPath -AccessPath $systemPartition.AccessPaths[0] - } - - if ([System.String]::IsNullOrEmpty($vhdFinalName)) - { - # We need to generate a file name. - Write-W2VInfo "Generating name for $($VHDFormat)..." - $hive = Mount-RegistryHive -Hive (Join-Path $windowsDrive "Windows\System32\Config\Software") - - $buildLabEx = (Get-ItemProperty "HKLM:\$($hive)\Microsoft\Windows NT\CurrentVersion").BuildLabEx - $installType = (Get-ItemProperty "HKLM:\$($hive)\Microsoft\Windows NT\CurrentVersion").InstallationType - $editionId = (Get-ItemProperty "HKLM:\$($hive)\Microsoft\Windows NT\CurrentVersion").EditionID - $skuFamily = $null - - Dismount-RegistryHive -HiveMountPoint $hive - - # Is this ServerCore? - # Since we're only doing this string comparison against the InstallType key, we won't get - # false positives with the Core SKU. - if ($installType.ToUpper().Contains("CORE")) - { - $editionId += "Core" - } - - # What type of SKU are we? - if ($installType.ToUpper().Contains("SERVER")) - { - $skuFamily = "Server" - } - elseif ($installType.ToUpper().Contains("CLIENT")) - { - $skuFamily = "Client" - } - else - { - $skuFamily = "Unknown" - } - - # - # ISSUE - do we want VL here? - # - $vhdFinalName = "$($buildLabEx)_$($skuFamily)_$($editionId)_$($openImage.ImageDefaultLanguage).$($VHDFormat.ToLower())" - Write-W2VTrace "$VHDFormat final name is : $vhdFinalName" - } - - if ($hyperVEnabled) - { - Write-W2VInfo "Dismounting $VHDFormat..." - Dismount-VHD -Path $VHDPath - } - else - { - Write-W2VInfo "Closing $VHDFormat..." - Dismount-DiskImage -ImagePath $VHDPath - } - - $vhdFinalPath = Join-Path (Split-Path $VHDPath -Parent) $vhdFinalName - Write-W2VTrace "$VHDFormat final path is : $vhdFinalPath" - - if (Test-Path $vhdFinalPath) - { - Write-W2VInfo "Deleting pre-existing $VHDFormat : $(Split-Path $vhdFinalPath -Leaf)..." - Remove-Item -Path $vhdFinalPath -Force - } - - Write-W2VTrace -Text "Renaming $VHDFormat at $VHDPath to $vhdFinalName" - Rename-Item -Path (Resolve-Path $VHDPath).Path -NewName $vhdFinalName -Force - $vhd += Get-DiskImage -ImagePath $vhdFinalPath - - $vhdFinalName = $null - } - catch - { - Write-W2VError $_ - Write-W2VInfo "Log folder is $logFolder" - } - finally - { - # If we still have a WIM image open, close it. - if ($openWim -ne $null) - { - Write-W2VInfo "Closing Windows image..." - $openWim.Close() - } - - # If we still have a registry hive mounted, dismount it. - if ($mountedHive -ne $null) - { - Write-W2VInfo "Closing registry hive..." - Dismount-RegistryHive -HiveMountPoint $mountedHive - } - - # If VHD is mounted, unmount it - if (Test-Path $VHDPath) - { - if ($hyperVEnabled) - { - if ((Get-VHD -Path $VHDPath).Attached) - { - Dismount-VHD -Path $VHDPath - } - } - else - { - Dismount-DiskImage -ImagePath $VHDPath - } - } - - # If we still have an ISO open, close it. - if ($openIso -ne $null) - { - Write-W2VInfo "Closing ISO..." - Dismount-DiskImage $ISOPath - } - - if (-not $CacheSource) - { - if ($tempSource -and (Test-Path $tempSource)) - { - Remove-Item -Path $tempSource -Force - } - } - - # Close out the transcript and tell the user we're done. - Write-W2VInfo "Done." - if ($transcripting) - { - $null = Stop-Transcript - } - } - } - - End - { - if ($Passthru) - { - return $vhd - } - } - #endregion Code - -} - - -function -Add-WindowsImageTypes -{ - $code = @" -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Xml.Linq; -using System.Xml.XPath; -using Microsoft.Win32.SafeHandles; - -namespace WIM2VHD -{ - -/// <summary> -/// P/Invoke methods and associated enums, flags, and structs. -/// </summary> -public class -NativeMethods -{ - - #region Delegates and Callbacks - #region WIMGAPI - - ///<summary> - ///User-defined function used with the RegisterMessageCallback or UnregisterMessageCallback function. - ///</summary> - ///<param name="MessageId">Specifies the message being sent.</param> - ///<param name="wParam">Specifies additional message information. The contents of this parameter depend on the value of the - ///MessageId parameter.</param> - ///<param name="lParam">Specifies additional message information. The contents of this parameter depend on the value of the - ///MessageId parameter.</param> - ///<param name="UserData">Specifies the user-defined value passed to RegisterCallback.</param> - ///<returns> - ///To indicate success and to enable other subscribers to process the message return WIM_MSG_SUCCESS. - ///To prevent other subscribers from receiving the message, return WIM_MSG_DONE. - ///To cancel an image apply or capture, return WIM_MSG_ABORT_IMAGE when handling the WIM_MSG_PROCESS message. - ///</returns> - public delegate uint - WimMessageCallback( - uint MessageId, - IntPtr wParam, - IntPtr lParam, - IntPtr UserData - ); - - public static void - RegisterMessageCallback( - WimFileHandle hWim, - WimMessageCallback callback) - { - - uint _callback = NativeMethods.WimRegisterMessageCallback(hWim, callback, IntPtr.Zero); - int rc = Marshal.GetLastWin32Error(); - if (0 != rc) - { - // Throw an exception if something bad happened on the Win32 end. - throw - new InvalidOperationException( - string.Format( - CultureInfo.CurrentCulture, - "Unable to register message callback." - )); - } - } - - public static void - UnregisterMessageCallback( - WimFileHandle hWim, - WimMessageCallback registeredCallback) - { - - bool status = NativeMethods.WimUnregisterMessageCallback(hWim, registeredCallback); - int rc = Marshal.GetLastWin32Error(); - if (!status) - { - throw - new InvalidOperationException( - string.Format( - CultureInfo.CurrentCulture, - "Unable to unregister message callback." - )); - } - } - - #endregion WIMGAPI - #endregion Delegates and Callbacks - - #region Constants - - #region VDiskInterop - - /// <summary> - /// The default depth in a VHD parent chain that this library will search through. - /// If you want to go more than one disk deep into the parent chain, provide a different value. - /// </summary> - public const uint OPEN_VIRTUAL_DISK_RW_DEFAULT_DEPTH = 0x00000001; - - public const uint DEFAULT_BLOCK_SIZE = 0x00080000; - public const uint DISK_SECTOR_SIZE = 0x00000200; - - internal const uint ERROR_VIRTDISK_NOT_VIRTUAL_DISK = 0xC03A0015; - internal const uint ERROR_NOT_FOUND = 0x00000490; - internal const uint ERROR_IO_PENDING = 0x000003E5; - internal const uint ERROR_INSUFFICIENT_BUFFER = 0x0000007A; - internal const uint ERROR_ERROR_DEV_NOT_EXIST = 0x00000037; - internal const uint ERROR_BAD_COMMAND = 0x00000016; - internal const uint ERROR_SUCCESS = 0x00000000; - - public const uint GENERIC_READ = 0x80000000; - public const uint GENERIC_WRITE = 0x40000000; - public const short FILE_ATTRIBUTE_NORMAL = 0x00000080; - public const uint CREATE_NEW = 0x00000001; - public const uint CREATE_ALWAYS = 0x00000002; - public const uint OPEN_EXISTING = 0x00000003; - public const short INVALID_HANDLE_VALUE = -1; - - internal static Guid VirtualStorageTypeVendorUnknown = new Guid("00000000-0000-0000-0000-000000000000"); - internal static Guid VirtualStorageTypeVendorMicrosoft = new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B"); - - #endregion VDiskInterop - - #region WIMGAPI - - public const uint WIM_FLAG_VERIFY = 0x00000002; - public const uint WIM_FLAG_INDEX = 0x00000004; - - public const uint WM_APP = 0x00008000; - - #endregion WIMGAPI - - #endregion Constants - - #region Enums and Flags - - #region VDiskInterop - - /// <summary> - /// Indicates the version of the virtual disk to create. - /// </summary> - public enum CreateVirtualDiskVersion : int - { - VersionUnspecified = 0x00000000, - Version1 = 0x00000001, - Version2 = 0x00000002 - } - - public enum OpenVirtualDiskVersion : int - { - VersionUnspecified = 0x00000000, - Version1 = 0x00000001, - Version2 = 0x00000002 - } - - /// <summary> - /// Contains the version of the virtual hard disk (VHD) ATTACH_VIRTUAL_DISK_PARAMETERS structure to use in calls to VHD functions. - /// </summary> - public enum AttachVirtualDiskVersion : int - { - VersionUnspecified = 0x00000000, - Version1 = 0x00000001, - Version2 = 0x00000002 - } - - public enum CompactVirtualDiskVersion : int - { - VersionUnspecified = 0x00000000, - Version1 = 0x00000001 - } - - /// <summary> - /// Contains the type and provider (vendor) of the virtual storage device. - /// </summary> - public enum VirtualStorageDeviceType : int - { - /// <summary> - /// The storage type is unknown or not valid. - /// </summary> - Unknown = 0x00000000, - /// <summary> - /// For internal use only. This type is not supported. - /// </summary> - ISO = 0x00000001, - /// <summary> - /// Virtual Hard Disk device type. - /// </summary> - VHD = 0x00000002, - /// <summary> - /// Virtual Hard Disk v2 device type. - /// </summary> - VHDX = 0x00000003 - } - - /// <summary> - /// Contains virtual hard disk (VHD) open request flags. - /// </summary> - [Flags] - public enum OpenVirtualDiskFlags - { - /// <summary> - /// No flags. Use system defaults. - /// </summary> - None = 0x00000000, - /// <summary> - /// Open the VHD file (backing store) without opening any differencing-chain parents. Used to correct broken parent links. - /// </summary> - NoParents = 0x00000001, - /// <summary> - /// Reserved. - /// </summary> - BlankFile = 0x00000002, - /// <summary> - /// Reserved. - /// </summary> - BootDrive = 0x00000004, - } - - /// <summary> - /// Contains the bit mask for specifying access rights to a virtual hard disk (VHD). - /// </summary> - [Flags] - public enum VirtualDiskAccessMask - { - /// <summary> - /// Only Version2 of OpenVirtualDisk API accepts this parameter - /// </summary> - None = 0x00000000, - /// <summary> - /// Open the virtual disk for read-only attach access. The caller must have READ access to the virtual disk image file. - /// </summary> - /// <remarks> - /// If used in a request to open a virtual disk that is already open, the other handles must be limited to either - /// VIRTUAL_DISK_ACCESS_DETACH or VIRTUAL_DISK_ACCESS_GET_INFO access, otherwise the open request with this flag will fail. - /// </remarks> - AttachReadOnly = 0x00010000, - /// <summary> - /// Open the virtual disk for read-write attaching access. The caller must have (READ | WRITE) access to the virtual disk image file. - /// </summary> - /// <remarks> - /// If used in a request to open a virtual disk that is already open, the other handles must be limited to either - /// VIRTUAL_DISK_ACCESS_DETACH or VIRTUAL_DISK_ACCESS_GET_INFO access, otherwise the open request with this flag will fail. - /// If the virtual disk is part of a differencing chain, the disk for this request cannot be less than the readWriteDepth specified - /// during the prior open request for that differencing chain. - /// </remarks> - AttachReadWrite = 0x00020000, - /// <summary> - /// Open the virtual disk to allow detaching of an attached virtual disk. The caller must have - /// (FILE_READ_ATTRIBUTES | FILE_READ_DATA) access to the virtual disk image file. - /// </summary> - Detach = 0x00040000, - /// <summary> - /// Information retrieval access to the virtual disk. The caller must have READ access to the virtual disk image file. - /// </summary> - GetInfo = 0x00080000, - /// <summary> - /// Virtual disk creation access. - /// </summary> - Create = 0x00100000, - /// <summary> - /// Open the virtual disk to perform offline meta-operations. The caller must have (READ | WRITE) access to the virtual - /// disk image file, up to readWriteDepth if working with a differencing chain. - /// </summary> - /// <remarks> - /// If the virtual disk is part of a differencing chain, the backing store (host volume) is opened in RW exclusive mode up to readWriteDepth. - /// </remarks> - MetaOperations = 0x00200000, - /// <summary> - /// Reserved. - /// </summary> - Read = 0x000D0000, - /// <summary> - /// Allows unrestricted access to the virtual disk. The caller must have unrestricted access rights to the virtual disk image file. - /// </summary> - All = 0x003F0000, - /// <summary> - /// Reserved. - /// </summary> - Writable = 0x00320000 - } - - /// <summary> - /// Contains virtual hard disk (VHD) creation flags. - /// </summary> - [Flags] - public enum CreateVirtualDiskFlags - { - /// <summary> - /// Contains virtual hard disk (VHD) creation flags. - /// </summary> - None = 0x00000000, - /// <summary> - /// Pre-allocate all physical space necessary for the size of the virtual disk. - /// </summary> - /// <remarks> - /// The CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION flag is used for the creation of a fixed VHD. - /// </remarks> - FullPhysicalAllocation = 0x00000001 - } - - /// <summary> - /// Contains virtual disk attach request flags. - /// </summary> - [Flags] - public enum AttachVirtualDiskFlags - { - /// <summary> - /// No flags. Use system defaults. - /// </summary> - None = 0x00000000, - /// <summary> - /// Attach the virtual disk as read-only. - /// </summary> - ReadOnly = 0x00000001, - /// <summary> - /// No drive letters are assigned to the disk's volumes. - /// </summary> - /// <remarks>Oddly enough, this doesn't apply to NTFS mount points.</remarks> - NoDriveLetter = 0x00000002, - /// <summary> - /// Will decouple the virtual disk lifetime from that of the VirtualDiskHandle. - /// The virtual disk will be attached until the Detach() function is called, even if all open handles to the virtual disk are closed. - /// </summary> - PermanentLifetime = 0x00000004, - /// <summary> - /// Reserved. - /// </summary> - NoLocalHost = 0x00000008 - } - - [Flags] - public enum DetachVirtualDiskFlag - { - None = 0x00000000 - } - - [Flags] - public enum CompactVirtualDiskFlags - { - None = 0x00000000, - NoZeroScan = 0x00000001, - NoBlockMoves = 0x00000002 - } - - #endregion VDiskInterop - - #region WIMGAPI - - [FlagsAttribute] - internal enum - WimCreateFileDesiredAccess : uint - { - WimQuery = 0x00000000, - WimGenericRead = 0x80000000 - } - - public enum WimMessage : uint - { - WIM_MSG = WM_APP + 0x1476, - WIM_MSG_TEXT, - ///<summary> - ///Indicates an update in the progress of an image application. - ///</summary> - WIM_MSG_PROGRESS, - ///<summary> - ///Enables the caller to prevent a file or a directory from being captured or applied. - ///</summary> - WIM_MSG_PROCESS, - ///<summary> - ///Indicates that volume information is being gathered during an image capture. - ///</summary> - WIM_MSG_SCANNING, - ///<summary> - ///Indicates the number of files that will be captured or applied. - ///</summary> - WIM_MSG_SETRANGE, - ///<summary> - ///Indicates the number of files that have been captured or applied. - ///</summary> - WIM_MSG_SETPOS, - ///<summary> - ///Indicates that a file has been either captured or applied. - ///</summary> - WIM_MSG_STEPIT, - ///<summary> - ///Enables the caller to prevent a file resource from being compressed during a capture. - ///</summary> - WIM_MSG_COMPRESS, - ///<summary> - ///Alerts the caller that an error has occurred while capturing or applying an image. - ///</summary> - WIM_MSG_ERROR, - ///<summary> - ///Enables the caller to align a file resource on a particular alignment boundary. - ///</summary> - WIM_MSG_ALIGNMENT, - WIM_MSG_RETRY, - ///<summary> - ///Enables the caller to align a file resource on a particular alignment boundary. - ///</summary> - WIM_MSG_SPLIT, - WIM_MSG_SUCCESS = 0x00000000, - WIM_MSG_ABORT_IMAGE = 0xFFFFFFFF - } - - internal enum - WimCreationDisposition : uint - { - WimOpenExisting = 0x00000003, - } - - internal enum - WimActionFlags : uint - { - WimIgnored = 0x00000000 - } - - internal enum - WimCompressionType : uint - { - WimIgnored = 0x00000000 - } - - internal enum - WimCreationResult : uint - { - WimCreatedNew = 0x00000000, - WimOpenedExisting = 0x00000001 - } - - #endregion WIMGAPI - - #endregion Enums and Flags - - #region Structs - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct CreateVirtualDiskParameters - { - /// <summary> - /// A CREATE_VIRTUAL_DISK_VERSION enumeration that specifies the version of the CREATE_VIRTUAL_DISK_PARAMETERS structure being passed to or from the virtual hard disk (VHD) functions. - /// </summary> - public CreateVirtualDiskVersion Version; - - /// <summary> - /// Unique identifier to assign to the virtual disk object. If this member is set to zero, a unique identifier is created by the system. - /// </summary> - public Guid UniqueId; - - /// <summary> - /// The maximum virtual size of the virtual disk object. Must be a multiple of 512. - /// If a ParentPath is specified, this value must be zero. - /// If a SourcePath is specified, this value can be zero to specify the size of the source VHD to be used, otherwise the size specified must be greater than or equal to the size of the source disk. - /// </summary> - public ulong MaximumSize; - - /// <summary> - /// Internal size of the virtual disk object blocks. - /// The following are predefined block sizes and their behaviors. For a fixed VHD type, this parameter must be zero. - /// </summary> - public uint BlockSizeInBytes; - - /// <summary> - /// Internal size of the virtual disk object sectors. Must be set to 512. - /// </summary> - public uint SectorSizeInBytes; - - /// <summary> - /// Optional path to a parent virtual disk object. Associates the new virtual disk with an existing virtual disk. - /// If this parameter is not NULL, SourcePath must be NULL. - /// </summary> - public string ParentPath; - - /// <summary> - /// Optional path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a VHD or a physical disk. - /// If this parameter is not NULL, ParentPath must be NULL. - /// </summary> - public string SourcePath; - - /// <summary> - /// Flags for opening the VHD - /// </summary> - public OpenVirtualDiskFlags OpenFlags; - - /// <summary> - /// GetInfoOnly flag for V2 handles - /// </summary> - public bool GetInfoOnly; - - /// <summary> - /// Virtual Storage Type of the parent disk - /// </summary> - public VirtualStorageType ParentVirtualStorageType; - - /// <summary> - /// Virtual Storage Type of the source disk - /// </summary> - public VirtualStorageType SourceVirtualStorageType; - - /// <summary> - /// A GUID to use for fallback resiliency over SMB. - /// </summary> - public Guid ResiliencyGuid; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct VirtualStorageType - { - public VirtualStorageDeviceType DeviceId; - public Guid VendorId; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct SecurityDescriptor - { - public byte revision; - public byte size; - public short control; - public IntPtr owner; - public IntPtr group; - public IntPtr sacl; - public IntPtr dacl; - } - - #endregion Structs - - #region VirtDisk.DLL P/Invoke - - [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)] - public static extern uint - CreateVirtualDisk( - [In, Out] ref VirtualStorageType VirtualStorageType, - [In] string Path, - [In] VirtualDiskAccessMask VirtualDiskAccessMask, - [In, Out] ref SecurityDescriptor SecurityDescriptor, - [In] CreateVirtualDiskFlags Flags, - [In] uint ProviderSpecificFlags, - [In, Out] ref CreateVirtualDiskParameters Parameters, - [In] IntPtr Overlapped, - [Out] out SafeFileHandle Handle); - - #endregion VirtDisk.DLL P/Invoke - - #region Win32 P/Invoke - - [DllImport("advapi32", SetLastError = true)] - public static extern bool InitializeSecurityDescriptor( - [Out] out SecurityDescriptor pSecurityDescriptor, - [In] uint dwRevision); - - #endregion Win32 P/Invoke - - #region WIMGAPI P/Invoke - - #region SafeHandle wrappers for WimFileHandle and WimImageHandle - - public sealed class WimFileHandle : SafeHandle - { - - public WimFileHandle( - string wimPath) - : base(IntPtr.Zero, true) - { - - if (String.IsNullOrEmpty(wimPath)) - { - throw new ArgumentNullException("wimPath"); - } - - if (!File.Exists(Path.GetFullPath(wimPath))) - { - throw new FileNotFoundException((new FileNotFoundException()).Message, wimPath); - } - - NativeMethods.WimCreationResult creationResult; - - this.handle = NativeMethods.WimCreateFile( - wimPath, - NativeMethods.WimCreateFileDesiredAccess.WimGenericRead, - NativeMethods.WimCreationDisposition.WimOpenExisting, - NativeMethods.WimActionFlags.WimIgnored, - NativeMethods.WimCompressionType.WimIgnored, - out creationResult - ); - - // Check results. - if (creationResult != NativeMethods.WimCreationResult.WimOpenedExisting) - { - throw new Win32Exception(); - } - - if (this.handle == IntPtr.Zero) - { - throw new Win32Exception(); - } - - // Set the temporary path. - NativeMethods.WimSetTemporaryPath( - this, - Environment.ExpandEnvironmentVariables("%TEMP%") - ); - } - - protected override bool ReleaseHandle() - { - return NativeMethods.WimCloseHandle(this.handle); - } - - public override bool IsInvalid - { - get { return this.handle == IntPtr.Zero; } - } - } - - public sealed class WimImageHandle : SafeHandle - { - public WimImageHandle( - WimFile Container, - uint ImageIndex) - : base(IntPtr.Zero, true) - { - - if (null == Container) - { - throw new ArgumentNullException("Container"); - } - - if ((Container.Handle.IsClosed) || (Container.Handle.IsInvalid)) - { - throw new ArgumentNullException("The handle to the WIM file has already been closed, or is invalid.", "Container"); - } - - if (ImageIndex > Container.ImageCount) - { - throw new ArgumentOutOfRangeException("ImageIndex", "The index does not exist in the specified WIM file."); - } - - this.handle = NativeMethods.WimLoadImage( - Container.Handle.DangerousGetHandle(), - ImageIndex); - } - - protected override bool ReleaseHandle() - { - return NativeMethods.WimCloseHandle(this.handle); - } - - public override bool IsInvalid - { - get { return this.handle == IntPtr.Zero; } - } - } - - #endregion SafeHandle wrappers for WimFileHandle and WimImageHandle - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMCreateFile")] - internal static extern IntPtr - WimCreateFile( - [In, MarshalAs(UnmanagedType.LPWStr)] string WimPath, - [In] WimCreateFileDesiredAccess DesiredAccess, - [In] WimCreationDisposition CreationDisposition, - [In] WimActionFlags FlagsAndAttributes, - [In] WimCompressionType CompressionType, - [Out, Optional] out WimCreationResult CreationResult - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMCloseHandle")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool - WimCloseHandle( - [In] IntPtr Handle - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMLoadImage")] - internal static extern IntPtr - WimLoadImage( - [In] IntPtr Handle, - [In] uint ImageIndex - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMGetImageCount")] - internal static extern uint - WimGetImageCount( - [In] WimFileHandle Handle - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMGetImageInformation")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool - WimGetImageInformation( - [In] SafeHandle Handle, - [Out] out StringBuilder ImageInfo, - [Out] out uint SizeOfImageInfo - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMSetTemporaryPath")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool - WimSetTemporaryPath( - [In] WimFileHandle Handle, - [In] string TempPath - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMRegisterMessageCallback", CallingConvention = CallingConvention.StdCall)] - internal static extern uint - WimRegisterMessageCallback( - [In, Optional] WimFileHandle hWim, - [In] WimMessageCallback MessageProc, - [In, Optional] IntPtr ImageInfo - ); - - [DllImport("Wimgapi.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WIMUnregisterMessageCallback", CallingConvention = CallingConvention.StdCall)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool - WimUnregisterMessageCallback( - [In, Optional] WimFileHandle hWim, - [In] WimMessageCallback MessageProc - ); - - - #endregion WIMGAPI P/Invoke -} - -#region WIM Interop - -public class WimFile -{ - - internal XDocument m_xmlInfo; - internal List<WimImage> m_imageList; - - private static NativeMethods.WimMessageCallback wimMessageCallback; - - #region Events - - /// <summary> - /// DefaultImageEvent handler - /// </summary> - public delegate void DefaultImageEventHandler(object sender, DefaultImageEventArgs e); - - ///<summary> - ///ProcessFileEvent handler - ///</summary> - public delegate void ProcessFileEventHandler(object sender, ProcessFileEventArgs e); - - ///<summary> - ///Enable the caller to prevent a file resource from being compressed during a capture. - ///</summary> - public event ProcessFileEventHandler ProcessFileEvent; - - ///<summary> - ///Indicate an update in the progress of an image application. - ///</summary> - public event DefaultImageEventHandler ProgressEvent; - - ///<summary> - ///Alert the caller that an error has occurred while capturing or applying an image. - ///</summary> - public event DefaultImageEventHandler ErrorEvent; - - ///<summary> - ///Indicate that a file has been either captured or applied. - ///</summary> - public event DefaultImageEventHandler StepItEvent; - - ///<summary> - ///Indicate the number of files that will be captured or applied. - ///</summary> - public event DefaultImageEventHandler SetRangeEvent; - - ///<summary> - ///Indicate the number of files that have been captured or applied. - ///</summary> - public event DefaultImageEventHandler SetPosEvent; - - #endregion Events - - private - enum - ImageEventMessage : uint - { - ///<summary> - ///Enables the caller to prevent a file or a directory from being captured or applied. - ///</summary> - Progress = NativeMethods.WimMessage.WIM_MSG_PROGRESS, - ///<summary> - ///Notification sent to enable the caller to prevent a file or a directory from being captured or applied. - ///To prevent a file or a directory from being captured or applied, call WindowsImageContainer.SkipFile(). - ///</summary> - Process = NativeMethods.WimMessage.WIM_MSG_PROCESS, - ///<summary> - ///Enables the caller to prevent a file resource from being compressed during a capture. - ///</summary> - Compress = NativeMethods.WimMessage.WIM_MSG_COMPRESS, - ///<summary> - ///Alerts the caller that an error has occurred while capturing or applying an image. - ///</summary> - Error = NativeMethods.WimMessage.WIM_MSG_ERROR, - ///<summary> - ///Enables the caller to align a file resource on a particular alignment boundary. - ///</summary> - Alignment = NativeMethods.WimMessage.WIM_MSG_ALIGNMENT, - ///<summary> - ///Enables the caller to align a file resource on a particular alignment boundary. - ///</summary> - Split = NativeMethods.WimMessage.WIM_MSG_SPLIT, - ///<summary> - ///Indicates that volume information is being gathered during an image capture. - ///</summary> - Scanning = NativeMethods.WimMessage.WIM_MSG_SCANNING, - ///<summary> - ///Indicates the number of files that will be captured or applied. - ///</summary> - SetRange = NativeMethods.WimMessage.WIM_MSG_SETRANGE, - ///<summary> - ///Indicates the number of files that have been captured or applied. - /// </summary> - SetPos = NativeMethods.WimMessage.WIM_MSG_SETPOS, - ///<summary> - ///Indicates that a file has been either captured or applied. - ///</summary> - StepIt = NativeMethods.WimMessage.WIM_MSG_STEPIT, - ///<summary> - ///Success. - ///</summary> - Success = NativeMethods.WimMessage.WIM_MSG_SUCCESS, - ///<summary> - ///Abort. - ///</summary> - Abort = NativeMethods.WimMessage.WIM_MSG_ABORT_IMAGE - } - - ///<summary> - ///Event callback to the Wimgapi events - ///</summary> - private - uint - ImageEventMessagePump( - uint MessageId, - IntPtr wParam, - IntPtr lParam, - IntPtr UserData) - { - - uint status = (uint) NativeMethods.WimMessage.WIM_MSG_SUCCESS; - - DefaultImageEventArgs eventArgs = new DefaultImageEventArgs(wParam, lParam, UserData); - - switch ((ImageEventMessage)MessageId) - { - - case ImageEventMessage.Progress: - ProgressEvent(this, eventArgs); - break; - - case ImageEventMessage.Process: - if (null != ProcessFileEvent) - { - string fileToImage = Marshal.PtrToStringUni(wParam); - ProcessFileEventArgs fileToProcess = new ProcessFileEventArgs(fileToImage, lParam); - ProcessFileEvent(this, fileToProcess); - - if (fileToProcess.Abort == true) - { - status = (uint)ImageEventMessage.Abort; - } - } - break; - - case ImageEventMessage.Error: - if (null != ErrorEvent) - { - ErrorEvent(this, eventArgs); - } - break; - - case ImageEventMessage.SetRange: - if (null != SetRangeEvent) - { - SetRangeEvent(this, eventArgs); - } - break; - - case ImageEventMessage.SetPos: - if (null != SetPosEvent) - { - SetPosEvent(this, eventArgs); - } - break; - - case ImageEventMessage.StepIt: - if (null != StepItEvent) - { - StepItEvent(this, eventArgs); - } - break; - - default: - break; - } - return status; - - } - - /// <summary> - /// Constructor. - /// </summary> - /// <param name="wimPath">Path to the WIM container.</param> - public - WimFile(string wimPath) - { - if (string.IsNullOrEmpty(wimPath)) - { - throw new ArgumentNullException("wimPath"); - } - - if (!File.Exists(Path.GetFullPath(wimPath))) - { - throw new FileNotFoundException((new FileNotFoundException()).Message, wimPath); - } - - Handle = new NativeMethods.WimFileHandle(wimPath); - - // Hook up the events before we return. - //wimMessageCallback = new NativeMethods.WimMessageCallback(ImageEventMessagePump); - //NativeMethods.RegisterMessageCallback(this.Handle, wimMessageCallback); - } - - /// <summary> - /// Closes the WIM file. - /// </summary> - public void - Close() - { - foreach (WimImage image in Images) - { - image.Close(); - } - - if (null != wimMessageCallback) - { - NativeMethods.UnregisterMessageCallback(this.Handle, wimMessageCallback); - wimMessageCallback = null; - } - - if ((!Handle.IsClosed) && (!Handle.IsInvalid)) - { - Handle.Close(); - } - } - - /// <summary> - /// Provides a list of WimImage objects, representing the images in the WIM container file. - /// </summary> - public List<WimImage> - Images - { - get - { - if (null == m_imageList) - { - - int imageCount = (int)ImageCount; - m_imageList = new List<WimImage>(imageCount); - for (int i = 0; i < imageCount; i++) - { - - // Load up each image so it's ready for us. - m_imageList.Add( - new WimImage(this, (uint)i + 1)); - } - } - - return m_imageList; - } - } - - /// <summary> - /// Provides a list of names of the images in the specified WIM container file. - /// </summary> - public List<string> - ImageNames - { - get - { - List<string> nameList = new List<string>(); - foreach (WimImage image in Images) - { - nameList.Add(image.ImageName); - } - return nameList; - } - } - - /// <summary> - /// Indexer for WIM images inside the WIM container, indexed by the image number. - /// The list of Images is 0-based, but the WIM container is 1-based, so we automatically compensate for that. - /// this[1] returns the 0th image in the WIM container. - /// </summary> - /// <param name="ImageIndex">The 1-based index of the image to retrieve.</param> - /// <returns>WinImage object.</returns> - public WimImage - this[int ImageIndex] - { - get { return Images[ImageIndex - 1]; } - } - - /// <summary> - /// Indexer for WIM images inside the WIM container, indexed by the image name. - /// WIMs created by different processes sometimes contain different information - including the name. - /// Some images have their name stored in the Name field, some in the Flags field, and some in the EditionID field. - /// We take all of those into account in while searching the WIM. - /// </summary> - /// <param name="ImageName"></param> - /// <returns></returns> - public WimImage - this[string ImageName] - { - get - { - return - Images.Where(i => ( - i.ImageName.ToUpper() == ImageName.ToUpper() || - i.ImageFlags.ToUpper() == ImageName.ToUpper() )) - .DefaultIfEmpty(null) - .FirstOrDefault<WimImage>(); - } - } - - /// <summary> - /// Returns the number of images in the WIM container. - /// </summary> - internal uint - ImageCount - { - get { return NativeMethods.WimGetImageCount(Handle); } - } - - /// <summary> - /// Returns an XDocument representation of the XML metadata for the WIM container and associated images. - /// </summary> - internal XDocument - XmlInfo - { - get - { - - if (null == m_xmlInfo) - { - StringBuilder builder; - uint bytes; - if (!NativeMethods.WimGetImageInformation(Handle, out builder, out bytes)) - { - throw new Win32Exception(); - } - - // Ensure the length of the returned bytes to avoid garbage characters at the end. - int charCount = (int)bytes / sizeof(char); - if (null != builder) - { - // Get rid of the unicode file marker at the beginning of the XML. - builder.Remove(0, 1); - builder.EnsureCapacity(charCount - 1); - builder.Length = charCount - 1; - - // This isn't likely to change while we have the image open, so cache it. - m_xmlInfo = XDocument.Parse(builder.ToString().Trim()); - } - else - { - m_xmlInfo = null; - } - } - - return m_xmlInfo; - } - } - - public NativeMethods.WimFileHandle Handle - { - get; - private set; - } -} - -public class -WimImage -{ - - internal XDocument m_xmlInfo; - - public - WimImage( - WimFile Container, - uint ImageIndex) - { - - if (null == Container) - { - throw new ArgumentNullException("Container"); - } - - if ((Container.Handle.IsClosed) || (Container.Handle.IsInvalid)) - { - throw new ArgumentNullException("The handle to the WIM file has already been closed, or is invalid.", "Container"); - } - - if (ImageIndex > Container.ImageCount) - { - throw new ArgumentOutOfRangeException("ImageIndex", "The index does not exist in the specified WIM file."); - } - - Handle = new NativeMethods.WimImageHandle(Container, ImageIndex); - } - - public enum - Architectures : uint - { - x86 = 0x0, - ARM = 0x5, - IA64 = 0x6, - AMD64 = 0x9, - ARM64 = 0xC - } - - public void - Close() - { - if ((!Handle.IsClosed) && (!Handle.IsInvalid)) - { - Handle.Close(); - } - } - - public NativeMethods.WimImageHandle - Handle - { - get; - private set; - } - - internal XDocument - XmlInfo - { - get - { - - if (null == m_xmlInfo) - { - StringBuilder builder; - uint bytes; - if (!NativeMethods.WimGetImageInformation(Handle, out builder, out bytes)) - { - throw new Win32Exception(); - } - - // Ensure the length of the returned bytes to avoid garbage characters at the end. - int charCount = (int)bytes / sizeof(char); - if (null != builder) - { - // Get rid of the unicode file marker at the beginning of the XML. - builder.Remove(0, 1); - builder.EnsureCapacity(charCount - 1); - builder.Length = charCount - 1; - - // This isn't likely to change while we have the image open, so cache it. - m_xmlInfo = XDocument.Parse(builder.ToString().Trim()); - } - else - { - m_xmlInfo = null; - } - } - - return m_xmlInfo; - } - } - - public string - ImageIndex - { - get { return XmlInfo.Element("IMAGE").Attribute("INDEX").Value; } - } - - public string - ImageName - { - get { return XmlInfo.XPathSelectElement("/IMAGE/NAME").Value; } - } - - public string - ImageEditionId - { - get { return XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/EDITIONID").Value; } - } - - public string - ImageFlags - { - get - { - string flagValue = String.Empty; - - try - { - flagValue = XmlInfo.XPathSelectElement("/IMAGE/FLAGS").Value; - } - catch - { - - // Some WIM files don't contain a FLAGS element in the metadata. - // In an effort to support those WIMs too, inherit the EditionId if there - // are no Flags. - - if (String.IsNullOrEmpty(flagValue)) - { - flagValue = this.ImageEditionId; - - // Check to see if the EditionId is "ServerHyper". If so, - // tweak it to be "ServerHyperCore" instead. - - if (0 == String.Compare("serverhyper", flagValue, true)) - { - flagValue = "ServerHyperCore"; - } - } - - } - - return flagValue; - } - } - - public string - ImageProductType - { - get { return XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/PRODUCTTYPE").Value; } - } - - public string - ImageInstallationType - { - get { return XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/INSTALLATIONTYPE").Value; } - } - - public string - ImageDescription - { - get { return XmlInfo.XPathSelectElement("/IMAGE/DESCRIPTION").Value; } - } - - public ulong - ImageSize - { - get { return ulong.Parse(XmlInfo.XPathSelectElement("/IMAGE/TOTALBYTES").Value); } - } - - public Architectures - ImageArchitecture - { - get - { - int arch = -1; - try - { - arch = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/ARCH").Value); - } - catch { } - - return (Architectures)arch; - } - } - - public string - ImageDefaultLanguage - { - get - { - string lang = null; - try - { - lang = XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/LANGUAGES/DEFAULT").Value; - } - catch { } - - return lang; - } - } - - public Version - ImageVersion - { - get - { - int major = 0; - int minor = 0; - int build = 0; - int revision = 0; - - try - { - major = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/MAJOR").Value); - minor = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/MINOR").Value); - build = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/BUILD").Value); - revision = int.Parse(XmlInfo.XPathSelectElement("/IMAGE/WINDOWS/VERSION/SPBUILD").Value); - } - catch { } - - return (new Version(major, minor, build, revision)); - } - } - - public string - ImageDisplayName - { - get { return XmlInfo.XPathSelectElement("/IMAGE/DISPLAYNAME").Value; } - } - - public string - ImageDisplayDescription - { - get { return XmlInfo.XPathSelectElement("/IMAGE/DISPLAYDESCRIPTION").Value; } - } -} - -///<summary> -///Describes the file that is being processed for the ProcessFileEvent. -///</summary> -public class -DefaultImageEventArgs : EventArgs -{ - ///<summary> - ///Default constructor. - ///</summary> - public - DefaultImageEventArgs( - IntPtr wideParameter, - IntPtr leftParameter, - IntPtr userData) - { - - WideParameter = wideParameter; - LeftParameter = leftParameter; - UserData = userData; - } - - ///<summary> - ///wParam - ///</summary> - public IntPtr WideParameter - { - get; - private set; - } - - ///<summary> - ///lParam - ///</summary> - public IntPtr LeftParameter - { - get; - private set; - } - - ///<summary> - ///UserData - ///</summary> - public IntPtr UserData - { - get; - private set; - } -} - -///<summary> -///Describes the file that is being processed for the ProcessFileEvent. -///</summary> -public class -ProcessFileEventArgs : EventArgs -{ - ///<summary> - ///Default constructor. - ///</summary> - ///<param name="file">Fully qualified path and file name. For example: c:\file.sys.</param> - ///<param name="skipFileFlag">Default is false - skip file and continue. - ///Set to true to abort the entire image capture.</param> - public - ProcessFileEventArgs( - string file, - IntPtr skipFileFlag) - { - - m_FilePath = file; - m_SkipFileFlag = skipFileFlag; - } - - ///<summary> - ///Skip file from being imaged. - ///</summary> - public void - SkipFile() - { - byte[] byteBuffer = - { - 0 - }; - int byteBufferSize = byteBuffer.Length; - Marshal.Copy(byteBuffer, 0, m_SkipFileFlag, byteBufferSize); - } - - ///<summary> - ///Fully qualified path and file name. - ///</summary> - public string - FilePath - { - get - { - string stringToReturn = ""; - if (m_FilePath != null) - { - stringToReturn = m_FilePath; - } - return stringToReturn; - } - } - - ///<summary> - ///Flag to indicate if the entire image capture should be aborted. - ///Default is false - skip file and continue. Setting to true will - ///abort the entire image capture. - ///</summary> - public bool Abort - { - set { m_Abort = value; } - get { return m_Abort; } - } - - private string m_FilePath; - private bool m_Abort; - private IntPtr m_SkipFileFlag; - -} - -#endregion WIM Interop - -#region VHD Interop -// Based on code written by the Hyper-V Test team. -/// <summary> -/// The Virtual Hard Disk class provides methods for creating and manipulating Virtual Hard Disk files. -/// </summary> -public class -VirtualHardDisk -{ - #region Static Methods - - #region Sparse Disks - - /// <summary> - /// Abbreviated signature of CreateSparseDisk so it's easier to use from WIM2VHD. - /// </summary> - /// <param name="virtualStorageDeviceType">The type of disk to create, VHD or VHDX.</param> - /// <param name="path">The path of the disk to create.</param> - /// <param name="size">The maximum size of the disk to create.</param> - /// <param name="overwrite">Overwrite the VHD if it already exists.</param> - public static void - CreateSparseDisk( - NativeMethods.VirtualStorageDeviceType virtualStorageDeviceType, - string path, - ulong size, - bool overwrite) - { - - CreateSparseDisk( - path, - size, - overwrite, - null, - IntPtr.Zero, - (virtualStorageDeviceType == NativeMethods.VirtualStorageDeviceType.VHD) - ? NativeMethods.DEFAULT_BLOCK_SIZE - : 0, - virtualStorageDeviceType, - NativeMethods.DISK_SECTOR_SIZE); - } - - /// <summary> - /// Creates a new sparse (dynamically expanding) virtual hard disk (.vhd). Supports both sync and async modes. - /// The VHD image file uses only as much space on the backing store as needed to store the actual data the VHD currently contains. - /// </summary> - /// <param name="path">The path and name of the VHD to create.</param> - /// <param name="size">The size of the VHD to create in bytes. - /// When creating this type of VHD, the VHD API does not test for free space on the physical backing store based on the maximum size requested, - /// therefore it is possible to successfully create a dynamic VHD with a maximum size larger than the available physical disk free space. - /// The maximum size of a dynamic VHD is 2,040 GB. The minimum size is 3 MB.</param> - /// <param name="source">Optional path to pre-populate the new virtual disk object with block data from an existing disk - /// This path may refer to a VHD or a physical disk. Use NULL if you don't want a source.</param> - /// <param name="overwrite">If the VHD exists, setting this parameter to 'True' will delete it and create a new one.</param> - /// <param name="overlapped">If not null, the operation runs in async mode</param> - /// <param name="blockSizeInBytes">Block size for the VHD.</param> - /// <param name="virtualStorageDeviceType">VHD format version (VHD1 or VHD2)</param> - /// <param name="sectorSizeInBytes">Sector size for the VHD.</param> - /// <exception cref="ArgumentOutOfRangeException">Thrown when an invalid size is specified</exception> - /// <exception cref="FileNotFoundException">Thrown when source VHD is not found.</exception> - /// <exception cref="SecurityException">Thrown when there was an error while creating the default security descriptor.</exception> - /// <exception cref="Win32Exception">Thrown when an error occurred while creating the VHD.</exception> - public static void - CreateSparseDisk( - string path, - ulong size, - bool overwrite, - string source, - IntPtr overlapped, - uint blockSizeInBytes, - NativeMethods.VirtualStorageDeviceType virtualStorageDeviceType, - uint sectorSizeInBytes) - { - - // Validate the virtualStorageDeviceType - if (virtualStorageDeviceType != NativeMethods.VirtualStorageDeviceType.VHD && virtualStorageDeviceType != NativeMethods.VirtualStorageDeviceType.VHDX) - { - - throw ( - new ArgumentOutOfRangeException( - "virtualStorageDeviceType", - virtualStorageDeviceType, - "VirtualStorageDeviceType must be VHD or VHDX." - )); - } - - // Validate size. It needs to be a multiple of DISK_SECTOR_SIZE (512)... - if ((size % NativeMethods.DISK_SECTOR_SIZE) != 0) - { - - throw ( - new ArgumentOutOfRangeException( - "size", - size, - "The size of the virtual disk must be a multiple of 512." - )); - } - - if ((!String.IsNullOrEmpty(source)) && (!System.IO.File.Exists(source))) - { - - throw ( - new System.IO.FileNotFoundException( - "Unable to find the source file.", - source - )); - } - - if ((overwrite) && (System.IO.File.Exists(path))) - { - - System.IO.File.Delete(path); - } - - NativeMethods.CreateVirtualDiskParameters createParams = new NativeMethods.CreateVirtualDiskParameters(); - - // Select the correct version. - createParams.Version = (virtualStorageDeviceType == NativeMethods.VirtualStorageDeviceType.VHD) - ? NativeMethods.CreateVirtualDiskVersion.Version1 - : NativeMethods.CreateVirtualDiskVersion.Version2; - - createParams.UniqueId = Guid.NewGuid(); - createParams.MaximumSize = size; - createParams.BlockSizeInBytes = blockSizeInBytes; - createParams.SectorSizeInBytes = sectorSizeInBytes; - createParams.ParentPath = null; - createParams.SourcePath = source; - createParams.OpenFlags = NativeMethods.OpenVirtualDiskFlags.None; - createParams.GetInfoOnly = false; - createParams.ParentVirtualStorageType = new NativeMethods.VirtualStorageType(); - createParams.SourceVirtualStorageType = new NativeMethods.VirtualStorageType(); - - // - // Create and init a security descriptor. - // Since we're creating an essentially blank SD to use with CreateVirtualDisk - // the VHD will take on the security values from the parent directory. - // - - NativeMethods.SecurityDescriptor securityDescriptor; - if (!NativeMethods.InitializeSecurityDescriptor(out securityDescriptor, 1)) - { - - throw ( - new SecurityException( - "Unable to initialize the security descriptor for the virtual disk." - )); - } - - NativeMethods.VirtualStorageType virtualStorageType = new NativeMethods.VirtualStorageType(); - virtualStorageType.DeviceId = virtualStorageDeviceType; - virtualStorageType.VendorId = NativeMethods.VirtualStorageTypeVendorMicrosoft; - - SafeFileHandle vhdHandle; - - uint returnCode = NativeMethods.CreateVirtualDisk( - ref virtualStorageType, - path, - (virtualStorageDeviceType == NativeMethods.VirtualStorageDeviceType.VHD) - ? NativeMethods.VirtualDiskAccessMask.All - : NativeMethods.VirtualDiskAccessMask.None, - ref securityDescriptor, - NativeMethods.CreateVirtualDiskFlags.None, - 0, - ref createParams, - overlapped, - out vhdHandle); - - vhdHandle.Close(); - - if (NativeMethods.ERROR_SUCCESS != returnCode && NativeMethods.ERROR_IO_PENDING != returnCode) - { - - throw ( - new Win32Exception( - (int)returnCode - )); - } - } - - #endregion Sparse Disks - - #endregion Static Methods - -} -#endregion VHD Interop -} -"@ - - Add-Type -TypeDefinition $code -ReferencedAssemblies "System.Xml","System.Linq","System.Xml.Linq" -ErrorAction SilentlyContinue -} diff --git a/src/support/Convert-XSDToMD.ps1 b/src/support/Convert-XSDToMD.ps1 deleted file mode 100644 index e5ba68d3..00000000 --- a/src/support/Convert-XSDToMD.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -<# -.Synopsis - Uses the MSXSL.EXE to apply an XML Stylesheet Transformation to an XML file. -.Parameter XmlFile - The full path to the XML file to transform. -.Parameter XslFile - The full path to the XSLT file to use as the transformation. -.Parameter OutputFile - The full path to the output file to create. -#> -param -( - [System.String] - $XmlFile, - - [System.String] - $XslFile, - - [System.String] - $OutputFile -) -& "$PSScriptRoot\tools\msxsl.exe" @($XmlFile,$XslFile,'-o',$OutputFile) -$content = Get-Content -Raw -Encoding Unicode -Path $OutputFile -[System.IO.File]::WriteAllText($OutputFile, $content, [System.Text.Encoding]::UTF8) diff --git a/src/support/New-SelfSignedCertificateEx.ps1 b/src/support/New-SelfSignedCertificateEx.ps1 deleted file mode 100644 index 458be9ca..00000000 --- a/src/support/New-SelfSignedCertificateEx.ps1 +++ /dev/null @@ -1,501 +0,0 @@ -##################################################################### -# New-SelfSignedCertificateEx.ps1 -# Version 1.0 -# -# Creates self-signed certificate. This tool is a base replacement -# for deprecated makecert.exe -# -# Vadims Podans (c) 2013 -# http://en-us.sysadmins.lv/ -##################################################################### -#requires -Version 2.0 - -function New-SelfSignedCertificateEx { -<# -.Synopsis - This cmdlet generates a self-signed certificate. -.Description - This cmdlet generates a self-signed certificate with the required data. -.Parameter Subject - Specifies the certificate subject in a X500 distinguished name format. - Example: CN=Test Cert, OU=Sandbox -.Parameter NotBefore - Specifies the date and time when the certificate become valid. By default previous day - date is used. -.Parameter NotAfter - Specifies the date and time when the certificate expires. By default, the certificate is - valid for 1 year. -.Parameter SerialNumber - Specifies the desired serial number in a hex format. - Example: 01a4ff2 -.Parameter ProviderName - Specifies the Cryptography Service Provider (CSP) name. You can use either legacy CSP - and Key Storage Providers (KSP). By default "Microsoft Enhanced Cryptographic Provider v1.0" - CSP is used. -.Parameter AlgorithmName - Specifies the public key algorithm. By default RSA algorithm is used. RSA is the only - algorithm supported by legacy CSPs. With key storage providers (KSP) you can use CNG - algorithms, like ECDH. For CNG algorithms you must use full name: - ECDH_P256 - ECDH_P384 - ECDH_P521 - - In addition, KeyLength parameter must be specified explicitly when non-RSA algorithm is used. -.Parameter KeyLength - Specifies the key length to generate. By default 2048-bit key is generated. -.Parameter KeySpec - Specifies the public key operations type. The possible values are: Exchange and Signature. - Default value is Exchange. -.Parameter EnhancedKeyUsage - Specifies the intended uses of the public key contained in a certificate. You can - specify either, EKU friendly name (for example 'Server Authentication') or - object identifier (OID) value (for example '1.3.6.1.5.5.7.3.1'). -.Parameter KeyUsages - Specifies restrictions on the operations that can be performed by the public key contained in the certificate. - Possible values (and their respective integer values to make bitwise operations) are: - EncipherOnly - CrlSign - KeyCertSign - KeyAgreement - DataEncipherment - KeyEncipherment - NonRepudiation - DigitalSignature - DecipherOnly - - you can combine key usages values by using bitwise OR operation. when combining multiple - flags, they must be enclosed in quotes and separated by a comma character. For example, - to combine KeyEncipherment and DigitalSignature flags you should type: - "KeyEncipherment, DigitalSignature". - - If the certificate is CA certificate (see IsCA parameter), key usages extension is generated - automatically with the following key usages: Certificate Signing, Off-line CRL Signing, CRL Signing. -.Parameter SubjectAlternativeName - Specifies alternative names for the subject. Unlike Subject field, this extension - allows to specify more than one name. Also, multiple types of alternative names - are supported. The cmdlet supports the following SAN types: - RFC822 Name - IP address (both, IPv4 and IPv6) - Guid - Directory name - DNS name -.Parameter IsCA - Specifies whether the certificate is CA (IsCA = $true) or end entity (IsCA = $false) - certificate. If this parameter is set to $false, PathLength parameter is ignored. - Basic Constraints extension is marked as critical. -.PathLength - Specifies the number of additional CA certificates in the chain under this certificate. If - PathLength parameter is set to zero, then no additional (subordinate) CA certificates are - permitted under this CA. -.CustomExtension - Specifies the custom extension to include to a self-signed certificate. This parameter - must not be used to specify the extension that is supported via other parameters. In order - to use this parameter, the extension must be formed in a collection of initialized - System.Security.Cryptography.X509Certificates.X509Extension objects. -.Parameter SignatureAlgorithm - Specifies signature algorithm used to sign the certificate. By default 'SHA1' - algorithm is used. -.Parameter FriendlyName - Specifies friendly name for the certificate. -.Parameter StoreLocation - Specifies the store location to store self-signed certificate. Possible values are: - 'CurrentUser' and 'LocalMachine'. 'CurrentUser' store is intended for user certificates - and computer (as well as CA) certificates must be stored in 'LocalMachine' store. -.Parameter StoreName - Specifies the container name in the certificate store. Possible container names are: - AddressBook - AuthRoot - CertificateAuthority - Disallowed - My - Root - TrustedPeople - TrustedPublisher -.Parameter Path - Specifies the path to a PFX file to export a self-signed certificate. -.Parameter Password - Specifies the password for PFX file. -.Parameter AllowSMIME - Enables Secure/Multipurpose Internet Mail Extensions for the certificate. -.Parameter Exportable - Marks private key as exportable. Smart card providers usually do not allow - exportable keys. -.Example - New-SelfsignedCertificateEx -Subject "CN=Test Code Signing" -EKU "Code Signing" -KeySpec "Signature" ` - -KeyUsage "DigitalSignature" -FriendlyName "Test code signing" -NotAfter [datetime]::now.AddYears(5) - - Creates a self-signed certificate intended for code signing and which is valid for 5 years. Certificate - is saved in the Personal store of the current user account. -.Example - New-SelfsignedCertificateEx -Subject "CN=www.domain.com" -EKU "Server Authentication", "Client authentication" ` - -KeyUsage "KeyEcipherment, DigitalSignature" -SAN "sub.domain.com","www.domain.com","192.168.1.1" ` - -AllowSMIME -Path C:\test\ssl.pfx -Password (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force) -Exportable ` - -StoreLocation "LocalMachine" - - Creates a self-signed SSL certificate with multiple subject names and saves it to a file. Additionally, the - certificate is saved in the Personal store of the Local Machine store. Private key is marked as exportable, - so you can export the certificate with a associated private key to a file at any time. The certificate - includes SMIME capabilities. -.Example - New-SelfsignedCertificateEx -Subject "CN=www.domain.com" -EKU "Server Authentication", "Client authentication" ` - -KeyUsage "KeyEcipherment, DigitalSignature" -SAN "sub.domain.com","www.domain.com","192.168.1.1" ` - -StoreLocation "LocalMachine" -ProviderName "Microsoft Software Key Storae Provider" -AlgorithmName ecdh_256 ` - -KeyLength 256 -SignatureAlgorithm sha256 - - Creates a self-signed SSL certificate with multiple subject names and saves it to a file. Additionally, the - certificate is saved in the Personal store of the Local Machine store. Private key is marked as exportable, - so you can export the certificate with a associated private key to a file at any time. Certificate uses - Ellyptic Curve Cryptography (ECC) key algorithm ECDH with 256-bit key. The certificate is signed by using - SHA256 algorithm. -.Example - New-SelfsignedCertificateEx -Subject "CN=Test Root CA, OU=Sandbox" -IsCA $true -ProviderName ` - "Microsoft Software Key Storage Provider" -Exportable - - Creates self-signed root CA certificate. -#> -[CmdletBinding(DefaultParameterSetName = '__store')] - param ( - [Parameter(Mandatory = $true, Position = 0)] - [System.String]$Subject, - [Parameter(Position = 1)] - [datetime]$NotBefore = [DateTime]::Now.AddDays(-1), - [Parameter(Position = 2)] - [datetime]$NotAfter = $NotBefore.AddDays(365), - [System.String]$SerialNumber, - [Alias('CSP')] - [System.String]$ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0", - [System.String]$AlgorithmName = "RSA", - [System.Int32]$KeyLength = 2048, - [validateSet("Exchange","Signature")] - [System.String]$KeySpec = "Exchange", - [Alias('EKU')] - [Security.Cryptography.Oid[]]$EnhancedKeyUsage, - [Alias('KU')] - [Security.Cryptography.X509Certificates.X509KeyUsageFlags]$KeyUsage, - [Alias('SAN')] - [System.String[]]$SubjectAlternativeName, - [bool]$IsCA, - [System.Int32]$PathLength = -1, - [Security.Cryptography.X509Certificates.X509ExtensionCollection]$CustomExtension, - [ValidateSet('MD5','SHA1','SHA256','SHA384','SHA512')] - [System.String]$SignatureAlgorithm = "SHA1", - [System.String]$FriendlyName, - [Parameter(ParameterSetName = '__store')] - [Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation = "CurrentUser", - [Parameter(ParameterSetName = '__store')] - [Security.Cryptography.X509Certificates.StoreName]$StoreName = "My", - [Parameter(Mandatory = $true, ParameterSetName = '__file')] - [Alias('OutFile','OutPath','Out')] - [IO.FileInfo]$Path, - [Parameter(Mandatory = $true, ParameterSetName = '__file')] - [Security.SecureString]$Password, - [switch]$AllowSMIME, - [switch]$Exportable - ) - $ErrorActionPreference = "Stop" - if ([Environment]::OSVersion.Version.Major -lt 6) { - $NotSupported = New-Object NotSupportedException -ArgumentList "Windows XP and Windows Server 2003 are not supported!" - throw $NotSupported - } - $ExtensionsToAdd = @() - -#region constants - # contexts - New-Variable -Name UserContext -Value 0x1 -Option Constant - New-Variable -Name MachineContext -Value 0x2 -Option Constant - # encoding - New-Variable -Name Base64Header -Value 0x0 -Option Constant - New-Variable -Name Base64 -Value 0x1 -Option Constant - New-Variable -Name Binary -Value 0x3 -Option Constant - New-Variable -Name Base64RequestHeader -Value 0x4 -Option Constant - # SANs - New-Variable -Name OtherName -Value 0x1 -Option Constant - New-Variable -Name RFC822Name -Value 0x2 -Option Constant - New-Variable -Name DNSName -Value 0x3 -Option Constant - New-Variable -Name DirectoryName -Value 0x5 -Option Constant - New-Variable -Name URL -Value 0x7 -Option Constant - New-Variable -Name IPAddress -Value 0x8 -Option Constant - New-Variable -Name RegisteredID -Value 0x9 -Option Constant - New-Variable -Name Guid -Value 0xa -Option Constant - New-Variable -Name UPN -Value 0xb -Option Constant - # installation options - New-Variable -Name AllowNone -Value 0x0 -Option Constant - New-Variable -Name AllowNoOutstandingRequest -Value 0x1 -Option Constant - New-Variable -Name AllowUntrustedCertificate -Value 0x2 -Option Constant - New-Variable -Name AllowUntrustedRoot -Value 0x4 -Option Constant - # PFX export options - New-Variable -Name PFXExportEEOnly -Value 0x0 -Option Constant - New-Variable -Name PFXExportChainNoRoot -Value 0x1 -Option Constant - New-Variable -Name PFXExportChainWithRoot -Value 0x2 -Option Constant -#endregion - -#region Subject processing - # http://msdn.microsoft.com/en-us/library/aa377051(VS.85).aspx - $SubjectDN = New-Object -ComObject X509Enrollment.CX500DistinguishedName - $SubjectDN.Encode($Subject, 0x0) -#endregion - -#region Extensions - -#region Enhanced Key Usages processing - if ($EnhancedKeyUsage) { - $OIDs = New-Object -ComObject X509Enrollment.CObjectIDs - $EnhancedKeyUsage | %{ - $OID = New-Object -ComObject X509Enrollment.CObjectID - $OID.InitializeFromValue($_.Value) - # http://msdn.microsoft.com/en-us/library/aa376785(VS.85).aspx - $OIDs.Add($OID) - } - # http://msdn.microsoft.com/en-us/library/aa378132(VS.85).aspx - $EKU = New-Object -ComObject X509Enrollment.CX509ExtensionEnhancedKeyUsage - $EKU.InitializeEncode($OIDs) - $ExtensionsToAdd += "EKU" - } -#endregion - -#region Key Usages processing - if ($KeyUsage -ne $null) { - $KU = New-Object -ComObject X509Enrollment.CX509ExtensionKeyUsage - $KU.InitializeEncode([System.Int32]$KeyUsage) - $KU.Critical = $true - $ExtensionsToAdd += "KU" - } -#endregion - -#region Basic Constraints processing - if ($PSBoundParameters.Keys.Contains("IsCA")) { - # http://msdn.microsoft.com/en-us/library/aa378108(v=vs.85).aspx - $BasicConstraints = New-Object -ComObject X509Enrollment.CX509ExtensionBasicConstraints - if (!$IsCA) {$PathLength = -1} - $BasicConstraints.InitializeEncode($IsCA,$PathLength) - $BasicConstraints.Critical = $IsCA - $ExtensionsToAdd += "BasicConstraints" - } -#endregion - -#region SAN processing - if ($SubjectAlternativeName) { - $SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames - $Names = New-Object -ComObject X509Enrollment.CAlternativeNames - foreach ($altname in $SubjectAlternativeName) { - $Name = New-Object -ComObject X509Enrollment.CAlternativeName - if ($altname.Contains("@")) { - $Name.InitializeFromString($RFC822Name,$altname) - } else { - try { - $Bytes = [Net.IPAddress]::Parse($altname).GetAddressBytes() - $Name.InitializeFromRawData($IPAddress,$Base64,[Convert]::ToBase64String($Bytes)) - } catch { - try { - $Bytes = [Guid]::Parse($altname).ToByteArray() - $Name.InitializeFromRawData($Guid,$Base64,[Convert]::ToBase64String($Bytes)) - } catch { - try { - $Bytes = ([Security.Cryptography.X509Certificates.X500DistinguishedName]$altname).RawData - $Name.InitializeFromRawData($DirectoryName,$Base64,[Convert]::ToBase64String($Bytes)) - } catch {$Name.InitializeFromString($DNSName,$altname)} - } - } - } - $Names.Add($Name) - } - $SAN.InitializeEncode($Names) - $ExtensionsToAdd += "SAN" - } -#endregion - -#region Custom Extensions - if ($CustomExtension) { - $count = 0 - foreach ($ext in $CustomExtension) { - # http://msdn.microsoft.com/en-us/library/aa378077(v=vs.85).aspx - $Extension = New-Object -ComObject X509Enrollment.CX509Extension - $EOID = New-Object -ComObject X509Enrollment.CObjectId - $EOID.InitializeFromValue($ext.Oid.Value) - $EValue = [Convert]::ToBase64String($ext.RawData) - $Extension.Initialize($EOID,$Base64,$EValue) - $Extension.Critical = $ext.Critical - New-Variable -Name ("ext" + $count) -Value $Extension - $ExtensionsToAdd += ("ext" + $count) - $count++ - } - } -#endregion - -#endregion - -#region Private Key - # http://msdn.microsoft.com/en-us/library/aa378921(VS.85).aspx - $PrivateKey = New-Object -ComObject X509Enrollment.CX509PrivateKey - $PrivateKey.ProviderName = $ProviderName - $AlgID = New-Object -ComObject X509Enrollment.CObjectId - $AlgID.InitializeFromValue(([Security.Cryptography.Oid]$AlgorithmName).Value) - $PrivateKey.Algorithm = $AlgID - # http://msdn.microsoft.com/en-us/library/aa379409(VS.85).aspx - $PrivateKey.KeySpec = switch ($KeySpec) {"Exchange" {1}; "Signature" {2}} - $PrivateKey.Length = $KeyLength - # key will be stored in current user certificate store - switch ($PSCmdlet.ParameterSetName) { - '__store' { - $PrivateKey.MachineContext = if ($StoreLocation -eq "LocalMachine") {$true} else {$false} - } - '__file' { - $PrivateKey.MachineContext = $false - } - } - $PrivateKey.ExportPolicy = if ($Exportable) {1} else {0} - $PrivateKey.Create() -#endregion - - # http://msdn.microsoft.com/en-us/library/aa377124(VS.85).aspx - $Cert = New-Object -ComObject X509Enrollment.CX509CertificateRequestCertificate - if ($PrivateKey.MachineContext) { - $Cert.InitializeFromPrivateKey($MachineContext,$PrivateKey,"") - } else { - $Cert.InitializeFromPrivateKey($UserContext,$PrivateKey,"") - } - $Cert.Subject = $SubjectDN - $Cert.Issuer = $Cert.Subject - $Cert.NotBefore = $NotBefore - $Cert.NotAfter = $NotAfter - foreach ($item in $ExtensionsToAdd) {$Cert.X509Extensions.Add((Get-Variable -Name $item -ValueOnly))} - if (![System.String]::IsNullOrEmpty($SerialNumber)) { - if ($SerialNumber -match "[^0-9a-fA-F]") {throw "Invalid serial number specified."} - if ($SerialNumber.Length % 2) {$SerialNumber = "0" + $SerialNumber} - $Bytes = $SerialNumber -split "(.{2})" | ?{$_} | %{[Convert]::ToByte($_,16)} - $ByteString = [Convert]::ToBase64String($Bytes) - $Cert.SerialNumber.InvokeSet($ByteString,1) - } - if ($AllowSMIME) {$Cert.SmimeCapabilities = $true} - $SigOID = New-Object -ComObject X509Enrollment.CObjectId - $SigOID.InitializeFromValue(([Security.Cryptography.Oid]$SignatureAlgorithm).Value) - $Cert.SignatureInformation.HashAlgorithm = $SigOID - # completing certificate request template building - $Cert.Encode() - - # interface: http://msdn.microsoft.com/en-us/library/aa377809(VS.85).aspx - $Request = New-Object -ComObject X509Enrollment.CX509enrollment - $Request.InitializeFromRequest($Cert) - $Request.CertificateFriendlyName = $FriendlyName - $endCert = $Request.CreateRequest($Base64) - $Request.InstallResponse($AllowUntrustedCertificate,$endCert,$Base64,"") - switch ($PSCmdlet.ParameterSetName) { - '__file' { - $PFXString = $Request.CreatePFX( - [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)), - $PFXExportEEOnly, - $Base64 - ) - Set-Content -Path $Path -Value ([Convert]::FromBase64String($PFXString)) -Encoding Byte - } - } -} -# SIG # Begin signature block -# MIIT9wYJKoZIhvcNAQcCoIIT6DCCE+QCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB -# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR -# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUAeqLu7+6JFZ1eT+FMVIQ0Tgq -# Y0Gggg8tMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B -# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG -# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh -# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg -# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV -# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu -# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN -# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q -# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC -# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4 -# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3 -# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI -# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd -# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG -# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB -# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro -# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV -# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y -# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf -# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y -# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq -# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3 -# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh -# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD -# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE -# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT -# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN -# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow -# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0 -# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu -# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh -# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz -# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB -# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO -# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw -# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90 -# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx -# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy -# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV -# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa -# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH -# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73 -# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR -# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW -# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu -# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw -# ggaQMIIFeKADAgECAhAGnC2gXFmy7q5ox0B+K5/xMA0GCSqGSIb3DQEBBQUAMG8x -# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 -# dy5kaWdpY2VydC5jb20xLjAsBgNVBAMTJURpZ2lDZXJ0IEFzc3VyZWQgSUQgQ29k -# ZSBTaWduaW5nIENBLTEwHhcNMTMwMTI4MDAwMDAwWhcNMTQwMjA1MTIwMDAwWjBc -# MQswCQYDVQQGEwJMVjEKMAgGA1UECBMBLTENMAsGA1UEBxMEUmlnYTEYMBYGA1UE -# ChMPU3lzYWRtaW5zIExWIElLMRgwFgYDVQQDEw9TeXNhZG1pbnMgTFYgSUswggEi -# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChzYQDqMuYBs/jnfLsMvbbuZTV -# wwY1yHJ92TvD7bNwVx1OFEENNGrXkLNz9Ro6XtJ8zcB/80FmxE9jL2ARLd2TmEJt -# aYBvvmGMsS17zCGYDZZU7aVjaKZX2R665V+LWJUEIaHCcY5XjfmeZvCk1tHOtTAX -# qKjUd6fGIWXpxrSP9WKxW7FpTDGzQ2BpkZ+snmPS9yWDgeu709zPeoSTbdEIva6J -# ckzFj0uK7k2BqlLG3dsxBIzUqr+yTbdAuWfhR731iyWHk5GT6XCtBjBmuouKOCT1 -# Jn0xmYNAwgdtSiBlTL4A/Rm3YuP57VP+EBrrgA5g7Pekdo9APU+7QqWF51YhAgMB -# AAGjggM5MIIDNTAfBgNVHSMEGDAWgBR7aM4pqsAXvkl64eU/1qf3RY81MjAdBgNV -# HQ4EFgQUgiIRdHkZ2SctPGaLDBBOX67N7NQwDgYDVR0PAQH/BAQDAgeAMBMGA1Ud -# JQQMMAoGCCsGAQUFBwMDMHMGA1UdHwRsMGowM6AxoC+GLWh0dHA6Ly9jcmwzLmRp -# Z2ljZXJ0LmNvbS9hc3N1cmVkLWNzLTIwMTFhLmNybDAzoDGgL4YtaHR0cDovL2Ny -# bDQuZGlnaWNlcnQuY29tL2Fzc3VyZWQtY3MtMjAxMWEuY3JsMIIBxAYDVR0gBIIB -# uzCCAbcwggGzBglghkgBhv1sAwEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -# LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -# AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -# AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -# AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -# AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -# AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -# AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -# AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -# AHIAZQBuAGMAZQAuMIGCBggrBgEFBQcBAQR2MHQwJAYIKwYBBQUHMAGGGGh0dHA6 -# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBMBggrBgEFBQcwAoZAaHR0cDovL2NhY2VydHMu -# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ29kZVNpZ25pbmdDQS0xLmNy -# dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQAh3nRpJ8WxlJZ8NI1y -# B4iM7RzjL7D57lVj/shWkbCp2znzBLVMGnYVK+Z0QL2PSxpxX52Khhc2MHXTM+Yf -# 74sO5XZm5IMMAnlpK2FeyQBGIKcFmrzkvj3LUcCc7RU0duioVHQ+C+hOQmpmSYiA -# 0zOoJgO4zFy5SKT1mzPEElup1B2aiE+WQZpcEWUv4I+/lYvYIBhyz+WZ2xm4kLbG -# QYR/08cei9X70x02wpgMSK9yKhSzcpwbq+ccnOtFUlTLNyRr9OuRnTi3ZCM8w5Is -# a2+UsnxsF5F5CGsw+GMRT/Jrm2mHMcKIW+qp8reUXattRTjobnbARJSQS3NBt4wp -# wTIZMYIENDCCBDACAQEwgYMwbzELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD -# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEuMCwGA1UEAxMlRGln -# aUNlcnQgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EtMQIQBpwtoFxZsu6uaMdA -# fiuf8TAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkq -# hkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGC -# NwIBFTAjBgkqhkiG9w0BCQQxFgQU/ix9ZAr39xxjbsYVBZ5BgjBjq+MwDQYJKoZI -# hvcNAQEBBQAEggEAI0yNayiE8E3Ttsmvd5tvmAUN9ngGpez7qvGtjcUEFXyjRHjW -# d3XieT98UHYL1/+IoX6QiEXP/t5SreMqQmql9wvlhyDt2Qw2+E80MXFCGvKuAJAp -# LvTY3F4i9fY+wCT33B2dDUSAI+mFGbJxl1GgulqSxLOUiGnlpWvtJ07lgSki6xN0 -# MTlNCt4xem5P43iRLFtlkEJjYpznClPx0Ipu1IG3gND4hos5jVofxJ5ZcVbMQ07c -# +9Rq4iTccmNE9oV4H0ZRRCrJbNfIart3je0/aSeOpo1WGwZr6hyJEvETGruyDbSW -# uK9kIfAZbVSvpx4clfeP9SIxcyhom1e8xp6daKGCAgswggIHBgkqhkiG9w0BCQYx -# ggH4MIIB9AIBATByMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBD -# b3Jwb3JhdGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2 -# aWNlcyBDQSAtIEcyAhAOz/Q4yP6/NW4E2GqYGxpQMAkGBSsOAwIaBQCgXTAYBgkq -# hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xMzA0MTQxMDU2 -# NTFaMCMGCSqGSIb3DQEJBDEWBBR3BvjwJvcH3OSIjiXabjSSjC3hizANBgkqhkiG -# 9w0BAQEFAASCAQBa1lxJa2qVZmZKDY2Rm7XNJEIoxOzdw+WZViQC9qsi9FrFVPwj -# y3UjlfVm5AynoSXs0+mXgyU4xW3c/xDgFKEyLN+mocD8mx+ufc/bG1l0SWVYITgV -# WiQe5G0oOW/mRQNLhaiJGE3fZuxvTbN0rGYIOeZ5Nsl6yyJpsx96em0skNk2XxVA -# rYoniI/OCP7a9VyqXZr/ScgG18ZreKZikyrjBz6zQh9ibrApLprm/nJ+7go+yeHA -# hqUEwDtiRAyCJfH3DKLPCm6YqWOaT07+LWZSm2DM+mcRydj7gKGoOILfgLhjnUdz -# IJUMgUNRqvIxWuUtU1wAquBSObIOtRD4hkBG -# SIG # End signature block diff --git a/src/support/tools/msxsl.exe b/src/support/tools/msxsl.exe deleted file mode 100644 index 865a7b2bd3354d58b2d858ef9085117e3725c5d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24896 zcmeHvdt6l2+W#JAfDuLq6$%w~s~FzEfk6ck1%VM0bdZ~9UJzyk<krj{@d5-6nB#WR z%C09%EzLZg>V&3g30@K{t+bn$E>>2X;1q@_CN{tCv-b?3W#{+)^LyX(`Mn$0ti9HH z*0Y}V+}5+!nlX9qMn=vsi~`BwV3>W#>4_0P|N7%BntKi2-;3GZ<J6FSvZPZ(((>}n zY^kYai7C5?&B-n<F5%e42G(RLX7h{L_*toJQAw^Lq-RgBKqu<Hd9&AL_Ly|em2yY- zJC}~KV$|?+3&i^9a~9<NM-4f*5cxPpaV|q_mx=l8{G2@Mdpo821cphHDVQG2`Y2c1 zMMf?2mU%J^4bDon{=Ue4kYcEe4|;c#_(3(q7xAgWCkkcRjL0t$kYK5Un0IDW$wXlM z<@B<MVdnIu7w40RQTn+G|Mz(*XeuS}7>0S~FS><rhUFaE?;nN_qKoF*ISIVuVL?@h zDL0$LTox%&5rgD~Gy<vH6N8oz2~29zqY-p4Vcmf68pALRAttja2k+us7vY8Y8jjTM zi2<mdZJVe;9p%x;Q;^^5%+sh|Q8y7}bv`o^X3tDW(uRfP78WwG@forD>CQq)p~3k! zD=8s1J<VCjoIZ2<%!D)%z7xy;zTAle8T@63-=dfpM$f-5{N~Ou3y!4oEh#B{f|8F> zSPdPF)zHoqFnW7JhdwVC5)_L38OD0lr?#Oy&SB|rjPudQFv5?67$$EE@HoroQ7&NY z3GMnk19gfJJE_pAm&4NT1lj{2pb+$TQQj^jBG1Y?aah4@Wr4J|!Q#GeCvYCw8hA@F zlP?cc);cU|K0Z)o9J1Wa*vM#@=2hATt0Omq=K_7qzA;vZm$A`9!<c&%pk3#tVdfNg z*%dl{rjUkltVdND4!=3TUhVJ+Vi<dp?l+U4wV}h>E-#lC$mbNu=Z^l+XbnsOK+IG% zWA0~<RW^Iu^}ieWiAGxle>mz;*=1v+EQH~YEm(M1$$)XyEo;GPiFp~BT1RFV-)t2f zwu*LP?A>T@1^=S1sn^#6IJJw3+Xvpt#K%zP6I6U+JYO2aFHhp<Boa^0>QjX4zdIbj z{iHra_(^)*hlzbFzSb^xpfOeWL~4Ktj3k0R;;aMs*4nG{x=Ud!Mj<J@3zH=&97dj% zwGfj!fk{C@Fc168Ikw6qTV<N9QZGF2$1t`^jjeKwtuk?8qsPl&BA|~DsAE~8R3WSa z8a|psTb5(l7{_OhWg5q4z$4_MyJK0BFqg(z2z|2`2Cl7cc})ghWZ08i1RKb=+spAO z?C--c+6$?+`JBUVBMs9U)mGu1AzU56Fioe4)#H3nU2xU$skPnlyg$^0n^5qpRPv?L z$bu_UiPV%EV6Qz+Txk@uO+euAQvm@~t(U`3gEze}5`1c2#@C(&pgmdtd;mHMBS9Sv zUY<h~9_w&8sKj~O6ZCrFT@;&jk@H7zy&aYj=p(#Kq!D<yJ)s41((9cQn$XADFd$<7 zFs_fimZm8J2yfq%kXEFo3f_3v7x>s~@1y1nVH=hZXsllN7DEA?)8|#d>k40kht{M1 zFsnh`t?xdt$6?`|W;RnQ2~8L~%i*^PvvgA93-3?!nTn}u8=~44s2^*XXMJb+i6erM z*yQl@1@K9jnIbVGlfTaSCURbh7XO1xYz)(^I`}zSo81rYLsrlut8M#2ZNmasFATxj z;**-ek5ix}lU#@PLVZ8Rjmb>Si;0h6^1z-dTXL&ye!FdchiyJY6m`UMzcrcxubBh% z$qlt9T+#FL%mT(ysw5lvw6=kpYHfG3Ot&6USfd!rmjz7TYT`lwCYY6FOK7)NT!BU~ z7?g8&SV-^{9pWo`^9k(>7tA|xgxB&Fts=F}AL@lkU|{n*l9FfGDt&B~N?WDM;pYz= z7*8EpsVG-Jpsbz@IXtk;-8R`)sUXvkyd?QIhhG3-ft^q9I6*cbd<|HePHD?fi8jG| zc<U70Na0;nkJc#%WT<M-JkY<~Gn!DA^>&QUBliM4Ebz#5KD^Fn!4Ye=57YE2Ac*(P zy{*^1D!r`Ndvfm9>#W5qCPuwiX}#W~;s<bBgt*IkU0JCnu)YM=3t(Ii5v=xv<y%0h z_X3f%y+?UpYdhg;CtU5UMe&-f*)6O=l3Gd%d3=&I?+It+@F8cRUVjYYf}2tvpa@YY zpy?$jjl9xWdqQ3FH#8QwEmZG4ZT)d57i9g>of~TX(SuWr{>-A1H#R18z;qaN(DNS( zdZ};oJ@Q8W9RFdnr!ne1uBW`Q6QAX$$che&Cwj@z3u5P!+j*JYqRg1>&?|@>O9b4j zG0NW}msLP7l<=z(U1y-Nop{u>ONn_E?Yt-dfkUqpy<SBxXbeXha|p|k%f~6m$H8w2 zC)5}biw7m)HxyuTwGB31;sWP7B>DK%42MO<gChQ%5Y-!#ch8DUR&k1`Lzc;z^@&h# z?HOa+iwiB9+6McKB+dse+}iM)9%Ne^RQhI55J9V&@HXrbmOHjFE`}ns=Kgvhw$GS@ zNeLeTG1~f~4U00vCo63;0{LW>ZN`WN^X7vPZG$Z~z!s~r#roP}6}DK`7VBe+^|!?; z*TzPyjh(PIcKq7d@ME##WVYBCTWq{7c9N*GB;Zu=K3T=fGBArwzJ2uXrybS`ADOx) z8=_J_v>pSZELU$>O+%-~Sby^@Tj>aV?Ct(g^wfCkj|`V}FE<S^ZkC}@t^Dt1AER|j zH0LpGT2!RzO4RQbVstSW-SjG9Xy+S8A3NQy&*0w^4nRs^YC=c(2=K)g(LBUvR@qkh zG{(id0k*l1&8)DkQfeC_;v(RUVr+5od_o6V?qL;UGyB?B`CD_~b$+y&18l3<=3lT> z^T+E)Om(u|;%<wZ)ciSShFVRWtF}jD+$0hIVW}m8#MA&WS*V3Hw0S3Jt|8?(C59o* zywN&2+7eXj;D%WpvPutP3FnU(<2MBevo?5GkGa|6Vr-#J^GMa?{Cm)|rrCJU%z)g5 zi@mT$k-}4Pj<5sKcvCEj+A~ulQ)9}Z22DJ1r%`)i^dWVP8bl<jYiLbMv~UG(dAT4l zLpamZsfQLN8#k`073@RXePY`ioozji#f8h7K9U;K#KxyO8;A38@rj%|(b5+)M99(5 z)DL7h8GbA-Le}&&I%-d(WRie>rWGb{2{Ga*R%+NlQze?!dp)e}84rYT@m669H_$5d z;?fcmxkRhr&P`0TjDn()#Wu|X>^=kHjiyArS%o4l7+^yPtZyQBE|F8D%x-?-SloD7 zQy>72#Z8bk4Zw>nHKwUAa^N93HF@G4bQ5P<u^56g=f$k{CuAupO(~c{sDBLe3etY0 z!$<-Bq?C?47YQrSWnLGry@18VJ2elhPD4{8kXzejRL-aJUX*!VjI*kMs$LL5+z4bT zl}RLs3aL!`(S#3_-7R;eI+l&#-*+qv6!zaBrWK;Kd6u<Z(Y@(8v8jlf!maI|-J5E} zrV?uEXKnYe4C+FdFE*8;Ntlc!3Q}mE7P&xS@r_)dvZzz@Ow<yHmaMGikW71JQYM8~ zvmmgjBjo}7%mDaRKf6w&x9i5};jx#oDf|b*Tj&5&I<ij|!&DviA;(u$sbsjGj%5mm zUtkPsjY@0*umXsY8~oyUirf_Rp`;HLefZFaFMR|Go4kmB`r3vp&VyI#9Vtr3vPF%$ zIfYO{p$v^;h(}uriU(>iL}oqs$2y||EGsHR0p!Q$W08i#Pj&d^0g9jRlW9-xNFkqS zlcSy1f6D+@kXEB2|A-|YYk=0_M`5OOfrp#0mIo>rZZQ0~QcNPk+6F!;P-T-@Jooit zJgb`Bv<(0iLlu9x=G2T#dwR=k?bW=Epxl-$U@|sKi_Ow%v$Wd~OFX<0Wb^G&zgKu? z3d`W0n>=B$d_oJaw5PYu#>@k$wfq}Cp*8B5x`vJ<sHvTr^2DY_-qMbxGD8SzhjO>_ zUp9LiYa7%xuK-kj&B%KgqoWy1OMxP<3N)k&pY{+Xk9qeu@^MOP<-GJlD0<g4a304J z9I|FbmBGQl6TwqlBcw*Y7f@NR2XP+w9BN6&D4rOFOOi&wDs{Q4!ogTP<=2`_Fe2Kl zfXpi(^WiJv6l)#o_2eP=WWgS*7~Q5n{X6zSW(onW2%)gcW^(UV(!g!tLsk|K`L*-o zNC+fC#9iP4hIA?fc+{=XDEe|SNW?<w5AhbbBgTV({nnEdvBXylqY58~#W&APqfDYz zmdUf=oo^M~Q9{g%AZ$LiDxv_nRBeN}rbQjk$h6JBWJ|bWOAu@ct;8bE+t`?JQVE%U z+&s)Nj`j%lRdZks^RfO)0nSs9BmwngY!x3@t@4NVDJ=oKyugv_*vOKYi7M<nDiIYl zEv1TzOH~i_V=TP^1jcOicLJ4PsCuA(SGA9;`lu7GgE$Oi<#oK=p4^^dZSmnd?3R<; zw{K6~?${`}nl~A(743|=76BJSkXKSLNUO6|y<QUBm2RpBoa*)SM0twUEu?nhW>397 zzN`8oRgaNo9gJ#g#YKi&#wVZ;DUv;1AOKbZP<8eDoT@ca^{?cbEdoL@4?f`%UmEDJ zwAvG1F7Se_elA?|5S3Ps&33|i)Laz4bJk?!RgyG|#mLIHIk38-#e-2*S$rVdYHCSC z3;dZk@Hs3k!V>IGcuR}JB1ql{4hIdNf!Ho}f>^Yq&fH)`6EGB!qL@G&SmZ4ulxMLB zlk+5vEu)G?R7jB@A_{SPi+CG5T%a5Srv@M4!}r#wIA)LF+iM*a{+Yr$ETquKWvaZD zgkX(?k%u0;wmK?4mQBsjUe#WRELCLMlG`gwY=~SDwRKRmF<yR%PgL=GWeWU=B=AxM zg;yZ++0gs#4t=7i5`AbB5yM&&I@q|VIg0Xw%^n$o3Zr9^gQPYdlpOlj-NDHfd8>$; zc4?KTEeYl$hO$+a1DH%sX`eYlc%u!HiVsv#L@PSy0C7^vOkofFXLD}?#U2)rX@Iop zVld<WyiNf|%6Xm0#pN{0Bho11oGJ8-k!FtgQI%Ps$f~~rccpD;dyyN28mhSDs_4ll z_*hwHpBt8zW-szJ$v!rjwqYTR*Xey(3XBnzR^gyY^uispVarN=V?wJU0UiZ@RaEX% z(0<wwyeCW}1r+Pfbk><W>kg8YAhb*n5IFKmFdU|Ru_r@zb`d<+Z+99-c_o}G<UUMq zPi__VK=}x11q)%Dm3b>@;!lB1q&`Jk=`evPglp-<XisQ$jnW-?xG2pbRja^UrG3T- zVeBuUmuRu4dyySY630<y2xGlPDG;DH0lt43QV;{)TQqzS9|w_e3jS=foSz4=q~?_q zbvMu*)PZ^@uY}Q98l1Wyo<~_*RHT^L&_zywN=i!^=N(z5unetwAb_z1WZ0(#>hm5T zbek05z*!)S`cl1K^juJ0oj*TQ!Dsl|=Xa#)Q}RYYBD3K;C;x^iovpo~UjGBwUG;!C z)mbBic+9{t0b4E{C}nrT9C5*T+W{lAVwTeqX?)I?B>I7XBS9y+CrNrbRva|IKEJ*D zpeL?NQ02?I2K)%f9Ayd`v0eDt1&}TRh@-6(M@xd06V7ESaj8qm^T$LoXk=HHp|pkp z%N1cCB4C<ZxmqNSs!|bj@@FB#F1f%UyBE6oq=gIkBQ9ek+j{nAI$g2pTwxKN4d@g$ z51Y={rt`7sR5o4UT3!FOx&do-{x)4a?9ir5!oJg{)7x}u$8`N<bmEYjPDh)*ybgP| z>zGTXo<cewton!{*1s(B4Rci4GZnMNreCj0O%IDrkfX0XQz<o_7n{mgikuFxXR4&? zBT{t^zhDd>yd0~seVUIx)$xfw6(jAC8g={vjeVMLXY~fDdh9w~INXfy+G+mlbmP`e z>$grfe(kjW>vR*=P8+aJ7hyeyC3o$#09zR=<ba!S0Q2B~5vn2n#7V6zn6VymyLJ{K zsrJk@L~hpKdMtCd9?oo*TMuiDw%)O^wGCyLjMgE{^yx<aE+a2P<#eOXW4cZkb=lOi z;0T6Sz;^vcfN-(k@Eb#~sr-F`DtnwvFPw+2P^3DDIH}6y-{VgoYI5V-+qU!XTECXJ zy-*eCF9yhB7x@KyoSR<Q?(8)9PMr`O$;Dn2i&XfKm$P`)HdJ^N$O^og#o)#V`)$Do zh~SvAvgRMrHblZLb2It0K%66Bw%QX2+;lpGY_X;u_?%lNw;p1#L*?D3PBliyGN_H7 z%b>P_bzqGG%W6+>?#8I&+)?%357m1QjXu2K2!BX>!ltw(;-7+!qO6sOgJ(K}c?Z;A zr{H@Xx`_2diIBcq39)^*5`uhJ$sVut!O>J9_(;es`xjzIFjFCHhP%zefUyd`w>W`A z7Zo<nM@bc6E#F%zs~oY41rRfdMmlZR<5ej<L*28E(9sotyjg`ADUH^MiCnLk7*QiE z|3Y-GWJVu4-QIi+Y)!Ez_~OifP7)Cq=HXfoOMwcDr!W{!>%@@)iVqnChNOa&1W(k{ zz~kuZK%h;dV;o&7Scg*uBYch;>u{<;IB-Px80{iuw10n|KtP)^3mAkqQIkhCV*4W~ z;b2BVq|<sUJdFyHpa_y7P-KSoKDAdXRn_0qfm!wY4E7taU}@2m&e-S(CQcC*r_9JK z*W2S6oFphA7`-roJPjg`ES$et)Ooa35mo>vX5V}tudThx1@JQzhrV{_4?%UIxW`4s zb!Y`I^vr@+vdMX~0?Y29@7?8EEB13%BTh~{>^c%;IRq(jPTFSC`Idrv(h-Lqu2UA0 zVUG;VRwb$mY=={};w}Z;6n9eaUv$nZE-<&vCbN8Y=VTOmp@2N;ZIgK!qNmB|GP=!1 z@rT(=LRV=v3&1Gen@<OS6gFR_vjo}{wlt+kwiwBgmSC;BfLJbazKXw${UjHjibt9* za=&m)9viV67GKvkGFl?0X$@RtwSz&lUg2)nshSh`H7XRMj&RDTgR~Tu2kV8GuqU@q zgBy7iFHuKu;w%DLRFafF0{@#|w~63VchF(;;D)EBW<qO8Te}mXt|=qU@rbxD!eNok z;yu;|c;fWgB?gJR(rMj%6*i}nHoJ8a$zOh;JDFIAoMcvlOgB)MDg2DZ7p~4lqf?W{ zf<|Kg%<g2?ULYONWWuFelw?T`zX*|2xMJ(P73oW}I|{d8w3aXbogs?pJ#aa$g1Qk6 zr;`Z=LIxg|VK_6;3zZ-wsu7z+Y4<%yV%82(Gn)Ir0jlgG8+oP8%NAjq><p~C5a|?7 zOiTV0(|r=AzJI_}spyK)t6LHw#8mACYcyTw(8;H-`mqE@R2#O7S8Vf-+U84_5iRPP zr@&^u!k_oVWuOCh3Jr=#f6G-E)}f174W2-}0NG=2>9WoX;CIu{=tQ>}3BD2Y9b7Ly z!I!!w_$q9g<|)pLMtz3ez7Gl~q=1)EM-WM>*Y8I^>~ozB2hr;6=WKr(?N(0(PO(cn zqS`FmfE}BlR4xb#mB+xxkybhU;vp{_F#GgmSca|=Vo<gv_%tt|>!-hfn*=x-$%l*N zD_X245U2d|%C*zeQ?UPX(qwTFTaL>um9uhzXg60P=lfX39IQg{7->;*LsM~Mm2eWe zt`sgKphF}{dY>UgL2$V7#DsD65_N+cj9FOv;xMGyYo;T7ub>BT7H0QLrjS6dwE3Ha z7l-Aj5RM#sC2ZMfgCp+u$>+4=UK%$QLI6<HEet7bDJd-?`0`0oY&Mz#?hXpLeZ}hG z2<IGr6xM*O4#A&>$`XBjvlr3a2e@!5(><;ed(gQ?Efhdm;RY{GyDa#c>K5AaTMbtj zx(;xyZNwmfL|hD|5C;vW8(XYed+6lWv4<Klmc#EpLfY-tv5w-<R*E&JBBIL@2NKs! z@j60U@my=$Xov)-cjWP`(c%(jPXKp2phw~Ysmh~Dn$lYs?Sy#lFF^<`n6wK>T+;yb z1c0z)$uUC-R@$>+86Y0lHsIh6`#f%hxTxFXeOjCQMU^UW^buiKjmHL8d!cOyHwiFS z!6)jl`CF_}fnbVKcmts!-&(+A<lRsE3k$}{0GYxf)YA!0=ZfPfRnaA%>zt0Zh%W2G zEtKf7&S)E&Pt%O~bX6*jXK+MW;bTQxa)+hI>{Q{O;2JG8ocHL@?!_Xs%1z(wHrsia zh_gS7ug$~KOL9IVNvGPI2jKV#TlCCS!4s58J7YvQ(qW=>Oy!F`LTDzxLV?jo*mX*K zq5>z9<7bKIrKCylYW{kZglvh4p4MY5+~&vP;Q+-5p!0H-(U;LWhE)0>oJSaxh<>=o zRBvjCtW>Vm`B?_#k=Y89eidCdmRE~&NvRSi)#b+Mdl^2Y{3Mi2uu@&K8x8{Ou*X3G zaFB=FzT}7*zUo@~k`O-w?i}V1<S7nZtIU9&CP{kdEv<mo_9Bb1YLy=Ymldb1is(-V zC`v1f4YY`iON+!AMumzut4ypIEO&;=yi%5|8kyYDh=CZ!Qqvqv#~l<;cY(Yso1q#h zZtk1adf`Ke9VfQU>SJ-fGE8JNw#3Q4HoS^Av(RXfVcM${d-!*zQn2iHYpE4Ai()F# z460jvVT@pj*3s$W`ODnE%)BblR^XPINjJ_hqT!{10T4qoEp~LrDSiipe3sqbLYBLm zcz!nOs|p-LwF`T2E|S$8dpn{%1nTh90~erxPFPQX2$AXB()mS#i;yR_?22PLrL1#L z2s-{JJ+ajn%90+ufTJ;nIr5TFmU&(%+q9vnY;HkQnanPf9at}vnF@rmJcCfyhLoKz zl+AolC_A%4DBCu%sVr$oQ<>=*p)B&CP&V2uls#A?lwB+p%BEBcWvh%rnet0P#JzFj z<}%G2o6DF!o68nGvbn4QiKQ~0h!MkZ(ZMj4Yj%_!oU*;_`{?arJ=OITznDgE4D;sQ zG0eY^zQ8pk#Ldtho(~d(^kG~K(}1)QsR}6q$qg`c1E@r5pBlp)N7{$97HK(>28r&h zMj&U9T4R9&={2M(q*A02Nc1E#smvUFCgIC(A&ibOF$VmlVbeix*-S1zbD4N%7GUC; z5+;YSFh!^<W;noyBWXlDhw{&@edO}Ty>~2RVjd4m`R-P{|Gt02U-q8^Dw9B!0b`dk zg`kVWd-w5=+}3|pL_}`n_{bb>urV|;EI2eYH#|6d{5WH<MiZ`y9Ip)<7it)XubQ}K za4Wt4RX%7t`Jmn4`D@BMAN97q$uRAMzZr5X-aGN3tAE&E_P-M!y84g5t^X?HIL(9z zqjr37&bV=*#0PC~MC9T~@Ij+7MuZv-VPT=*gA2p0^!``*Ksq#D)Q5oE=(rW{o%qnz zf52b%zY`z2`VYCSzmpGPq5p;tE(~!=sng>zljYF3LTEegYoK|!SrzLfGhPlIq-WyL z7$)4A{vH~`Tpd8)1S0z}QnUD!qD<Cf046i=6f;YhU@<pCmrby$9OU_cDaLnFgPh|A z1D0b}fCB25hff2_LCi=OS}|{sN;=I%vnpWJ`@R419kYvxFN^L@7t!=kSPb)My>{gI zRcCs?-iv9N_2lG5)wh-3N<irCeBPMGFsc}ad97RBH>hJ!H%Zq8cTXb2tVf-Hw>k~# z@=zDvtu769HK_Zu=P2-=A>4RYqR%>{r;&Cd9Yy*QNkF=aqySt2@&M#vNQp>ukqVKj zkRC#M8fhoe0>JqoPeM))b{17QWT`ri<bmXgq(q{&$)Jy(KFHmWTG5Z5tIRY57gthL zlwF*glwWMfWY)?H&77&wP@IWfi43ht*=8=mWGXS?TV7PoFqq8wCB+HLW0{AkoXX`U zqIV$_=6NPzc|NB%mE;)AW~SIJ#bPk6&>Kv~5)%f<G3ZQ&`z?mzoE1#bAKKzdEXAC` z#7uQdwiL$a<0fb%6IWPbHaL;r<CavClU*2FSXh$7Fr%ar5E__;@=Q}cXVB$?gxT`U z{9Hp^UbZQ%1bDdosVg`G6Gu3u_p}lh0CSen5nWs|h|OMNh-H{_x7L6I3uuPPCApSD zCmpCuq&gZcZAGbpdBAP9Av;$boJnBfOonWUlvpOQTPc&d<y+Crd$QSvCF1mE7*;Ts zs5Gn8P>cfc^_DM9GmDwHlJw&I;(RVYyD)zx_#A<n>D|gcC`%(xFE(?;X@<GOcdePF z%yi;&u_*S%5P7<(Fq8S;uV72|@E`Gs_~vs&vHOtz_xb;w0}3%7Mx<JWn??8>rV5wc z%p&AI$Z-{1g{<2n6XWoWcc|a_C-rb9Ov9hl%TRyzPwIPMA!_Y5qVy_bs9qfWmNu!L z&TDUdq<R+hnry6(Ze6XKT>NGQm%OgWA7y+=s`FV}$}lIp!H+--ixh(-V~7Y4-K|}$ z==`XXW>i(h;0+0Xcxh;8Ku$Hd9iT6G;Fdtl@JSa22rE$eho@evxuxz#_oBR>srPQv zb^^$6+_+I+*HvcJb(#b6&Sr-3j?&`7yYrb+uc`CC(KSNU4NaT6+lbaFQ?#TLoljJo zroJ0Kd3}BTfi|uDMpWAkErylfU~XVYdHs~SdaZ=E9`v?}?L=o(>kTb4rS1T=V_f-x zI-;rW2L5S5c+?H8x{f*nL<_>|>ol4hZPX6@n))`hGxc?1e`=@xXrX`TPq?DmB;+D~ zj4ZE{kW+s&Ba#>KWBdcoS$hLHxJLNJS=QIp*VolWX<S^;YA_LvoN>XsSn==5uKq6g z+sm%{Tk?*<4oAD%;b@RM991$Yx!y&EAx)urd|?;K6G`3xl|=q$57kq<=S9HdJ4otw zXX=Q}!zeV8E78cLfMnY&>mzD8S&U><r0Tg)e<M-=-j_g~Ymqbx*cQsk`8lQ%bBU2- zN6a3{&Ph#5W1S&fiHUVad+hMgo?d_rfc~UYv^0@TP6a4yF0q($46K=3QD`vd84Mgd zucVYid}yA}&MVEvj|!ugWfxiuAt52CSYA|EY@S$ZG8prhM_Ww!RNK?5r`Ig0%fy~u zES}&g^yzw1^GeECj((+BBn4_tpc2-I7_2zE$N)$&0%S`pT&aa)xe}JDb436rbP-6* zHc==C@KkyQP6NxCvWv|Wp3;vUr9lAU?6bVgMFa9w&Xixu7MBzUoAZ|x=Nt3!TS<;B z%R}U8F3koDjHVI{BC!MUa5t8?&=H}oCmuB7mzE}jxukHJfyFNX5tC&XvbkUlMRs5y zn+pyVFJZw&vw<zm%Qm|RTjs(-c?N<){Nm5R=9c7GiVVfvUv%WUcN7_0SdyK~61kn6 z$fm(rF24w4o1LVW=6CCe=(pSC*!0;+(%d23;BI=LpH4h=GQ>rj6Q6}$ChZ~!Z8~O` z&6-OMImFe@mJ%bWSaQ-tHYhA)e9#y+NE@P|EF75=PdBPqHa^>&$6Cygd$!p%ZnkNO z$P<?4X=Z`WDJL#BzxdxH>C)=k5kd7OflG*B8q9H|R7w7>>59V=FJUkA3=d_C@(T;| z&4!$k;#_bywQGbw>oYv;&hR-UMWrx+ZrFieduMpVazl>AC0iFg;kUulJOYu@i%lRA zdcG3ulnk1MAcBX728EC#U;&eyZ#I*DIz{ZVMzNi6bhdZR@fMiyZZJ;Eb+xnEM%V+! zD+zLrE@G8_p4x?%teAeCESh^~pQ79P#9$mfc_IVh6dgto=D`+O%;Ha-jV0JqmSLWY zSHKe3py(hr2OG%j9DviXh^d&F=q;Ift|6xojUpreOK6xyz>@}_bISmoRKO|b7;+^l z$TNv_!UnR*{++bLA7_gVe;~z0XV*MM+Q?}-rIgKzewdYJOLC)8ZlT4=WBp&x_ZFP# zv#0-CjA_897hOIp64mWT=92fz4UwFHv^oqAjSj-P5JYYq`_o*h6AIaIHPf9mRylc@ zUrY{P^k0}wH}BJFO|H>OxIFQ9?bxh$vs{cE%dv$9oN=%v#Re7(2V06dr9y54G7(Lk z#!qsNUyAW@Vo{hYdhr}_*Do=YG^=EBfgy(j?&R#Hpvq!`mF8u0l0WNoa4>H0GN;4@ z$#aD(;4QI46&8}g(OP6+i?Va_utBFG$yUTe7`7xQ$ATWYwDiFGX>lN7!W+;T4qNE# z0QiVI^%2RXPNIKh<7Op0DK?9<yW?2I4C>a&TN0<l5Q-!Il0H`-C;#qDo9NYOly1WO zzZj=;PUJ#Fl@Uiux)WVy1IN)Q#OJP&g0OH<Y(OodV}aUr4o-gH4s(<oVV9wbdXB!g zV*ht@cY5eM&7H;g6o$pY<I!@WSw1|hr`K@paE%z6iLo3sR9tBYj?VL_Ku~cdg_fe? ziC7@eBV6j?4}6ol7O}}LJn^VcBetwCb1(&`0pY)hScaoD)<8#rEd7@SFnp7<SZpa; z4BrZhLZwNPeE*08M|Tvc5LYA<ajp7>Tqkch^y{)KCww--q|U|26*#!UgWHXTW%Xpv z_#Z3`!A^_1&F&CQu%NsUq=^1~i;LFV*6!RAgIO}~l433!u?#H^w+1;u^gn0NVJ9gJ zjb^gsh%1BWM_}C)T25pqO}Vuj%i-iOI*6D!cDcDQh|Pn0j1KD3!(fZ4FlfqTNv<w= z1O;1ME1Tq>^0E;*y8Jpuyfse36va?U<ShAMl1ET*X?`aXmuoagQ4=l<CBM@}2iqC; zU@p!Gty5Mf-N;j9;QneMJ`<SPc$<#fw3#SR$EObO^t`S3`FCk=BAq?ZC+$q8h^=&P zt#IyCoE6B%h`X0m+z6PQHwrrBbXPDPw+KeuB`}Oqgwen^YM4l*aGaEq9}36)ql}3I z4Be<@1AYal$wtW_4v-8?IpUp#88AkSL47>X$2CSgAk897J}|iME~s-6>X#!G;$6ci zyY<OH9}`-ndk%sP#f=dqjhJIj;uu6f-SC-^X9G_cW!-TvXGQ^c65y7IP&AuT%!9bG z1h*xko&2GW#r;Yg&dpieXx=_YR^*5YSVGVq2CC?f#Z8T?6bhOriM*TT9Ea|5h(;G* zs3dW*i`Ed(DBTvtGkrj_9(_x&-;{2Bx@HHvMtu?fNFuoyH3>8pqc@9NTImjy<Ul;V z702zIy`8UQA2MiQHb!<)+NC8F&nf_shE^JxWJh<bU2^K7c#Q!5V;=GUqH%L$G^_gs zd-NE!CUMQRo-$9jEvx$tLREh^nJl!IM&+TDTHM^-8O;(8uhAYdg>3Z%H<@BfmL^l9 z?N+1K442E8E$p^IRRPcd`kMt?BI_WFBW;~be;W2yv<jcH7XCBYrHhxdX1DG14nDwd zS?wF4S?%@~(r`DQd;6V<Sid?eVEcUYLcjT+Kd<T8Nv=!*s;Zw29jqDXAx~F$^}P!R z4f(0qS+i*-ipysjxUv${($Kp#egxp%x0efG3_HCzCnR)?W|Y)0%+<iAVV^~Nd+dwg zj#CY$W%<~b&n_w9Lc=v-67=Yqv)H8Ru~VlfO;5{WW8>ly^l1t4W7rWnBO@o!X@^LJ zG=^r--8~~GXvT$x(T%c3GxzSExM2^~ObFACiyRj@_kaEbs-Nr5bD29MueO8lHn-|( z=A#g{HLrY3a7cB)KRsT5(R*+2o|)%U&sx4cp&j-5$5(sI3vX-M_<Il4hZhFSee={0 zSJ(b?bHjtfn=9t{m<yJ_cfa4wV{@*K{QH~*k1K8lFYZ03I^f;=H+?d6&e%^*`nuPQ z-@j?s?&N!$eu^6UkBp}(hdfibrs3XcPZaFlG5(W|9>E{&j{KV&bWYZtJg9^mbY-Xa zn!Kv<kv^5ZeI6Qm^5eR`qvr|nm%Nuh`$A>gi=L)I-(>xKvgTXcrlboqvwyC8;f5|+ zAMX8JX~xxuMm=!%``_i%ugLc_hrIUKh;_gIYu8I>=AZI<*QdvZ_v>CA@%YgdBWoV{ z+_8Vj)WjVdeJ<5!w?C8m-J_??Q`&ECo>lX5`stg!b8<AR6>gf<@}*nkZZbDFALYuT z`$`r+I&1&Yn{i+J`ft9C;{W+N040XbhJ{7ks>I_vm3WKG$h`hwWKQ$+zN2|YX(A=N z3QIGXip;cs!U+tEy-ps-m6|7x9a~mb7P1UxMW<#V*rkj$m1dK*X+lGTG{dR0yzd~F z-Tk=<YgWsKc4s^3@oJeG4p8Rhwpu1*8dhdM*!=p7@|*#q`)7YywbyUp7Jg&&fuKif zd?TA{-yivihcWR5`7FCI>&4br7n~m3ukqo_d!AkO@R0YL9N*4t?RfREIs0;gzklfJ zNdHr%59yEm>+u6I7Y+^=rlx;-we}Z}ANZ#NLfho(kCUDW9QFBt0UK7gz4r5q$=~|E zcyYt0jd_Q&=e%V4xLuPt>)hCq9QK^;-PNlO&v<LZJJoqVe7`#8W!=Or*Qbm>m2@C& z>fDuvRlnC<YMhq#@|)2uRgL|x9o1G>pBVMnjRzJ?d~l=hg_9wcUw&Ttee_F<4o;Qr znzd`*&M8AfpX=jy-Mn?sYF;t=N1a<z(T2hLkjG|>TJBN9ML)b@%(PI^l%B8Nr>Wkf ziSbav)w{cU%47<7J&ndy*2vZbl29O%k{omCtwN!ivDo@>7PF=Ygfg%%jDXR^(<`fp z){vaN7p+N@t!Yn?0kvk7W`ql|o9wQDzmm45FJT$3P-(neUF1rQH&v<?3b~s{LwDgi zy+Cl=teiz-{LlXysGeP?YOZ^0N$ufb|JtDI`9c5WgKgz2SxuOK9{<RJu%}ud-52HO z5wvQun+Nm4kVk6zdcE`HcQKjoxDOXzeYJPNOW#Gk*EH<vrjhew(%ZJDZazMKVx&Q7 zHop*d=GB+$8{H>45?YQ3=L1i_pYuwO7jJws;M<$AUImZC3-rM%_M7Ad)XX7<pEPN0 z@6(g7=Cphja~r{$($LYGk(dzHsiAQtr7KMNOY(4>o->jSjl{Cjb;2H3Vk!*@9i$16 zfc<W5DUr^VLWgJuQ@gyce^)y#V{EL2%PYZ`9acD(rqEE0W`c8R3e$v!X|Xto#s3D1 z{+*G&<o5QV((j{wnK@v@b5AT^p!spzOZMRl+kby7>G{3CZ{EgEE>GF^w{061g)Kcj zHFw2NFE2ZhcK(;<XVwI4c&^4+_s-Ili-&zWaMG7**(1W^N9%))Pd}Y^&r=^x98<5_ zGv}T|x*xnIM?5~}r4f-kTc%s5ep{nH@N{8%_RFiww=N1UOKN&*cW%_v`hZYnpzm`p z{qXQ;|L>!p%<)|`$KCMUzzMS-y!PVHo7|2K_^3WTq0UxSKd~ik)67?HzPPf8oB68$ z$;W$)7{X+3T$DfIz>GeglQJChZoFXhQtmiYoss$T8&M1HsxDKUzk2YMs>gnR?bHLG zzS!S%-=ufnYE?czR8!|sd!mjl>s$Mk(@1w}s<&&Zw~_YC6xB~_s-LLxnfGDo&-tcj zhow~dzMlM$<Grn>|M&7+{cm&}9#_UZCU`g4+n(?r_pg1jz_Vq&+wNNw_T01H?@e}p zc<qK06TctwOKaw%WA<#Bc6{;A9iN?yikkb<__X}r1B<4dIJxUf_wp}7?a|NqlolNL zz0WNF{DzJX<G$@Zm!0+F;#IHi>UVtfgyF#l4O{!H8?MfI{#shV^&uxd^=q5`a&cUk z=grl3w|}>!uxHBEw=d6beEWx^nhrL!$J&99jqIQN$w0U5m#Z$ych9@>&o7Q={$!Zm zI6Li)-SQEA92-AvRc@%<_ryDYpD^aq%1b-TzFoG3`LJNhp)=#xU5xFsb6mlIf(zrm z{y0E!X=j4s_*`v7adJS<#e2QBt^eqgv?;n%0qHwRFZ7xC;G>r3UOck}UV%q!V3xds zSGLC}ySQ!e?(PsM;I7^g>Mc4_g-2hv^wiM4n%-p5%D!HixTQdN%9Rv{`e=GlrDtEy z*@oOATnM-vgIC`>IEFiU846JU;AMtshDctdf0q}bvm=(mY>JTeak1n_5FKd4BEv$% zCWMWr5I0OSj*40;YO1&Xzn)0wA12~-m-0JsMe|ot?n8H#oWJ{{ZxbGS{pF9o`ye!W z;L!&U%uIB@&S$(d`oWyz@#i%seBWwb-hcnmAHOl-t8~V{eYsdy{Oq;({lfka4uA4% z)Z^3kr7SpIwq)q4)KS4d4&6{b)8V#ffOgA|k2U-6Uv@qF4CB3NQFzq!HDl_|J@jZu z@Y0*#rtjLftN3E+m2HbW#!6PdS{8#~Ws)fGfohq|;avCrWM!0#JEDQT6n80nf8H4L z<*bVX>JrX+%2z#g-O|rEuD_=GCGs7^6xCZ1dsg|5yzk8cZ=BNpbl};nifiA!8*;Q~ z_=v6C3;!J+#HYafsaXxom%Dm@d^YCkod>_y+)JbmR>WyyHB+`kZ;4tH>5M>fOobsu zu6t)OEG%7`KNhuPaiErqLp1YPQdQDaXlV!<ZnH4a9VwQa{NRiGlcybi$eQ10>9&QB z-{UE4IQ!AFb1wwt4;*-9Qu>J0FM0<CJ@QIcMoiPu<jpM?{$9E1Z_(_SalZu3Y&agd z*N}61obi0>+p(2d_r5TD@2@wHzrA&p$G#`eKKavW?{JH;=jX8-j=c6rP3EDgD{GHE z-1kD-KfbJt{hWQZX~2oYVWIEXo8J4)I^)osN5hVO_U);afB&jN`OLA}C?U2w;-Aa= zy%llo)Y8N6?@HP@(0c59nJsVE4;ute{iv`nKKtP9lPk|nR6AaeUzd5j|Abu6yDL_i zX6QV&j+i$rb5Oy}hlgroPFrU5krjIksTp6gbCl|V$*+tXeRXezx3cednQb%vv4O4M z2X}M=?&zP+t;!$Rf7^chza>i1AS^+YBJMuOyNnAB#eP~U{^z*K)iSob0!llOp0oo| ziaU^PHGZjQy*0hUA9G9`aJX&ks-g+QlH1O#|L*3iH{Wp!9~{{D-PX_3S}NVRX@9>E zHs>zil#vmaD_`Hee&Vz}ku!_a4u`5H7Tq{?>OK84173OY?74gInX>1d_clEK&GcW2 z&Ne+h`Ahc?T6d&R_{Z2qr>e52Zk?WXuiAg_jB}4ZrI|a;lDqq(17E!Hch%-u`^-`P z6L;-?aQ$m*UrU}fcxLZC+N!U5PRuQdZw!C?-berT){cSK-G|LwG;+hqQNPqY{nS5p zUhpbi^=WwQ#_fBJjrR>0_`J5)-_qp$r#!iF-^m}M6<l1vhHH`E*S(y$a_OjEi)Go7 z%ZhJKe$sP>?`4_prXO>0+WA?R+%HuPcazDVUo9H}egytmxG(?Dh<JQFdN_~C{AAde z$e3=v_|N>Tre9|VUpIwnkQdG|E%bd|CbrxDMyAuK$q)7=zwfS*BkQh9wBe!HDq+qy zPscvcvAI66tn8Kb=f3avPUi0ZZ|{C{q1)Dkg2=4vZw5URocwIZwo4J~B65dL{p!uJ zqdwUCsmHs`qv|jAuPwijqKv*W^rMd(iq=;9#V^dY<{o`%)0lM^Hcq%#y;t}+d&9D2 zU!NQ92&{R`uE<E+v^8Mj<kfHewEe;L0qbY1Sh#2UZwtcmCk{^ghb8%|+#fWPF62&+ zzj5Pez|{M{-x8hh(^BTSm#4nHzjs~6r5m4Y8&!RF@XT%L2ZJ`0?$}m1z>&6Y^;^|5 zc5MBpapg;QzxbX<!?fldbxomusnMep^$v65=X_9HM~?7Q;DhrHjz2Ag{u*{}!B;C* z?$^AWzvkW>>wDJ^u%~5(u2w97Lz|29OWMc&r$kV0-J`%;PF=TDzf20^JfQ6%4^?$L zb5&pq*mWlBEuPqRmHn^*?Chur?W6GdsMp1afzbo@UyN#hT=zxGqZ)m;?yAtKnwTyA zRd?asKNY`anu#;@RGe~4zbG<n4XhgKTz8Aixy83FyoLFTP1&XuxBE9mb=9KdH@}&= zdDt(3iMy5>kG#RJt82-6b{GFH8@qQ_g7=cwl{@D?xNUCHzv2eob85|vPn);gA0F?1 zbzA+xmD_HVuFL(0-;ixyOK-m5x%ruoJ>K2FH{m7kl5e)Qo@*_8-Ep>|&-?BBu8&>w z%z#xj<-g3!a$7iPl*iuJKDfT}=2Jbn3-u1RAY<!lw<PSr6T4X95xUwf5>?|wC8_%# zfB5z%$A`D7NS4OGYj*Nb@6Lj!3<}bz0`8$|F;0&R)rLmm{5fLo9~7iE`F!@h^}D}_ z*s)3R>bxILb$2mXt`w_f%-@eB)IRKav~B<6UoEOpzj@Ddn$kAg$<O|kV$R{$gk||1 zJL<pZ^!cB5hgEp)F8=<PxFPoxRE+xk`Sr1(^WE$>wiG@2%9p(w`^Wod$9{O>o~wPo zj(+}Z;hWlZzm=c==83PK{{EC<Meg?Li)(iH-?!_-y^DLN{&e%6v5ier{@J#$=gOz! z<7f7;md&nH#C~_N|1>@&V*0s<U%%eg^T=wY$CNfrxO~EtKKv_r8=w4c+Mp+6LM-=w zz18dWQ^k|#_bd)y?U=XXsRN(98+Yj&+nUWg6Q2Kh*z4Lo{ZH(g@lA+&6xa4f`Oh;d v_gp*H_otfXH$OPk`_;P*ul;&_Vb832eWt(n%g!~Q=UWFKduOuqhUfnPfVFR9 diff --git a/src/support/transform/labbuilderconfig-schema-transformtomd.xsl b/src/support/transform/labbuilderconfig-schema-transformtomd.xsl deleted file mode 100644 index 8da86023..00000000 --- a/src/support/transform/labbuilderconfig-schema-transformtomd.xsl +++ /dev/null @@ -1,95 +0,0 @@ -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> -<xsl:output method="text" omit-xml-declaration="yes" indent="no"/> -<xsl:variable name="Title1" select="1" /> -<xsl:variable name="Title2" select="1" /> -<xsl:variable name="Title3" select="1" /> -<xsl:variable name="Title4" select="1" /> -<xsl:variable name="Title5" select="1" /> -<xsl:template match="/"> -# LabBuilder Configuration XML File Format -> labbuilderconfig xmlns="labbuilderconfig" - <xsl:for-each select="xs:schema/xs:element/xs:complexType"> - <xsl:for-each select="xs:attribute"> - <xsl:variable name="Title1" select="position()" /> -### <xsl:value-of select="$Title1"/>.0a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute -> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:all/xs:element|xs:sequence/xs:element"> - <xsl:variable name="Title1" select="position()" /> -### <xsl:value-of select="$Title1"/>.0e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element -<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:attribute"> - <xsl:variable name="Title2" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute -> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:all/xs:element|xs:complexType/xs:sequence/xs:element"> - <xsl:variable name="Title2" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element -<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:attribute"> - <xsl:variable name="Title3" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute -> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:all/xs:element|xs:complexType/xs:sequence/xs:element"> - <xsl:variable name="Title3" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element -<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:attribute"> - <xsl:variable name="Title4" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>.<xsl:value-of select="$Title4"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute -> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:all/xs:element|xs:complexType/xs:sequence/xs:element"> - <xsl:variable name="Title4" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>.<xsl:value-of select="$Title4"/>e - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="number(@minOccurs)>0"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Element -<xsl:choose><xsl:when test="@type"><xsl:text>&#13;&#10;</xsl:text>> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - <xsl:for-each select="xs:complexType/xs:attribute"> - <xsl:variable name="Title5" select="position()" /> -### <xsl:value-of select="$Title1"/>.<xsl:value-of select="$Title2"/>.<xsl:value-of select="$Title3"/>.<xsl:value-of select="$Title4"/>.<xsl:value-of select="$Title5"/>a - <xsl:value-of select="translate(@name,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:choose><xsl:when test="@use='required'"> Required</xsl:when><xsl:otherwise> Optional</xsl:otherwise></xsl:choose> Attribute -> <xsl:value-of select="@name"/>="<xsl:value-of select="@type"/>"<xsl:text>&#13;&#10;&#13;&#10;</xsl:text> - <xsl:for-each select="xs:annotation"> - <xsl:for-each select="xs:documentation"><xsl:value-of select="."/><xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - <xsl:for-each select="xs:appinfo">``` <xsl:value-of select="."/> ```<xsl:text>&#13;&#10;</xsl:text></xsl:for-each> - </xsl:for-each> - </xsl:for-each> - </xsl:for-each> - </xsl:for-each> - </xsl:for-each> - </xsl:for-each> - </xsl:for-each> -</xsl:template> -</xsl:stylesheet> \ No newline at end of file diff --git a/src/template/labbuilderconfig-template.xml b/src/template/labbuilderconfig-template.xml deleted file mode 100644 index b8d60925..00000000 --- a/src/template/labbuilderconfig-template.xml +++ /dev/null @@ -1,145 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<labbuilderconfig xmlns="labbuilderconfig" - name="" - version=""> - - <description></description> - - <settings labpath="" /> - - <resources> - <msu name="WMF5.1-WS2012R2-W81" - url="https://download.microsoft.com/download/6/F/5/6F5FF66C-6775-42B0-86C4-47D41F2DA187/W2K12-KB3191565-x64.msu" /> - <msu name="WMF5.0-WS2012-W8" - url="https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/W2K12-KB3134759-x64.msu" /> - <msu name="WMF5.0-WS2008R2-W7" - url="https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win7AndW2K8R2-KB3134760-x64.msu" /> - </resources> - - <switches> - <switch name="External" type="External" > - <adapters> - <adapter name="Cluster" macaddress="00155D010701" vlan="5"/> - <adapter name="Management" macaddress="00155D010702" vlan="6"/> - <adapter name="SMB" macaddress="00155D010703" /> - <adapter name="LM" macaddress="00155D010704" /> - </adapters> - </switch> - </switches> - - <templatevhds isopath="ISOFiles" - vhdpath="VHDFiles" > - <templatevhd name="Windows Server 2012 R2 Datacenter Full" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Full.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTER" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2012 R2 Datacenter Core" - iso="9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2012-r2" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - edition="Windows Server 2012 R2 SERVERDATACENTERCORE" - ostype="Server" - packages="WMF5.1-WS2012R2-W81" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - <templatevhd name="Windows Server 2016 TP5 Datacenter FULL" - iso="14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" - vhd="Windows Server 2016 TP5 Datacenter Full.vhdx" - edition="Windows Server 2016 Technical Preview 4 SERVERDATACENTER" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="40GB" /> - <templatevhd name="Windows Server 2016 TP5 Datacenter CORE" - iso="14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" - vhd="Windows Server 2016 TP5 Datacenter Core.vhdx" - edition="Windows Server 2016 Technical Preview 4 SERVERDATACENTERCORE" - ostype="Server" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - <templatevhd name="Windows 10 Enterprise" - iso="10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise" - vhd="Windows 10 Enterprise.vhdx" - edition="Windows 10 Enterprise" - ostype="Client" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - <templatevhd name="Nano Server 2016 TP5 Basic" - iso="14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO" - url="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview" - vhd="Nano Server 2016 TP5 Basic.vhdx" - ostype="Nano" - packages="Microsoft-NanoServer-Guest-Package.cab" - vhdformat="vhdx" - vhdtype="dynamic" - generation="2" - vhdsize="25GB" /> - </templatevhds> - - <templates> - <template name="Pester Windows Server 2012 R2 Datacenter Full" - vhd="Server 2012 R2 Datacenter Full.vhdx" - sourcevhd="VhdFiles\Windows Server 2012 R2 Datacenter Full.vhdx" - memorystartupbytes="1GB" - dynamicmemoryenabled="Y" - processorcount="1" - administratorpassword="None" - productkey="AAAAA-AAAAA-AAAAA-AAAAA-AAAAA" - timezone="Pacific Standard Time" - ostype="Server" - integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" /> - <template name="Windows Server 2012 R2 Datacenter Core" - vhd="Windows Server 2012 R2 Datacenter Core.vhdx" - templatevhd="Windows Server 2012 R2 Datacenter Core" - memorystartupbytes="1GB" - dynamicmemoryenabled="N" - processorcount="1" - exposevirtualizationextensions="Y" - administratorpassword="None" - productkey="BBBBB-BBBBB-BBBBB-BBBBB-BBBBB" - timezone="Pacific Standard Time" - ostype="Server" - integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" /> - <template name="Windows 10 Enterprise" - templatevhd="Windows 10 Enterprise" - memorystartupbytes="2GB" - processorcount="1" - administratorpassword="None" - productkey="CCCCC-CCCCC-CCCCC-CCCCC-CCCCC" - timezone="Pacific Standard Time" - ostype="Client" - integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" /> - <template name="Pester Nano Server" - vhd="MyNanoServerVHD.vhdx" - sourcevhd="VhdFiles\Nano Server 2016 TP5 Basic.vhdx" - memorystartupbytes="1GB" - processorcount="1" - administratorpassword="None" - timezone="Pacific Standard Time" - ostype="Nano" - integrationservices="Guest Service Interface,Heartbeat,Key-Value Pair Exchange,Shutdown,Time Synchronization,VSS" - packages="Storage"/> - </templates> - - <vms> - </vms> - -</labbuilderconfig> diff --git a/test/Invoke-LabSample.ps1 b/tests/Invoke-LabSample.ps1 similarity index 100% rename from test/Invoke-LabSample.ps1 rename to tests/Invoke-LabSample.ps1 diff --git a/test/pestertestconfig/PesterTestConfig.OK.xml b/tests/pestertestconfig/PesterTestConfig.OK.xml similarity index 100% rename from test/pestertestconfig/PesterTestConfig.OK.xml rename to tests/pestertestconfig/PesterTestConfig.OK.xml diff --git a/test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 b/tests/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 similarity index 100% rename from test/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 rename to tests/pestertestconfig/dsclibrary/PesterTest.DSC.ps1 diff --git a/test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt b/tests/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt rename to tests/pestertestconfig/expectedcontent/ExpectedDSCConfig.txt diff --git a/test/pestertestconfig/expectedcontent/ExpectedDSCModules.json b/tests/pestertestconfig/expectedcontent/ExpectedDSCModules.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedDSCModules.json rename to tests/pestertestconfig/expectedcontent/ExpectedDSCModules.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedResourceISOs.json b/tests/pestertestconfig/expectedcontent/ExpectedResourceISOs.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedResourceISOs.json rename to tests/pestertestconfig/expectedcontent/ExpectedResourceISOs.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedResourceMSUs.json b/tests/pestertestconfig/expectedcontent/ExpectedResourceMSUs.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedResourceMSUs.json rename to tests/pestertestconfig/expectedcontent/ExpectedResourceMSUs.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedResourceModules.json b/tests/pestertestconfig/expectedcontent/ExpectedResourceModules.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedResourceModules.json rename to tests/pestertestconfig/expectedcontent/ExpectedResourceModules.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedSwitches.json b/tests/pestertestconfig/expectedcontent/ExpectedSwitches.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedSwitches.json rename to tests/pestertestconfig/expectedcontent/ExpectedSwitches.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedTemplateVHDs.json b/tests/pestertestconfig/expectedcontent/ExpectedTemplateVHDs.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedTemplateVHDs.json rename to tests/pestertestconfig/expectedcontent/ExpectedTemplateVHDs.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedTemplates.FromVM.json b/tests/pestertestconfig/expectedcontent/ExpectedTemplates.FromVM.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedTemplates.FromVM.json rename to tests/pestertestconfig/expectedcontent/ExpectedTemplates.FromVM.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedTemplates.json b/tests/pestertestconfig/expectedcontent/ExpectedTemplates.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedTemplates.json rename to tests/pestertestconfig/expectedcontent/ExpectedTemplates.json diff --git a/test/pestertestconfig/expectedcontent/ExpectedUnattendFile.xml b/tests/pestertestconfig/expectedcontent/ExpectedUnattendFile.xml similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedUnattendFile.xml rename to tests/pestertestconfig/expectedcontent/ExpectedUnattendFile.xml diff --git a/test/pestertestconfig/expectedcontent/ExpectedVMs.json b/tests/pestertestconfig/expectedcontent/ExpectedVMs.json similarity index 100% rename from test/pestertestconfig/expectedcontent/ExpectedVMs.json rename to tests/pestertestconfig/expectedcontent/ExpectedVMs.json diff --git a/test/pestertestconfig/isofiles/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO b/tests/pestertestconfig/isofiles/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO similarity index 100% rename from test/pestertestconfig/isofiles/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO rename to tests/pestertestconfig/isofiles/10586.0.151029-1700.TH2_RELEASE_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO diff --git a/test/pestertestconfig/isofiles/14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO b/tests/pestertestconfig/isofiles/14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO similarity index 100% rename from test/pestertestconfig/isofiles/14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO rename to tests/pestertestconfig/isofiles/14393.0.161119-1705.RS1_REFRESH_SERVER_EVAL_X64FRE_EN-US.ISO diff --git a/test/pestertestconfig/isofiles/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso b/tests/pestertestconfig/isofiles/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso similarity index 100% rename from test/pestertestconfig/isofiles/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso rename to tests/pestertestconfig/isofiles/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.iso diff --git a/test/pestertestconfig/isofiles/SQLFULL_ENU.iso b/tests/pestertestconfig/isofiles/SQLFULL_ENU.iso similarity index 100% rename from test/pestertestconfig/isofiles/SQLFULL_ENU.iso rename to tests/pestertestconfig/isofiles/SQLFULL_ENU.iso diff --git a/test/pestertestconfig/isofiles/SQLServer2014SP1-FullSlipstream-x64-ENU.iso b/tests/pestertestconfig/isofiles/SQLServer2014SP1-FullSlipstream-x64-ENU.iso similarity index 100% rename from test/pestertestconfig/isofiles/SQLServer2014SP1-FullSlipstream-x64-ENU.iso rename to tests/pestertestconfig/isofiles/SQLServer2014SP1-FullSlipstream-x64-ENU.iso diff --git a/test/pestertestconfig/vhdfiles/DataDisk.vhdx b/tests/pestertestconfig/vhdfiles/DataDisk.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/DataDisk.vhdx rename to tests/pestertestconfig/vhdfiles/DataDisk.vhdx diff --git a/test/pestertestconfig/vhdfiles/Nano Server 2016 Datacenter.vhdx b/tests/pestertestconfig/vhdfiles/Nano Server 2016 Datacenter.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/Nano Server 2016 Datacenter.vhdx rename to tests/pestertestconfig/vhdfiles/Nano Server 2016 Datacenter.vhdx diff --git a/test/pestertestconfig/vhdfiles/Windows 10 Enterprise.vhdx b/tests/pestertestconfig/vhdfiles/Windows 10 Enterprise.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/Windows 10 Enterprise.vhdx rename to tests/pestertestconfig/vhdfiles/Windows 10 Enterprise.vhdx diff --git a/test/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Core.vhdx b/tests/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Core.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Core.vhdx rename to tests/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Core.vhdx diff --git a/test/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Full.vhdx b/tests/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Full.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Full.vhdx rename to tests/pestertestconfig/vhdfiles/Windows Server 2012 R2 Datacenter Full.vhdx diff --git a/test/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Core.vhdx b/tests/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Core.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Core.vhdx rename to tests/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Core.vhdx diff --git a/test/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Full.vhdx b/tests/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Full.vhdx similarity index 100% rename from test/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Full.vhdx rename to tests/pestertestconfig/vhdfiles/Windows Server 2016 Datacenter Full.vhdx diff --git a/test/testhelper/testhelper.psm1 b/tests/testhelper/testhelper.psm1 similarity index 100% rename from test/testhelper/testhelper.psm1 rename to tests/testhelper/testhelper.psm1 diff --git a/tests/unit/CodeCoverage.xml b/tests/unit/CodeCoverage.xml new file mode 100644 index 00000000..321dc10d --- /dev/null +++ b/tests/unit/CodeCoverage.xml @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.1//EN" "report.dtd"><report name="Pester (11/10/2019 09:34:39)"><sessioninfo id="this" start="1573378419142" dump="1573378479804" /><package name="lib/private"><class name="lib/private/Assert-LabValidConfigurationXMLSchema" sourcefilename="Assert-LabValidConfigurationXMLSchema.ps1"><method name="Assert-LabValidConfigurationXMLSchema" desc="()" line="31"><counter type="INSTRUCTION" missed="17" covered="13" /><counter type="LINE" missed="14" covered="13" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="17" covered="13" /><counter type="LINE" missed="14" covered="13" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Assert-LabValidIpAddress" sourcefilename="Assert-LabValidIpAddress.ps1"><method name="Assert-LabValidIpAddress" desc="()" line="30"><counter type="INSTRUCTION" missed="0" covered="9" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="9" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/ConvertTo-LabAbsolutePath" sourcefilename="ConvertTo-LabAbsolutePath.ps1"><method name="ConvertTo-LabAbsolutePath" desc="()" line="29"><counter type="INSTRUCTION" missed="0" covered="3" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="3" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Copy-LabOdjFile" sourcefilename="Copy-LabOdjFile.ps1"><method name="Copy-LabOdjFile" desc="()" line="47"><counter type="INSTRUCTION" missed="59" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="59" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Enable-LabWSMan" sourcefilename="Enable-LabWSMan.ps1"><method name="Enable-LabWSMan" desc="()" line="26"><counter type="INSTRUCTION" missed="7" covered="16" /><counter type="LINE" missed="6" covered="11" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="7" covered="16" /><counter type="LINE" missed="6" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabBuilderModulePath" sourcefilename="Get-LabBuilderModulePath.ps1"><method name="Get-LabBuilderModulePath" desc="()" line="14"><counter type="INSTRUCTION" missed="12" covered="4" /><counter type="LINE" missed="10" covered="4" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="12" covered="4" /><counter type="LINE" missed="10" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabCertificatePsFileContent" sourcefilename="Get-LabCertificatePsFileContent.ps1"><method name="Get-LabCertificatePsFileContent" desc="()" line="47"><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabDSCNetworkingConfig" sourcefilename="Get-LabDSCNetworkingConfig.ps1"><method name="Get-LabDSCNetworkingConfig" desc="()" line="38"><counter type="INSTRUCTION" missed="46" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><method name="&lt;script&gt;" desc="()" line="189"><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="48" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="2" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabIntegrationServiceName" sourcefilename="Get-LabIntegrationServiceName.ps1"><method name="Get-LabIntegrationServiceName" desc="()" line="22"><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabManagementSwitchName" sourcefilename="Get-LabManagementSwitchName.ps1"><method name="Get-LabManagementSwitchName" desc="()" line="32"><counter type="INSTRUCTION" missed="6" covered="0" /><counter type="LINE" missed="5" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="6" covered="0" /><counter type="LINE" missed="5" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabModulesInDSCConfig" sourcefilename="Get-LabModulesInDSCConfig.ps1"><method name="Get-LabModulesInDSCConfig" desc="()" line="52"><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabNextIpAddress" sourcefilename="Get-LabNextIpAddress.ps1"><method name="Get-LabNextIpAddress" desc="()" line="38"><counter type="INSTRUCTION" missed="0" covered="11" /><counter type="LINE" missed="0" covered="11" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="11" /><counter type="LINE" missed="0" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabNextMacAddress" sourcefilename="Get-LabNextMacAddress.ps1"><method name="Get-NextMacAddress" desc="()" line="32"><counter type="INSTRUCTION" missed="0" covered="1" /><counter type="LINE" missed="0" covered="1" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="1" /><counter type="LINE" missed="0" covered="1" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabUnattendFileContent" sourcefilename="Get-LabUnattendFileContent.ps1"><method name="Get-LabUnattendFileContent" desc="()" line="40"><counter type="INSTRUCTION" missed="18" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="18" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabVMManagementIPAddress" sourcefilename="Get-LabVMManagementIPAddress.ps1"><method name="Get-LabVMManagementIPAddress" desc="()" line="38"><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabBootVHD" sourcefilename="Initialize-LabBootVHD.ps1"><method name="Initialize-LabBootVHD" desc="()" line="54"><counter type="INSTRUCTION" missed="120" covered="0" /><counter type="LINE" missed="93" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="120" covered="0" /><counter type="LINE" missed="93" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabDSC" sourcefilename="Initialize-LabDSC.ps1"><method name="Initialize-LabDSC" desc="()" line="46"><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabManagementSwitch" sourcefilename="Initialize-LabManagementSwitch.ps1"><method name="Initialize-LabManagementSwitch" desc="()" line="32"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabVHD" sourcefilename="Initialize-LabVHD.ps1"><method name="Initialize-LabVHD" desc="()" line="115"><counter type="INSTRUCTION" missed="96" covered="0" /><counter type="LINE" missed="75" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="96" covered="0" /><counter type="LINE" missed="75" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabVMPath" sourcefilename="Initialize-LabVMPath.ps1"><method name="Initialize-LabVMPath" desc="()" line="31"><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="10" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="10" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Install-LabHyperV" sourcefilename="Install-LabHyperV.ps1"><method name="Install-LabHyperV" desc="()" line="23"><counter type="INSTRUCTION" missed="7" covered="9" /><counter type="LINE" missed="6" covered="7" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="7" covered="9" /><counter type="LINE" missed="6" covered="7" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Install-LabPackageProvider" sourcefilename="Install-LabPackageProvider.ps1"><method name="Install-LabPackageProvider" desc="()" line="28"><counter type="INSTRUCTION" missed="6" covered="13" /><counter type="LINE" missed="5" covered="10" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="6" covered="13" /><counter type="LINE" missed="5" covered="10" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Invoke-LabDownloadAndUnzipFile" sourcefilename="Invoke-LabDownloadAndUnzipFile.ps1"><method name="Invoke-LabDownloadAndUnzipFile" desc="()" line="26"><counter type="INSTRUCTION" missed="0" covered="33" /><counter type="LINE" missed="0" covered="27" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="33" /><counter type="LINE" missed="0" covered="27" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Invoke-LabDownloadResourceModule" sourcefilename="Invoke-LabDownloadResourceModule.ps1"><method name="Invoke-LabDownloadResourceModule" desc="()" line="72"><counter type="INSTRUCTION" missed="1" covered="50" /><counter type="LINE" missed="1" covered="39" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="1" covered="50" /><counter type="LINE" missed="1" covered="39" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/New-LabCredential" sourcefilename="New-LabCredential.ps1"><method name="New-LabCredential" desc="()" line="22"><counter type="INSTRUCTION" missed="0" covered="4" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="4" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/New-LabException" sourcefilename="New-LabException.ps1"><method name="New-LabException" desc="()" line="54"><counter type="INSTRUCTION" missed="1" covered="4" /><counter type="LINE" missed="1" covered="4" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="1" covered="4" /><counter type="LINE" missed="1" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/New-LabHostSelfSignedCertificate" sourcefilename="New-LabHostSelfSignedCertificate.ps1"><method name="New-LabHostSelfSignedCertificate" desc="()" line="41"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/New-LabVMInitializationFile" sourcefilename="New-LabVMInitializationFile.ps1"><method name="New-LabVMInitializationFile" desc="()" line="42"><counter type="INSTRUCTION" missed="45" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="45" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Recieve-LabSelfSignedCertificate" sourcefilename="Recieve-LabSelfSignedCertificate.ps1"><method name="Recieve-LabSelfSignedCertificate" desc="()" line="50"><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Register-LabPackageSource" sourcefilename="Register-LabPackageSource.ps1"><method name="Register-LabPackageSource" desc="()" line="28"><counter type="INSTRUCTION" missed="12" covered="25" /><counter type="LINE" missed="10" covered="23" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="12" covered="25" /><counter type="LINE" missed="10" covered="23" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Request-LabSelfSignedCertificate" sourcefilename="Request-LabSelfSignedCertificate.ps1"><method name="Request-LabSelfSignedCertificate" desc="()" line="50"><counter type="INSTRUCTION" missed="87" covered="0" /><counter type="LINE" missed="61" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="87" covered="0" /><counter type="LINE" missed="61" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Set-LabDSC" sourcefilename="Set-LabDSC.ps1"><method name="Set-LabDSC" desc="()" line="45"><counter type="INSTRUCTION" missed="37" covered="0" /><counter type="LINE" missed="31" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="37" covered="0" /><counter type="LINE" missed="31" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Set-LabModulesInDSCConfig" sourcefilename="Set-LabModulesInDSCConfig.ps1"><method name="Set-LabModulesInDSCConfig" desc="()" line="58"><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="28" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="28" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Set-LabSwitchAdapter" sourcefilename="Set-LabSwitchAdapter.ps1"><method name="Set-LabSwitchAdapter" desc="()" line="60"><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Start-LabDSC" sourcefilename="Start-LabDSC.ps1"><method name="Start-LabDSC" desc="()" line="51"><counter type="INSTRUCTION" missed="108" covered="0" /><counter type="LINE" missed="82" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="108" covered="0" /><counter type="LINE" missed="82" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabDSC" sourcefilename="Update-LabDSC.ps1"><method name="Update-LabDSC" desc="()" line="45"><counter type="INSTRUCTION" missed="175" covered="0" /><counter type="LINE" missed="141" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="175" covered="0" /><counter type="LINE" missed="141" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabVMDataDisk" sourcefilename="Update-LabVMDataDisk.ps1"><method name="Update-LabVMDataDisk" desc="()" line="50"><counter type="INSTRUCTION" missed="146" covered="0" /><counter type="LINE" missed="115" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="146" covered="0" /><counter type="LINE" missed="115" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabVMDvdDrive" sourcefilename="Update-LabVMDvdDrive.ps1"><method name="Update-LabVMDvdDrive" desc="()" line="49"><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="20" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="20" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabVMIntegrationService" sourcefilename="Update-LabVMIntegrationService.ps1"><method name="Update-LabVMIntegrationService" desc="()" line="55"><counter type="INSTRUCTION" missed="19" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="19" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Wait-LabVMInitializationComplete" sourcefilename="Wait-LabVMInitializationComplete.ps1"><method name="Wait-LabVMInitializationComplete" desc="()" line="47"><counter type="INSTRUCTION" missed="55" covered="0" /><counter type="LINE" missed="40" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="55" covered="0" /><counter type="LINE" missed="40" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Wait-LabVMOff" sourcefilename="Wait-LabVMOff.ps1"><method name="Wait-LabVMOff" desc="()" line="21"><counter type="INSTRUCTION" missed="4" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="4" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Wait-LabVMStarted" sourcefilename="Wait-LabVMStarted.ps1"><method name="Wait-LabVMStarted" desc="()" line="22"><counter type="INSTRUCTION" missed="20" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="20" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Write-LabMessage" sourcefilename="Write-LabMessage.ps1"><method name="Write-LabMessage" desc="()" line="51"><counter type="INSTRUCTION" missed="0" covered="12" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="12" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><sourcefile name="Assert-LabValidConfigurationXMLSchema.ps1"><line nr="31" mi="0" ci="1" /><line nr="32" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="34" mi="0" ci="1" /><line nr="37" mi="0" ci="1" /><line nr="38" mi="0" ci="1" /><line nr="39" mi="0" ci="1" /><line nr="40" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="44" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="53" mi="0" ci="1" /><line nr="57" mi="0" ci="1" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="74" mi="0" ci="1" /><line nr="78" mi="0" ci="1" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><counter type="INSTRUCTION" missed="17" covered="13" /><counter type="LINE" missed="14" covered="13" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Assert-LabValidIpAddress.ps1"><line nr="30" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="34" mi="0" ci="1" /><line nr="35" mi="0" ci="1" /><line nr="36" mi="0" ci="2" /><line nr="38" mi="0" ci="1" /><line nr="40" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="9" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="ConvertTo-LabAbsolutePath.ps1"><line nr="29" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="36" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="3" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Copy-LabOdjFile.ps1"><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="64" mi="2" ci="0" /><line nr="65" mi="3" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="78" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="97" mi="2" ci="0" /><line nr="98" mi="3" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="115" mi="2" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="125" mi="3" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="133" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="135" mi="2" ci="0" /><line nr="138" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><counter type="INSTRUCTION" missed="59" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Enable-LabWSMan.ps1"><line nr="26" mi="0" ci="2" /><line nr="28" mi="0" ci="2" /><line nr="32" mi="0" ci="1" /><line nr="36" mi="0" ci="1" /><line nr="43" mi="0" ci="2" /><line nr="45" mi="0" ci="1" /><line nr="46" mi="0" ci="1" /><line nr="47" mi="0" ci="1" /><line nr="48" mi="0" ci="2" /><line nr="50" mi="0" ci="1" /><line nr="55" mi="0" ci="2" /><line nr="59" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="2" ci="0" /><line nr="68" mi="1" ci="0" /><counter type="INSTRUCTION" missed="7" covered="16" /><counter type="LINE" missed="6" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabBuilderModulePath.ps1"><line nr="14" mi="0" ci="1" /><line nr="16" mi="0" ci="1" /><line nr="18" mi="1" ci="0" /><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="21" mi="2" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="26" mi="0" ci="1" /><line nr="28" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="31" mi="2" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="36" mi="0" ci="1" /><counter type="INSTRUCTION" missed="12" covered="4" /><counter type="LINE" missed="10" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabCertificatePsFileContent.ps1"><line nr="47" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabDSCNetworkingConfig.ps1"><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="123" mi="2" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="133" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="176" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><counter type="INSTRUCTION" missed="48" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="2" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabIntegrationServiceName.ps1"><line nr="22" mi="1" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="26" mi="1" ci="0" /><line nr="27" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="2" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabManagementSwitchName.ps1"><line nr="32" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="39" mi="2" ci="0" /><line nr="41" mi="1" ci="0" /><counter type="INSTRUCTION" missed="6" covered="0" /><counter type="LINE" missed="5" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabModulesInDSCConfig.ps1"><line nr="52" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabNextIpAddress.ps1"><line nr="38" mi="0" ci="1" /><line nr="42" mi="0" ci="1" /><line nr="43" mi="0" ci="1" /><line nr="45" mi="0" ci="1" /><line nr="47" mi="0" ci="1" /><line nr="49" mi="0" ci="1" /><line nr="50" mi="0" ci="1" /><line nr="51" mi="0" ci="1" /><line nr="55" mi="0" ci="1" /><line nr="56" mi="0" ci="1" /><line nr="60" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="11" /><counter type="LINE" missed="0" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabNextMacAddress.ps1"><line nr="32" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="1" /><counter type="LINE" missed="0" covered="1" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabUnattendFileContent.ps1"><line nr="40" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><counter type="INSTRUCTION" missed="18" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVMManagementIPAddress.ps1"><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabBootVHD.ps1"><line nr="54" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="90" mi="2" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="95" mi="2" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="107" mi="2" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="121" mi="2" ci="0" /><line nr="123" mi="2" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="145" mi="2" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="159" mi="2" ci="0" /><line nr="161" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="2" ci="0" /><line nr="167" mi="1" ci="0" /><line nr="170" mi="2" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="191" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="194" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="2" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="203" mi="2" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="208" mi="2" ci="0" /><line nr="211" mi="1" ci="0" /><line nr="214" mi="2" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="227" mi="2" ci="0" /><line nr="229" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="246" mi="2" ci="0" /><line nr="249" mi="2" ci="0" /><line nr="251" mi="2" ci="0" /><line nr="254" mi="1" ci="0" /><line nr="258" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="268" mi="1" ci="0" /><line nr="271" mi="2" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="280" mi="2" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="288" mi="2" ci="0" /><line nr="290" mi="1" ci="0" /><line nr="291" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="298" mi="1" ci="0" /><line nr="299" mi="2" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="308" mi="2" ci="0" /><line nr="310" mi="1" ci="0" /><line nr="311" mi="1" ci="0" /><counter type="INSTRUCTION" missed="120" covered="0" /><counter type="LINE" missed="93" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabDSC.ps1"><line nr="46" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabManagementSwitch.ps1"><line nr="32" mi="1" ci="0" /><line nr="34" mi="2" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="85" mi="1" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVHD.ps1"><line nr="115" mi="2" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="120" mi="2" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="131" mi="2" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="143" mi="2" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="150" mi="2" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="155" mi="2" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="168" mi="2" ci="0" /><line nr="169" mi="3" ci="0" /><line nr="171" mi="2" ci="0" /><line nr="174" mi="2" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="186" mi="2" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="204" mi="1" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="225" mi="1" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="231" mi="1" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="238" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="243" mi="1" ci="0" /><line nr="244" mi="2" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="251" mi="2" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="254" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="270" mi="2" ci="0" /><line nr="271" mi="1" ci="0" /><line nr="273" mi="2" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="285" mi="1" ci="0" /><line nr="290" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="299" mi="2" ci="0" /><line nr="305" mi="2" ci="0" /><line nr="307" mi="1" ci="0" /><line nr="308" mi="1" ci="0" /><line nr="309" mi="1" ci="0" /><line nr="310" mi="2" ci="0" /><line nr="313" mi="1" ci="0" /><line nr="317" mi="1" ci="0" /><line nr="323" mi="2" ci="0" /><line nr="330" mi="1" ci="0" /><counter type="INSTRUCTION" missed="96" covered="0" /><counter type="LINE" missed="75" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVMPath.ps1"><line nr="31" mi="2" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="38" mi="2" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="45" mi="2" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="52" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="59" mi="2" ci="0" /><line nr="61" mi="1" ci="0" /><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="10" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Install-LabHyperV.ps1"><line nr="23" mi="0" ci="2" /><line nr="26" mi="0" ci="1" /><line nr="27" mi="0" ci="1" /><line nr="28" mi="0" ci="1" /><line nr="30" mi="0" ci="2" /><line nr="32" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="2" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><counter type="INSTRUCTION" missed="7" covered="9" /><counter type="LINE" missed="6" covered="7" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Install-LabPackageProvider.ps1"><line nr="28" mi="0" ci="2" /><line nr="29" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="35" mi="0" ci="1" /><line nr="36" mi="0" ci="2" /><line nr="38" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="42" mi="0" ci="1" /><line nr="45" mi="0" ci="2" /><line nr="48" mi="0" ci="1" /><line nr="57" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><counter type="INSTRUCTION" missed="6" covered="13" /><counter type="LINE" missed="5" covered="10" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Invoke-LabDownloadAndUnzipFile.ps1"><line nr="26" mi="0" ci="1" /><line nr="28" mi="0" ci="2" /><line nr="30" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="32" mi="0" ci="1" /><line nr="33" mi="0" ci="2" /><line nr="36" mi="0" ci="1" /><line nr="39" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="44" mi="0" ci="1" /><line nr="49" mi="0" ci="1" /><line nr="52" mi="0" ci="2" /><line nr="57" mi="0" ci="1" /><line nr="64" mi="0" ci="1" /><line nr="65" mi="0" ci="1" /><line nr="66" mi="0" ci="1" /><line nr="67" mi="0" ci="2" /><line nr="69" mi="0" ci="1" /><line nr="72" mi="0" ci="1" /><line nr="74" mi="0" ci="2" /><line nr="80" mi="0" ci="1" /><line nr="88" mi="0" ci="1" /><line nr="89" mi="0" ci="1" /><line nr="90" mi="0" ci="1" /><line nr="91" mi="0" ci="2" /><line nr="93" mi="0" ci="1" /><line nr="98" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="33" /><counter type="LINE" missed="0" covered="27" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Invoke-LabDownloadResourceModule.ps1"><line nr="72" mi="0" ci="2" /><line nr="75" mi="0" ci="1" /><line nr="77" mi="0" ci="1" /><line nr="78" mi="0" ci="3" /><line nr="81" mi="0" ci="1" /><line nr="83" mi="0" ci="1" /><line nr="85" mi="0" ci="1" /><line nr="86" mi="0" ci="3" /><line nr="89" mi="0" ci="1" /><line nr="93" mi="0" ci="1" /><line nr="94" mi="0" ci="1" /><line nr="97" mi="0" ci="1" /><line nr="101" mi="0" ci="1" /><line nr="103" mi="0" ci="2" /><line nr="107" mi="0" ci="1" /><line nr="111" mi="0" ci="2" /><line nr="114" mi="0" ci="2" /><line nr="116" mi="0" ci="1" /><line nr="121" mi="0" ci="1" /><line nr="125" mi="0" ci="1" /><line nr="127" mi="0" ci="1" /><line nr="129" mi="1" ci="0" /><line nr="132" mi="0" ci="1" /><line nr="133" mi="0" ci="1" /><line nr="138" mi="0" ci="2" /><line nr="145" mi="0" ci="1" /><line nr="151" mi="0" ci="1" /><line nr="156" mi="0" ci="2" /><line nr="158" mi="0" ci="1" /><line nr="161" mi="0" ci="1" /><line nr="162" mi="0" ci="1" /><line nr="165" mi="0" ci="1" /><line nr="168" mi="0" ci="1" /><line nr="169" mi="0" ci="1" /><line nr="175" mi="0" ci="1" /><line nr="179" mi="0" ci="1" /><line nr="180" mi="0" ci="1" /><line nr="181" mi="0" ci="1" /><line nr="182" mi="0" ci="2" /><line nr="185" mi="0" ci="1" /><counter type="INSTRUCTION" missed="1" covered="50" /><counter type="LINE" missed="1" covered="39" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="New-LabCredential.ps1"><line nr="22" mi="0" ci="1" /><line nr="24" mi="0" ci="2" /><line nr="26" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="4" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="New-LabException.ps1"><line nr="54" mi="0" ci="1" /><line nr="56" mi="0" ci="1" /><line nr="59" mi="0" ci="1" /><line nr="62" mi="1" ci="0" /><line nr="67" mi="0" ci="1" /><counter type="INSTRUCTION" missed="1" covered="4" /><counter type="LINE" missed="1" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="New-LabHostSelfSignedCertificate.ps1"><line nr="41" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="2" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="96" mi="2" ci="0" /><line nr="98" mi="2" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="New-LabVMInitializationFile.ps1"><line nr="42" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="169" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="178" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="183" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="192" mi="1" ci="0" /><line nr="193" mi="2" ci="0" /><line nr="198" mi="2" ci="0" /><counter type="INSTRUCTION" missed="45" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Recieve-LabSelfSignedCertificate.ps1"><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="57" mi="2" ci="0" /><line nr="58" mi="3" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="70" mi="2" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="83" mi="3" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="96" mi="2" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="106" mi="3" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="2" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="123" mi="2" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="134" mi="3" ci="0" /><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Register-LabPackageSource.ps1"><line nr="28" mi="0" ci="1" /><line nr="29" mi="0" ci="1" /><line nr="30" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="32" mi="0" ci="1" /><line nr="35" mi="0" ci="1" /><line nr="36" mi="0" ci="1" /><line nr="37" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="43" mi="0" ci="1" /><line nr="45" mi="0" ci="1" /><line nr="46" mi="0" ci="1" /><line nr="47" mi="0" ci="1" /><line nr="50" mi="0" ci="1" /><line nr="52" mi="0" ci="1" /><line nr="54" mi="0" ci="1" /><line nr="55" mi="0" ci="1" /><line nr="59" mi="0" ci="2" /><line nr="62" mi="0" ci="1" /><line nr="71" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="84" mi="0" ci="1" /><line nr="85" mi="0" ci="1" /><line nr="88" mi="0" ci="2" /><line nr="91" mi="0" ci="1" /><line nr="102" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><counter type="INSTRUCTION" missed="12" covered="25" /><counter type="LINE" missed="10" covered="23" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Request-LabSelfSignedCertificate.ps1"><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="69" mi="3" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="81" mi="2" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="90" mi="2" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="95" mi="2" ci="0" /><line nr="96" mi="3" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="125" mi="2" ci="0" /><line nr="126" mi="3" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="148" mi="2" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="154" mi="3" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="167" mi="2" ci="0" /><line nr="170" mi="1" ci="0" /><line nr="176" mi="2" ci="0" /><line nr="177" mi="3" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="186" mi="1" ci="0" /><line nr="187" mi="2" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="194" mi="2" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="1" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="202" mi="3" ci="0" /><counter type="INSTRUCTION" missed="87" covered="0" /><counter type="LINE" missed="61" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Set-LabDSC.ps1"><line nr="45" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="101" mi="2" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><counter type="INSTRUCTION" missed="37" covered="0" /><counter type="LINE" missed="31" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Set-LabModulesInDSCConfig.ps1"><line nr="58" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="2" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="84" mi="2" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="2" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="119" mi="2" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="28" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Set-LabSwitchAdapter.ps1"><line nr="60" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Start-LabDSC.ps1"><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="61" mi="3" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="95" mi="3" ci="0" /><line nr="99" mi="2" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="104" mi="2" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="126" mi="2" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="135" mi="2" ci="0" /><line nr="136" mi="3" ci="0" /><line nr="139" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="146" mi="2" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="2" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="172" mi="1" ci="0" /><line nr="176" mi="2" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="189" mi="2" ci="0" /><line nr="192" mi="1" ci="0" /><line nr="197" mi="1" ci="0" /><line nr="201" mi="2" ci="0" /><line nr="202" mi="3" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="209" mi="1" ci="0" /><line nr="210" mi="1" ci="0" /><line nr="211" mi="1" ci="0" /><line nr="212" mi="2" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="219" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="224" mi="2" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><counter type="INSTRUCTION" missed="108" covered="0" /><counter type="LINE" missed="82" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabDSC.ps1"><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="3" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="97" mi="3" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="125" mi="2" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="2" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="156" mi="2" ci="0" /><line nr="159" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="169" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="186" mi="2" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="191" mi="2" ci="0" /><line nr="194" mi="1" ci="0" /><line nr="197" mi="1" ci="0" /><line nr="199" mi="2" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="205" mi="2" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="223" mi="2" ci="0" /><line nr="226" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="231" mi="2" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="245" mi="1" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="248" mi="2" ci="0" /><line nr="251" mi="2" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="258" mi="2" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="261" mi="1" ci="0" /><line nr="262" mi="1" ci="0" /><line nr="263" mi="2" ci="0" /><line nr="266" mi="1" ci="0" /><line nr="270" mi="2" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="287" mi="1" ci="0" /><line nr="291" mi="2" ci="0" /><line nr="295" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="298" mi="1" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="305" mi="1" ci="0" /><line nr="306" mi="1" ci="0" /><line nr="307" mi="1" ci="0" /><line nr="308" mi="2" ci="0" /><line nr="311" mi="1" ci="0" /><line nr="316" mi="1" ci="0" /><line nr="322" mi="1" ci="0" /><line nr="324" mi="1" ci="0" /><line nr="326" mi="2" ci="0" /><line nr="329" mi="1" ci="0" /><line nr="333" mi="1" ci="0" /><line nr="336" mi="1" ci="0" /><line nr="337" mi="1" ci="0" /><line nr="343" mi="1" ci="0" /><line nr="347" mi="1" ci="0" /><line nr="349" mi="1" ci="0" /><line nr="354" mi="1" ci="0" /><line nr="357" mi="1" ci="0" /><line nr="360" mi="1" ci="0" /><line nr="365" mi="2" ci="0" /><line nr="367" mi="1" ci="0" /><line nr="368" mi="1" ci="0" /><line nr="369" mi="1" ci="0" /><line nr="370" mi="2" ci="0" /><line nr="372" mi="1" ci="0" /><line nr="376" mi="1" ci="0" /><line nr="380" mi="2" ci="0" /><line nr="383" mi="2" ci="0" /><line nr="384" mi="1" ci="0" /><line nr="389" mi="1" ci="0" /><line nr="392" mi="1" ci="0" /><line nr="397" mi="1" ci="0" /><line nr="399" mi="2" ci="0" /><line nr="400" mi="1" ci="0" /><line nr="405" mi="1" ci="0" /><line nr="408" mi="1" ci="0" /><counter type="INSTRUCTION" missed="175" covered="0" /><counter type="LINE" missed="141" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabVMDataDisk.ps1"><line nr="50" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="129" mi="2" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="149" mi="2" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="161" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="168" mi="2" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="182" mi="2" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="203" mi="1" ci="0" /><line nr="204" mi="1" ci="0" /><line nr="205" mi="2" ci="0" /><line nr="208" mi="1" ci="0" /><line nr="210" mi="2" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="214" mi="1" ci="0" /><line nr="215" mi="2" ci="0" /><line nr="218" mi="1" ci="0" /><line nr="222" mi="2" ci="0" /><line nr="225" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="237" mi="1" ci="0" /><line nr="238" mi="1" ci="0" /><line nr="239" mi="2" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="248" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="261" mi="2" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="269" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="271" mi="1" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="279" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="287" mi="1" ci="0" /><line nr="292" mi="2" ci="0" /><line nr="295" mi="1" ci="0" /><line nr="300" mi="2" ci="0" /><line nr="302" mi="2" ci="0" /><line nr="305" mi="1" ci="0" /><line nr="313" mi="2" ci="0" /><line nr="316" mi="1" ci="0" /><line nr="326" mi="1" ci="0" /><line nr="328" mi="1" ci="0" /><line nr="329" mi="1" ci="0" /><line nr="330" mi="1" ci="0" /><line nr="331" mi="1" ci="0" /><line nr="334" mi="1" ci="0" /><line nr="336" mi="1" ci="0" /><line nr="337" mi="1" ci="0" /><line nr="341" mi="2" ci="0" /><line nr="344" mi="1" ci="0" /><line nr="349" mi="2" ci="0" /><line nr="352" mi="1" ci="0" /><line nr="360" mi="1" ci="0" /><line nr="364" mi="3" ci="0" /><line nr="367" mi="2" ci="0" /><line nr="374" mi="2" ci="0" /><line nr="375" mi="1" ci="0" /><line nr="377" mi="1" ci="0" /><line nr="378" mi="1" ci="0" /><line nr="379" mi="1" ci="0" /><line nr="380" mi="1" ci="0" /><line nr="381" mi="1" ci="0" /><line nr="382" mi="1" ci="0" /><line nr="383" mi="1" ci="0" /><line nr="385" mi="1" ci="0" /><line nr="387" mi="1" ci="0" /><line nr="388" mi="1" ci="0" /><line nr="392" mi="4" ci="0" /><line nr="394" mi="1" ci="0" /><counter type="INSTRUCTION" missed="146" covered="0" /><counter type="LINE" missed="115" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabVMDvdDrive.ps1"><line nr="49" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="58" mi="2" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="87" mi="2" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="97" mi="2" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="20" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabVMIntegrationService.ps1"><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="3" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="78" mi="2" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="88" mi="2" ci="0" /><line nr="90" mi="2" ci="0" /><counter type="INSTRUCTION" missed="19" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Wait-LabVMInitializationComplete.ps1"><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="72" mi="2" ci="0" /><line nr="73" mi="3" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="86" mi="2" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="93" mi="2" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="98" mi="2" ci="0" /><line nr="99" mi="3" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="113" mi="2" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="122" mi="2" ci="0" /><line nr="123" mi="3" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="140" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><counter type="INSTRUCTION" missed="55" covered="0" /><counter type="LINE" missed="40" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Wait-LabVMOff.ps1"><line nr="21" mi="1" ci="0" /><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><counter type="INSTRUCTION" missed="4" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Wait-LabVMStarted.ps1"><line nr="22" mi="2" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="26" mi="2" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="33" mi="4" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="3" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="43" mi="1" ci="0" /><counter type="INSTRUCTION" missed="20" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Write-LabMessage.ps1"><line nr="51" mi="0" ci="1" /><line nr="53" mi="0" ci="1" /><line nr="57" mi="0" ci="1" /><line nr="63" mi="0" ci="2" /><line nr="69" mi="0" ci="2" /><line nr="75" mi="0" ci="2" /><line nr="81" mi="0" ci="2" /><line nr="87" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="12" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><counter type="INSTRUCTION" missed="1357" covered="207" /><counter type="LINE" missed="1081" covered="172" /><counter type="METHOD" missed="29" covered="15" /><counter type="CLASS" missed="28" covered="15" /></package><package name="lib/public"><class name="lib/public/Connect-LabVm" sourcefilename="Connect-LabVm.ps1"><method name="Connect-LabVM" desc="()" line="19"><counter type="INSTRUCTION" missed="42" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="42" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Disconnect-LabVm" sourcefilename="Disconnect-LabVm.ps1"><method name="Disconnect-LabVM" desc="()" line="12"><counter type="INSTRUCTION" missed="28" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="28" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-Lab" sourcefilename="Get-Lab.ps1"><method name="Get-Lab" desc="()" line="30"><counter type="INSTRUCTION" missed="24" covered="40" /><counter type="LINE" missed="21" covered="38" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="24" covered="40" /><counter type="LINE" missed="21" covered="38" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/public/Get-LabResourceIso" sourcefilename="Get-LabResourceIso.ps1"><method name="Get-LabResourceISO" desc="()" line="26"><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabResourceModule" sourcefilename="Get-LabResourceModule.ps1"><method name="Get-LabResourceModule" desc="()" line="19"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabResourceMsu" sourcefilename="Get-LabResourceMsu.ps1"><method name="Get-LabResourceMSU" desc="()" line="19"><counter type="INSTRUCTION" missed="25" covered="0" /><counter type="LINE" missed="22" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="25" covered="0" /><counter type="LINE" missed="22" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabSwitch" sourcefilename="Get-LabSwitch.ps1"><method name="Get-LabSwitch" desc="()" line="19"><counter type="INSTRUCTION" missed="58" covered="0" /><counter type="LINE" missed="48" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="58" covered="0" /><counter type="LINE" missed="48" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabVm" sourcefilename="Get-LabVm.ps1"><method name="Get-LabVM" desc="()" line="30"><counter type="INSTRUCTION" missed="473" covered="0" /><counter type="LINE" missed="413" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="473" covered="0" /><counter type="LINE" missed="413" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabVMTemplate" sourcefilename="Get-LabVMTemplate.ps1"><method name="Get-LabVMTemplate" desc="()" line="24"><counter type="INSTRUCTION" missed="130" covered="0" /><counter type="LINE" missed="112" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="130" covered="0" /><counter type="LINE" missed="112" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabVmTemplateVhd" sourcefilename="Get-LabVmTemplateVhd.ps1"><method name="Get-LabVMTemplateVHD" desc="()" line="20"><counter type="INSTRUCTION" missed="150" covered="0" /><counter type="LINE" missed="127" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="150" covered="0" /><counter type="LINE" missed="127" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabResourceIso" sourcefilename="Initialize-LabResourceIso.ps1"><method name="Initialize-LabResourceISO" desc="()" line="23"><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="26" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="26" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabResourceModule" sourcefilename="Initialize-LabResourceModule.ps1"><method name="Initialize-LabResourceModule" desc="()" line="23"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="15" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="15" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabResourceMsu" sourcefilename="Initialize-LabResourceMsu.ps1"><method name="Initialize-LabResourceMSU" desc="()" line="23"><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabSwitch" sourcefilename="Initialize-LabSwitch.ps1"><method name="Initialize-LabSwitch" desc="()" line="23"><counter type="INSTRUCTION" missed="168" covered="0" /><counter type="LINE" missed="131" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="168" covered="0" /><counter type="LINE" missed="131" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabVm" sourcefilename="Initialize-LabVm.ps1"><method name="Initialize-LabVM" desc="()" line="23"><counter type="INSTRUCTION" missed="121" covered="0" /><counter type="LINE" missed="87" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="121" covered="0" /><counter type="LINE" missed="87" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabVmTemplate" sourcefilename="Initialize-LabVmTemplate.ps1"><method name="Initialize-LabVMTemplate" desc="()" line="27"><counter type="INSTRUCTION" missed="86" covered="0" /><counter type="LINE" missed="66" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="86" covered="0" /><counter type="LINE" missed="66" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabVmTemplateVhd" sourcefilename="Initialize-LabVmTemplateVhd.ps1"><method name="Initialize-LabVMTemplateVHD" desc="()" line="22"><counter type="INSTRUCTION" missed="171" covered="0" /><counter type="LINE" missed="138" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="171" covered="0" /><counter type="LINE" missed="138" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Install-Lab" sourcefilename="Install-Lab.ps1"><method name="Install-Lab" desc="()" line="44"><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Install-LabVm" sourcefilename="Install-LabVm.ps1"><method name="Install-LabVM" desc="()" line="18"><counter type="INSTRUCTION" missed="34" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="34" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/New-Lab" sourcefilename="New-Lab.ps1"><method name="New-Lab" desc="()" line="53"><counter type="INSTRUCTION" missed="36" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="36" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabSwitch" sourcefilename="Remove-LabSwitch.ps1"><method name="Remove-LabSwitch" desc="()" line="29"><counter type="INSTRUCTION" missed="39" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="39" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabVm" sourcefilename="Remove-LabVm.ps1"><method name="Remove-LabVM" desc="()" line="27"><counter type="INSTRUCTION" missed="30" covered="0" /><counter type="LINE" missed="21" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="30" covered="0" /><counter type="LINE" missed="21" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabVMTemplate" sourcefilename="Remove-LabVMTemplate.ps1"><method name="Remove-LabVMTemplate" desc="()" line="23"><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabVmTemplateVhd" sourcefilename="Remove-LabVmTemplateVhd.ps1"><method name="Remove-LabVMTemplateVHD" desc="()" line="22"><counter type="INSTRUCTION" missed="14" covered="0" /><counter type="LINE" missed="11" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="14" covered="0" /><counter type="LINE" missed="11" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Start-Lab" sourcefilename="Start-Lab.ps1"><method name="Start-Lab" desc="()" line="35"><counter type="INSTRUCTION" missed="66" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="66" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Stop-Lab" sourcefilename="Stop-Lab.ps1"><method name="Stop-Lab" desc="()" line="31"><counter type="INSTRUCTION" missed="53" covered="0" /><counter type="LINE" missed="37" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="53" covered="0" /><counter type="LINE" missed="37" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Uninstall-Lab" sourcefilename="Uninstall-Lab.ps1"><method name="Uninstall-Lab" desc="()" line="53"><counter type="INSTRUCTION" missed="40" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="40" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Update-Lab" sourcefilename="Update-Lab.ps1"><method name="Update-Lab" desc="()" line="30"><counter type="INSTRUCTION" missed="5" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="5" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><sourcefile name="Connect-LabVm.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="21" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="26" mi="2" ci="0" /><line nr="27" mi="3" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="48" mi="3" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="88" mi="2" ci="0" /><line nr="93" mi="2" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><counter type="INSTRUCTION" missed="42" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Disconnect-LabVm.ps1"><line nr="12" mi="1" ci="0" /><line nr="17" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="58" mi="3" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="61" mi="3" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><counter type="INSTRUCTION" missed="28" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-Lab.ps1"><line nr="30" mi="0" ci="1" /><line nr="32" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="37" mi="0" ci="2" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="48" mi="0" ci="1" /><line nr="50" mi="0" ci="1" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="61" mi="0" ci="1" /><line nr="64" mi="0" ci="1" /><line nr="70" mi="0" ci="1" /><line nr="71" mi="0" ci="1" /><line nr="72" mi="0" ci="1" /><line nr="75" mi="0" ci="1" /><line nr="77" mi="0" ci="1" /><line nr="78" mi="0" ci="1" /><line nr="80" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="94" mi="0" ci="1" /><line nr="95" mi="0" ci="1" /><line nr="97" mi="0" ci="1" /><line nr="99" mi="1" ci="0" /><line nr="103" mi="0" ci="1" /><line nr="106" mi="0" ci="1" /><line nr="109" mi="0" ci="1" /><line nr="111" mi="1" ci="0" /><line nr="115" mi="0" ci="1" /><line nr="119" mi="0" ci="1" /><line nr="121" mi="0" ci="1" /><line nr="123" mi="1" ci="0" /><line nr="127" mi="0" ci="1" /><line nr="128" mi="0" ci="1" /><line nr="131" mi="0" ci="1" /><line nr="133" mi="0" ci="1" /><line nr="135" mi="0" ci="2" /><line nr="139" mi="0" ci="1" /><line nr="140" mi="0" ci="1" /><line nr="143" mi="0" ci="1" /><line nr="145" mi="0" ci="1" /><line nr="147" mi="1" ci="0" /><line nr="151" mi="0" ci="1" /><line nr="152" mi="0" ci="1" /><line nr="159" mi="0" ci="1" /><line nr="161" mi="0" ci="1" /><line nr="163" mi="0" ci="1" /><line nr="166" mi="0" ci="1" /><line nr="168" mi="0" ci="1" /><line nr="172" mi="0" ci="1" /><counter type="INSTRUCTION" missed="24" covered="40" /><counter type="LINE" missed="21" covered="38" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabResourceIso.ps1"><line nr="26" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="2" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="2" ci="0" /><line nr="91" mi="1" ci="0" /><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabResourceModule.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="2" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabResourceMsu.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="2" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><counter type="INSTRUCTION" missed="25" covered="0" /><counter type="LINE" missed="22" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabSwitch.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="21" mi="1" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="29" mi="2" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="78" mi="2" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="87" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="93" mi="2" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="2" ci="0" /><line nr="115" mi="1" ci="0" /><counter type="INSTRUCTION" missed="58" covered="0" /><counter type="LINE" missed="48" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVm.ps1"><line nr="30" mi="1" ci="0" /><line nr="32" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="79" mi="2" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="2" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="2" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="177" mi="1" ci="0" /><line nr="178" mi="1" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="194" mi="1" ci="0" /><line nr="197" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="201" mi="1" ci="0" /><line nr="203" mi="1" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="216" mi="1" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="225" mi="1" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="229" mi="1" ci="0" /><line nr="232" mi="2" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="237" mi="1" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="248" mi="1" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="266" mi="1" ci="0" /><line nr="267" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="271" mi="1" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="284" mi="2" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="289" mi="1" ci="0" /><line nr="291" mi="1" ci="0" /><line nr="293" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="298" mi="1" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="303" mi="2" ci="0" /><line nr="306" mi="1" ci="0" /><line nr="313" mi="1" ci="0" /><line nr="315" mi="1" ci="0" /><line nr="317" mi="1" ci="0" /><line nr="321" mi="1" ci="0" /><line nr="325" mi="1" ci="0" /><line nr="328" mi="1" ci="0" /><line nr="330" mi="1" ci="0" /><line nr="335" mi="1" ci="0" /><line nr="337" mi="1" ci="0" /><line nr="342" mi="2" ci="0" /><line nr="344" mi="1" ci="0" /><line nr="345" mi="1" ci="0" /><line nr="346" mi="1" ci="0" /><line nr="347" mi="2" ci="0" /><line nr="350" mi="1" ci="0" /><line nr="355" mi="1" ci="0" /><line nr="357" mi="1" ci="0" /><line nr="362" mi="1" ci="0" /><line nr="364" mi="1" ci="0" /><line nr="369" mi="2" ci="0" /><line nr="371" mi="1" ci="0" /><line nr="372" mi="1" ci="0" /><line nr="373" mi="1" ci="0" /><line nr="374" mi="2" ci="0" /><line nr="377" mi="1" ci="0" /><line nr="382" mi="1" ci="0" /><line nr="384" mi="2" ci="0" /><line nr="388" mi="2" ci="0" /><line nr="391" mi="2" ci="0" /><line nr="394" mi="1" ci="0" /><line nr="396" mi="1" ci="0" /><line nr="410" mi="1" ci="0" /><line nr="412" mi="1" ci="0" /><line nr="413" mi="1" ci="0" /><line nr="414" mi="1" ci="0" /><line nr="415" mi="2" ci="0" /><line nr="418" mi="1" ci="0" /><line nr="421" mi="1" ci="0" /><line nr="423" mi="1" ci="0" /><line nr="424" mi="1" ci="0" /><line nr="425" mi="1" ci="0" /><line nr="426" mi="2" ci="0" /><line nr="429" mi="1" ci="0" /><line nr="437" mi="1" ci="0" /><line nr="438" mi="1" ci="0" /><line nr="439" mi="1" ci="0" /><line nr="440" mi="2" ci="0" /><line nr="443" mi="1" ci="0" /><line nr="447" mi="2" ci="0" /><line nr="451" mi="1" ci="0" /><line nr="453" mi="2" ci="0" /><line nr="455" mi="1" ci="0" /><line nr="457" mi="1" ci="0" /><line nr="458" mi="1" ci="0" /><line nr="459" mi="1" ci="0" /><line nr="460" mi="2" ci="0" /><line nr="463" mi="1" ci="0" /><line nr="465" mi="1" ci="0" /><line nr="469" mi="1" ci="0" /><line nr="471" mi="2" ci="0" /><line nr="473" mi="1" ci="0" /><line nr="475" mi="1" ci="0" /><line nr="476" mi="1" ci="0" /><line nr="477" mi="1" ci="0" /><line nr="478" mi="2" ci="0" /><line nr="481" mi="1" ci="0" /><line nr="483" mi="1" ci="0" /><line nr="487" mi="1" ci="0" /><line nr="489" mi="1" ci="0" /><line nr="496" mi="1" ci="0" /><line nr="500" mi="1" ci="0" /><line nr="502" mi="1" ci="0" /><line nr="503" mi="1" ci="0" /><line nr="504" mi="1" ci="0" /><line nr="505" mi="2" ci="0" /><line nr="508" mi="1" ci="0" /><line nr="511" mi="1" ci="0" /><line nr="513" mi="1" ci="0" /><line nr="514" mi="1" ci="0" /><line nr="515" mi="1" ci="0" /><line nr="516" mi="2" ci="0" /><line nr="519" mi="1" ci="0" /><line nr="524" mi="1" ci="0" /><line nr="526" mi="2" ci="0" /><line nr="532" mi="1" ci="0" /><line nr="534" mi="1" ci="0" /><line nr="539" mi="2" ci="0" /><line nr="541" mi="1" ci="0" /><line nr="542" mi="1" ci="0" /><line nr="543" mi="1" ci="0" /><line nr="544" mi="2" ci="0" /><line nr="547" mi="1" ci="0" /><line nr="551" mi="1" ci="0" /><line nr="555" mi="1" ci="0" /><line nr="557" mi="2" ci="0" /><line nr="558" mi="1" ci="0" /><line nr="560" mi="1" ci="0" /><line nr="561" mi="1" ci="0" /><line nr="562" mi="1" ci="0" /><line nr="563" mi="2" ci="0" /><line nr="566" mi="1" ci="0" /><line nr="571" mi="1" ci="0" /><line nr="572" mi="4" ci="0" /><line nr="575" mi="1" ci="0" /><line nr="576" mi="1" ci="0" /><line nr="577" mi="1" ci="0" /><line nr="578" mi="2" ci="0" /><line nr="581" mi="1" ci="0" /><line nr="584" mi="2" ci="0" /><line nr="588" mi="1" ci="0" /><line nr="589" mi="1" ci="0" /><line nr="591" mi="1" ci="0" /><line nr="593" mi="1" ci="0" /><line nr="596" mi="1" ci="0" /><line nr="599" mi="1" ci="0" /><line nr="605" mi="1" ci="0" /><line nr="607" mi="1" ci="0" /><line nr="612" mi="2" ci="0" /><line nr="614" mi="1" ci="0" /><line nr="617" mi="1" ci="0" /><line nr="618" mi="1" ci="0" /><line nr="619" mi="1" ci="0" /><line nr="620" mi="2" ci="0" /><line nr="623" mi="1" ci="0" /><line nr="627" mi="1" ci="0" /><line nr="628" mi="1" ci="0" /><line nr="631" mi="2" ci="0" /><line nr="635" mi="1" ci="0" /><line nr="637" mi="1" ci="0" /><line nr="639" mi="1" ci="0" /><line nr="641" mi="1" ci="0" /><line nr="645" mi="1" ci="0" /><line nr="650" mi="2" ci="0" /><line nr="652" mi="1" ci="0" /><line nr="653" mi="1" ci="0" /><line nr="654" mi="1" ci="0" /><line nr="655" mi="2" ci="0" /><line nr="658" mi="1" ci="0" /><line nr="663" mi="1" ci="0" /><line nr="665" mi="1" ci="0" /><line nr="667" mi="1" ci="0" /><line nr="669" mi="1" ci="0" /><line nr="673" mi="1" ci="0" /><line nr="678" mi="1" ci="0" /><line nr="680" mi="1" ci="0" /><line nr="681" mi="1" ci="0" /><line nr="682" mi="1" ci="0" /><line nr="683" mi="2" ci="0" /><line nr="686" mi="1" ci="0" /><line nr="689" mi="2" ci="0" /><line nr="691" mi="1" ci="0" /><line nr="692" mi="1" ci="0" /><line nr="693" mi="1" ci="0" /><line nr="694" mi="2" ci="0" /><line nr="697" mi="1" ci="0" /><line nr="702" mi="1" ci="0" /><line nr="705" mi="1" ci="0" /><line nr="707" mi="1" ci="0" /><line nr="709" mi="1" ci="0" /><line nr="711" mi="1" ci="0" /><line nr="717" mi="1" ci="0" /><line nr="720" mi="1" ci="0" /><line nr="722" mi="1" ci="0" /><line nr="723" mi="1" ci="0" /><line nr="724" mi="1" ci="0" /><line nr="725" mi="2" ci="0" /><line nr="728" mi="1" ci="0" /><line nr="731" mi="2" ci="0" /><line nr="733" mi="1" ci="0" /><line nr="734" mi="1" ci="0" /><line nr="735" mi="1" ci="0" /><line nr="736" mi="2" ci="0" /><line nr="739" mi="1" ci="0" /><line nr="742" mi="1" ci="0" /><line nr="744" mi="1" ci="0" /><line nr="745" mi="1" ci="0" /><line nr="746" mi="1" ci="0" /><line nr="747" mi="2" ci="0" /><line nr="750" mi="1" ci="0" /><line nr="755" mi="1" ci="0" /><line nr="757" mi="1" ci="0" /><line nr="763" mi="2" ci="0" /><line nr="767" mi="2" ci="0" /><line nr="770" mi="1" ci="0" /><line nr="772" mi="1" ci="0" /><line nr="774" mi="2" ci="0" /><line nr="776" mi="1" ci="0" /><line nr="778" mi="1" ci="0" /><line nr="782" mi="1" ci="0" /><line nr="784" mi="1" ci="0" /><line nr="786" mi="2" ci="0" /><line nr="788" mi="1" ci="0" /><line nr="790" mi="1" ci="0" /><line nr="794" mi="1" ci="0" /><line nr="796" mi="1" ci="0" /><line nr="798" mi="2" ci="0" /><line nr="800" mi="1" ci="0" /><line nr="802" mi="1" ci="0" /><line nr="806" mi="1" ci="0" /><line nr="808" mi="2" ci="0" /><line nr="810" mi="1" ci="0" /><line nr="812" mi="1" ci="0" /><line nr="819" mi="2" ci="0" /><line nr="821" mi="1" ci="0" /><line nr="822" mi="1" ci="0" /><line nr="823" mi="1" ci="0" /><line nr="824" mi="2" ci="0" /><line nr="827" mi="1" ci="0" /><line nr="830" mi="1" ci="0" /><line nr="832" mi="1" ci="0" /><line nr="834" mi="1" ci="0" /><line nr="838" mi="1" ci="0" /><line nr="840" mi="1" ci="0" /><line nr="842" mi="1" ci="0" /><line nr="844" mi="1" ci="0" /><line nr="848" mi="1" ci="0" /><line nr="850" mi="1" ci="0" /><line nr="852" mi="1" ci="0" /><line nr="854" mi="1" ci="0" /><line nr="856" mi="1" ci="0" /><line nr="860" mi="1" ci="0" /><line nr="862" mi="1" ci="0" /><line nr="864" mi="1" ci="0" /><line nr="866" mi="1" ci="0" /><line nr="868" mi="1" ci="0" /><line nr="872" mi="1" ci="0" /><line nr="874" mi="1" ci="0" /><line nr="876" mi="1" ci="0" /><line nr="878" mi="1" ci="0" /><line nr="880" mi="1" ci="0" /><line nr="884" mi="1" ci="0" /><line nr="886" mi="1" ci="0" /><line nr="888" mi="1" ci="0" /><line nr="890" mi="1" ci="0" /><line nr="892" mi="1" ci="0" /><line nr="896" mi="1" ci="0" /><line nr="898" mi="1" ci="0" /><line nr="900" mi="1" ci="0" /><line nr="904" mi="1" ci="0" /><line nr="906" mi="1" ci="0" /><line nr="908" mi="1" ci="0" /><line nr="910" mi="1" ci="0" /><line nr="912" mi="1" ci="0" /><line nr="916" mi="1" ci="0" /><line nr="918" mi="1" ci="0" /><line nr="920" mi="1" ci="0" /><line nr="922" mi="1" ci="0" /><line nr="924" mi="1" ci="0" /><line nr="928" mi="1" ci="0" /><line nr="930" mi="1" ci="0" /><line nr="932" mi="1" ci="0" /><line nr="934" mi="1" ci="0" /><line nr="936" mi="1" ci="0" /><line nr="940" mi="1" ci="0" /><line nr="942" mi="1" ci="0" /><line nr="945" mi="1" ci="0" /><line nr="947" mi="1" ci="0" /><line nr="949" mi="1" ci="0" /><line nr="953" mi="1" ci="0" /><line nr="954" mi="1" ci="0" /><line nr="955" mi="1" ci="0" /><line nr="956" mi="1" ci="0" /><line nr="957" mi="1" ci="0" /><line nr="958" mi="1" ci="0" /><line nr="959" mi="1" ci="0" /><line nr="960" mi="1" ci="0" /><line nr="961" mi="1" ci="0" /><line nr="962" mi="1" ci="0" /><line nr="963" mi="1" ci="0" /><line nr="964" mi="1" ci="0" /><line nr="965" mi="1" ci="0" /><line nr="966" mi="1" ci="0" /><line nr="967" mi="1" ci="0" /><line nr="968" mi="1" ci="0" /><line nr="969" mi="1" ci="0" /><line nr="970" mi="1" ci="0" /><line nr="971" mi="1" ci="0" /><line nr="972" mi="1" ci="0" /><line nr="973" mi="1" ci="0" /><line nr="974" mi="1" ci="0" /><line nr="975" mi="1" ci="0" /><line nr="976" mi="1" ci="0" /><line nr="977" mi="1" ci="0" /><line nr="978" mi="1" ci="0" /><line nr="981" mi="1" ci="0" /><line nr="984" mi="2" ci="0" /><line nr="988" mi="1" ci="0" /><counter type="INSTRUCTION" missed="473" covered="0" /><counter type="LINE" missed="413" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVMTemplate.ps1"><line nr="24" mi="1" ci="0" /><line nr="26" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="2" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="2" ci="0" /><line nr="53" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="97" mi="2" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="120" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="2" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="161" mi="1" ci="0" /><line nr="167" mi="2" ci="0" /><line nr="169" mi="1" ci="0" /><line nr="170" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="172" mi="2" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="186" mi="1" ci="0" /><line nr="191" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="201" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="203" mi="2" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="210" mi="1" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="216" mi="1" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="224" mi="1" ci="0" /><line nr="226" mi="2" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="234" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="238" mi="1" ci="0" /><line nr="240" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="244" mi="1" ci="0" /><line nr="246" mi="1" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="251" mi="1" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="255" mi="2" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="261" mi="1" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="267" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="272" mi="2" ci="0" /><line nr="274" mi="1" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="292" mi="1" ci="0" /><line nr="295" mi="1" ci="0" /><counter type="INSTRUCTION" missed="130" covered="0" /><counter type="LINE" missed="112" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVmTemplateVhd.ps1"><line nr="20" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="32" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="43" mi="2" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="72" mi="2" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="100" mi="2" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="118" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="138" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="146" mi="2" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="159" mi="2" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="174" mi="2" ci="0" /><line nr="176" mi="1" ci="0" /><line nr="177" mi="1" ci="0" /><line nr="178" mi="2" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="183" mi="1" ci="0" /><line nr="185" mi="2" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="191" mi="1" ci="0" /><line nr="192" mi="2" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="209" mi="2" ci="0" /><line nr="211" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="214" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="216" mi="2" ci="0" /><line nr="219" mi="1" ci="0" /><line nr="223" mi="1" ci="0" /><line nr="224" mi="1" ci="0" /><line nr="226" mi="2" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="231" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="233" mi="2" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="240" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="243" mi="2" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="248" mi="1" ci="0" /><line nr="250" mi="1" ci="0" /><line nr="252" mi="2" ci="0" /><line nr="254" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="256" mi="1" ci="0" /><line nr="257" mi="2" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="264" mi="1" ci="0" /><line nr="266" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="272" mi="1" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="279" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="285" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="287" mi="2" ci="0" /><line nr="289" mi="1" ci="0" /><counter type="INSTRUCTION" missed="150" covered="0" /><counter type="LINE" missed="127" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabResourceIso.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="2" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="70" mi="2" ci="0" /><line nr="73" mi="1" ci="0" /><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="26" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabResourceModule.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="2" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="15" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabResourceMsu.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="35" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabSwitch.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="32" mi="2" ci="0" /><line nr="38" mi="4" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="61" mi="2" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="70" mi="2" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="88" mi="2" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="95" mi="3" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="109" mi="4" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="2" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="176" mi="2" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="181" mi="2" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="184" mi="2" ci="0" /><line nr="186" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="189" mi="2" ci="0" /><line nr="192" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="3" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="201" mi="2" ci="0" /><line nr="204" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="209" mi="1" ci="0" /><line nr="214" mi="2" ci="0" /><line nr="219" mi="1" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="223" mi="1" ci="0" /><line nr="224" mi="2" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="229" mi="1" ci="0" /><line nr="230" mi="3" ci="0" /><line nr="231" mi="1" ci="0" /><line nr="233" mi="1" ci="0" /><line nr="234" mi="1" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="236" mi="2" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="241" mi="2" ci="0" /><line nr="246" mi="1" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="252" mi="2" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="264" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="266" mi="2" ci="0" /><line nr="269" mi="1" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="291" mi="1" ci="0" /><line nr="293" mi="1" ci="0" /><line nr="295" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="297" mi="1" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="303" mi="1" ci="0" /><line nr="307" mi="1" ci="0" /><line nr="309" mi="1" ci="0" /><line nr="310" mi="1" ci="0" /><line nr="314" mi="1" ci="0" /><counter type="INSTRUCTION" missed="168" covered="0" /><counter type="LINE" missed="131" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVm.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="2" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="3" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="86" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="90" mi="2" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="100" mi="2" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="121" mi="2" ci="0" /><line nr="126" mi="2" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="139" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="158" mi="2" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="167" mi="4" ci="0" /><line nr="168" mi="2" ci="0" /><line nr="170" mi="2" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="177" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="183" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="186" mi="2" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="1" ci="0" /><line nr="198" mi="2" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="227" mi="3" ci="0" /><line nr="229" mi="2" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="237" mi="1" ci="0" /><line nr="240" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="245" mi="2" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="251" mi="3" ci="0" /><line nr="253" mi="2" ci="0" /><line nr="256" mi="1" ci="0" /><line nr="262" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="267" mi="1" ci="0" /><line nr="268" mi="1" ci="0" /><line nr="272" mi="2" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="281" mi="2" ci="0" /><line nr="285" mi="1" ci="0" /><line nr="287" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="293" mi="1" ci="0" /><line nr="294" mi="1" ci="0" /><line nr="299" mi="6" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="305" mi="1" ci="0" /><line nr="307" mi="3" ci="0" /><line nr="308" mi="1" ci="0" /><line nr="309" mi="1" ci="0" /><line nr="314" mi="1" ci="0" /><counter type="INSTRUCTION" missed="121" covered="0" /><counter type="LINE" missed="87" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVmTemplate.ps1"><line nr="27" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="50" mi="2" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="62" mi="2" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="96" mi="2" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="118" mi="2" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="2" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="2" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="2" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="161" mi="2" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="170" mi="2" ci="0" /><line nr="172" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="182" mi="2" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="191" mi="2" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="200" mi="2" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="208" mi="1" ci="0" /><line nr="209" mi="1" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="216" mi="2" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><counter type="INSTRUCTION" missed="86" covered="0" /><counter type="LINE" missed="66" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVmTemplateVhd.ps1"><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="92" mi="2" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="122" mi="2" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="127" mi="2" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="133" mi="1" ci="0" /><line nr="138" mi="1" ci="0" /><line nr="139" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="151" mi="2" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="159" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="170" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="176" mi="1" ci="0" /><line nr="178" mi="2" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="206" mi="2" ci="0" /><line nr="208" mi="2" ci="0" /><line nr="210" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="224" mi="1" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="232" mi="2" ci="0" /><line nr="234" mi="2" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="243" mi="2" ci="0" /><line nr="245" mi="1" ci="0" /><line nr="246" mi="1" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="248" mi="2" ci="0" /><line nr="251" mi="1" ci="0" /><line nr="253" mi="2" ci="0" /><line nr="256" mi="2" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="261" mi="2" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="264" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="266" mi="2" ci="0" /><line nr="269" mi="1" ci="0" /><line nr="271" mi="2" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="279" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="289" mi="1" ci="0" /><line nr="290" mi="1" ci="0" /><line nr="291" mi="2" ci="0" /><line nr="294" mi="1" ci="0" /><line nr="297" mi="1" ci="0" /><line nr="298" mi="2" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="303" mi="2" ci="0" /><line nr="306" mi="1" ci="0" /><line nr="308" mi="2" ci="0" /><line nr="311" mi="1" ci="0" /><line nr="312" mi="1" ci="0" /><line nr="318" mi="1" ci="0" /><line nr="321" mi="1" ci="0" /><line nr="325" mi="2" ci="0" /><line nr="329" mi="1" ci="0" /><line nr="334" mi="2" ci="0" /><line nr="336" mi="1" ci="0" /><line nr="342" mi="1" ci="0" /><line nr="346" mi="1" ci="0" /><line nr="347" mi="1" ci="0" /><line nr="348" mi="1" ci="0" /><line nr="349" mi="2" ci="0" /><line nr="352" mi="1" ci="0" /><line nr="357" mi="2" ci="0" /><line nr="360" mi="1" ci="0" /><counter type="INSTRUCTION" missed="171" covered="0" /><counter type="LINE" missed="138" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Install-Lab.ps1"><line nr="44" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="95" mi="2" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="100" mi="2" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="112" mi="2" ci="0" /><line nr="114" mi="2" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="126" mi="2" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="172" mi="1" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="189" mi="2" ci="0" /><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Install-LabVm.ps1"><line nr="18" mi="1" ci="0" /><line nr="21" mi="2" ci="0" /><line nr="23" mi="2" ci="0" /><line nr="26" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="33" mi="3" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="2" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="45" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="2" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="72" mi="2" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><counter type="INSTRUCTION" missed="34" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="New-Lab.ps1"><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="73" mi="2" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="154" mi="2" ci="0" /><counter type="INSTRUCTION" missed="36" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabSwitch.ps1"><line nr="29" mi="1" ci="0" /><line nr="32" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="58" mi="2" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="130" mi="2" ci="0" /><line nr="133" mi="1" ci="0" /><counter type="INSTRUCTION" missed="39" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabVm.ps1"><line nr="27" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="47" mi="3" ci="0" /><line nr="50" mi="2" ci="0" /><line nr="52" mi="2" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="62" mi="2" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="83" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><counter type="INSTRUCTION" missed="30" covered="0" /><counter type="LINE" missed="21" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabVMTemplate.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="30" mi="2" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="44" mi="1" ci="0" /><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabVmTemplateVhd.ps1"><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="39" mi="2" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="52" mi="2" ci="0" /><counter type="INSTRUCTION" missed="14" covered="0" /><counter type="LINE" missed="11" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Start-Lab.ps1"><line nr="35" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="51" mi="3" ci="0" /><line nr="52" mi="3" ci="0" /><line nr="53" mi="4" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="2" ci="0" /><line nr="63" mi="5" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="92" mi="2" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="139" mi="2" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="159" mi="2" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="167" mi="2" ci="0" /><counter type="INSTRUCTION" missed="66" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Stop-Lab.ps1"><line nr="31" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="46" mi="3" ci="0" /><line nr="47" mi="3" ci="0" /><line nr="48" mi="4" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="54" mi="2" ci="0" /><line nr="58" mi="2" ci="0" /><line nr="59" mi="3" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="103" mi="2" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="139" mi="2" ci="0" /><counter type="INSTRUCTION" missed="53" covered="0" /><counter type="LINE" missed="37" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Uninstall-Lab.ps1"><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="139" mi="3" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="144" mi="2" ci="0" /><line nr="148" mi="2" ci="0" /><counter type="INSTRUCTION" missed="40" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-Lab.ps1"><line nr="30" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="43" mi="2" ci="0" /><counter type="INSTRUCTION" missed="5" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><counter type="INSTRUCTION" missed="1972" covered="40" /><counter type="LINE" missed="1603" covered="38" /><counter type="METHOD" missed="27" covered="1" /><counter type="CLASS" missed="27" covered="1" /></package><counter type="INSTRUCTION" missed="3329" covered="247" /><counter type="LINE" missed="2684" covered="210" /><counter type="METHOD" missed="56" covered="16" /><counter type="CLASS" missed="55" covered="16" /></report> diff --git a/test/unit/LabBuilder.tests.ps1 b/tests/unit/LabBuilder.tests.ps1 similarity index 100% rename from test/unit/LabBuilder.tests.ps1 rename to tests/unit/LabBuilder.tests.ps1 diff --git a/tests/unit/TestResults.unit.xml b/tests/unit/TestResults.unit.xml new file mode 100644 index 00000000..64386f44 --- /dev/null +++ b/tests/unit/TestResults.unit.xml @@ -0,0 +1,355 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<test-results xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="nunit_schema_2.5.xsd" name="Pester" total="86" errors="0" failures="0" not-run="0" inconclusive="0" ignored="0" skipped="0" invalid="0" date="2019-11-10" time="09:35:04"> + <environment user="Dan" machine-name="PLAGUE02" cwd="C:\Users\Dan\Source\GitHub\LabBuilder" user-domain="PLAGUE02" platform="Microsoft Windows 10 Enterprise|C:\WINDOWS|\Device\Harddisk1\Partition4" nunit-version="2.5.8.0" os-version="10.0.19013" clr-version="4.0.30319.42000" /> + <culture-info current-culture="en-NZ" current-uiculture="en-US" /> + <test-suite type="TestFixture" name="Pester" executed="True" result="Success" success="True" time="60.6614" asserts="0" description="Pester"> + <results> + <test-suite type="TestFixture" name=".\test\unit\lib\private\utils.tests.ps1" executed="True" result="Success" success="True" time="60.6614" asserts="0" description=".\test\unit\lib\private\utils.tests.ps1"> + <results> + <test-suite type="TestFixture" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1" executed="True" result="Success" success="True" time="2.6504" asserts="0" description="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1"> + <results> + <test-suite type="TestFixture" name="When Download folder does not exist" executed="True" result="Success" success="True" time="0.4384" asserts="0" description="When Download folder does not exist"> + <results> + <test-case description="Throws a DownloadFolderDoesNotExistError Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download folder does not exist.Throws a DownloadFolderDoesNotExistError Exception" time="0.0495" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download folder does not exist.Calls appropriate mocks" time="0.0412" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Download fails" executed="True" result="Success" success="True" time="0.4787" asserts="0" description="When Download fails"> + <results> + <test-case description="Throws a FileDownloadError Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download fails.Throws a FileDownloadError Exception" time="0.0755" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download fails.Calls appropriate mocks" time="0.0796" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Download OK" executed="True" result="Success" success="True" time="0.4688" asserts="0" description="When Download OK"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download OK.Does not throw an Exception" time="0.0859" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download OK.Calls appropriate mocks" time="0.0784" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Zip Download OK, Extract fails" executed="True" result="Success" success="True" time="0.5929" asserts="0" description="When Zip Download OK, Extract fails"> + <results> + <test-case description="Throws a FileExtractError Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract fails.Throws a FileExtractError Exception" time="0.1348" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract fails.Calls appropriate mocks" time="0.1108" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Zip Download OK, Extract OK" executed="True" result="Success" success="True" time="0.5102" asserts="0" description="When Zip Download OK, Extract OK"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract OK.Does not throw an Exception" time="0.1397" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract OK.Calls appropriate mocks" time="0.0649" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Invoke-LabDownloadResourceModule.ps1" executed="True" result="Success" success="True" time="46.7703" asserts="0" description="\lib\private\Invoke-LabDownloadResourceModule.ps1"> + <results> + <test-suite type="TestFixture" name="When Correct module already installed; Valid URL and Folder passed" executed="True" result="Success" success="True" time="0.4072" asserts="0" description="When Correct module already installed; Valid URL and Folder passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct module already installed; Valid URL and Folder passed.Does not throw an Exception" time="0.0683" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct module already installed; Valid URL and Folder passed.Should call appropriate Mocks" time="0.1538" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Module is not installed; Valid URL and Folder passed" executed="True" result="Success" success="True" time="0.8757" asserts="0" description="When Module is not installed; Valid URL and Folder passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Valid URL and Folder passed.Does not throw an Exception" time="0.3254" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Valid URL and Folder passed.Should call appropriate Mocks" time="0.3111" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Module is not installed; No URL or Folder passed" executed="True" result="Success" success="True" time="6.8872" asserts="0" description="When Module is not installed; No URL or Folder passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; No URL or Folder passed.Does not throw an Exception" time="6.5676" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; No URL or Folder passed.Should call appropriate Mocks" time="0.1485" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Wrong version of module is installed; Valid URL, Folder and Required Version passed" executed="True" result="Success" success="True" time="0.7559" asserts="0" description="When Wrong version of module is installed; Valid URL, Folder and Required Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Required Version passed.Does not throw an Exception" time="0.2566" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Required Version passed.Should call appropriate Mocks" time="0.3057" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed" executed="True" result="Success" success="True" time="6.7012" asserts="0" description="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed.Does not throw an Exception" time="6.3145" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed.Should call appropriate Mocks" time="0.177" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Correct version of module is installed; Valid URL, Folder and Required Version passed" executed="True" result="Success" success="True" time="0.4226" asserts="0" description="When Correct version of module is installed; Valid URL, Folder and Required Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Required Version passed.Does not throw an Exception" time="0.0429" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Required Version passed.Should call appropriate Mocks" time="0.1951" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Correct version of module is installed; No URL and Folder passed, but Required Version passed" executed="True" result="Success" success="True" time="0.4667" asserts="0" description="When Correct version of module is installed; No URL and Folder passed, but Required Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Required Version passed.Does not throw an Exception" time="0.056" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Required Version passed.Should call appropriate Mocks" time="0.1771" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed" executed="True" result="Success" success="True" time="0.8585" asserts="0" description="When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed.Does not throw an Exception" time="0.2969" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed.Should call appropriate Mocks" time="0.3598" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed" executed="True" result="Success" success="True" time="6.7849" asserts="0" description="When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed.Does not throw an Exception" time="6.3636" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed.Should call appropriate Mocks" time="0.2156" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Correct version of module is installed; Valid URL, Folder and Minimum Version passed" executed="True" result="Success" success="True" time="0.4306" asserts="0" description="When Correct version of module is installed; Valid URL, Folder and Minimum Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Minimum Version passed.Does not throw an Exception" time="0.0465" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Minimum Version passed.Should call appropriate Mocks" time="0.193" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed" executed="True" result="Success" success="True" time="0.4509" asserts="0" description="When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed.Does not throw an Exception" time="0.0431" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed.Should call appropriate Mocks" time="0.1984" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Module is not installed; Bad URL passed" executed="True" result="Success" success="True" time="0.5579" asserts="0" description="When Module is not installed; Bad URL passed"> + <results> + <test-case description="Throws a FileDownloadError exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Bad URL passed.Throws a FileDownloadError exception" time="0.1229" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Bad URL passed.Should call appropriate Mocks" time="0.2297" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Module is not installed; Not available in Repository" executed="True" result="Success" success="True" time="6.751" asserts="0" description="When Module is not installed; Not available in Repository"> + <results> + <test-case description="Throws a ModuleNotAvailableError exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Not available in Repository.Throws a ModuleNotAvailableError exception" time="6.2992" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Not available in Repository.Should call appropriate Mocks" time="0.2442" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available" executed="True" result="Success" success="True" time="6.7785" asserts="0" description="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available"> + <results> + <test-case description=" Throws a ModuleNotAvailableError Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available. Throws a ModuleNotAvailableError Exception" time="6.3179" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available.Should call appropriate Mocks" time="0.2502" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available" executed="True" result="Success" success="True" time="6.7765" asserts="0" description="When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available"> + <results> + <test-case description=" Throws a ModuleNotAvailableError Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available. Throws a ModuleNotAvailableError Exception" time="6.3179" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available.Should call appropriate Mocks" time="0.2524" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\New-LabCredential.ps1" executed="True" result="Success" success="True" time="0.1957" asserts="0" description="\lib\private\New-LabCredential.ps1"> + <results> + <test-suite type="TestFixture" name="When Username and Password provided" executed="True" result="Success" success="True" time="0.1248" asserts="0" description="When Username and Password provided"> + <results> + <test-case description="Should return the exepected credential object" name="\lib\private\New-LabCredential.ps1.When Username and Password provided.Should return the exepected credential object" time="0.0316" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Install-LabHyperV.ps1" executed="True" result="Success" success="True" time="1.0841" asserts="0" description="\lib\private\Install-LabHyperV.ps1"> + <results> + <test-suite type="TestFixture" name="When The function is called" executed="True" result="Success" success="True" time="0.5271" asserts="0" description="When The function is called"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Install-LabHyperV.ps1.When The function is called.Does not throw an Exception" time="0.2281" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Install-LabHyperV.ps1.When The function is called.Calls appropriate mocks" time="0.0537" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Enable-LabWSMan.ps1" executed="True" result="Success" success="True" time="0.756" asserts="0" description="\lib\private\Enable-LabWSMan.ps1"> + <results> + <test-suite type="TestFixture" name="When WS-Man is already enabled" executed="True" result="Success" success="True" time="0.2811" asserts="0" description="When WS-Man is already enabled"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is already enabled.Does not throw an Exception" time="0.0436" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is already enabled.Calls appropriate mocks" time="0.018" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When WS-Man is not enabled, user declines install" executed="True" result="Success" success="True" time="0.3989" asserts="0" description="When WS-Man is not enabled, user declines install"> + <results> + <test-case description="Should throw WSManNotEnabledError exception" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is not enabled, user declines install.Should throw WSManNotEnabledError exception" time="0.1403" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is not enabled, user declines install.Calls appropriate mocks" time="0.0319" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Get-NextMacAddress.ps1" executed="True" result="Success" success="True" time="0.4021" asserts="0" description="\lib\private\Get-NextMacAddress.ps1"> + <results> + <test-suite type="TestFixture" name="When MAC address 00155D0106ED is passed" executed="True" result="Success" success="True" time="0.0952" asserts="0" description="When MAC address 00155D0106ED is passed"> + <results> + <test-case description="Returns MAC address 00155D0106EE" name="\lib\private\Get-NextMacAddress.ps1.When MAC address 00155D0106ED is passed.Returns MAC address 00155D0106EE" time="0.0186" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When MAC address 00155D0106ED and step 10 is passed" executed="True" result="Success" success="True" time="0.1148" asserts="0" description="When MAC address 00155D0106ED and step 10 is passed"> + <results> + <test-case description="Returns IP address 00155D0106F7" name="\lib\private\Get-NextMacAddress.ps1.When MAC address 00155D0106ED and step 10 is passed.Returns IP address 00155D0106F7" time="0.0216" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When MAC address 00155D0106ED and step 0 is passed" executed="True" result="Success" success="True" time="0.0979" asserts="0" description="When MAC address 00155D0106ED and step 0 is passed"> + <results> + <test-case description="Returns IP address 00155D0106ED" name="\lib\private\Get-NextMacAddress.ps1.When MAC address 00155D0106ED and step 0 is passed.Returns IP address 00155D0106ED" time="0.0209" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Get-LabNextIpAddress.ps1" executed="True" result="Success" success="True" time="0.8508" asserts="0" description="\lib\private\Get-LabNextIpAddress.ps1"> + <results> + <test-suite type="TestFixture" name="When Invalid IP Address is passed" executed="True" result="Success" success="True" time="0.1033" asserts="0" description="When Invalid IP Address is passed"> + <results> + <test-case description="Throws a IPAddressError Exception" name="\lib\private\Get-LabNextIpAddress.ps1.When Invalid IP Address is passed.Throws a IPAddressError Exception" time="0.0308" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address 192.168.1.1 is passed" executed="True" result="Success" success="True" time="0.1035" asserts="0" description="When IP address 192.168.1.1 is passed"> + <results> + <test-case description="Returns IP address 192.168.1.2" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.1 is passed.Returns IP address 192.168.1.2" time="0.0304" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address 192.168.1.255 is passed" executed="True" result="Success" success="True" time="0.1002" asserts="0" description="When IP address 192.168.1.255 is passed"> + <results> + <test-case description="Returns IP address 192.168.2.0" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.255 is passed.Returns IP address 192.168.2.0" time="0.0225" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address 192.168.1.255 and Step 10 is passed" executed="True" result="Success" success="True" time="0.1004" asserts="0" description="When IP address 192.168.1.255 and Step 10 is passed"> + <results> + <test-case description="Returns IP address 192.168.2.9" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.255 and Step 10 is passed.Returns IP address 192.168.2.9" time="0.0181" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address 192.168.1.255 and Step 0 is passed" executed="True" result="Success" success="True" time="0.1137" asserts="0" description="When IP address 192.168.1.255 and Step 0 is passed"> + <results> + <test-case description="Returns IP address 192.168.1.255" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.255 and Step 0 is passed.Returns IP address 192.168.1.255" time="0.0234" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address 10.255.255.255 is passed" executed="True" result="Success" success="True" time="0.0988" asserts="0" description="When IP address 10.255.255.255 is passed"> + <results> + <test-case description="Returns IP address 11.0.0.0" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 10.255.255.255 is passed.Returns IP address 11.0.0.0" time="0.0234" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address fe80::15b4:b934:5d23:1a31 is passed" executed="True" result="Success" success="True" time="0.1055" asserts="0" description="When IP address fe80::15b4:b934:5d23:1a31 is passed"> + <results> + <test-case description="Returns IP address fe80::15b4:b934:5d23:1a32" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address fe80::15b4:b934:5d23:1a31 is passed.Returns IP address fe80::15b4:b934:5d23:1a32" time="0.0218" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Assert-LabValidIpAddress.ps1" executed="True" result="Success" success="True" time="0.4949" asserts="0" description="\lib\private\Assert-LabValidIpAddress.ps1"> + <results> + <test-suite type="TestFixture" name="When IP address 192.168.1.1 is passed" executed="True" result="Success" success="True" time="0.0936" asserts="0" description="When IP address 192.168.1.1 is passed"> + <results> + <test-case description="Returns IP Address" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address 192.168.1.1 is passed.Returns IP Address" time="0.0193" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address 192.168.1.1000 is passed" executed="True" result="Success" success="True" time="0.0998" asserts="0" description="When IP address 192.168.1.1000 is passed"> + <results> + <test-case description="Should Throw an Exception" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address 192.168.1.1000 is passed.Should Throw an Exception" time="0.0241" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address fe80::15b4:b934:5d23:1a31 is passed" executed="True" result="Success" success="True" time="0.1005" asserts="0" description="When IP address fe80::15b4:b934:5d23:1a31 is passed"> + <results> + <test-case description="Returns IP Address" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address fe80::15b4:b934:5d23:1a31 is passed.Returns IP Address" time="0.0155" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When IP address fe80::15b4:b934:5d23:1a3x is passed" executed="True" result="Success" success="True" time="0.1024" asserts="0" description="When IP address fe80::15b4:b934:5d23:1a3x is passed"> + <results> + <test-case description="Should Throw an Exception" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address fe80::15b4:b934:5d23:1a3x is passed.Should Throw an Exception" time="0.0248" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Install-LabPackageProvider.ps1" executed="True" result="Success" success="True" time="1.7908" asserts="0" description="\lib\private\Install-LabPackageProvider.ps1"> + <results> + <test-suite type="TestFixture" name="When Required package providers already installed" executed="True" result="Success" success="True" time="0.2837" asserts="0" description="When Required package providers already installed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers already installed.Does not throw an Exception" time="0.051" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers already installed.Calls appropriate mocks" time="0.0433" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Required package providers not installed" executed="True" result="Success" success="True" time="1.4311" asserts="0" description="When Required package providers not installed"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers not installed.Does not throw an Exception" time="1.1744" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers not installed.Calls appropriate mocks" time="0.0697" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Register-LabPackageSource.ps1" executed="True" result="Success" success="True" time="2.8745" asserts="0" description="\lib\private\Register-LabPackageSource.ps1"> + <results> + <test-suite type="TestFixture" name="When Required package sources already registered and trusted" executed="True" result="Success" success="True" time="0.3466" asserts="0" description="When Required package sources already registered and trusted"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered and trusted.Does not throw an Exception" time="0.1221" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered and trusted.Calls appropriate mocks" time="0.08" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Required package sources already registered but not trusted" executed="True" result="Success" success="True" time="1.7393" asserts="0" description="When Required package sources already registered but not trusted"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered but not trusted.Does not throw an Exception" time="1.3419" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered but not trusted.Calls appropriate mocks" time="0.2364" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Required package sources are not registered" executed="True" result="Success" success="True" time="0.6974" asserts="0" description="When Required package sources are not registered"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources are not registered.Does not throw an Exception" time="0.2165" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources are not registered.Calls appropriate mocks" time="0.3006" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Write-LabMessage.ps1" executed="True" result="Success" success="True" time="1.7227" asserts="0" description="\lib\private\Write-LabMessage.ps1"> + <results> + <test-suite type="TestFixture" name="When Write an error message" executed="True" result="Success" success="True" time="0.2643" asserts="0" description="When Write an error message"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write an error message.Does not throw an Exception" time="0.0624" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write an error message.Calls appropriate mocks" time="0.0392" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Write a warning message" executed="True" result="Success" success="True" time="0.2521" asserts="0" description="When Write a warning message"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write a warning message.Does not throw an Exception" time="0.0651" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write a warning message.Calls appropriate mocks" time="0.0303" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Write a verbose message" executed="True" result="Success" success="True" time="0.2502" asserts="0" description="When Write a verbose message"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write a verbose message.Does not throw an Exception" time="0.0606" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write a verbose message.Calls appropriate mocks" time="0.0353" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Write a debug message" executed="True" result="Success" success="True" time="0.2469" asserts="0" description="When Write a debug message"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write a debug message.Does not throw an Exception" time="0.0617" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write a debug message.Calls appropriate mocks" time="0.0312" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Write an information message" executed="True" result="Success" success="True" time="0.2496" asserts="0" description="When Write an information message"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write an information message.Does not throw an Exception" time="0.0635" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write an information message.Calls appropriate mocks" time="0.0297" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When Write an alert message" executed="True" result="Success" success="True" time="0.2767" asserts="0" description="When Write an alert message"> + <results> + <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write an alert message.Does not throw an Exception" time="0.0692" asserts="0" success="True" result="Success" executed="True" /> + <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write an alert message.Calls appropriate mocks" time="0.0358" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\ConvertTo-LabAbsolutePath.ps1" executed="True" result="Success" success="True" time="0.2706" asserts="0" description="\lib\private\ConvertTo-LabAbsolutePath.ps1"> + <results> + <test-suite type="TestFixture" name="When absolute Path is passed" executed="True" result="Success" success="True" time="0.0961" asserts="0" description="When absolute Path is passed"> + <results> + <test-case description="Should return the absolute path" name="\lib\private\ConvertTo-LabAbsolutePath.ps1.When absolute Path is passed.Should return the absolute path" time="0.021" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + <test-suite type="TestFixture" name="When relative Path is passed" executed="True" result="Success" success="True" time="0.0961" asserts="0" description="When relative Path is passed"> + <results> + <test-case description="Should return the absolute path" name="\lib\private\ConvertTo-LabAbsolutePath.ps1.When relative Path is passed.Should return the absolute path" time="0.0152" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + <test-suite type="TestFixture" name="\lib\private\Get-LabBuilderModulePath.ps1" executed="True" result="Success" success="True" time="0.0962" asserts="0" description="\lib\private\Get-LabBuilderModulePath.ps1"> + <results> + <test-case description="Should return the path to the LabBuilder Module" name="\lib\private\Get-LabBuilderModulePath.ps1.Should return the path to the LabBuilder Module" time="0.0193" asserts="0" success="True" result="Success" executed="True" /> + </results> + </test-suite> + </results> + </test-suite> + </results> + </test-suite> +</test-results> \ No newline at end of file diff --git a/test/unit/lib/private/ManagementSwitch.tests.ps1 b/tests/unit/lib/private/ManagementSwitch.tests.ps1 similarity index 100% rename from test/unit/lib/private/ManagementSwitch.tests.ps1 rename to tests/unit/lib/private/ManagementSwitch.tests.ps1 diff --git a/test/unit/lib/private/dsc.tests.ps1 b/tests/unit/lib/private/dsc.tests.ps1 similarity index 100% rename from test/unit/lib/private/dsc.tests.ps1 rename to tests/unit/lib/private/dsc.tests.ps1 diff --git a/test/unit/lib/private/utils.tests.ps1 b/tests/unit/lib/private/utils.tests.ps1 similarity index 100% rename from test/unit/lib/private/utils.tests.ps1 rename to tests/unit/lib/private/utils.tests.ps1 diff --git a/test/unit/lib/private/vhd.tests.ps1 b/tests/unit/lib/private/vhd.tests.ps1 similarity index 100% rename from test/unit/lib/private/vhd.tests.ps1 rename to tests/unit/lib/private/vhd.tests.ps1 diff --git a/test/unit/lib/private/vm.tests.ps1 b/tests/unit/lib/private/vm.tests.ps1 similarity index 100% rename from test/unit/lib/private/vm.tests.ps1 rename to tests/unit/lib/private/vm.tests.ps1 diff --git a/test/unit/lib/public/lab.tests.ps1 b/tests/unit/lib/public/lab.tests.ps1 similarity index 100% rename from test/unit/lib/public/lab.tests.ps1 rename to tests/unit/lib/public/lab.tests.ps1 diff --git a/test/unit/lib/public/resource.tests.ps1 b/tests/unit/lib/public/resource.tests.ps1 similarity index 100% rename from test/unit/lib/public/resource.tests.ps1 rename to tests/unit/lib/public/resource.tests.ps1 diff --git a/test/unit/lib/public/switch.tests.ps1 b/tests/unit/lib/public/switch.tests.ps1 similarity index 100% rename from test/unit/lib/public/switch.tests.ps1 rename to tests/unit/lib/public/switch.tests.ps1 diff --git a/test/unit/lib/public/templatevhd.tests.ps1 b/tests/unit/lib/public/templatevhd.tests.ps1 similarity index 100% rename from test/unit/lib/public/templatevhd.tests.ps1 rename to tests/unit/lib/public/templatevhd.tests.ps1 diff --git a/test/unit/lib/public/vm.tests.ps1 b/tests/unit/lib/public/vm.tests.ps1 similarity index 100% rename from test/unit/lib/public/vm.tests.ps1 rename to tests/unit/lib/public/vm.tests.ps1 diff --git a/test/unit/lib/public/vmtemplate.tests.ps1 b/tests/unit/lib/public/vmtemplate.tests.ps1 similarity index 100% rename from test/unit/lib/public/vmtemplate.tests.ps1 rename to tests/unit/lib/public/vmtemplate.tests.ps1 From 3c2cecd2ea85003cdc9904d782fa37cb0523284d Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Mon, 20 Apr 2020 08:16:24 +1200 Subject: [PATCH 07/14] Fix build --- CHANGELOG.md | 2 ++ source/build.psd1 | 2 +- ...alizedData.psd1 => LabBuilder.strings.psd1} | 0 source/en-US/about_LabBuilder.help.txt | 18 +++++++++--------- source/prefix.ps1 | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) rename source/en-US/{LabBuilder_LocalizedData.psd1 => LabBuilder.strings.psd1} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1b3b99..d239bab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Renamed `LabBuilder_LocalizedData.psd1` to `LabBuilder.strings.psd1` to + align to other PowerShell modules. - Convert all DSC configurations to use ComputerManagementDsc version 7.1.0.0. - Clean up code style on all DSC Library files. diff --git a/source/build.psd1 b/source/build.psd1 index afbc9a75..aebb4314 100644 --- a/source/build.psd1 +++ b/source/build.psd1 @@ -1,5 +1,5 @@ @{ - Path = 'CosmosDB.psd1' #or build breaks on Linux + Path = 'LabBuilder.psd1' #or build breaks on Linux } # Waiting for ModuleBuilder to do away with this file # when all parameters are provided to the function diff --git a/source/en-US/LabBuilder_LocalizedData.psd1 b/source/en-US/LabBuilder.strings.psd1 similarity index 100% rename from source/en-US/LabBuilder_LocalizedData.psd1 rename to source/en-US/LabBuilder.strings.psd1 diff --git a/source/en-US/about_LabBuilder.help.txt b/source/en-US/about_LabBuilder.help.txt index 5197dad9..b17ad7af 100644 --- a/source/en-US/about_LabBuilder.help.txt +++ b/source/en-US/about_LabBuilder.help.txt @@ -1,24 +1,24 @@ TOPIC - about_CosmosDB + about_LabBuilder SHORT DESCRIPTION - PowerShell module for working with CosmosDB. + Builds Hyper-V Windows multi-machine/Active Directory labs using XML + configuration files and DSC Resources. LONG DESCRIPTION - This module provides cmdlets for working with Azure Cosmos DB databases, collections, - documents, attachments, offers, users, permissions, triggers, stored procedures - and user defined functions. + This module provides cmdlets for building Hyper-V Windows multi-machine/Active + Directory labs using XML configuration files and DSC Resources. EXAMPLES - PS C:\> Get-Command -Module CosmosDB + PS C:\> Get-Command -Module LabBuilder TROUBLESHOOTING NOTE: Go to the Github repository for read about issues, submit a new issue, and read - about new releases. https://github.com/PlagueHO/CosmosDB + about new releases. https://github.com/PlagueHO/LabBuilder SEE ALSO - - https://github.com/PlagueHO/CosmosDB + - https://github.com/PlagueHO/LabBuilder KEYWORDS - CosmosDB, DocumentDb, Azure + Hyper-V, Lab, DesiredStateConfiguration, DSC diff --git a/source/prefix.ps1 b/source/prefix.ps1 index b6f33f49..9c59347f 100644 --- a/source/prefix.ps1 +++ b/source/prefix.ps1 @@ -25,7 +25,7 @@ else Import-LocalizedData ` -BindingVariable LocalizedData ` - -Filename 'CosmosDB.strings.psd1' ` + -Filename 'LabBuilder.strings.psd1' ` -BaseDirectory $script:moduleRoot ` -UICulture $culture #endregion From de8ec03b3f23ef5a8f8004e3450b213dd2179f50 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Mon, 20 Apr 2020 08:17:51 +1200 Subject: [PATCH 08/14] Remove code coverage files --- tests/unit/CodeCoverage.xml | 1 - tests/unit/TestResults.unit.xml | 355 -------------------------------- 2 files changed, 356 deletions(-) delete mode 100644 tests/unit/CodeCoverage.xml delete mode 100644 tests/unit/TestResults.unit.xml diff --git a/tests/unit/CodeCoverage.xml b/tests/unit/CodeCoverage.xml deleted file mode 100644 index 321dc10d..00000000 --- a/tests/unit/CodeCoverage.xml +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.1//EN" "report.dtd"><report name="Pester (11/10/2019 09:34:39)"><sessioninfo id="this" start="1573378419142" dump="1573378479804" /><package name="lib/private"><class name="lib/private/Assert-LabValidConfigurationXMLSchema" sourcefilename="Assert-LabValidConfigurationXMLSchema.ps1"><method name="Assert-LabValidConfigurationXMLSchema" desc="()" line="31"><counter type="INSTRUCTION" missed="17" covered="13" /><counter type="LINE" missed="14" covered="13" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="17" covered="13" /><counter type="LINE" missed="14" covered="13" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Assert-LabValidIpAddress" sourcefilename="Assert-LabValidIpAddress.ps1"><method name="Assert-LabValidIpAddress" desc="()" line="30"><counter type="INSTRUCTION" missed="0" covered="9" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="9" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/ConvertTo-LabAbsolutePath" sourcefilename="ConvertTo-LabAbsolutePath.ps1"><method name="ConvertTo-LabAbsolutePath" desc="()" line="29"><counter type="INSTRUCTION" missed="0" covered="3" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="3" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Copy-LabOdjFile" sourcefilename="Copy-LabOdjFile.ps1"><method name="Copy-LabOdjFile" desc="()" line="47"><counter type="INSTRUCTION" missed="59" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="59" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Enable-LabWSMan" sourcefilename="Enable-LabWSMan.ps1"><method name="Enable-LabWSMan" desc="()" line="26"><counter type="INSTRUCTION" missed="7" covered="16" /><counter type="LINE" missed="6" covered="11" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="7" covered="16" /><counter type="LINE" missed="6" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabBuilderModulePath" sourcefilename="Get-LabBuilderModulePath.ps1"><method name="Get-LabBuilderModulePath" desc="()" line="14"><counter type="INSTRUCTION" missed="12" covered="4" /><counter type="LINE" missed="10" covered="4" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="12" covered="4" /><counter type="LINE" missed="10" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabCertificatePsFileContent" sourcefilename="Get-LabCertificatePsFileContent.ps1"><method name="Get-LabCertificatePsFileContent" desc="()" line="47"><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabDSCNetworkingConfig" sourcefilename="Get-LabDSCNetworkingConfig.ps1"><method name="Get-LabDSCNetworkingConfig" desc="()" line="38"><counter type="INSTRUCTION" missed="46" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><method name="&lt;script&gt;" desc="()" line="189"><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="48" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="2" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabIntegrationServiceName" sourcefilename="Get-LabIntegrationServiceName.ps1"><method name="Get-LabIntegrationServiceName" desc="()" line="22"><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabManagementSwitchName" sourcefilename="Get-LabManagementSwitchName.ps1"><method name="Get-LabManagementSwitchName" desc="()" line="32"><counter type="INSTRUCTION" missed="6" covered="0" /><counter type="LINE" missed="5" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="6" covered="0" /><counter type="LINE" missed="5" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabModulesInDSCConfig" sourcefilename="Get-LabModulesInDSCConfig.ps1"><method name="Get-LabModulesInDSCConfig" desc="()" line="52"><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabNextIpAddress" sourcefilename="Get-LabNextIpAddress.ps1"><method name="Get-LabNextIpAddress" desc="()" line="38"><counter type="INSTRUCTION" missed="0" covered="11" /><counter type="LINE" missed="0" covered="11" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="11" /><counter type="LINE" missed="0" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabNextMacAddress" sourcefilename="Get-LabNextMacAddress.ps1"><method name="Get-NextMacAddress" desc="()" line="32"><counter type="INSTRUCTION" missed="0" covered="1" /><counter type="LINE" missed="0" covered="1" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="1" /><counter type="LINE" missed="0" covered="1" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Get-LabUnattendFileContent" sourcefilename="Get-LabUnattendFileContent.ps1"><method name="Get-LabUnattendFileContent" desc="()" line="40"><counter type="INSTRUCTION" missed="18" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="18" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Get-LabVMManagementIPAddress" sourcefilename="Get-LabVMManagementIPAddress.ps1"><method name="Get-LabVMManagementIPAddress" desc="()" line="38"><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabBootVHD" sourcefilename="Initialize-LabBootVHD.ps1"><method name="Initialize-LabBootVHD" desc="()" line="54"><counter type="INSTRUCTION" missed="120" covered="0" /><counter type="LINE" missed="93" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="120" covered="0" /><counter type="LINE" missed="93" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabDSC" sourcefilename="Initialize-LabDSC.ps1"><method name="Initialize-LabDSC" desc="()" line="46"><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabManagementSwitch" sourcefilename="Initialize-LabManagementSwitch.ps1"><method name="Initialize-LabManagementSwitch" desc="()" line="32"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabVHD" sourcefilename="Initialize-LabVHD.ps1"><method name="Initialize-LabVHD" desc="()" line="115"><counter type="INSTRUCTION" missed="96" covered="0" /><counter type="LINE" missed="75" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="96" covered="0" /><counter type="LINE" missed="75" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Initialize-LabVMPath" sourcefilename="Initialize-LabVMPath.ps1"><method name="Initialize-LabVMPath" desc="()" line="31"><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="10" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="10" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Install-LabHyperV" sourcefilename="Install-LabHyperV.ps1"><method name="Install-LabHyperV" desc="()" line="23"><counter type="INSTRUCTION" missed="7" covered="9" /><counter type="LINE" missed="6" covered="7" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="7" covered="9" /><counter type="LINE" missed="6" covered="7" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Install-LabPackageProvider" sourcefilename="Install-LabPackageProvider.ps1"><method name="Install-LabPackageProvider" desc="()" line="28"><counter type="INSTRUCTION" missed="6" covered="13" /><counter type="LINE" missed="5" covered="10" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="6" covered="13" /><counter type="LINE" missed="5" covered="10" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Invoke-LabDownloadAndUnzipFile" sourcefilename="Invoke-LabDownloadAndUnzipFile.ps1"><method name="Invoke-LabDownloadAndUnzipFile" desc="()" line="26"><counter type="INSTRUCTION" missed="0" covered="33" /><counter type="LINE" missed="0" covered="27" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="33" /><counter type="LINE" missed="0" covered="27" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Invoke-LabDownloadResourceModule" sourcefilename="Invoke-LabDownloadResourceModule.ps1"><method name="Invoke-LabDownloadResourceModule" desc="()" line="72"><counter type="INSTRUCTION" missed="1" covered="50" /><counter type="LINE" missed="1" covered="39" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="1" covered="50" /><counter type="LINE" missed="1" covered="39" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/New-LabCredential" sourcefilename="New-LabCredential.ps1"><method name="New-LabCredential" desc="()" line="22"><counter type="INSTRUCTION" missed="0" covered="4" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="4" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/New-LabException" sourcefilename="New-LabException.ps1"><method name="New-LabException" desc="()" line="54"><counter type="INSTRUCTION" missed="1" covered="4" /><counter type="LINE" missed="1" covered="4" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="1" covered="4" /><counter type="LINE" missed="1" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/New-LabHostSelfSignedCertificate" sourcefilename="New-LabHostSelfSignedCertificate.ps1"><method name="New-LabHostSelfSignedCertificate" desc="()" line="41"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/New-LabVMInitializationFile" sourcefilename="New-LabVMInitializationFile.ps1"><method name="New-LabVMInitializationFile" desc="()" line="42"><counter type="INSTRUCTION" missed="45" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="45" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Recieve-LabSelfSignedCertificate" sourcefilename="Recieve-LabSelfSignedCertificate.ps1"><method name="Recieve-LabSelfSignedCertificate" desc="()" line="50"><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Register-LabPackageSource" sourcefilename="Register-LabPackageSource.ps1"><method name="Register-LabPackageSource" desc="()" line="28"><counter type="INSTRUCTION" missed="12" covered="25" /><counter type="LINE" missed="10" covered="23" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="12" covered="25" /><counter type="LINE" missed="10" covered="23" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/private/Request-LabSelfSignedCertificate" sourcefilename="Request-LabSelfSignedCertificate.ps1"><method name="Request-LabSelfSignedCertificate" desc="()" line="50"><counter type="INSTRUCTION" missed="87" covered="0" /><counter type="LINE" missed="61" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="87" covered="0" /><counter type="LINE" missed="61" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Set-LabDSC" sourcefilename="Set-LabDSC.ps1"><method name="Set-LabDSC" desc="()" line="45"><counter type="INSTRUCTION" missed="37" covered="0" /><counter type="LINE" missed="31" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="37" covered="0" /><counter type="LINE" missed="31" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Set-LabModulesInDSCConfig" sourcefilename="Set-LabModulesInDSCConfig.ps1"><method name="Set-LabModulesInDSCConfig" desc="()" line="58"><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="28" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="28" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Set-LabSwitchAdapter" sourcefilename="Set-LabSwitchAdapter.ps1"><method name="Set-LabSwitchAdapter" desc="()" line="60"><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Start-LabDSC" sourcefilename="Start-LabDSC.ps1"><method name="Start-LabDSC" desc="()" line="51"><counter type="INSTRUCTION" missed="108" covered="0" /><counter type="LINE" missed="82" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="108" covered="0" /><counter type="LINE" missed="82" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabDSC" sourcefilename="Update-LabDSC.ps1"><method name="Update-LabDSC" desc="()" line="45"><counter type="INSTRUCTION" missed="175" covered="0" /><counter type="LINE" missed="141" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="175" covered="0" /><counter type="LINE" missed="141" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabVMDataDisk" sourcefilename="Update-LabVMDataDisk.ps1"><method name="Update-LabVMDataDisk" desc="()" line="50"><counter type="INSTRUCTION" missed="146" covered="0" /><counter type="LINE" missed="115" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="146" covered="0" /><counter type="LINE" missed="115" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabVMDvdDrive" sourcefilename="Update-LabVMDvdDrive.ps1"><method name="Update-LabVMDvdDrive" desc="()" line="49"><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="20" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="20" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Update-LabVMIntegrationService" sourcefilename="Update-LabVMIntegrationService.ps1"><method name="Update-LabVMIntegrationService" desc="()" line="55"><counter type="INSTRUCTION" missed="19" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="19" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Wait-LabVMInitializationComplete" sourcefilename="Wait-LabVMInitializationComplete.ps1"><method name="Wait-LabVMInitializationComplete" desc="()" line="47"><counter type="INSTRUCTION" missed="55" covered="0" /><counter type="LINE" missed="40" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="55" covered="0" /><counter type="LINE" missed="40" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Wait-LabVMOff" sourcefilename="Wait-LabVMOff.ps1"><method name="Wait-LabVMOff" desc="()" line="21"><counter type="INSTRUCTION" missed="4" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="4" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Wait-LabVMStarted" sourcefilename="Wait-LabVMStarted.ps1"><method name="Wait-LabVMStarted" desc="()" line="22"><counter type="INSTRUCTION" missed="20" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="20" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/private/Write-LabMessage" sourcefilename="Write-LabMessage.ps1"><method name="Write-LabMessage" desc="()" line="51"><counter type="INSTRUCTION" missed="0" covered="12" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="0" covered="12" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><sourcefile name="Assert-LabValidConfigurationXMLSchema.ps1"><line nr="31" mi="0" ci="1" /><line nr="32" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="34" mi="0" ci="1" /><line nr="37" mi="0" ci="1" /><line nr="38" mi="0" ci="1" /><line nr="39" mi="0" ci="1" /><line nr="40" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="44" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="53" mi="0" ci="1" /><line nr="57" mi="0" ci="1" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="74" mi="0" ci="1" /><line nr="78" mi="0" ci="1" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><counter type="INSTRUCTION" missed="17" covered="13" /><counter type="LINE" missed="14" covered="13" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Assert-LabValidIpAddress.ps1"><line nr="30" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="34" mi="0" ci="1" /><line nr="35" mi="0" ci="1" /><line nr="36" mi="0" ci="2" /><line nr="38" mi="0" ci="1" /><line nr="40" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="9" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="ConvertTo-LabAbsolutePath.ps1"><line nr="29" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="36" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="3" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Copy-LabOdjFile.ps1"><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="64" mi="2" ci="0" /><line nr="65" mi="3" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="78" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="97" mi="2" ci="0" /><line nr="98" mi="3" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="115" mi="2" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="125" mi="3" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="133" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="135" mi="2" ci="0" /><line nr="138" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><counter type="INSTRUCTION" missed="59" covered="0" /><counter type="LINE" missed="44" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Enable-LabWSMan.ps1"><line nr="26" mi="0" ci="2" /><line nr="28" mi="0" ci="2" /><line nr="32" mi="0" ci="1" /><line nr="36" mi="0" ci="1" /><line nr="43" mi="0" ci="2" /><line nr="45" mi="0" ci="1" /><line nr="46" mi="0" ci="1" /><line nr="47" mi="0" ci="1" /><line nr="48" mi="0" ci="2" /><line nr="50" mi="0" ci="1" /><line nr="55" mi="0" ci="2" /><line nr="59" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="2" ci="0" /><line nr="68" mi="1" ci="0" /><counter type="INSTRUCTION" missed="7" covered="16" /><counter type="LINE" missed="6" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabBuilderModulePath.ps1"><line nr="14" mi="0" ci="1" /><line nr="16" mi="0" ci="1" /><line nr="18" mi="1" ci="0" /><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="21" mi="2" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="26" mi="0" ci="1" /><line nr="28" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="31" mi="2" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="36" mi="0" ci="1" /><counter type="INSTRUCTION" missed="12" covered="4" /><counter type="LINE" missed="10" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabCertificatePsFileContent.ps1"><line nr="47" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabDSCNetworkingConfig.ps1"><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="123" mi="2" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="133" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="176" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><counter type="INSTRUCTION" missed="48" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="2" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabIntegrationServiceName.ps1"><line nr="22" mi="1" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="26" mi="1" ci="0" /><line nr="27" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="2" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><counter type="INSTRUCTION" missed="13" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabManagementSwitchName.ps1"><line nr="32" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="39" mi="2" ci="0" /><line nr="41" mi="1" ci="0" /><counter type="INSTRUCTION" missed="6" covered="0" /><counter type="LINE" missed="5" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabModulesInDSCConfig.ps1"><line nr="52" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabNextIpAddress.ps1"><line nr="38" mi="0" ci="1" /><line nr="42" mi="0" ci="1" /><line nr="43" mi="0" ci="1" /><line nr="45" mi="0" ci="1" /><line nr="47" mi="0" ci="1" /><line nr="49" mi="0" ci="1" /><line nr="50" mi="0" ci="1" /><line nr="51" mi="0" ci="1" /><line nr="55" mi="0" ci="1" /><line nr="56" mi="0" ci="1" /><line nr="60" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="11" /><counter type="LINE" missed="0" covered="11" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabNextMacAddress.ps1"><line nr="32" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="1" /><counter type="LINE" missed="0" covered="1" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabUnattendFileContent.ps1"><line nr="40" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><counter type="INSTRUCTION" missed="18" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVMManagementIPAddress.ps1"><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="14" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabBootVHD.ps1"><line nr="54" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="90" mi="2" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="95" mi="2" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="107" mi="2" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="121" mi="2" ci="0" /><line nr="123" mi="2" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="145" mi="2" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="159" mi="2" ci="0" /><line nr="161" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="2" ci="0" /><line nr="167" mi="1" ci="0" /><line nr="170" mi="2" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="191" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="194" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="2" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="203" mi="2" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="208" mi="2" ci="0" /><line nr="211" mi="1" ci="0" /><line nr="214" mi="2" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="227" mi="2" ci="0" /><line nr="229" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="246" mi="2" ci="0" /><line nr="249" mi="2" ci="0" /><line nr="251" mi="2" ci="0" /><line nr="254" mi="1" ci="0" /><line nr="258" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="268" mi="1" ci="0" /><line nr="271" mi="2" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="280" mi="2" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="288" mi="2" ci="0" /><line nr="290" mi="1" ci="0" /><line nr="291" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="298" mi="1" ci="0" /><line nr="299" mi="2" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="308" mi="2" ci="0" /><line nr="310" mi="1" ci="0" /><line nr="311" mi="1" ci="0" /><counter type="INSTRUCTION" missed="120" covered="0" /><counter type="LINE" missed="93" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabDSC.ps1"><line nr="46" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><counter type="INSTRUCTION" missed="2" covered="0" /><counter type="LINE" missed="2" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabManagementSwitch.ps1"><line nr="32" mi="1" ci="0" /><line nr="34" mi="2" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="85" mi="1" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVHD.ps1"><line nr="115" mi="2" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="120" mi="2" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="131" mi="2" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="143" mi="2" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="150" mi="2" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="155" mi="2" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="168" mi="2" ci="0" /><line nr="169" mi="3" ci="0" /><line nr="171" mi="2" ci="0" /><line nr="174" mi="2" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="186" mi="2" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="204" mi="1" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="225" mi="1" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="231" mi="1" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="238" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="243" mi="1" ci="0" /><line nr="244" mi="2" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="251" mi="2" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="254" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="270" mi="2" ci="0" /><line nr="271" mi="1" ci="0" /><line nr="273" mi="2" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="285" mi="1" ci="0" /><line nr="290" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="299" mi="2" ci="0" /><line nr="305" mi="2" ci="0" /><line nr="307" mi="1" ci="0" /><line nr="308" mi="1" ci="0" /><line nr="309" mi="1" ci="0" /><line nr="310" mi="2" ci="0" /><line nr="313" mi="1" ci="0" /><line nr="317" mi="1" ci="0" /><line nr="323" mi="2" ci="0" /><line nr="330" mi="1" ci="0" /><counter type="INSTRUCTION" missed="96" covered="0" /><counter type="LINE" missed="75" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVMPath.ps1"><line nr="31" mi="2" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="38" mi="2" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="45" mi="2" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="52" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="59" mi="2" ci="0" /><line nr="61" mi="1" ci="0" /><counter type="INSTRUCTION" missed="15" covered="0" /><counter type="LINE" missed="10" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Install-LabHyperV.ps1"><line nr="23" mi="0" ci="2" /><line nr="26" mi="0" ci="1" /><line nr="27" mi="0" ci="1" /><line nr="28" mi="0" ci="1" /><line nr="30" mi="0" ci="2" /><line nr="32" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="2" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><counter type="INSTRUCTION" missed="7" covered="9" /><counter type="LINE" missed="6" covered="7" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Install-LabPackageProvider.ps1"><line nr="28" mi="0" ci="2" /><line nr="29" mi="0" ci="1" /><line nr="33" mi="0" ci="1" /><line nr="35" mi="0" ci="1" /><line nr="36" mi="0" ci="2" /><line nr="38" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="42" mi="0" ci="1" /><line nr="45" mi="0" ci="2" /><line nr="48" mi="0" ci="1" /><line nr="57" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><counter type="INSTRUCTION" missed="6" covered="13" /><counter type="LINE" missed="5" covered="10" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Invoke-LabDownloadAndUnzipFile.ps1"><line nr="26" mi="0" ci="1" /><line nr="28" mi="0" ci="2" /><line nr="30" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="32" mi="0" ci="1" /><line nr="33" mi="0" ci="2" /><line nr="36" mi="0" ci="1" /><line nr="39" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="44" mi="0" ci="1" /><line nr="49" mi="0" ci="1" /><line nr="52" mi="0" ci="2" /><line nr="57" mi="0" ci="1" /><line nr="64" mi="0" ci="1" /><line nr="65" mi="0" ci="1" /><line nr="66" mi="0" ci="1" /><line nr="67" mi="0" ci="2" /><line nr="69" mi="0" ci="1" /><line nr="72" mi="0" ci="1" /><line nr="74" mi="0" ci="2" /><line nr="80" mi="0" ci="1" /><line nr="88" mi="0" ci="1" /><line nr="89" mi="0" ci="1" /><line nr="90" mi="0" ci="1" /><line nr="91" mi="0" ci="2" /><line nr="93" mi="0" ci="1" /><line nr="98" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="33" /><counter type="LINE" missed="0" covered="27" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Invoke-LabDownloadResourceModule.ps1"><line nr="72" mi="0" ci="2" /><line nr="75" mi="0" ci="1" /><line nr="77" mi="0" ci="1" /><line nr="78" mi="0" ci="3" /><line nr="81" mi="0" ci="1" /><line nr="83" mi="0" ci="1" /><line nr="85" mi="0" ci="1" /><line nr="86" mi="0" ci="3" /><line nr="89" mi="0" ci="1" /><line nr="93" mi="0" ci="1" /><line nr="94" mi="0" ci="1" /><line nr="97" mi="0" ci="1" /><line nr="101" mi="0" ci="1" /><line nr="103" mi="0" ci="2" /><line nr="107" mi="0" ci="1" /><line nr="111" mi="0" ci="2" /><line nr="114" mi="0" ci="2" /><line nr="116" mi="0" ci="1" /><line nr="121" mi="0" ci="1" /><line nr="125" mi="0" ci="1" /><line nr="127" mi="0" ci="1" /><line nr="129" mi="1" ci="0" /><line nr="132" mi="0" ci="1" /><line nr="133" mi="0" ci="1" /><line nr="138" mi="0" ci="2" /><line nr="145" mi="0" ci="1" /><line nr="151" mi="0" ci="1" /><line nr="156" mi="0" ci="2" /><line nr="158" mi="0" ci="1" /><line nr="161" mi="0" ci="1" /><line nr="162" mi="0" ci="1" /><line nr="165" mi="0" ci="1" /><line nr="168" mi="0" ci="1" /><line nr="169" mi="0" ci="1" /><line nr="175" mi="0" ci="1" /><line nr="179" mi="0" ci="1" /><line nr="180" mi="0" ci="1" /><line nr="181" mi="0" ci="1" /><line nr="182" mi="0" ci="2" /><line nr="185" mi="0" ci="1" /><counter type="INSTRUCTION" missed="1" covered="50" /><counter type="LINE" missed="1" covered="39" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="New-LabCredential.ps1"><line nr="22" mi="0" ci="1" /><line nr="24" mi="0" ci="2" /><line nr="26" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="4" /><counter type="LINE" missed="0" covered="3" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="New-LabException.ps1"><line nr="54" mi="0" ci="1" /><line nr="56" mi="0" ci="1" /><line nr="59" mi="0" ci="1" /><line nr="62" mi="1" ci="0" /><line nr="67" mi="0" ci="1" /><counter type="INSTRUCTION" missed="1" covered="4" /><counter type="LINE" missed="1" covered="4" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="New-LabHostSelfSignedCertificate.ps1"><line nr="41" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="2" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="96" mi="2" ci="0" /><line nr="98" mi="2" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="New-LabVMInitializationFile.ps1"><line nr="42" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="169" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="178" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="183" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="192" mi="1" ci="0" /><line nr="193" mi="2" ci="0" /><line nr="198" mi="2" ci="0" /><counter type="INSTRUCTION" missed="45" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Recieve-LabSelfSignedCertificate.ps1"><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="57" mi="2" ci="0" /><line nr="58" mi="3" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="70" mi="2" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="83" mi="3" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="96" mi="2" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="106" mi="3" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="2" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="123" mi="2" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="134" mi="3" ci="0" /><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Register-LabPackageSource.ps1"><line nr="28" mi="0" ci="1" /><line nr="29" mi="0" ci="1" /><line nr="30" mi="0" ci="1" /><line nr="31" mi="0" ci="1" /><line nr="32" mi="0" ci="1" /><line nr="35" mi="0" ci="1" /><line nr="36" mi="0" ci="1" /><line nr="37" mi="0" ci="1" /><line nr="41" mi="0" ci="1" /><line nr="43" mi="0" ci="1" /><line nr="45" mi="0" ci="1" /><line nr="46" mi="0" ci="1" /><line nr="47" mi="0" ci="1" /><line nr="50" mi="0" ci="1" /><line nr="52" mi="0" ci="1" /><line nr="54" mi="0" ci="1" /><line nr="55" mi="0" ci="1" /><line nr="59" mi="0" ci="2" /><line nr="62" mi="0" ci="1" /><line nr="71" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="84" mi="0" ci="1" /><line nr="85" mi="0" ci="1" /><line nr="88" mi="0" ci="2" /><line nr="91" mi="0" ci="1" /><line nr="102" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><counter type="INSTRUCTION" missed="12" covered="25" /><counter type="LINE" missed="10" covered="23" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Request-LabSelfSignedCertificate.ps1"><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="69" mi="3" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="81" mi="2" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="90" mi="2" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="95" mi="2" ci="0" /><line nr="96" mi="3" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="125" mi="2" ci="0" /><line nr="126" mi="3" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="148" mi="2" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="154" mi="3" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="167" mi="2" ci="0" /><line nr="170" mi="1" ci="0" /><line nr="176" mi="2" ci="0" /><line nr="177" mi="3" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="186" mi="1" ci="0" /><line nr="187" mi="2" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="194" mi="2" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="1" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="202" mi="3" ci="0" /><counter type="INSTRUCTION" missed="87" covered="0" /><counter type="LINE" missed="61" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Set-LabDSC.ps1"><line nr="45" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="101" mi="2" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><counter type="INSTRUCTION" missed="37" covered="0" /><counter type="LINE" missed="31" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Set-LabModulesInDSCConfig.ps1"><line nr="58" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="2" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="84" mi="2" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="2" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="119" mi="2" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="28" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Set-LabSwitchAdapter.ps1"><line nr="60" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Start-LabDSC.ps1"><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="61" mi="3" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="95" mi="3" ci="0" /><line nr="99" mi="2" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="104" mi="2" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="126" mi="2" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="135" mi="2" ci="0" /><line nr="136" mi="3" ci="0" /><line nr="139" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="146" mi="2" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="2" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="172" mi="1" ci="0" /><line nr="176" mi="2" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="181" mi="1" ci="0" /><line nr="189" mi="2" ci="0" /><line nr="192" mi="1" ci="0" /><line nr="197" mi="1" ci="0" /><line nr="201" mi="2" ci="0" /><line nr="202" mi="3" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="209" mi="1" ci="0" /><line nr="210" mi="1" ci="0" /><line nr="211" mi="1" ci="0" /><line nr="212" mi="2" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="219" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="224" mi="2" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><counter type="INSTRUCTION" missed="108" covered="0" /><counter type="LINE" missed="82" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabDSC.ps1"><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="3" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="97" mi="3" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="125" mi="2" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="2" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="156" mi="2" ci="0" /><line nr="159" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="169" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="186" mi="2" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="191" mi="2" ci="0" /><line nr="194" mi="1" ci="0" /><line nr="197" mi="1" ci="0" /><line nr="199" mi="2" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="205" mi="2" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="223" mi="2" ci="0" /><line nr="226" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="231" mi="2" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="245" mi="1" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="248" mi="2" ci="0" /><line nr="251" mi="2" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="258" mi="2" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="261" mi="1" ci="0" /><line nr="262" mi="1" ci="0" /><line nr="263" mi="2" ci="0" /><line nr="266" mi="1" ci="0" /><line nr="270" mi="2" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="287" mi="1" ci="0" /><line nr="291" mi="2" ci="0" /><line nr="295" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="298" mi="1" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="305" mi="1" ci="0" /><line nr="306" mi="1" ci="0" /><line nr="307" mi="1" ci="0" /><line nr="308" mi="2" ci="0" /><line nr="311" mi="1" ci="0" /><line nr="316" mi="1" ci="0" /><line nr="322" mi="1" ci="0" /><line nr="324" mi="1" ci="0" /><line nr="326" mi="2" ci="0" /><line nr="329" mi="1" ci="0" /><line nr="333" mi="1" ci="0" /><line nr="336" mi="1" ci="0" /><line nr="337" mi="1" ci="0" /><line nr="343" mi="1" ci="0" /><line nr="347" mi="1" ci="0" /><line nr="349" mi="1" ci="0" /><line nr="354" mi="1" ci="0" /><line nr="357" mi="1" ci="0" /><line nr="360" mi="1" ci="0" /><line nr="365" mi="2" ci="0" /><line nr="367" mi="1" ci="0" /><line nr="368" mi="1" ci="0" /><line nr="369" mi="1" ci="0" /><line nr="370" mi="2" ci="0" /><line nr="372" mi="1" ci="0" /><line nr="376" mi="1" ci="0" /><line nr="380" mi="2" ci="0" /><line nr="383" mi="2" ci="0" /><line nr="384" mi="1" ci="0" /><line nr="389" mi="1" ci="0" /><line nr="392" mi="1" ci="0" /><line nr="397" mi="1" ci="0" /><line nr="399" mi="2" ci="0" /><line nr="400" mi="1" ci="0" /><line nr="405" mi="1" ci="0" /><line nr="408" mi="1" ci="0" /><counter type="INSTRUCTION" missed="175" covered="0" /><counter type="LINE" missed="141" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabVMDataDisk.ps1"><line nr="50" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="129" mi="2" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="149" mi="2" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="161" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="168" mi="2" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="182" mi="2" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="203" mi="1" ci="0" /><line nr="204" mi="1" ci="0" /><line nr="205" mi="2" ci="0" /><line nr="208" mi="1" ci="0" /><line nr="210" mi="2" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="214" mi="1" ci="0" /><line nr="215" mi="2" ci="0" /><line nr="218" mi="1" ci="0" /><line nr="222" mi="2" ci="0" /><line nr="225" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="237" mi="1" ci="0" /><line nr="238" mi="1" ci="0" /><line nr="239" mi="2" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="248" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="261" mi="2" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="269" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="271" mi="1" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="279" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="287" mi="1" ci="0" /><line nr="292" mi="2" ci="0" /><line nr="295" mi="1" ci="0" /><line nr="300" mi="2" ci="0" /><line nr="302" mi="2" ci="0" /><line nr="305" mi="1" ci="0" /><line nr="313" mi="2" ci="0" /><line nr="316" mi="1" ci="0" /><line nr="326" mi="1" ci="0" /><line nr="328" mi="1" ci="0" /><line nr="329" mi="1" ci="0" /><line nr="330" mi="1" ci="0" /><line nr="331" mi="1" ci="0" /><line nr="334" mi="1" ci="0" /><line nr="336" mi="1" ci="0" /><line nr="337" mi="1" ci="0" /><line nr="341" mi="2" ci="0" /><line nr="344" mi="1" ci="0" /><line nr="349" mi="2" ci="0" /><line nr="352" mi="1" ci="0" /><line nr="360" mi="1" ci="0" /><line nr="364" mi="3" ci="0" /><line nr="367" mi="2" ci="0" /><line nr="374" mi="2" ci="0" /><line nr="375" mi="1" ci="0" /><line nr="377" mi="1" ci="0" /><line nr="378" mi="1" ci="0" /><line nr="379" mi="1" ci="0" /><line nr="380" mi="1" ci="0" /><line nr="381" mi="1" ci="0" /><line nr="382" mi="1" ci="0" /><line nr="383" mi="1" ci="0" /><line nr="385" mi="1" ci="0" /><line nr="387" mi="1" ci="0" /><line nr="388" mi="1" ci="0" /><line nr="392" mi="4" ci="0" /><line nr="394" mi="1" ci="0" /><counter type="INSTRUCTION" missed="146" covered="0" /><counter type="LINE" missed="115" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabVMDvdDrive.ps1"><line nr="49" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="58" mi="2" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="87" mi="2" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="97" mi="2" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><counter type="INSTRUCTION" missed="26" covered="0" /><counter type="LINE" missed="20" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-LabVMIntegrationService.ps1"><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="3" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="78" mi="2" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="88" mi="2" ci="0" /><line nr="90" mi="2" ci="0" /><counter type="INSTRUCTION" missed="19" covered="0" /><counter type="LINE" missed="13" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Wait-LabVMInitializationComplete.ps1"><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="72" mi="2" ci="0" /><line nr="73" mi="3" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="86" mi="2" ci="0" /><line nr="89" mi="1" ci="0" /><line nr="93" mi="2" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="98" mi="2" ci="0" /><line nr="99" mi="3" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="113" mi="2" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="122" mi="2" ci="0" /><line nr="123" mi="3" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="140" mi="2" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><counter type="INSTRUCTION" missed="55" covered="0" /><counter type="LINE" missed="40" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Wait-LabVMOff.ps1"><line nr="21" mi="1" ci="0" /><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><counter type="INSTRUCTION" missed="4" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Wait-LabVMStarted.ps1"><line nr="22" mi="2" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="26" mi="2" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="33" mi="4" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="3" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="43" mi="1" ci="0" /><counter type="INSTRUCTION" missed="20" covered="0" /><counter type="LINE" missed="12" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Write-LabMessage.ps1"><line nr="51" mi="0" ci="1" /><line nr="53" mi="0" ci="1" /><line nr="57" mi="0" ci="1" /><line nr="63" mi="0" ci="2" /><line nr="69" mi="0" ci="2" /><line nr="75" mi="0" ci="2" /><line nr="81" mi="0" ci="2" /><line nr="87" mi="0" ci="1" /><counter type="INSTRUCTION" missed="0" covered="12" /><counter type="LINE" missed="0" covered="8" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><counter type="INSTRUCTION" missed="1357" covered="207" /><counter type="LINE" missed="1081" covered="172" /><counter type="METHOD" missed="29" covered="15" /><counter type="CLASS" missed="28" covered="15" /></package><package name="lib/public"><class name="lib/public/Connect-LabVm" sourcefilename="Connect-LabVm.ps1"><method name="Connect-LabVM" desc="()" line="19"><counter type="INSTRUCTION" missed="42" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="42" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Disconnect-LabVm" sourcefilename="Disconnect-LabVm.ps1"><method name="Disconnect-LabVM" desc="()" line="12"><counter type="INSTRUCTION" missed="28" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="28" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-Lab" sourcefilename="Get-Lab.ps1"><method name="Get-Lab" desc="()" line="30"><counter type="INSTRUCTION" missed="24" covered="40" /><counter type="LINE" missed="21" covered="38" /><counter type="METHOD" missed="0" covered="1" /></method><counter type="INSTRUCTION" missed="24" covered="40" /><counter type="LINE" missed="21" covered="38" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></class><class name="lib/public/Get-LabResourceIso" sourcefilename="Get-LabResourceIso.ps1"><method name="Get-LabResourceISO" desc="()" line="26"><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabResourceModule" sourcefilename="Get-LabResourceModule.ps1"><method name="Get-LabResourceModule" desc="()" line="19"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabResourceMsu" sourcefilename="Get-LabResourceMsu.ps1"><method name="Get-LabResourceMSU" desc="()" line="19"><counter type="INSTRUCTION" missed="25" covered="0" /><counter type="LINE" missed="22" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="25" covered="0" /><counter type="LINE" missed="22" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabSwitch" sourcefilename="Get-LabSwitch.ps1"><method name="Get-LabSwitch" desc="()" line="19"><counter type="INSTRUCTION" missed="58" covered="0" /><counter type="LINE" missed="48" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="58" covered="0" /><counter type="LINE" missed="48" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabVm" sourcefilename="Get-LabVm.ps1"><method name="Get-LabVM" desc="()" line="30"><counter type="INSTRUCTION" missed="473" covered="0" /><counter type="LINE" missed="413" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="473" covered="0" /><counter type="LINE" missed="413" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabVMTemplate" sourcefilename="Get-LabVMTemplate.ps1"><method name="Get-LabVMTemplate" desc="()" line="24"><counter type="INSTRUCTION" missed="130" covered="0" /><counter type="LINE" missed="112" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="130" covered="0" /><counter type="LINE" missed="112" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Get-LabVmTemplateVhd" sourcefilename="Get-LabVmTemplateVhd.ps1"><method name="Get-LabVMTemplateVHD" desc="()" line="20"><counter type="INSTRUCTION" missed="150" covered="0" /><counter type="LINE" missed="127" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="150" covered="0" /><counter type="LINE" missed="127" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabResourceIso" sourcefilename="Initialize-LabResourceIso.ps1"><method name="Initialize-LabResourceISO" desc="()" line="23"><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="26" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="26" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabResourceModule" sourcefilename="Initialize-LabResourceModule.ps1"><method name="Initialize-LabResourceModule" desc="()" line="23"><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="15" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="15" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabResourceMsu" sourcefilename="Initialize-LabResourceMsu.ps1"><method name="Initialize-LabResourceMSU" desc="()" line="23"><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabSwitch" sourcefilename="Initialize-LabSwitch.ps1"><method name="Initialize-LabSwitch" desc="()" line="23"><counter type="INSTRUCTION" missed="168" covered="0" /><counter type="LINE" missed="131" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="168" covered="0" /><counter type="LINE" missed="131" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabVm" sourcefilename="Initialize-LabVm.ps1"><method name="Initialize-LabVM" desc="()" line="23"><counter type="INSTRUCTION" missed="121" covered="0" /><counter type="LINE" missed="87" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="121" covered="0" /><counter type="LINE" missed="87" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabVmTemplate" sourcefilename="Initialize-LabVmTemplate.ps1"><method name="Initialize-LabVMTemplate" desc="()" line="27"><counter type="INSTRUCTION" missed="86" covered="0" /><counter type="LINE" missed="66" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="86" covered="0" /><counter type="LINE" missed="66" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Initialize-LabVmTemplateVhd" sourcefilename="Initialize-LabVmTemplateVhd.ps1"><method name="Initialize-LabVMTemplateVHD" desc="()" line="22"><counter type="INSTRUCTION" missed="171" covered="0" /><counter type="LINE" missed="138" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="171" covered="0" /><counter type="LINE" missed="138" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Install-Lab" sourcefilename="Install-Lab.ps1"><method name="Install-Lab" desc="()" line="44"><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Install-LabVm" sourcefilename="Install-LabVm.ps1"><method name="Install-LabVM" desc="()" line="18"><counter type="INSTRUCTION" missed="34" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="34" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/New-Lab" sourcefilename="New-Lab.ps1"><method name="New-Lab" desc="()" line="53"><counter type="INSTRUCTION" missed="36" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="36" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabSwitch" sourcefilename="Remove-LabSwitch.ps1"><method name="Remove-LabSwitch" desc="()" line="29"><counter type="INSTRUCTION" missed="39" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="39" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabVm" sourcefilename="Remove-LabVm.ps1"><method name="Remove-LabVM" desc="()" line="27"><counter type="INSTRUCTION" missed="30" covered="0" /><counter type="LINE" missed="21" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="30" covered="0" /><counter type="LINE" missed="21" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabVMTemplate" sourcefilename="Remove-LabVMTemplate.ps1"><method name="Remove-LabVMTemplate" desc="()" line="23"><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Remove-LabVmTemplateVhd" sourcefilename="Remove-LabVmTemplateVhd.ps1"><method name="Remove-LabVMTemplateVHD" desc="()" line="22"><counter type="INSTRUCTION" missed="14" covered="0" /><counter type="LINE" missed="11" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="14" covered="0" /><counter type="LINE" missed="11" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Start-Lab" sourcefilename="Start-Lab.ps1"><method name="Start-Lab" desc="()" line="35"><counter type="INSTRUCTION" missed="66" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="66" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Stop-Lab" sourcefilename="Stop-Lab.ps1"><method name="Stop-Lab" desc="()" line="31"><counter type="INSTRUCTION" missed="53" covered="0" /><counter type="LINE" missed="37" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="53" covered="0" /><counter type="LINE" missed="37" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Uninstall-Lab" sourcefilename="Uninstall-Lab.ps1"><method name="Uninstall-Lab" desc="()" line="53"><counter type="INSTRUCTION" missed="40" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="40" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><class name="lib/public/Update-Lab" sourcefilename="Update-Lab.ps1"><method name="Update-Lab" desc="()" line="30"><counter type="INSTRUCTION" missed="5" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /></method><counter type="INSTRUCTION" missed="5" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></class><sourcefile name="Connect-LabVm.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="21" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="26" mi="2" ci="0" /><line nr="27" mi="3" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="48" mi="3" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="88" mi="2" ci="0" /><line nr="93" mi="2" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><counter type="INSTRUCTION" missed="42" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Disconnect-LabVm.ps1"><line nr="12" mi="1" ci="0" /><line nr="17" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="58" mi="3" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="61" mi="3" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><counter type="INSTRUCTION" missed="28" covered="0" /><counter type="LINE" missed="17" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-Lab.ps1"><line nr="30" mi="0" ci="1" /><line nr="32" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="37" mi="0" ci="2" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="48" mi="0" ci="1" /><line nr="50" mi="0" ci="1" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="61" mi="0" ci="1" /><line nr="64" mi="0" ci="1" /><line nr="70" mi="0" ci="1" /><line nr="71" mi="0" ci="1" /><line nr="72" mi="0" ci="1" /><line nr="75" mi="0" ci="1" /><line nr="77" mi="0" ci="1" /><line nr="78" mi="0" ci="1" /><line nr="80" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="94" mi="0" ci="1" /><line nr="95" mi="0" ci="1" /><line nr="97" mi="0" ci="1" /><line nr="99" mi="1" ci="0" /><line nr="103" mi="0" ci="1" /><line nr="106" mi="0" ci="1" /><line nr="109" mi="0" ci="1" /><line nr="111" mi="1" ci="0" /><line nr="115" mi="0" ci="1" /><line nr="119" mi="0" ci="1" /><line nr="121" mi="0" ci="1" /><line nr="123" mi="1" ci="0" /><line nr="127" mi="0" ci="1" /><line nr="128" mi="0" ci="1" /><line nr="131" mi="0" ci="1" /><line nr="133" mi="0" ci="1" /><line nr="135" mi="0" ci="2" /><line nr="139" mi="0" ci="1" /><line nr="140" mi="0" ci="1" /><line nr="143" mi="0" ci="1" /><line nr="145" mi="0" ci="1" /><line nr="147" mi="1" ci="0" /><line nr="151" mi="0" ci="1" /><line nr="152" mi="0" ci="1" /><line nr="159" mi="0" ci="1" /><line nr="161" mi="0" ci="1" /><line nr="163" mi="0" ci="1" /><line nr="166" mi="0" ci="1" /><line nr="168" mi="0" ci="1" /><line nr="172" mi="0" ci="1" /><counter type="INSTRUCTION" missed="24" covered="40" /><counter type="LINE" missed="21" covered="38" /><counter type="METHOD" missed="0" covered="1" /><counter type="CLASS" missed="0" covered="1" /></sourcefile><sourcefile name="Get-LabResourceIso.ps1"><line nr="26" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="2" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="2" ci="0" /><line nr="91" mi="1" ci="0" /><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="29" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabResourceModule.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="2" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="18" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabResourceMsu.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="25" mi="2" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><counter type="INSTRUCTION" missed="25" covered="0" /><counter type="LINE" missed="22" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabSwitch.ps1"><line nr="19" mi="1" ci="0" /><line nr="20" mi="1" ci="0" /><line nr="21" mi="1" ci="0" /><line nr="23" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="29" mi="2" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="78" mi="2" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="87" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="93" mi="2" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="2" ci="0" /><line nr="115" mi="1" ci="0" /><counter type="INSTRUCTION" missed="58" covered="0" /><counter type="LINE" missed="48" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVm.ps1"><line nr="30" mi="1" ci="0" /><line nr="32" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="2" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="79" mi="2" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="97" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="2" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="2" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="177" mi="1" ci="0" /><line nr="178" mi="1" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="194" mi="1" ci="0" /><line nr="197" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="201" mi="1" ci="0" /><line nr="203" mi="1" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="216" mi="1" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="225" mi="1" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="229" mi="1" ci="0" /><line nr="232" mi="2" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="237" mi="1" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="248" mi="1" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="266" mi="1" ci="0" /><line nr="267" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="271" mi="1" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="275" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="284" mi="2" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="289" mi="1" ci="0" /><line nr="291" mi="1" ci="0" /><line nr="293" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="298" mi="1" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="303" mi="2" ci="0" /><line nr="306" mi="1" ci="0" /><line nr="313" mi="1" ci="0" /><line nr="315" mi="1" ci="0" /><line nr="317" mi="1" ci="0" /><line nr="321" mi="1" ci="0" /><line nr="325" mi="1" ci="0" /><line nr="328" mi="1" ci="0" /><line nr="330" mi="1" ci="0" /><line nr="335" mi="1" ci="0" /><line nr="337" mi="1" ci="0" /><line nr="342" mi="2" ci="0" /><line nr="344" mi="1" ci="0" /><line nr="345" mi="1" ci="0" /><line nr="346" mi="1" ci="0" /><line nr="347" mi="2" ci="0" /><line nr="350" mi="1" ci="0" /><line nr="355" mi="1" ci="0" /><line nr="357" mi="1" ci="0" /><line nr="362" mi="1" ci="0" /><line nr="364" mi="1" ci="0" /><line nr="369" mi="2" ci="0" /><line nr="371" mi="1" ci="0" /><line nr="372" mi="1" ci="0" /><line nr="373" mi="1" ci="0" /><line nr="374" mi="2" ci="0" /><line nr="377" mi="1" ci="0" /><line nr="382" mi="1" ci="0" /><line nr="384" mi="2" ci="0" /><line nr="388" mi="2" ci="0" /><line nr="391" mi="2" ci="0" /><line nr="394" mi="1" ci="0" /><line nr="396" mi="1" ci="0" /><line nr="410" mi="1" ci="0" /><line nr="412" mi="1" ci="0" /><line nr="413" mi="1" ci="0" /><line nr="414" mi="1" ci="0" /><line nr="415" mi="2" ci="0" /><line nr="418" mi="1" ci="0" /><line nr="421" mi="1" ci="0" /><line nr="423" mi="1" ci="0" /><line nr="424" mi="1" ci="0" /><line nr="425" mi="1" ci="0" /><line nr="426" mi="2" ci="0" /><line nr="429" mi="1" ci="0" /><line nr="437" mi="1" ci="0" /><line nr="438" mi="1" ci="0" /><line nr="439" mi="1" ci="0" /><line nr="440" mi="2" ci="0" /><line nr="443" mi="1" ci="0" /><line nr="447" mi="2" ci="0" /><line nr="451" mi="1" ci="0" /><line nr="453" mi="2" ci="0" /><line nr="455" mi="1" ci="0" /><line nr="457" mi="1" ci="0" /><line nr="458" mi="1" ci="0" /><line nr="459" mi="1" ci="0" /><line nr="460" mi="2" ci="0" /><line nr="463" mi="1" ci="0" /><line nr="465" mi="1" ci="0" /><line nr="469" mi="1" ci="0" /><line nr="471" mi="2" ci="0" /><line nr="473" mi="1" ci="0" /><line nr="475" mi="1" ci="0" /><line nr="476" mi="1" ci="0" /><line nr="477" mi="1" ci="0" /><line nr="478" mi="2" ci="0" /><line nr="481" mi="1" ci="0" /><line nr="483" mi="1" ci="0" /><line nr="487" mi="1" ci="0" /><line nr="489" mi="1" ci="0" /><line nr="496" mi="1" ci="0" /><line nr="500" mi="1" ci="0" /><line nr="502" mi="1" ci="0" /><line nr="503" mi="1" ci="0" /><line nr="504" mi="1" ci="0" /><line nr="505" mi="2" ci="0" /><line nr="508" mi="1" ci="0" /><line nr="511" mi="1" ci="0" /><line nr="513" mi="1" ci="0" /><line nr="514" mi="1" ci="0" /><line nr="515" mi="1" ci="0" /><line nr="516" mi="2" ci="0" /><line nr="519" mi="1" ci="0" /><line nr="524" mi="1" ci="0" /><line nr="526" mi="2" ci="0" /><line nr="532" mi="1" ci="0" /><line nr="534" mi="1" ci="0" /><line nr="539" mi="2" ci="0" /><line nr="541" mi="1" ci="0" /><line nr="542" mi="1" ci="0" /><line nr="543" mi="1" ci="0" /><line nr="544" mi="2" ci="0" /><line nr="547" mi="1" ci="0" /><line nr="551" mi="1" ci="0" /><line nr="555" mi="1" ci="0" /><line nr="557" mi="2" ci="0" /><line nr="558" mi="1" ci="0" /><line nr="560" mi="1" ci="0" /><line nr="561" mi="1" ci="0" /><line nr="562" mi="1" ci="0" /><line nr="563" mi="2" ci="0" /><line nr="566" mi="1" ci="0" /><line nr="571" mi="1" ci="0" /><line nr="572" mi="4" ci="0" /><line nr="575" mi="1" ci="0" /><line nr="576" mi="1" ci="0" /><line nr="577" mi="1" ci="0" /><line nr="578" mi="2" ci="0" /><line nr="581" mi="1" ci="0" /><line nr="584" mi="2" ci="0" /><line nr="588" mi="1" ci="0" /><line nr="589" mi="1" ci="0" /><line nr="591" mi="1" ci="0" /><line nr="593" mi="1" ci="0" /><line nr="596" mi="1" ci="0" /><line nr="599" mi="1" ci="0" /><line nr="605" mi="1" ci="0" /><line nr="607" mi="1" ci="0" /><line nr="612" mi="2" ci="0" /><line nr="614" mi="1" ci="0" /><line nr="617" mi="1" ci="0" /><line nr="618" mi="1" ci="0" /><line nr="619" mi="1" ci="0" /><line nr="620" mi="2" ci="0" /><line nr="623" mi="1" ci="0" /><line nr="627" mi="1" ci="0" /><line nr="628" mi="1" ci="0" /><line nr="631" mi="2" ci="0" /><line nr="635" mi="1" ci="0" /><line nr="637" mi="1" ci="0" /><line nr="639" mi="1" ci="0" /><line nr="641" mi="1" ci="0" /><line nr="645" mi="1" ci="0" /><line nr="650" mi="2" ci="0" /><line nr="652" mi="1" ci="0" /><line nr="653" mi="1" ci="0" /><line nr="654" mi="1" ci="0" /><line nr="655" mi="2" ci="0" /><line nr="658" mi="1" ci="0" /><line nr="663" mi="1" ci="0" /><line nr="665" mi="1" ci="0" /><line nr="667" mi="1" ci="0" /><line nr="669" mi="1" ci="0" /><line nr="673" mi="1" ci="0" /><line nr="678" mi="1" ci="0" /><line nr="680" mi="1" ci="0" /><line nr="681" mi="1" ci="0" /><line nr="682" mi="1" ci="0" /><line nr="683" mi="2" ci="0" /><line nr="686" mi="1" ci="0" /><line nr="689" mi="2" ci="0" /><line nr="691" mi="1" ci="0" /><line nr="692" mi="1" ci="0" /><line nr="693" mi="1" ci="0" /><line nr="694" mi="2" ci="0" /><line nr="697" mi="1" ci="0" /><line nr="702" mi="1" ci="0" /><line nr="705" mi="1" ci="0" /><line nr="707" mi="1" ci="0" /><line nr="709" mi="1" ci="0" /><line nr="711" mi="1" ci="0" /><line nr="717" mi="1" ci="0" /><line nr="720" mi="1" ci="0" /><line nr="722" mi="1" ci="0" /><line nr="723" mi="1" ci="0" /><line nr="724" mi="1" ci="0" /><line nr="725" mi="2" ci="0" /><line nr="728" mi="1" ci="0" /><line nr="731" mi="2" ci="0" /><line nr="733" mi="1" ci="0" /><line nr="734" mi="1" ci="0" /><line nr="735" mi="1" ci="0" /><line nr="736" mi="2" ci="0" /><line nr="739" mi="1" ci="0" /><line nr="742" mi="1" ci="0" /><line nr="744" mi="1" ci="0" /><line nr="745" mi="1" ci="0" /><line nr="746" mi="1" ci="0" /><line nr="747" mi="2" ci="0" /><line nr="750" mi="1" ci="0" /><line nr="755" mi="1" ci="0" /><line nr="757" mi="1" ci="0" /><line nr="763" mi="2" ci="0" /><line nr="767" mi="2" ci="0" /><line nr="770" mi="1" ci="0" /><line nr="772" mi="1" ci="0" /><line nr="774" mi="2" ci="0" /><line nr="776" mi="1" ci="0" /><line nr="778" mi="1" ci="0" /><line nr="782" mi="1" ci="0" /><line nr="784" mi="1" ci="0" /><line nr="786" mi="2" ci="0" /><line nr="788" mi="1" ci="0" /><line nr="790" mi="1" ci="0" /><line nr="794" mi="1" ci="0" /><line nr="796" mi="1" ci="0" /><line nr="798" mi="2" ci="0" /><line nr="800" mi="1" ci="0" /><line nr="802" mi="1" ci="0" /><line nr="806" mi="1" ci="0" /><line nr="808" mi="2" ci="0" /><line nr="810" mi="1" ci="0" /><line nr="812" mi="1" ci="0" /><line nr="819" mi="2" ci="0" /><line nr="821" mi="1" ci="0" /><line nr="822" mi="1" ci="0" /><line nr="823" mi="1" ci="0" /><line nr="824" mi="2" ci="0" /><line nr="827" mi="1" ci="0" /><line nr="830" mi="1" ci="0" /><line nr="832" mi="1" ci="0" /><line nr="834" mi="1" ci="0" /><line nr="838" mi="1" ci="0" /><line nr="840" mi="1" ci="0" /><line nr="842" mi="1" ci="0" /><line nr="844" mi="1" ci="0" /><line nr="848" mi="1" ci="0" /><line nr="850" mi="1" ci="0" /><line nr="852" mi="1" ci="0" /><line nr="854" mi="1" ci="0" /><line nr="856" mi="1" ci="0" /><line nr="860" mi="1" ci="0" /><line nr="862" mi="1" ci="0" /><line nr="864" mi="1" ci="0" /><line nr="866" mi="1" ci="0" /><line nr="868" mi="1" ci="0" /><line nr="872" mi="1" ci="0" /><line nr="874" mi="1" ci="0" /><line nr="876" mi="1" ci="0" /><line nr="878" mi="1" ci="0" /><line nr="880" mi="1" ci="0" /><line nr="884" mi="1" ci="0" /><line nr="886" mi="1" ci="0" /><line nr="888" mi="1" ci="0" /><line nr="890" mi="1" ci="0" /><line nr="892" mi="1" ci="0" /><line nr="896" mi="1" ci="0" /><line nr="898" mi="1" ci="0" /><line nr="900" mi="1" ci="0" /><line nr="904" mi="1" ci="0" /><line nr="906" mi="1" ci="0" /><line nr="908" mi="1" ci="0" /><line nr="910" mi="1" ci="0" /><line nr="912" mi="1" ci="0" /><line nr="916" mi="1" ci="0" /><line nr="918" mi="1" ci="0" /><line nr="920" mi="1" ci="0" /><line nr="922" mi="1" ci="0" /><line nr="924" mi="1" ci="0" /><line nr="928" mi="1" ci="0" /><line nr="930" mi="1" ci="0" /><line nr="932" mi="1" ci="0" /><line nr="934" mi="1" ci="0" /><line nr="936" mi="1" ci="0" /><line nr="940" mi="1" ci="0" /><line nr="942" mi="1" ci="0" /><line nr="945" mi="1" ci="0" /><line nr="947" mi="1" ci="0" /><line nr="949" mi="1" ci="0" /><line nr="953" mi="1" ci="0" /><line nr="954" mi="1" ci="0" /><line nr="955" mi="1" ci="0" /><line nr="956" mi="1" ci="0" /><line nr="957" mi="1" ci="0" /><line nr="958" mi="1" ci="0" /><line nr="959" mi="1" ci="0" /><line nr="960" mi="1" ci="0" /><line nr="961" mi="1" ci="0" /><line nr="962" mi="1" ci="0" /><line nr="963" mi="1" ci="0" /><line nr="964" mi="1" ci="0" /><line nr="965" mi="1" ci="0" /><line nr="966" mi="1" ci="0" /><line nr="967" mi="1" ci="0" /><line nr="968" mi="1" ci="0" /><line nr="969" mi="1" ci="0" /><line nr="970" mi="1" ci="0" /><line nr="971" mi="1" ci="0" /><line nr="972" mi="1" ci="0" /><line nr="973" mi="1" ci="0" /><line nr="974" mi="1" ci="0" /><line nr="975" mi="1" ci="0" /><line nr="976" mi="1" ci="0" /><line nr="977" mi="1" ci="0" /><line nr="978" mi="1" ci="0" /><line nr="981" mi="1" ci="0" /><line nr="984" mi="2" ci="0" /><line nr="988" mi="1" ci="0" /><counter type="INSTRUCTION" missed="473" covered="0" /><counter type="LINE" missed="413" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVMTemplate.ps1"><line nr="24" mi="1" ci="0" /><line nr="26" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="2" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="2" ci="0" /><line nr="53" mi="2" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="97" mi="2" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="120" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="2" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="161" mi="1" ci="0" /><line nr="167" mi="2" ci="0" /><line nr="169" mi="1" ci="0" /><line nr="170" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="172" mi="2" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="186" mi="1" ci="0" /><line nr="191" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="201" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="203" mi="2" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="210" mi="1" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="216" mi="1" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="224" mi="1" ci="0" /><line nr="226" mi="2" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="234" mi="1" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="238" mi="1" ci="0" /><line nr="240" mi="1" ci="0" /><line nr="242" mi="1" ci="0" /><line nr="244" mi="1" ci="0" /><line nr="246" mi="1" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="251" mi="1" ci="0" /><line nr="253" mi="1" ci="0" /><line nr="255" mi="2" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="261" mi="1" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="267" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="272" mi="2" ci="0" /><line nr="274" mi="1" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="292" mi="1" ci="0" /><line nr="295" mi="1" ci="0" /><counter type="INSTRUCTION" missed="130" covered="0" /><counter type="LINE" missed="112" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Get-LabVmTemplateVhd.ps1"><line nr="20" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="32" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="43" mi="2" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="72" mi="2" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="100" mi="2" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="118" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="133" mi="2" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="138" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="145" mi="1" ci="0" /><line nr="146" mi="2" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="159" mi="2" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="174" mi="2" ci="0" /><line nr="176" mi="1" ci="0" /><line nr="177" mi="1" ci="0" /><line nr="178" mi="2" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="183" mi="1" ci="0" /><line nr="185" mi="2" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="190" mi="1" ci="0" /><line nr="191" mi="1" ci="0" /><line nr="192" mi="2" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="207" mi="1" ci="0" /><line nr="209" mi="2" ci="0" /><line nr="211" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="214" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="216" mi="2" ci="0" /><line nr="219" mi="1" ci="0" /><line nr="223" mi="1" ci="0" /><line nr="224" mi="1" ci="0" /><line nr="226" mi="2" ci="0" /><line nr="228" mi="1" ci="0" /><line nr="230" mi="1" ci="0" /><line nr="231" mi="1" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="233" mi="2" ci="0" /><line nr="236" mi="1" ci="0" /><line nr="240" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="243" mi="2" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="248" mi="1" ci="0" /><line nr="250" mi="1" ci="0" /><line nr="252" mi="2" ci="0" /><line nr="254" mi="1" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="256" mi="1" ci="0" /><line nr="257" mi="2" ci="0" /><line nr="260" mi="1" ci="0" /><line nr="264" mi="1" ci="0" /><line nr="266" mi="1" ci="0" /><line nr="270" mi="1" ci="0" /><line nr="272" mi="1" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="279" mi="1" ci="0" /><line nr="280" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="285" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="287" mi="2" ci="0" /><line nr="289" mi="1" ci="0" /><counter type="INSTRUCTION" missed="150" covered="0" /><counter type="LINE" missed="127" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabResourceIso.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="49" mi="2" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="70" mi="2" ci="0" /><line nr="73" mi="1" ci="0" /><counter type="INSTRUCTION" missed="33" covered="0" /><counter type="LINE" missed="26" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabResourceModule.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="2" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="54" mi="1" ci="0" /><counter type="INSTRUCTION" missed="21" covered="0" /><counter type="LINE" missed="15" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabResourceMsu.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="31" mi="1" ci="0" /><line nr="33" mi="2" ci="0" /><line nr="35" mi="2" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabSwitch.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="32" mi="2" ci="0" /><line nr="38" mi="4" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="2" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="60" mi="2" ci="0" /><line nr="61" mi="2" ci="0" /><line nr="63" mi="2" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="70" mi="2" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="77" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="88" mi="2" ci="0" /><line nr="91" mi="1" ci="0" /><line nr="95" mi="3" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="109" mi="4" ci="0" /><line nr="110" mi="2" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="2" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="155" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="165" mi="2" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="176" mi="2" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="181" mi="2" ci="0" /><line nr="182" mi="1" ci="0" /><line nr="184" mi="2" ci="0" /><line nr="186" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="189" mi="2" ci="0" /><line nr="192" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="3" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="199" mi="1" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="201" mi="2" ci="0" /><line nr="204" mi="1" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="209" mi="1" ci="0" /><line nr="214" mi="2" ci="0" /><line nr="219" mi="1" ci="0" /><line nr="221" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="223" mi="1" ci="0" /><line nr="224" mi="2" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="229" mi="1" ci="0" /><line nr="230" mi="3" ci="0" /><line nr="231" mi="1" ci="0" /><line nr="233" mi="1" ci="0" /><line nr="234" mi="1" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="236" mi="2" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="241" mi="2" ci="0" /><line nr="246" mi="1" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="252" mi="2" ci="0" /><line nr="255" mi="1" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="264" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="266" mi="2" ci="0" /><line nr="269" mi="1" ci="0" /><line nr="273" mi="1" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="281" mi="1" ci="0" /><line nr="283" mi="1" ci="0" /><line nr="284" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="291" mi="1" ci="0" /><line nr="293" mi="1" ci="0" /><line nr="295" mi="1" ci="0" /><line nr="296" mi="1" ci="0" /><line nr="297" mi="1" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="303" mi="1" ci="0" /><line nr="307" mi="1" ci="0" /><line nr="309" mi="1" ci="0" /><line nr="310" mi="1" ci="0" /><line nr="314" mi="1" ci="0" /><counter type="INSTRUCTION" missed="168" covered="0" /><counter type="LINE" missed="131" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVm.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="35" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="48" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="2" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="73" mi="1" ci="0" /><line nr="75" mi="3" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="86" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="90" mi="2" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="100" mi="2" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="121" mi="2" ci="0" /><line nr="126" mi="2" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="139" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="158" mi="2" ci="0" /><line nr="160" mi="1" ci="0" /><line nr="167" mi="4" ci="0" /><line nr="168" mi="2" ci="0" /><line nr="170" mi="2" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="173" mi="1" ci="0" /><line nr="177" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="183" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="186" mi="2" ci="0" /><line nr="189" mi="1" ci="0" /><line nr="195" mi="1" ci="0" /><line nr="196" mi="1" ci="0" /><line nr="198" mi="2" ci="0" /><line nr="200" mi="1" ci="0" /><line nr="205" mi="1" ci="0" /><line nr="213" mi="1" ci="0" /><line nr="217" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="227" mi="3" ci="0" /><line nr="229" mi="2" ci="0" /><line nr="232" mi="1" ci="0" /><line nr="237" mi="1" ci="0" /><line nr="240" mi="1" ci="0" /><line nr="241" mi="1" ci="0" /><line nr="245" mi="2" ci="0" /><line nr="249" mi="1" ci="0" /><line nr="251" mi="3" ci="0" /><line nr="253" mi="2" ci="0" /><line nr="256" mi="1" ci="0" /><line nr="262" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="267" mi="1" ci="0" /><line nr="268" mi="1" ci="0" /><line nr="272" mi="2" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="278" mi="1" ci="0" /><line nr="281" mi="2" ci="0" /><line nr="285" mi="1" ci="0" /><line nr="287" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="293" mi="1" ci="0" /><line nr="294" mi="1" ci="0" /><line nr="299" mi="6" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="305" mi="1" ci="0" /><line nr="307" mi="3" ci="0" /><line nr="308" mi="1" ci="0" /><line nr="309" mi="1" ci="0" /><line nr="314" mi="1" ci="0" /><counter type="INSTRUCTION" missed="121" covered="0" /><counter type="LINE" missed="87" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVmTemplate.ps1"><line nr="27" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="40" mi="2" ci="0" /><line nr="46" mi="2" ci="0" /><line nr="50" mi="2" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="2" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="62" mi="2" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="82" mi="2" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="96" mi="2" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="118" mi="2" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="123" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="2" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="138" mi="2" ci="0" /><line nr="141" mi="2" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="151" mi="1" ci="0" /><line nr="152" mi="1" ci="0" /><line nr="153" mi="1" ci="0" /><line nr="154" mi="2" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="161" mi="2" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="170" mi="2" ci="0" /><line nr="172" mi="1" ci="0" /><line nr="175" mi="1" ci="0" /><line nr="182" mi="2" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="191" mi="2" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="200" mi="2" ci="0" /><line nr="206" mi="1" ci="0" /><line nr="208" mi="1" ci="0" /><line nr="209" mi="1" ci="0" /><line nr="212" mi="1" ci="0" /><line nr="216" mi="2" ci="0" /><line nr="218" mi="2" ci="0" /><line nr="220" mi="1" ci="0" /><counter type="INSTRUCTION" missed="86" covered="0" /><counter type="LINE" missed="66" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Initialize-LabVmTemplateVhd.ps1"><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="44" mi="1" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="2" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="68" mi="2" ci="0" /><line nr="75" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="2" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="85" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="92" mi="2" ci="0" /><line nr="95" mi="1" ci="0" /><line nr="101" mi="1" ci="0" /><line nr="104" mi="1" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="2" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="119" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="122" mi="2" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="127" mi="2" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="131" mi="1" ci="0" /><line nr="133" mi="1" ci="0" /><line nr="138" mi="1" ci="0" /><line nr="139" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="143" mi="1" ci="0" /><line nr="146" mi="1" ci="0" /><line nr="149" mi="1" ci="0" /><line nr="151" mi="2" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="159" mi="1" ci="0" /><line nr="162" mi="1" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="168" mi="1" ci="0" /><line nr="170" mi="1" ci="0" /><line nr="171" mi="1" ci="0" /><line nr="176" mi="1" ci="0" /><line nr="178" mi="2" ci="0" /><line nr="179" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="185" mi="1" ci="0" /><line nr="187" mi="1" ci="0" /><line nr="188" mi="1" ci="0" /><line nr="193" mi="1" ci="0" /><line nr="198" mi="1" ci="0" /><line nr="202" mi="1" ci="0" /><line nr="206" mi="2" ci="0" /><line nr="208" mi="2" ci="0" /><line nr="210" mi="1" ci="0" /><line nr="215" mi="1" ci="0" /><line nr="222" mi="1" ci="0" /><line nr="224" mi="1" ci="0" /><line nr="227" mi="1" ci="0" /><line nr="232" mi="2" ci="0" /><line nr="234" mi="2" ci="0" /><line nr="235" mi="1" ci="0" /><line nr="239" mi="1" ci="0" /><line nr="243" mi="2" ci="0" /><line nr="245" mi="1" ci="0" /><line nr="246" mi="1" ci="0" /><line nr="247" mi="1" ci="0" /><line nr="248" mi="2" ci="0" /><line nr="251" mi="1" ci="0" /><line nr="253" mi="2" ci="0" /><line nr="256" mi="2" ci="0" /><line nr="257" mi="1" ci="0" /><line nr="259" mi="1" ci="0" /><line nr="261" mi="2" ci="0" /><line nr="263" mi="1" ci="0" /><line nr="264" mi="1" ci="0" /><line nr="265" mi="1" ci="0" /><line nr="266" mi="2" ci="0" /><line nr="269" mi="1" ci="0" /><line nr="271" mi="2" ci="0" /><line nr="276" mi="1" ci="0" /><line nr="277" mi="1" ci="0" /><line nr="279" mi="1" ci="0" /><line nr="282" mi="1" ci="0" /><line nr="286" mi="1" ci="0" /><line nr="288" mi="1" ci="0" /><line nr="289" mi="1" ci="0" /><line nr="290" mi="1" ci="0" /><line nr="291" mi="2" ci="0" /><line nr="294" mi="1" ci="0" /><line nr="297" mi="1" ci="0" /><line nr="298" mi="2" ci="0" /><line nr="300" mi="1" ci="0" /><line nr="301" mi="1" ci="0" /><line nr="302" mi="1" ci="0" /><line nr="303" mi="2" ci="0" /><line nr="306" mi="1" ci="0" /><line nr="308" mi="2" ci="0" /><line nr="311" mi="1" ci="0" /><line nr="312" mi="1" ci="0" /><line nr="318" mi="1" ci="0" /><line nr="321" mi="1" ci="0" /><line nr="325" mi="2" ci="0" /><line nr="329" mi="1" ci="0" /><line nr="334" mi="2" ci="0" /><line nr="336" mi="1" ci="0" /><line nr="342" mi="1" ci="0" /><line nr="346" mi="1" ci="0" /><line nr="347" mi="1" ci="0" /><line nr="348" mi="1" ci="0" /><line nr="349" mi="2" ci="0" /><line nr="352" mi="1" ci="0" /><line nr="357" mi="2" ci="0" /><line nr="360" mi="1" ci="0" /><counter type="INSTRUCTION" missed="171" covered="0" /><counter type="LINE" missed="138" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Install-Lab.ps1"><line nr="44" mi="1" ci="0" /><line nr="46" mi="1" ci="0" /><line nr="48" mi="2" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="67" mi="2" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="95" mi="2" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="100" mi="2" ci="0" /><line nr="102" mi="2" ci="0" /><line nr="105" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="112" mi="2" ci="0" /><line nr="114" mi="2" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="126" mi="2" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="150" mi="1" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="164" mi="1" ci="0" /><line nr="166" mi="1" ci="0" /><line nr="172" mi="1" ci="0" /><line nr="174" mi="1" ci="0" /><line nr="180" mi="1" ci="0" /><line nr="184" mi="1" ci="0" /><line nr="189" mi="2" ci="0" /><counter type="INSTRUCTION" missed="51" covered="0" /><counter type="LINE" missed="41" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Install-LabVm.ps1"><line nr="18" mi="1" ci="0" /><line nr="21" mi="2" ci="0" /><line nr="23" mi="2" ci="0" /><line nr="26" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="33" mi="3" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="2" ci="0" /><line nr="41" mi="1" ci="0" /><line nr="43" mi="1" ci="0" /><line nr="45" mi="2" ci="0" /><line nr="50" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="52" mi="1" ci="0" /><line nr="53" mi="2" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="72" mi="2" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><counter type="INSTRUCTION" missed="34" covered="0" /><counter type="LINE" missed="25" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="New-Lab.ps1"><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="73" mi="2" ci="0" /><line nr="76" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="94" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="107" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="112" mi="1" ci="0" /><line nr="113" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="135" mi="1" ci="0" /><line nr="136" mi="1" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="142" mi="1" ci="0" /><line nr="147" mi="1" ci="0" /><line nr="154" mi="2" ci="0" /><counter type="INSTRUCTION" missed="36" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabSwitch.ps1"><line nr="29" mi="1" ci="0" /><line nr="32" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="53" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="58" mi="2" ci="0" /><line nr="60" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="65" mi="2" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="72" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="85" mi="1" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="103" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="118" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="127" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="129" mi="1" ci="0" /><line nr="130" mi="2" ci="0" /><line nr="133" mi="1" ci="0" /><counter type="INSTRUCTION" missed="39" covered="0" /><counter type="LINE" missed="34" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabVm.ps1"><line nr="27" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="30" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="39" mi="1" ci="0" /><line nr="41" mi="2" ci="0" /><line nr="47" mi="3" ci="0" /><line nr="50" mi="2" ci="0" /><line nr="52" mi="2" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="58" mi="1" ci="0" /><line nr="62" mi="2" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="69" mi="2" ci="0" /><line nr="74" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="83" mi="2" ci="0" /><line nr="86" mi="1" ci="0" /><counter type="INSTRUCTION" missed="30" covered="0" /><counter type="LINE" missed="21" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabVMTemplate.ps1"><line nr="23" mi="1" ci="0" /><line nr="25" mi="1" ci="0" /><line nr="28" mi="1" ci="0" /><line nr="30" mi="2" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="42" mi="2" ci="0" /><line nr="44" mi="1" ci="0" /><counter type="INSTRUCTION" missed="10" covered="0" /><counter type="LINE" missed="8" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Remove-LabVmTemplateVhd.ps1"><line nr="22" mi="1" ci="0" /><line nr="24" mi="1" ci="0" /><line nr="29" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="36" mi="1" ci="0" /><line nr="38" mi="1" ci="0" /><line nr="39" mi="2" ci="0" /><line nr="45" mi="1" ci="0" /><line nr="47" mi="2" ci="0" /><line nr="49" mi="1" ci="0" /><line nr="52" mi="2" ci="0" /><counter type="INSTRUCTION" missed="14" covered="0" /><counter type="LINE" missed="11" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Start-Lab.ps1"><line nr="35" mi="1" ci="0" /><line nr="37" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="47" mi="1" ci="0" /><line nr="51" mi="3" ci="0" /><line nr="52" mi="3" ci="0" /><line nr="53" mi="4" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="59" mi="2" ci="0" /><line nr="63" mi="5" ci="0" /><line nr="65" mi="1" ci="0" /><line nr="66" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="68" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="76" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="80" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="92" mi="2" ci="0" /><line nr="93" mi="1" ci="0" /><line nr="100" mi="1" ci="0" /><line nr="105" mi="2" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="115" mi="1" ci="0" /><line nr="116" mi="1" ci="0" /><line nr="117" mi="2" ci="0" /><line nr="121" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="132" mi="1" ci="0" /><line nr="139" mi="2" ci="0" /><line nr="140" mi="1" ci="0" /><line nr="144" mi="1" ci="0" /><line nr="148" mi="1" ci="0" /><line nr="153" mi="2" ci="0" /><line nr="156" mi="1" ci="0" /><line nr="157" mi="1" ci="0" /><line nr="158" mi="1" ci="0" /><line nr="159" mi="2" ci="0" /><line nr="163" mi="1" ci="0" /><line nr="167" mi="2" ci="0" /><counter type="INSTRUCTION" missed="66" covered="0" /><counter type="LINE" missed="46" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Stop-Lab.ps1"><line nr="31" mi="1" ci="0" /><line nr="34" mi="1" ci="0" /><line nr="42" mi="1" ci="0" /><line nr="46" mi="3" ci="0" /><line nr="47" mi="3" ci="0" /><line nr="48" mi="4" ci="0" /><line nr="51" mi="1" ci="0" /><line nr="54" mi="2" ci="0" /><line nr="58" mi="2" ci="0" /><line nr="59" mi="3" ci="0" /><line nr="61" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="63" mi="1" ci="0" /><line nr="64" mi="1" ci="0" /><line nr="67" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="71" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="78" mi="1" ci="0" /><line nr="81" mi="1" ci="0" /><line nr="82" mi="1" ci="0" /><line nr="83" mi="1" ci="0" /><line nr="84" mi="2" ci="0" /><line nr="88" mi="1" ci="0" /><line nr="92" mi="1" ci="0" /><line nr="94" mi="2" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="103" mi="2" ci="0" /><line nr="106" mi="1" ci="0" /><line nr="109" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="117" mi="1" ci="0" /><line nr="124" mi="2" ci="0" /><line nr="126" mi="1" ci="0" /><line nr="130" mi="1" ci="0" /><line nr="134" mi="1" ci="0" /><line nr="139" mi="2" ci="0" /><counter type="INSTRUCTION" missed="53" covered="0" /><counter type="LINE" missed="37" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Uninstall-Lab.ps1"><line nr="53" mi="1" ci="0" /><line nr="54" mi="1" ci="0" /><line nr="55" mi="1" ci="0" /><line nr="56" mi="1" ci="0" /><line nr="57" mi="1" ci="0" /><line nr="59" mi="1" ci="0" /><line nr="62" mi="1" ci="0" /><line nr="69" mi="1" ci="0" /><line nr="70" mi="1" ci="0" /><line nr="74" mi="1" ci="0" /><line nr="75" mi="1" ci="0" /><line nr="77" mi="2" ci="0" /><line nr="79" mi="1" ci="0" /><line nr="84" mi="1" ci="0" /><line nr="86" mi="1" ci="0" /><line nr="87" mi="1" ci="0" /><line nr="90" mi="1" ci="0" /><line nr="96" mi="1" ci="0" /><line nr="98" mi="1" ci="0" /><line nr="99" mi="1" ci="0" /><line nr="102" mi="1" ci="0" /><line nr="108" mi="1" ci="0" /><line nr="110" mi="1" ci="0" /><line nr="111" mi="1" ci="0" /><line nr="114" mi="1" ci="0" /><line nr="120" mi="1" ci="0" /><line nr="122" mi="1" ci="0" /><line nr="124" mi="1" ci="0" /><line nr="125" mi="1" ci="0" /><line nr="128" mi="1" ci="0" /><line nr="137" mi="1" ci="0" /><line nr="139" mi="3" ci="0" /><line nr="141" mi="1" ci="0" /><line nr="144" mi="2" ci="0" /><line nr="148" mi="2" ci="0" /><counter type="INSTRUCTION" missed="40" covered="0" /><counter type="LINE" missed="35" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><sourcefile name="Update-Lab.ps1"><line nr="30" mi="1" ci="0" /><line nr="33" mi="1" ci="0" /><line nr="40" mi="1" ci="0" /><line nr="43" mi="2" ci="0" /><counter type="INSTRUCTION" missed="5" covered="0" /><counter type="LINE" missed="4" covered="0" /><counter type="METHOD" missed="1" covered="0" /><counter type="CLASS" missed="1" covered="0" /></sourcefile><counter type="INSTRUCTION" missed="1972" covered="40" /><counter type="LINE" missed="1603" covered="38" /><counter type="METHOD" missed="27" covered="1" /><counter type="CLASS" missed="27" covered="1" /></package><counter type="INSTRUCTION" missed="3329" covered="247" /><counter type="LINE" missed="2684" covered="210" /><counter type="METHOD" missed="56" covered="16" /><counter type="CLASS" missed="55" covered="16" /></report> diff --git a/tests/unit/TestResults.unit.xml b/tests/unit/TestResults.unit.xml deleted file mode 100644 index 64386f44..00000000 --- a/tests/unit/TestResults.unit.xml +++ /dev/null @@ -1,355 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<test-results xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="nunit_schema_2.5.xsd" name="Pester" total="86" errors="0" failures="0" not-run="0" inconclusive="0" ignored="0" skipped="0" invalid="0" date="2019-11-10" time="09:35:04"> - <environment user="Dan" machine-name="PLAGUE02" cwd="C:\Users\Dan\Source\GitHub\LabBuilder" user-domain="PLAGUE02" platform="Microsoft Windows 10 Enterprise|C:\WINDOWS|\Device\Harddisk1\Partition4" nunit-version="2.5.8.0" os-version="10.0.19013" clr-version="4.0.30319.42000" /> - <culture-info current-culture="en-NZ" current-uiculture="en-US" /> - <test-suite type="TestFixture" name="Pester" executed="True" result="Success" success="True" time="60.6614" asserts="0" description="Pester"> - <results> - <test-suite type="TestFixture" name=".\test\unit\lib\private\utils.tests.ps1" executed="True" result="Success" success="True" time="60.6614" asserts="0" description=".\test\unit\lib\private\utils.tests.ps1"> - <results> - <test-suite type="TestFixture" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1" executed="True" result="Success" success="True" time="2.6504" asserts="0" description="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1"> - <results> - <test-suite type="TestFixture" name="When Download folder does not exist" executed="True" result="Success" success="True" time="0.4384" asserts="0" description="When Download folder does not exist"> - <results> - <test-case description="Throws a DownloadFolderDoesNotExistError Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download folder does not exist.Throws a DownloadFolderDoesNotExistError Exception" time="0.0495" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download folder does not exist.Calls appropriate mocks" time="0.0412" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Download fails" executed="True" result="Success" success="True" time="0.4787" asserts="0" description="When Download fails"> - <results> - <test-case description="Throws a FileDownloadError Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download fails.Throws a FileDownloadError Exception" time="0.0755" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download fails.Calls appropriate mocks" time="0.0796" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Download OK" executed="True" result="Success" success="True" time="0.4688" asserts="0" description="When Download OK"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download OK.Does not throw an Exception" time="0.0859" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Download OK.Calls appropriate mocks" time="0.0784" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Zip Download OK, Extract fails" executed="True" result="Success" success="True" time="0.5929" asserts="0" description="When Zip Download OK, Extract fails"> - <results> - <test-case description="Throws a FileExtractError Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract fails.Throws a FileExtractError Exception" time="0.1348" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract fails.Calls appropriate mocks" time="0.1108" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Zip Download OK, Extract OK" executed="True" result="Success" success="True" time="0.5102" asserts="0" description="When Zip Download OK, Extract OK"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract OK.Does not throw an Exception" time="0.1397" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Invoke-LabDownloadAndUnzipFile.ps1.When Zip Download OK, Extract OK.Calls appropriate mocks" time="0.0649" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Invoke-LabDownloadResourceModule.ps1" executed="True" result="Success" success="True" time="46.7703" asserts="0" description="\lib\private\Invoke-LabDownloadResourceModule.ps1"> - <results> - <test-suite type="TestFixture" name="When Correct module already installed; Valid URL and Folder passed" executed="True" result="Success" success="True" time="0.4072" asserts="0" description="When Correct module already installed; Valid URL and Folder passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct module already installed; Valid URL and Folder passed.Does not throw an Exception" time="0.0683" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct module already installed; Valid URL and Folder passed.Should call appropriate Mocks" time="0.1538" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Module is not installed; Valid URL and Folder passed" executed="True" result="Success" success="True" time="0.8757" asserts="0" description="When Module is not installed; Valid URL and Folder passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Valid URL and Folder passed.Does not throw an Exception" time="0.3254" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Valid URL and Folder passed.Should call appropriate Mocks" time="0.3111" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Module is not installed; No URL or Folder passed" executed="True" result="Success" success="True" time="6.8872" asserts="0" description="When Module is not installed; No URL or Folder passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; No URL or Folder passed.Does not throw an Exception" time="6.5676" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; No URL or Folder passed.Should call appropriate Mocks" time="0.1485" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Wrong version of module is installed; Valid URL, Folder and Required Version passed" executed="True" result="Success" success="True" time="0.7559" asserts="0" description="When Wrong version of module is installed; Valid URL, Folder and Required Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Required Version passed.Does not throw an Exception" time="0.2566" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Required Version passed.Should call appropriate Mocks" time="0.3057" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed" executed="True" result="Success" success="True" time="6.7012" asserts="0" description="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed.Does not throw an Exception" time="6.3145" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed.Should call appropriate Mocks" time="0.177" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Correct version of module is installed; Valid URL, Folder and Required Version passed" executed="True" result="Success" success="True" time="0.4226" asserts="0" description="When Correct version of module is installed; Valid URL, Folder and Required Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Required Version passed.Does not throw an Exception" time="0.0429" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Required Version passed.Should call appropriate Mocks" time="0.1951" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Correct version of module is installed; No URL and Folder passed, but Required Version passed" executed="True" result="Success" success="True" time="0.4667" asserts="0" description="When Correct version of module is installed; No URL and Folder passed, but Required Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Required Version passed.Does not throw an Exception" time="0.056" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Required Version passed.Should call appropriate Mocks" time="0.1771" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed" executed="True" result="Success" success="True" time="0.8585" asserts="0" description="When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed.Does not throw an Exception" time="0.2969" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; Valid URL, Folder and Minimum Version passed.Should call appropriate Mocks" time="0.3598" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed" executed="True" result="Success" success="True" time="6.7849" asserts="0" description="When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed.Does not throw an Exception" time="6.3636" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL and Folder passed, but Minimum Version passed.Should call appropriate Mocks" time="0.2156" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Correct version of module is installed; Valid URL, Folder and Minimum Version passed" executed="True" result="Success" success="True" time="0.4306" asserts="0" description="When Correct version of module is installed; Valid URL, Folder and Minimum Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Minimum Version passed.Does not throw an Exception" time="0.0465" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; Valid URL, Folder and Minimum Version passed.Should call appropriate Mocks" time="0.193" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed" executed="True" result="Success" success="True" time="0.4509" asserts="0" description="When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed.Does not throw an Exception" time="0.0431" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Correct version of module is installed; No URL and Folder passed, but Minimum Version passed.Should call appropriate Mocks" time="0.1984" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Module is not installed; Bad URL passed" executed="True" result="Success" success="True" time="0.5579" asserts="0" description="When Module is not installed; Bad URL passed"> - <results> - <test-case description="Throws a FileDownloadError exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Bad URL passed.Throws a FileDownloadError exception" time="0.1229" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Bad URL passed.Should call appropriate Mocks" time="0.2297" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Module is not installed; Not available in Repository" executed="True" result="Success" success="True" time="6.751" asserts="0" description="When Module is not installed; Not available in Repository"> - <results> - <test-case description="Throws a ModuleNotAvailableError exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Not available in Repository.Throws a ModuleNotAvailableError exception" time="6.2992" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Module is not installed; Not available in Repository.Should call appropriate Mocks" time="0.2442" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available" executed="True" result="Success" success="True" time="6.7785" asserts="0" description="When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available"> - <results> - <test-case description=" Throws a ModuleNotAvailableError Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available. Throws a ModuleNotAvailableError Exception" time="6.3179" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Required Version passed. Required Version is not available.Should call appropriate Mocks" time="0.2502" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available" executed="True" result="Success" success="True" time="6.7765" asserts="0" description="When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available"> - <results> - <test-case description=" Throws a ModuleNotAvailableError Exception" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available. Throws a ModuleNotAvailableError Exception" time="6.3179" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Should call appropriate Mocks" name="\lib\private\Invoke-LabDownloadResourceModule.ps1.When Wrong version of module is installed; No URL or Folder passed, but Minimum Version passed. Minimum Version is not available.Should call appropriate Mocks" time="0.2524" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\New-LabCredential.ps1" executed="True" result="Success" success="True" time="0.1957" asserts="0" description="\lib\private\New-LabCredential.ps1"> - <results> - <test-suite type="TestFixture" name="When Username and Password provided" executed="True" result="Success" success="True" time="0.1248" asserts="0" description="When Username and Password provided"> - <results> - <test-case description="Should return the exepected credential object" name="\lib\private\New-LabCredential.ps1.When Username and Password provided.Should return the exepected credential object" time="0.0316" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Install-LabHyperV.ps1" executed="True" result="Success" success="True" time="1.0841" asserts="0" description="\lib\private\Install-LabHyperV.ps1"> - <results> - <test-suite type="TestFixture" name="When The function is called" executed="True" result="Success" success="True" time="0.5271" asserts="0" description="When The function is called"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Install-LabHyperV.ps1.When The function is called.Does not throw an Exception" time="0.2281" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Install-LabHyperV.ps1.When The function is called.Calls appropriate mocks" time="0.0537" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Enable-LabWSMan.ps1" executed="True" result="Success" success="True" time="0.756" asserts="0" description="\lib\private\Enable-LabWSMan.ps1"> - <results> - <test-suite type="TestFixture" name="When WS-Man is already enabled" executed="True" result="Success" success="True" time="0.2811" asserts="0" description="When WS-Man is already enabled"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is already enabled.Does not throw an Exception" time="0.0436" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is already enabled.Calls appropriate mocks" time="0.018" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When WS-Man is not enabled, user declines install" executed="True" result="Success" success="True" time="0.3989" asserts="0" description="When WS-Man is not enabled, user declines install"> - <results> - <test-case description="Should throw WSManNotEnabledError exception" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is not enabled, user declines install.Should throw WSManNotEnabledError exception" time="0.1403" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Enable-LabWSMan.ps1.When WS-Man is not enabled, user declines install.Calls appropriate mocks" time="0.0319" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Get-NextMacAddress.ps1" executed="True" result="Success" success="True" time="0.4021" asserts="0" description="\lib\private\Get-NextMacAddress.ps1"> - <results> - <test-suite type="TestFixture" name="When MAC address 00155D0106ED is passed" executed="True" result="Success" success="True" time="0.0952" asserts="0" description="When MAC address 00155D0106ED is passed"> - <results> - <test-case description="Returns MAC address 00155D0106EE" name="\lib\private\Get-NextMacAddress.ps1.When MAC address 00155D0106ED is passed.Returns MAC address 00155D0106EE" time="0.0186" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When MAC address 00155D0106ED and step 10 is passed" executed="True" result="Success" success="True" time="0.1148" asserts="0" description="When MAC address 00155D0106ED and step 10 is passed"> - <results> - <test-case description="Returns IP address 00155D0106F7" name="\lib\private\Get-NextMacAddress.ps1.When MAC address 00155D0106ED and step 10 is passed.Returns IP address 00155D0106F7" time="0.0216" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When MAC address 00155D0106ED and step 0 is passed" executed="True" result="Success" success="True" time="0.0979" asserts="0" description="When MAC address 00155D0106ED and step 0 is passed"> - <results> - <test-case description="Returns IP address 00155D0106ED" name="\lib\private\Get-NextMacAddress.ps1.When MAC address 00155D0106ED and step 0 is passed.Returns IP address 00155D0106ED" time="0.0209" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Get-LabNextIpAddress.ps1" executed="True" result="Success" success="True" time="0.8508" asserts="0" description="\lib\private\Get-LabNextIpAddress.ps1"> - <results> - <test-suite type="TestFixture" name="When Invalid IP Address is passed" executed="True" result="Success" success="True" time="0.1033" asserts="0" description="When Invalid IP Address is passed"> - <results> - <test-case description="Throws a IPAddressError Exception" name="\lib\private\Get-LabNextIpAddress.ps1.When Invalid IP Address is passed.Throws a IPAddressError Exception" time="0.0308" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address 192.168.1.1 is passed" executed="True" result="Success" success="True" time="0.1035" asserts="0" description="When IP address 192.168.1.1 is passed"> - <results> - <test-case description="Returns IP address 192.168.1.2" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.1 is passed.Returns IP address 192.168.1.2" time="0.0304" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address 192.168.1.255 is passed" executed="True" result="Success" success="True" time="0.1002" asserts="0" description="When IP address 192.168.1.255 is passed"> - <results> - <test-case description="Returns IP address 192.168.2.0" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.255 is passed.Returns IP address 192.168.2.0" time="0.0225" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address 192.168.1.255 and Step 10 is passed" executed="True" result="Success" success="True" time="0.1004" asserts="0" description="When IP address 192.168.1.255 and Step 10 is passed"> - <results> - <test-case description="Returns IP address 192.168.2.9" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.255 and Step 10 is passed.Returns IP address 192.168.2.9" time="0.0181" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address 192.168.1.255 and Step 0 is passed" executed="True" result="Success" success="True" time="0.1137" asserts="0" description="When IP address 192.168.1.255 and Step 0 is passed"> - <results> - <test-case description="Returns IP address 192.168.1.255" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 192.168.1.255 and Step 0 is passed.Returns IP address 192.168.1.255" time="0.0234" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address 10.255.255.255 is passed" executed="True" result="Success" success="True" time="0.0988" asserts="0" description="When IP address 10.255.255.255 is passed"> - <results> - <test-case description="Returns IP address 11.0.0.0" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address 10.255.255.255 is passed.Returns IP address 11.0.0.0" time="0.0234" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address fe80::15b4:b934:5d23:1a31 is passed" executed="True" result="Success" success="True" time="0.1055" asserts="0" description="When IP address fe80::15b4:b934:5d23:1a31 is passed"> - <results> - <test-case description="Returns IP address fe80::15b4:b934:5d23:1a32" name="\lib\private\Get-LabNextIpAddress.ps1.When IP address fe80::15b4:b934:5d23:1a31 is passed.Returns IP address fe80::15b4:b934:5d23:1a32" time="0.0218" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Assert-LabValidIpAddress.ps1" executed="True" result="Success" success="True" time="0.4949" asserts="0" description="\lib\private\Assert-LabValidIpAddress.ps1"> - <results> - <test-suite type="TestFixture" name="When IP address 192.168.1.1 is passed" executed="True" result="Success" success="True" time="0.0936" asserts="0" description="When IP address 192.168.1.1 is passed"> - <results> - <test-case description="Returns IP Address" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address 192.168.1.1 is passed.Returns IP Address" time="0.0193" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address 192.168.1.1000 is passed" executed="True" result="Success" success="True" time="0.0998" asserts="0" description="When IP address 192.168.1.1000 is passed"> - <results> - <test-case description="Should Throw an Exception" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address 192.168.1.1000 is passed.Should Throw an Exception" time="0.0241" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address fe80::15b4:b934:5d23:1a31 is passed" executed="True" result="Success" success="True" time="0.1005" asserts="0" description="When IP address fe80::15b4:b934:5d23:1a31 is passed"> - <results> - <test-case description="Returns IP Address" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address fe80::15b4:b934:5d23:1a31 is passed.Returns IP Address" time="0.0155" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When IP address fe80::15b4:b934:5d23:1a3x is passed" executed="True" result="Success" success="True" time="0.1024" asserts="0" description="When IP address fe80::15b4:b934:5d23:1a3x is passed"> - <results> - <test-case description="Should Throw an Exception" name="\lib\private\Assert-LabValidIpAddress.ps1.When IP address fe80::15b4:b934:5d23:1a3x is passed.Should Throw an Exception" time="0.0248" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Install-LabPackageProvider.ps1" executed="True" result="Success" success="True" time="1.7908" asserts="0" description="\lib\private\Install-LabPackageProvider.ps1"> - <results> - <test-suite type="TestFixture" name="When Required package providers already installed" executed="True" result="Success" success="True" time="0.2837" asserts="0" description="When Required package providers already installed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers already installed.Does not throw an Exception" time="0.051" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers already installed.Calls appropriate mocks" time="0.0433" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Required package providers not installed" executed="True" result="Success" success="True" time="1.4311" asserts="0" description="When Required package providers not installed"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers not installed.Does not throw an Exception" time="1.1744" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Install-LabPackageProvider.ps1.When Required package providers not installed.Calls appropriate mocks" time="0.0697" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Register-LabPackageSource.ps1" executed="True" result="Success" success="True" time="2.8745" asserts="0" description="\lib\private\Register-LabPackageSource.ps1"> - <results> - <test-suite type="TestFixture" name="When Required package sources already registered and trusted" executed="True" result="Success" success="True" time="0.3466" asserts="0" description="When Required package sources already registered and trusted"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered and trusted.Does not throw an Exception" time="0.1221" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered and trusted.Calls appropriate mocks" time="0.08" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Required package sources already registered but not trusted" executed="True" result="Success" success="True" time="1.7393" asserts="0" description="When Required package sources already registered but not trusted"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered but not trusted.Does not throw an Exception" time="1.3419" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources already registered but not trusted.Calls appropriate mocks" time="0.2364" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Required package sources are not registered" executed="True" result="Success" success="True" time="0.6974" asserts="0" description="When Required package sources are not registered"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources are not registered.Does not throw an Exception" time="0.2165" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Register-LabPackageSource.ps1.When Required package sources are not registered.Calls appropriate mocks" time="0.3006" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Write-LabMessage.ps1" executed="True" result="Success" success="True" time="1.7227" asserts="0" description="\lib\private\Write-LabMessage.ps1"> - <results> - <test-suite type="TestFixture" name="When Write an error message" executed="True" result="Success" success="True" time="0.2643" asserts="0" description="When Write an error message"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write an error message.Does not throw an Exception" time="0.0624" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write an error message.Calls appropriate mocks" time="0.0392" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Write a warning message" executed="True" result="Success" success="True" time="0.2521" asserts="0" description="When Write a warning message"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write a warning message.Does not throw an Exception" time="0.0651" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write a warning message.Calls appropriate mocks" time="0.0303" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Write a verbose message" executed="True" result="Success" success="True" time="0.2502" asserts="0" description="When Write a verbose message"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write a verbose message.Does not throw an Exception" time="0.0606" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write a verbose message.Calls appropriate mocks" time="0.0353" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Write a debug message" executed="True" result="Success" success="True" time="0.2469" asserts="0" description="When Write a debug message"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write a debug message.Does not throw an Exception" time="0.0617" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write a debug message.Calls appropriate mocks" time="0.0312" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Write an information message" executed="True" result="Success" success="True" time="0.2496" asserts="0" description="When Write an information message"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write an information message.Does not throw an Exception" time="0.0635" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write an information message.Calls appropriate mocks" time="0.0297" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When Write an alert message" executed="True" result="Success" success="True" time="0.2767" asserts="0" description="When Write an alert message"> - <results> - <test-case description="Does not throw an Exception" name="\lib\private\Write-LabMessage.ps1.When Write an alert message.Does not throw an Exception" time="0.0692" asserts="0" success="True" result="Success" executed="True" /> - <test-case description="Calls appropriate mocks" name="\lib\private\Write-LabMessage.ps1.When Write an alert message.Calls appropriate mocks" time="0.0358" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\ConvertTo-LabAbsolutePath.ps1" executed="True" result="Success" success="True" time="0.2706" asserts="0" description="\lib\private\ConvertTo-LabAbsolutePath.ps1"> - <results> - <test-suite type="TestFixture" name="When absolute Path is passed" executed="True" result="Success" success="True" time="0.0961" asserts="0" description="When absolute Path is passed"> - <results> - <test-case description="Should return the absolute path" name="\lib\private\ConvertTo-LabAbsolutePath.ps1.When absolute Path is passed.Should return the absolute path" time="0.021" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - <test-suite type="TestFixture" name="When relative Path is passed" executed="True" result="Success" success="True" time="0.0961" asserts="0" description="When relative Path is passed"> - <results> - <test-case description="Should return the absolute path" name="\lib\private\ConvertTo-LabAbsolutePath.ps1.When relative Path is passed.Should return the absolute path" time="0.0152" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - <test-suite type="TestFixture" name="\lib\private\Get-LabBuilderModulePath.ps1" executed="True" result="Success" success="True" time="0.0962" asserts="0" description="\lib\private\Get-LabBuilderModulePath.ps1"> - <results> - <test-case description="Should return the path to the LabBuilder Module" name="\lib\private\Get-LabBuilderModulePath.ps1.Should return the path to the LabBuilder Module" time="0.0193" asserts="0" success="True" result="Success" executed="True" /> - </results> - </test-suite> - </results> - </test-suite> - </results> - </test-suite> -</test-results> \ No newline at end of file From 52a45e6c2a1ebf95d09bf76729b2d85174606c70 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Mon, 20 Apr 2020 08:19:24 +1200 Subject: [PATCH 09/14] Move tests --- tests/unit/{lib => }/private/ManagementSwitch.tests.ps1 | 0 tests/unit/{lib => }/private/dsc.tests.ps1 | 0 tests/unit/{lib => }/private/utils.tests.ps1 | 0 tests/unit/{lib => }/private/vhd.tests.ps1 | 0 tests/unit/{lib => }/private/vm.tests.ps1 | 0 tests/unit/{lib => }/public/lab.tests.ps1 | 0 tests/unit/{lib => }/public/resource.tests.ps1 | 0 tests/unit/{lib => }/public/switch.tests.ps1 | 0 tests/unit/{lib => }/public/templatevhd.tests.ps1 | 0 tests/unit/{lib => }/public/vm.tests.ps1 | 0 tests/unit/{lib => }/public/vmtemplate.tests.ps1 | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename tests/unit/{lib => }/private/ManagementSwitch.tests.ps1 (100%) rename tests/unit/{lib => }/private/dsc.tests.ps1 (100%) rename tests/unit/{lib => }/private/utils.tests.ps1 (100%) rename tests/unit/{lib => }/private/vhd.tests.ps1 (100%) rename tests/unit/{lib => }/private/vm.tests.ps1 (100%) rename tests/unit/{lib => }/public/lab.tests.ps1 (100%) rename tests/unit/{lib => }/public/resource.tests.ps1 (100%) rename tests/unit/{lib => }/public/switch.tests.ps1 (100%) rename tests/unit/{lib => }/public/templatevhd.tests.ps1 (100%) rename tests/unit/{lib => }/public/vm.tests.ps1 (100%) rename tests/unit/{lib => }/public/vmtemplate.tests.ps1 (100%) diff --git a/tests/unit/lib/private/ManagementSwitch.tests.ps1 b/tests/unit/private/ManagementSwitch.tests.ps1 similarity index 100% rename from tests/unit/lib/private/ManagementSwitch.tests.ps1 rename to tests/unit/private/ManagementSwitch.tests.ps1 diff --git a/tests/unit/lib/private/dsc.tests.ps1 b/tests/unit/private/dsc.tests.ps1 similarity index 100% rename from tests/unit/lib/private/dsc.tests.ps1 rename to tests/unit/private/dsc.tests.ps1 diff --git a/tests/unit/lib/private/utils.tests.ps1 b/tests/unit/private/utils.tests.ps1 similarity index 100% rename from tests/unit/lib/private/utils.tests.ps1 rename to tests/unit/private/utils.tests.ps1 diff --git a/tests/unit/lib/private/vhd.tests.ps1 b/tests/unit/private/vhd.tests.ps1 similarity index 100% rename from tests/unit/lib/private/vhd.tests.ps1 rename to tests/unit/private/vhd.tests.ps1 diff --git a/tests/unit/lib/private/vm.tests.ps1 b/tests/unit/private/vm.tests.ps1 similarity index 100% rename from tests/unit/lib/private/vm.tests.ps1 rename to tests/unit/private/vm.tests.ps1 diff --git a/tests/unit/lib/public/lab.tests.ps1 b/tests/unit/public/lab.tests.ps1 similarity index 100% rename from tests/unit/lib/public/lab.tests.ps1 rename to tests/unit/public/lab.tests.ps1 diff --git a/tests/unit/lib/public/resource.tests.ps1 b/tests/unit/public/resource.tests.ps1 similarity index 100% rename from tests/unit/lib/public/resource.tests.ps1 rename to tests/unit/public/resource.tests.ps1 diff --git a/tests/unit/lib/public/switch.tests.ps1 b/tests/unit/public/switch.tests.ps1 similarity index 100% rename from tests/unit/lib/public/switch.tests.ps1 rename to tests/unit/public/switch.tests.ps1 diff --git a/tests/unit/lib/public/templatevhd.tests.ps1 b/tests/unit/public/templatevhd.tests.ps1 similarity index 100% rename from tests/unit/lib/public/templatevhd.tests.ps1 rename to tests/unit/public/templatevhd.tests.ps1 diff --git a/tests/unit/lib/public/vm.tests.ps1 b/tests/unit/public/vm.tests.ps1 similarity index 100% rename from tests/unit/lib/public/vm.tests.ps1 rename to tests/unit/public/vm.tests.ps1 diff --git a/tests/unit/lib/public/vmtemplate.tests.ps1 b/tests/unit/public/vmtemplate.tests.ps1 similarity index 100% rename from tests/unit/lib/public/vmtemplate.tests.ps1 rename to tests/unit/public/vmtemplate.tests.ps1 From 8c2e6de0432903eda0a92b01f85759fbc935ad32 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Mon, 20 Apr 2020 10:21:05 +1200 Subject: [PATCH 10/14] Fix tests --- tests/{unit => Unit}/LabBuilder.tests.ps1 | 0 tests/{unit/private => Unit/Private}/ManagementSwitch.tests.ps1 | 0 tests/{unit/private => Unit/Private}/dsc.tests.ps1 | 0 tests/{unit/private => Unit/Private}/utils.tests.ps1 | 0 tests/{unit/private => Unit/Private}/vhd.tests.ps1 | 0 tests/{unit/private => Unit/Private}/vm.tests.ps1 | 0 tests/{unit/public => Unit/Public}/lab.tests.ps1 | 0 tests/{unit/public => Unit/Public}/resource.tests.ps1 | 0 tests/{unit/public => Unit/Public}/switch.tests.ps1 | 0 tests/{unit/public => Unit/Public}/templatevhd.tests.ps1 | 0 tests/{unit/public => Unit/Public}/vm.tests.ps1 | 0 tests/{unit/public => Unit/Public}/vmtemplate.tests.ps1 | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename tests/{unit => Unit}/LabBuilder.tests.ps1 (100%) rename tests/{unit/private => Unit/Private}/ManagementSwitch.tests.ps1 (100%) rename tests/{unit/private => Unit/Private}/dsc.tests.ps1 (100%) rename tests/{unit/private => Unit/Private}/utils.tests.ps1 (100%) rename tests/{unit/private => Unit/Private}/vhd.tests.ps1 (100%) rename tests/{unit/private => Unit/Private}/vm.tests.ps1 (100%) rename tests/{unit/public => Unit/Public}/lab.tests.ps1 (100%) rename tests/{unit/public => Unit/Public}/resource.tests.ps1 (100%) rename tests/{unit/public => Unit/Public}/switch.tests.ps1 (100%) rename tests/{unit/public => Unit/Public}/templatevhd.tests.ps1 (100%) rename tests/{unit/public => Unit/Public}/vm.tests.ps1 (100%) rename tests/{unit/public => Unit/Public}/vmtemplate.tests.ps1 (100%) diff --git a/tests/unit/LabBuilder.tests.ps1 b/tests/Unit/LabBuilder.tests.ps1 similarity index 100% rename from tests/unit/LabBuilder.tests.ps1 rename to tests/Unit/LabBuilder.tests.ps1 diff --git a/tests/unit/private/ManagementSwitch.tests.ps1 b/tests/Unit/Private/ManagementSwitch.tests.ps1 similarity index 100% rename from tests/unit/private/ManagementSwitch.tests.ps1 rename to tests/Unit/Private/ManagementSwitch.tests.ps1 diff --git a/tests/unit/private/dsc.tests.ps1 b/tests/Unit/Private/dsc.tests.ps1 similarity index 100% rename from tests/unit/private/dsc.tests.ps1 rename to tests/Unit/Private/dsc.tests.ps1 diff --git a/tests/unit/private/utils.tests.ps1 b/tests/Unit/Private/utils.tests.ps1 similarity index 100% rename from tests/unit/private/utils.tests.ps1 rename to tests/Unit/Private/utils.tests.ps1 diff --git a/tests/unit/private/vhd.tests.ps1 b/tests/Unit/Private/vhd.tests.ps1 similarity index 100% rename from tests/unit/private/vhd.tests.ps1 rename to tests/Unit/Private/vhd.tests.ps1 diff --git a/tests/unit/private/vm.tests.ps1 b/tests/Unit/Private/vm.tests.ps1 similarity index 100% rename from tests/unit/private/vm.tests.ps1 rename to tests/Unit/Private/vm.tests.ps1 diff --git a/tests/unit/public/lab.tests.ps1 b/tests/Unit/Public/lab.tests.ps1 similarity index 100% rename from tests/unit/public/lab.tests.ps1 rename to tests/Unit/Public/lab.tests.ps1 diff --git a/tests/unit/public/resource.tests.ps1 b/tests/Unit/Public/resource.tests.ps1 similarity index 100% rename from tests/unit/public/resource.tests.ps1 rename to tests/Unit/Public/resource.tests.ps1 diff --git a/tests/unit/public/switch.tests.ps1 b/tests/Unit/Public/switch.tests.ps1 similarity index 100% rename from tests/unit/public/switch.tests.ps1 rename to tests/Unit/Public/switch.tests.ps1 diff --git a/tests/unit/public/templatevhd.tests.ps1 b/tests/Unit/Public/templatevhd.tests.ps1 similarity index 100% rename from tests/unit/public/templatevhd.tests.ps1 rename to tests/Unit/Public/templatevhd.tests.ps1 diff --git a/tests/unit/public/vm.tests.ps1 b/tests/Unit/Public/vm.tests.ps1 similarity index 100% rename from tests/unit/public/vm.tests.ps1 rename to tests/Unit/Public/vm.tests.ps1 diff --git a/tests/unit/public/vmtemplate.tests.ps1 b/tests/Unit/Public/vmtemplate.tests.ps1 similarity index 100% rename from tests/unit/public/vmtemplate.tests.ps1 rename to tests/Unit/Public/vmtemplate.tests.ps1 From 2217f533b09dd48434ec19e30fad14e436d38f98 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Tue, 21 Apr 2020 22:42:40 +1200 Subject: [PATCH 11/14] Complete unit tests --- GitVersion.yml | 2 +- HISTORIC_CHANGELOG.md | 2 +- build.yaml | 5 +- docs/Start-Lab.md | 2 +- .../Assert-LabValidConfigurationXMLSchema.ps1 | 22 +-- source/Private/Copy-LabOdjFile.ps1 | 4 +- .../Get-LabCertificatePsFileContent.ps1 | 20 +- source/Private/Initialize-LabBootVHD.ps1 | 14 +- .../Initialize-LabManagementSwitch.ps1 | 2 +- .../Invoke-LabDownloadAndUnzipFile.ps1 | 2 +- .../New-LabHostSelfSignedCertificate.ps1 | 18 +- .../Private/New-LabVMInitializationFile.ps1 | 4 +- .../Recieve-LabSelfSignedCertificate.ps1 | 8 +- .../Request-LabSelfSignedCertificate.ps1 | 16 +- source/Private/Start-LabDSC.ps1 | 8 +- source/Private/Update-LabDSC.ps1 | 4 +- .../Wait-LabVMInitializationComplete.ps1 | 4 +- source/Private/Wait-LabVMOff.ps1 | 2 +- source/Private/Wait-LabVMStarted.ps1 | 4 +- source/Public/Connect-LabVm.ps1 | 8 +- source/Public/Get-Lab.ps1 | 4 +- source/Public/Get-LabVm.ps1 | 2 +- source/Public/Initialize-LabSwitch.ps1 | 2 +- source/Public/Initialize-LabVm.ps1 | 8 +- source/Public/Initialize-LabVmTemplateVhd.ps1 | 6 +- source/Public/Install-LabVm.ps1 | 2 +- source/Public/New-Lab.ps1 | 4 +- source/Public/Start-Lab.ps1 | 2 +- source/prefix.ps1 | 46 ++--- .../samples/Sample_WS2012R2_DCandDHCPOnly.xml | 2 +- tests/Invoke-LabSample.ps1 | 18 +- .../testhelper.psm1 | 0 tests/Unit/LabBuilder.tests.ps1 | 140 -------------- tests/Unit/Private/dsc.tests.ps1 | 103 +++++----- ...h.tests.ps1 => managementswitch.tests.ps1} | 58 +++--- tests/Unit/Private/utils.tests.ps1 | 83 ++++---- tests/Unit/Private/vhd.tests.ps1 | 57 +++--- tests/Unit/Private/vm.tests.ps1 | 87 +++++---- tests/Unit/Public/lab.tests.ps1 | 69 +++---- tests/Unit/Public/resource.tests.ps1 | 117 +++++------ tests/Unit/Public/switch.tests.ps1 | 81 ++++---- tests/Unit/Public/templatevhd.tests.ps1 | 131 ++++++------- tests/Unit/Public/vm.tests.ps1 | 181 +++++++++--------- tests/Unit/Public/vmtemplate.tests.ps1 | 88 +++++---- tests/Unit/labbuilder.tests.ps1 | 92 +++++++++ 45 files changed, 760 insertions(+), 774 deletions(-) rename tests/{testhelper => TestHelper}/testhelper.psm1 (100%) delete mode 100644 tests/Unit/LabBuilder.tests.ps1 rename tests/Unit/Private/{ManagementSwitch.tests.ps1 => managementswitch.tests.ps1} (92%) create mode 100644 tests/Unit/labbuilder.tests.ps1 diff --git a/GitVersion.yml b/GitVersion.yml index 05406c4a..ad1d7d44 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,6 +1,6 @@ mode: ContinuousDelivery -next-version: 3.0.0 +next-version: 1.0.5 major-version-bump-message: '\s?(breaking|major|breaking\schange)' minor-version-bump-message: '\s?(add|feature|minor)' patch-version-bump-message: '\s?(fix|patch)' diff --git a/HISTORIC_CHANGELOG.md b/HISTORIC_CHANGELOG.md index 923c9581..470d1fcb 100644 --- a/HISTORIC_CHANGELOG.md +++ b/HISTORIC_CHANGELOG.md @@ -351,7 +351,7 @@ change log short. ## [0.7.5.0] - Added VM InstanceCount attribute for creating multiple copies a VM in a Lab. -- Added $Script:CurrentBuild variable to allow easier access to OS build version. +- Added $script:currentBuild variable to allow easier access to OS build version. - Fix to prevent ExposeVirtualizationExtensions from being applied on Lab Hosts that don't support it. - Samples\Sample_WS2012R2_DCandDHCPandEdge.ps1: Added sample for creating Lab with diff --git a/build.yaml b/build.yaml index 6090f942..7a799e2f 100644 --- a/build.yaml +++ b/build.yaml @@ -77,10 +77,11 @@ BuildWorkflow: Pester: OutputFormat: NUnitXML ExcludeFromCodeCoverage: + - dsclibrary + - support Script: - tests/Unit - - tests/Integration - ExcludeTag: + ExcludeTag: Incomplete Tag: CodeCoverageThreshold: 70 diff --git a/docs/Start-Lab.md b/docs/Start-Lab.md index f80e2e39..6495ebb4 100644 --- a/docs/Start-Lab.md +++ b/docs/Start-Lab.md @@ -140,7 +140,7 @@ Aliases: Required: False Position: 5 -Default value: $Script:StartupTimeout +Default value: $script:StartupTimeout Accept pipeline input: False Accept wildcard characters: False ``` diff --git a/source/Private/Assert-LabValidConfigurationXMLSchema.ps1 b/source/Private/Assert-LabValidConfigurationXMLSchema.ps1 index 5ec7b6a3..c77eb2e1 100644 --- a/source/Private/Assert-LabValidConfigurationXMLSchema.ps1 +++ b/source/Private/Assert-LabValidConfigurationXMLSchema.ps1 @@ -28,26 +28,26 @@ function Assert-LabValidConfigurationXMLSchema ) # Define these variables so they are accesible inside the event handler. - $Script:XMLErrorCount = 0 - $Script:XMLFirstError = '' - $Script:XMLPath = $ConfigPath - $Script:ConfigurationXMLValidationMessage = $LocalizedData.ConfigurationXMLValidationMessage + $script:XMLErrorCount = 0 + $script:XMLFirstError = '' + $script:XMLPath = $ConfigPath + $script:ConfigurationXMLValidationMessage = $LocalizedData.ConfigurationXMLValidationMessage # Perform the XSD Validation $readerSettings = New-Object -TypeName System.Xml.XmlReaderSettings $readerSettings.ValidationType = [System.Xml.ValidationType]::Schema - $null = $readerSettings.Schemas.Add("labbuilderconfig", $Script:ConfigurationXMLSchema) + $null = $readerSettings.Schemas.Add("labbuilderconfig", $script:ConfigurationXMLSchema) $readerSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessInlineSchema -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation $readerSettings.add_ValidationEventHandler( { # Triggered each time an error is found in the XML file - if ([System.String]::IsNullOrWhitespace($Script:XMLFirstError)) + if ([System.String]::IsNullOrWhitespace($script:XMLFirstError)) { - $Script:XMLFirstError = $_.Message + $script:XMLFirstError = $_.Message } # if - Write-LabMessage -Message ($Script:ConfigurationXMLValidationMessage ` - -f $Script:XMLPath, $_.Message) - $Script:XMLErrorCount++ + Write-LabMessage -Message ($script:ConfigurationXMLValidationMessage ` + -f $script:XMLPath, $_.Message) + $script:XMLErrorCount++ }) $reader = [System.Xml.XmlReader]::Create([System.String] $ConfigPath, $readerSettings) @@ -81,7 +81,7 @@ function Assert-LabValidConfigurationXMLSchema $exceptionParameters = @{ errorId = 'ConfigurationXMLValidationError' errorCategory = 'InvalidArgument' - errorMessage = $($LocalizedData.ConfigurationXMLValidationError -f $ConfigPath, $Script:XMLFirstError) + errorMessage = $($LocalizedData.ConfigurationXMLValidationError -f $ConfigPath, $script:XMLFirstError) } New-LabException @exceptionParameters } # if diff --git a/source/Private/Copy-LabOdjFile.ps1 b/source/Private/Copy-LabOdjFile.ps1 index 6c0dc735..98ba2c6c 100644 --- a/source/Private/Copy-LabOdjFile.ps1 +++ b/source/Private/Copy-LabOdjFile.ps1 @@ -113,9 +113,9 @@ function Copy-LabOdjFile catch { Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` - -f $VM.Name,'ODJ',$Script:RetryConnectSeconds) + -f $VM.Name,'ODJ',$script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # try } # while } # if diff --git a/source/Private/Get-LabCertificatePsFileContent.ps1 b/source/Private/Get-LabCertificatePsFileContent.ps1 index 1601f983..e98b9f18 100644 --- a/source/Private/Get-LabCertificatePsFileContent.ps1 +++ b/source/Private/Get-LabCertificatePsFileContent.ps1 @@ -52,7 +52,7 @@ function Get-LabCertificatePsFileContent if ($CertificateSource -eq [LabCertificateSource]::Guest) { $createCertificatePs = @" -`$CertificateFriendlyName = '$($Script:DSCCertificateFriendlyName)' +`$CertificateFriendlyName = '$($script:DSCCertificateFriendlyName)' `$Cert = Get-ChildItem -Path cert:\LocalMachine\My `` | Where-Object { `$_.FriendlyName -eq `$CertificateFriendlyName } `` | Select-Object -First 1 @@ -68,10 +68,10 @@ if (-not `$Cert) -Exportable `` -StoreLocation 'LocalMachine' `` -StoreName 'My' `` - -KeyLength $($Script:SelfSignedCertKeyLength) `` - -ProviderName '$($Script:SelfSignedCertProviderName)' `` - -AlgorithmName $($Script:SelfSignedCertAlgorithmName) `` - -SignatureAlgorithm $($Script:SelfSignedCertSignatureAlgorithm) + -KeyLength $($script:SelfSignedCertKeyLength) `` + -ProviderName '$($script:SelfSignedCertProviderName)' `` + -AlgorithmName $($script:SelfSignedCertAlgorithmName) `` + -SignatureAlgorithm $($script:SelfSignedCertSignatureAlgorithm) # There is a slight delay before new cert shows up in Cert: # So wait for it to show. While (-not `$Cert) @@ -83,7 +83,7 @@ if (-not `$Cert) Export-Certificate `` -Type CERT `` -Cert `$Cert `` - -FilePath `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionCert`" + -FilePath `"`$(`$ENV:SystemRoot)\$script:DSCEncryptionCert`" Add-Content `` -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` -Value 'Encryption Certificate Imported from CER ...' `` @@ -93,10 +93,10 @@ Add-Content `` else { [System.String] $createCertificatePs = @" -if (Test-Path -Path `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`") +if (Test-Path -Path `"`$(`$ENV:SystemRoot)\$script:DSCEncryptionPfxCert`") { `$CertificatePassword = ConvertTo-SecureString `` - -String '$Script:DSCCertificatePassword' `` + -String '$script:DSCCertificatePassword' `` -Force `` -AsPlainText Add-Content `` @@ -104,8 +104,8 @@ if (Test-Path -Path `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`") -Value 'Importing Encryption Certificate from PFX ...' `` -Encoding Ascii Import-PfxCertificate `` - -Password '$Script:DSCCertificatePassword' `` - -FilePath `"`$(`$ENV:SystemRoot)\$Script:DSCEncryptionPfxCert`" `` + -Password '$script:DSCCertificatePassword' `` + -FilePath `"`$(`$ENV:SystemRoot)\$script:DSCEncryptionPfxCert`" `` -CertStoreLocation cert:\localMachine\root Add-Content `` -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` diff --git a/source/Private/Initialize-LabBootVHD.ps1 b/source/Private/Initialize-LabBootVHD.ps1 index ca4bce72..5b4081c8 100644 --- a/source/Private/Initialize-LabBootVHD.ps1 +++ b/source/Private/Initialize-LabBootVHD.ps1 @@ -150,10 +150,10 @@ function Initialize-LabBootVHD -Path $MountPoint # Generate the path to the Nano Language Package - $PackageLangFile = $Package -replace '.cab',"_$($Script:NanoPackageCulture).cab" + $PackageLangFile = $Package -replace '.cab',"_$($script:NanoPackageCulture).cab" $PackageLangFile = Join-Path ` -Path $NanoPackagesFolder ` - -ChildPath "$($Script:NanoPackageCulture)\$PackageLangFile" + -ChildPath "$($script:NanoPackageCulture)\$PackageLangFile" # Does it exist? if (-not (Test-Path -Path $PackageLangFile)) @@ -264,15 +264,15 @@ function Initialize-LabBootVHD # folder of the VM. $CertificatePfxPath = Join-Path ` -Path $VMLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionPfxCert + -ChildPath $script:DSCEncryptionPfxCert if (Test-Path -Path $CertificatePfxPath) { # Apply the CMD Setup Complete File Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` - -f $VM.Name,'Credential Certificate PFX',$Script:DSCEncryptionPfxCert) + -f $VM.Name,'Credential Certificate PFX',$script:DSCEncryptionPfxCert) $null = Copy-Item ` -Path $CertificatePfxPath ` - -Destination "$MountPoint\Windows\$Script:DSCEncryptionPfxCert" ` + -Destination "$MountPoint\Windows\$script:DSCEncryptionPfxCert" ` -Force } @@ -295,11 +295,11 @@ function Initialize-LabBootVHD # Apply the Certificate Generator script if not a Nano Server if ($VM.OSType -ne [LabOSType]::Nano) { - $CertGenFilename = Split-Path -Path $Script:SupportGertGenPath -Leaf + $CertGenFilename = Split-Path -Path $script:SupportGertGenPath -Leaf Write-LabMessage -Message $($LocalizedData.ApplyingVMBootDiskFileMessage ` -f $VM.Name,'Certificate Create Script',$CertGenFilename) $null = Copy-Item ` - -Path $Script:SupportGertGenPath ` + -Path $script:SupportGertGenPath ` -Destination "$MountPoint\Windows\Setup\Scripts\"` -Force } diff --git a/source/Private/Initialize-LabManagementSwitch.ps1 b/source/Private/Initialize-LabManagementSwitch.ps1 index d2f51d17..e8f42a2e 100644 --- a/source/Private/Initialize-LabManagementSwitch.ps1 +++ b/source/Private/Initialize-LabManagementSwitch.ps1 @@ -40,7 +40,7 @@ function Initialize-LabManagementSwitch } else { - $requiredManagementVlan = $Script:DefaultManagementVLan + $requiredManagementVlan = $script:DefaultManagementVLan } $managementSwitch = Get-VMSwitch | Where-Object -Property Name -eq $managementSwitchName diff --git a/source/Private/Invoke-LabDownloadAndUnzipFile.ps1 b/source/Private/Invoke-LabDownloadAndUnzipFile.ps1 index 583e4a3d..ec0a1234 100644 --- a/source/Private/Invoke-LabDownloadAndUnzipFile.ps1 +++ b/source/Private/Invoke-LabDownloadAndUnzipFile.ps1 @@ -41,7 +41,7 @@ function Invoke-LabDownloadAndUnzipFile if ($extension -eq '.zip') { # Download to a temp folder and unzip - $downloadPath = Join-Path -Path $Script:WorkingFolder -ChildPath $fileName + $downloadPath = Join-Path -Path $script:WorkingFolder -ChildPath $fileName } else { diff --git a/source/Private/New-LabHostSelfSignedCertificate.ps1 b/source/Private/New-LabHostSelfSignedCertificate.ps1 index dd943c19..62d63776 100644 --- a/source/Private/New-LabHostSelfSignedCertificate.ps1 +++ b/source/Private/New-LabHostSelfSignedCertificate.ps1 @@ -40,11 +40,11 @@ function New-LabHostSelfSignedCertificate # Get Path to LabBuilder files $vmLabBuilderFiles = $VM.LabBuilderFilesPath - $certificateFriendlyName = $Script:DSCCertificateFriendlyName + $certificateFriendlyName = $script:DSCCertificateFriendlyName $certificateSubject = "CN=$($VM.ComputerName)" # Create the self-signed certificate for the destination VM - . $Script:SupportGertGenPath + . $script:SupportGertGenPath New-SelfsignedCertificateEx ` -Subject $certificateSubject ` -EKU 'Document Encryption','Server Authentication','Client Authentication' ` @@ -54,10 +54,10 @@ function New-LabHostSelfSignedCertificate -Exportable ` -StoreLocation 'LocalMachine' ` -StoreName 'My' ` - -KeyLength $Script:SelfSignedCertKeyLength ` - -ProviderName $Script:SelfSignedCertProviderName ` - -AlgorithmName $Script:SelfSignedCertAlgorithmName ` - -SignatureAlgorithm $Script:SelfSignedCertSignatureAlgorithm ` + -KeyLength $script:SelfSignedCertKeyLength ` + -ProviderName $script:SelfSignedCertProviderName ` + -AlgorithmName $script:SelfSignedCertAlgorithmName ` + -SignatureAlgorithm $script:SelfSignedCertSignatureAlgorithm ` -ErrorAction Stop # Locate the newly created certificate @@ -70,12 +70,12 @@ function New-LabHostSelfSignedCertificate # Export the certificate with the Private key in # preparation for upload to the VM $certificatePassword = ConvertTo-SecureString ` - -String $Script:DSCCertificatePassword ` + -String $script:DSCCertificatePassword ` -Force ` -AsPlainText $certificatePfxDestination = Join-Path ` -Path $vmLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionPfxCert + -ChildPath $script:DSCEncryptionPfxCert $null = Export-PfxCertificate ` -FilePath $certificatePfxDestination ` -Cert $certificate ` @@ -85,7 +85,7 @@ function New-LabHostSelfSignedCertificate # Export the certificate without a private key $certificateDestination = Join-Path ` -Path $vmLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionCert + -ChildPath $script:DSCEncryptionCert $null = Export-Certificate ` -Type CERT ` -FilePath $certificateDestination ` diff --git a/source/Private/New-LabVMInitializationFile.ps1 b/source/Private/New-LabVMInitializationFile.ps1 index c3a25c19..39c98e46 100644 --- a/source/Private/New-LabVMInitializationFile.ps1 +++ b/source/Private/New-LabVMInitializationFile.ps1 @@ -88,7 +88,7 @@ Start-Sleep -Seconds 30 $getCertPs Add-Content `` -Path `"`$(`$ENV:SystemRoot)\Setup\Scripts\SetupComplete.log`" `` - -Value 'Certificate identified and saved to C:\Windows\$Script:DSCEncryptionCert ...' `` + -Value 'Certificate identified and saved to C:\Windows\$script:DSCEncryptionCert ...' `` -Encoding Ascii Enable-PSRemoting -SkipNetworkProfileCheck -Force Add-Content `` @@ -137,7 +137,7 @@ Add-Content `` $setupCompleteCmd = @" @echo SetupComplete.cmd Script Started... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log $setupCompleteCmd -certoc.exe -ImportPFX -p $Script:DSCCertificatePassword root $ENV:SystemRoot\$Script:DSCEncryptionPfxCert >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log +certoc.exe -ImportPFX -p $script:DSCCertificatePassword root $ENV:SystemRoot\$script:DSCEncryptionPfxCert >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log @echo SetupComplete.cmd Script Finished... >> %SYSTEMROOT%\Setup\Scripts\SetupComplete.log @echo Initial Setup Completed - this file indicates that setup has completed. >> %SYSTEMROOT%\Setup\Scripts\InitialSetupCompleted.txt "@ diff --git a/source/Private/Recieve-LabSelfSignedCertificate.ps1 b/source/Private/Recieve-LabSelfSignedCertificate.ps1 index 7e6dcfc3..56aadbfb 100644 --- a/source/Private/Recieve-LabSelfSignedCertificate.ps1 +++ b/source/Private/Recieve-LabSelfSignedCertificate.ps1 @@ -85,7 +85,7 @@ function Recieve-LabSelfSignedCertificate try { $null = Copy-Item ` - -Path "c:\windows\$Script:DSCEncryptionCert" ` + -Path "c:\windows\$script:DSCEncryptionCert" ` -Destination $vmLabBuilderFiles ` -FromSession $session ` -ErrorAction Stop @@ -94,9 +94,9 @@ function Recieve-LabSelfSignedCertificate catch { Write-LabMessage -Message $($LocalizedData.WaitingForCertificateMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) + -f $VM.Name,$script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # try } # while } # if @@ -131,5 +131,5 @@ function Recieve-LabSelfSignedCertificate } # if } # while - return (Get-Item -Path "$vmLabBuilderFiles\$($Script:DSCEncryptionCert)") + return (Get-Item -Path "$vmLabBuilderFiles\$($script:DSCEncryptionCert)") } diff --git a/source/Private/Request-LabSelfSignedCertificate.ps1 b/source/Private/Request-LabSelfSignedCertificate.ps1 index ba61afb3..a243a530 100644 --- a/source/Private/Request-LabSelfSignedCertificate.ps1 +++ b/source/Private/Request-LabSelfSignedCertificate.ps1 @@ -108,9 +108,9 @@ function Request-LabSelfSignedCertificate catch { Write-LabMessage -Message $($LocalizedData.FailedToUploadCertificateCreateScriptMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) + -f $VM.Name,$script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # try } # while } # if @@ -136,9 +136,9 @@ function Request-LabSelfSignedCertificate catch { Write-LabMessage -Message $($LocalizedData.FailedToExecuteCertificateCreateScriptMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) + -f $VM.Name,$script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # try } # while } # if @@ -155,7 +155,7 @@ function Request-LabSelfSignedCertificate { try { $null = Copy-Item ` - -Path "c:\windows\$($Script:DSCEncryptionCert)" ` + -Path "c:\windows\$($script:DSCEncryptionCert)" ` -Destination $vmLabBuilderFiles ` -FromSession $session ` -ErrorAction Stop @@ -165,9 +165,9 @@ function Request-LabSelfSignedCertificate catch { Write-LabMessage -Message $($LocalizedData.FailedToDownloadCertificateMessage ` - -f $VM.Name,$Script:RetryConnectSeconds) + -f $VM.Name,$script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # Try } # While } # If @@ -199,5 +199,5 @@ function Request-LabSelfSignedCertificate } # If } # While - return (Get-Item -Path "$vmLabBuilderFiles\$($Script:DSCEncryptionCert)") + return (Get-Item -Path "$vmLabBuilderFiles\$($script:DSCEncryptionCert)") } diff --git a/source/Private/Start-LabDSC.ps1 b/source/Private/Start-LabDSC.ps1 index accfd136..bc72a6da 100644 --- a/source/Private/Start-LabDSC.ps1 +++ b/source/Private/Start-LabDSC.ps1 @@ -124,9 +124,9 @@ function Start-LabDSC catch { Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` - -f $VM.Name, 'DSC', $Script:RetryConnectSeconds) + -f $VM.Name, 'DSC', $script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # try } # while } # if @@ -187,9 +187,9 @@ function Start-LabDSC catch { Write-LabMessage -Message $($LocalizedData.CopyingFilesToVMFailedMessage ` - -f $VM.Name, "DSC Module $moduleName", $Script:RetryConnectSeconds) + -f $VM.Name, "DSC Module $moduleName", $script:RetryConnectSeconds) - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # try } # if } # foreach diff --git a/source/Private/Update-LabDSC.ps1 b/source/Private/Update-LabDSC.ps1 index 803a283e..4a6a456d 100644 --- a/source/Private/Update-LabDSC.ps1 +++ b/source/Private/Update-LabDSC.ps1 @@ -228,14 +228,14 @@ function Update-LabDSC # Remove any old self-signed certifcates for this VM Get-ChildItem -Path cert:\LocalMachine\My | - Where-Object { $_.FriendlyName -eq $Script:DSCCertificateFriendlyName } | + Where-Object { $_.FriendlyName -eq $script:DSCCertificateFriendlyName } | Remove-Item } # if # Add the VM Self-Signed Certificate to the Local Machine store and get the Thumbprint $certificateFile = Join-Path ` -Path $vmLabBuilderFiles ` - -ChildPath $Script:DSCEncryptionCert + -ChildPath $script:DSCEncryptionCert $certificate = Import-Certificate ` -FilePath $certificateFile ` -CertStoreLocation 'Cert:LocalMachine\My' diff --git a/source/Private/Wait-LabVMInitializationComplete.ps1 b/source/Private/Wait-LabVMInitializationComplete.ps1 index 10e593fd..a339d4d1 100644 --- a/source/Private/Wait-LabVMInitializationComplete.ps1 +++ b/source/Private/Wait-LabVMInitializationComplete.ps1 @@ -111,9 +111,9 @@ function Wait-LabVMInitializationComplete catch { Write-LabMessage -Message $($LocalizedData.WaitingForInitialSetupCompleteMessage ` - -f $VM.Name, $Script:RetryConnectSeconds) + -f $VM.Name, $script:RetryConnectSeconds) Start-Sleep ` - -Seconds $Script:RetryConnectSeconds + -Seconds $script:RetryConnectSeconds } # try } # while } # if diff --git a/source/Private/Wait-LabVMOff.ps1 b/source/Private/Wait-LabVMOff.ps1 index 7e2286e7..50f5fd3f 100644 --- a/source/Private/Wait-LabVMOff.ps1 +++ b/source/Private/Wait-LabVMOff.ps1 @@ -22,6 +22,6 @@ function Wait-LabVMOff while ($runningVM.State -ne 'Off') { $runningVM = Get-VM -Name $VM.Name - Start-Sleep -Seconds $Script:RetryHeartbeatSeconds + Start-Sleep -Seconds $script:RetryHeartbeatSeconds } # while } diff --git a/source/Private/Wait-LabVMStarted.ps1 b/source/Private/Wait-LabVMStarted.ps1 index aea90963..dba31ecf 100644 --- a/source/Private/Wait-LabVMStarted.ps1 +++ b/source/Private/Wait-LabVMStarted.ps1 @@ -38,8 +38,8 @@ function Wait-LabVMStarted $heartbeat = Get-VMIntegrationService -VMName $VM.Name -Name $heartbeatCultureNeutral Write-LabMessage -Message $($LocalizedData.WaitingForVMHeartbeatMessage ` - -f $VM.Name,$Script:RetryHeartbeatSeconds) + -f $VM.Name,$script:RetryHeartbeatSeconds) - Start-Sleep -Seconds $Script:RetryHeartbeatSeconds + Start-Sleep -Seconds $script:RetryHeartbeatSeconds } # while } diff --git a/source/Public/Connect-LabVm.ps1 b/source/Public/Connect-LabVm.ps1 index 293b303b..c74617a7 100644 --- a/source/Public/Connect-LabVm.ps1 +++ b/source/Public/Connect-LabVm.ps1 @@ -78,7 +78,7 @@ function Connect-LabVM else { Write-LabMessage -Message $($LocalizedData.WaitingForIPAddressAssignedMessage ` - -f $VM.Name, $Script:RetryConnectSeconds) + -f $VM.Name, $script:RetryConnectSeconds) } } catch @@ -86,15 +86,15 @@ function Connect-LabVM if (-not $ipAddress) { Write-LabMessage -Message $($LocalizedData.WaitingForIPAddressAssignedMessage ` - -f $VM.Name, $Script:RetryConnectSeconds) + -f $VM.Name, $script:RetryConnectSeconds) } else { Write-LabMessage -Message $($LocalizedData.ConnectingVMFailedMessage ` - -f $VM.Name, $Script:RetryConnectSeconds, $_.Exception.Message) + -f $VM.Name, $script:RetryConnectSeconds, $_.Exception.Message) } - Start-Sleep -Seconds $Script:RetryConnectSeconds + Start-Sleep -Seconds $script:RetryConnectSeconds } # Try } # While diff --git a/source/Public/Get-Lab.ps1 b/source/Public/Get-Lab.ps1 index b03a048b..682c9504 100644 --- a/source/Public/Get-Lab.ps1 +++ b/source/Public/Get-Lab.ps1 @@ -75,13 +75,13 @@ function Get-Lab $requiredWindowsBuild = $lab.labbuilderconfig.settings.requiredwindowsbuild if ($requiredWindowsBuild -and ` - ($Script:CurrentBuild -lt $requiredWindowsBuild)) + ($script:currentBuild -lt $requiredWindowsBuild)) { $exceptionParameters = @{ errorId = 'RequiredBuildNotMetError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.RequiredBuildNotMetError ` - -f $Script:CurrentBuild, $requiredWindowsBuild) + -f $script:currentBuild, $requiredWindowsBuild) } New-LabException @exceptionParameters } # if diff --git a/source/Public/Get-LabVm.ps1 b/source/Public/Get-LabVm.ps1 index 2513b00c..c83c21b2 100644 --- a/source/Public/Get-LabVm.ps1 +++ b/source/Public/Get-LabVm.ps1 @@ -816,7 +816,7 @@ function Get-LabVM If VM requires ExposeVirtualizationExtensions but it is not supported on Host then throw an exception. #> - if ($exposeVirtualizationExtensions -and ($Script:CurrentBuild -lt 10565)) + if ($exposeVirtualizationExtensions -and ($script:currentBuild -lt 10565)) { $exceptionParameters = @{ errorId = 'VMVirtualizationExtError' diff --git a/source/Public/Initialize-LabSwitch.ps1 b/source/Public/Initialize-LabSwitch.ps1 index bc75d067..a179986e 100644 --- a/source/Public/Initialize-LabSwitch.ps1 +++ b/source/Public/Initialize-LabSwitch.ps1 @@ -145,7 +145,7 @@ function Initialize-LabSwitch 'NAT' { - if ($Script:CurrentBuild -lt 14295) + if ($script:currentBuild -lt 14295) { $exceptionParameters = @{ errorId = 'NatSwitchNotSupportedError' diff --git a/source/Public/Initialize-LabVm.ps1 b/source/Public/Initialize-LabVm.ps1 index f4072bed..b1a2902d 100644 --- a/source/Public/Initialize-LabVm.ps1 +++ b/source/Public/Initialize-LabVm.ps1 @@ -45,7 +45,7 @@ function Initialize-LabVM } else { - [Int32] $ManagementVlan = $Script:DefaultManagementVLan + [Int32] $ManagementVlan = $script:DefaultManagementVLan } # if foreach ($VM in $VMs) @@ -123,7 +123,7 @@ function Initialize-LabVM } # if # Create New VM from settings - if ($VM.Version -and ($Script:CurrentBuild -ge 14352)) + if ($VM.Version -and ($script:currentBuild -ge 14352)) { $null = New-VM ` -Name $VM.Name ` @@ -174,7 +174,7 @@ function Initialize-LabVM } # if # Is ExposeVirtualizationExtensions supported? - if ($Script:CurrentBuild -lt 10565) + if ($script:currentBuild -lt 10565) { # No, it is not supported - is it required by VM? if ($VM.ExposeVirtualizationExtensions) @@ -195,7 +195,7 @@ function Initialize-LabVM if ($VM.ExposeVirtualizationExtensions ` -ne (Get-VMProcessor -VMName $VM.Name).ExposeVirtualizationExtensions) { - if ($Script:CurrentBuild -ge 14352 -and ($VM.Version -eq "8.0")) + if ($script:currentBuild -ge 14352 -and ($VM.Version -eq "8.0")) { Set-VMSecurity ` -VMName $VM.Name ` diff --git a/source/Public/Initialize-LabVmTemplateVhd.ps1 b/source/Public/Initialize-LabVmTemplateVhd.ps1 index c8eacfdc..c24da3cc 100644 --- a/source/Public/Initialize-LabVmTemplateVhd.ps1 +++ b/source/Public/Initialize-LabVmTemplateVhd.ps1 @@ -253,10 +253,10 @@ function Initialize-LabVMTemplateVHD $Packages += @( $PackagePath ) # Generate the path to the Nano Language Package - $PackageLangFile = $Package -replace '.cab', "_$($Script:NanoPackageCulture).cab" + $PackageLangFile = $Package -replace '.cab', "_$($script:NanoPackageCulture).cab" $PackageLangPath = Join-Path ` -Path $NanoPackagesFolder ` - -ChildPath "$($Script:NanoPackageCulture)\$PackageLangFile" + -ChildPath "$($script:NanoPackageCulture)\$PackageLangFile" # Does it exist? if (-not (Test-Path -Path $PackageLangPath)) { @@ -333,7 +333,7 @@ function Initialize-LabVMTemplateVHD # Should only be done once if (-not (Test-Path -Path Function:Convert-WindowsImage)) { - . $Script:SupportConvertWindowsImagePath + . $script:SupportConvertWindowsImagePath } # if try diff --git a/source/Public/Install-LabVm.ps1 b/source/Public/Install-LabVm.ps1 index 46173164..b0895c8a 100644 --- a/source/Public/Install-LabVm.ps1 +++ b/source/Public/Install-LabVm.ps1 @@ -30,7 +30,7 @@ function Install-LabVM if ($VM.DSC.ConfigFile) { # Has this VM been initialized before (do we have a cert for it) - if (-not (Test-Path "$LabPath\$($VM.Name)\LabBuilder Files\$Script:DSCEncryptionCert")) + if (-not (Test-Path "$LabPath\$($VM.Name)\LabBuilder Files\$script:DSCEncryptionCert")) { # No, so check it is initialized and download the cert if required if (Wait-LabVMInitializationComplete -VM $VM -ErrorAction Continue) diff --git a/source/Public/New-Lab.ps1 b/source/Public/New-Lab.ps1 index 64990739..de45ff6f 100644 --- a/source/Public/New-Lab.ps1 +++ b/source/Public/New-Lab.ps1 @@ -100,7 +100,7 @@ function New-Lab # Get the Config Template into a variable $Content = Get-Content ` - -Path $Script:ConfigurationXMLTemplate + -Path $script:ConfigurationXMLTemplate # The XML passes the Schema check so load it. [XML] $Lab = New-Object System.Xml.XmlDocument @@ -145,7 +145,7 @@ function New-Lab # Copy the DSCLibrary $null = Copy-Item ` - -Path $Script:DSCLibraryPath ` + -Path $script:DSCLibraryPath ` -Destination $LabPath ` -Recurse ` -Force ` diff --git a/source/Public/Start-Lab.ps1 b/source/Public/Start-Lab.ps1 index 60e07b59..6de88118 100644 --- a/source/Public/Start-Lab.ps1 +++ b/source/Public/Start-Lab.ps1 @@ -26,7 +26,7 @@ function Start-Lab [Parameter( Position=4)] - [System.Int32] $StartupTimeout = $Script:StartupTimeout + [System.Int32] $StartupTimeout = $script:StartupTimeout ) # Param begin diff --git a/source/prefix.ps1 b/source/prefix.ps1 index 9c59347f..b5e5c9c5 100644 --- a/source/prefix.ps1 +++ b/source/prefix.ps1 @@ -4,7 +4,7 @@ #Requires -version 5.1 #Requires -RunAsAdministrator -$script:moduleRoot = Split-Path ` +$script:LabBuidlerModuleRoot = Split-Path ` -Path $MyInvocation.MyCommand.Path ` -Parent @@ -17,7 +17,7 @@ if ([System.String]::IsNullOrEmpty($culture)) } else { - if (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath $culture)) + if (Test-Path -Path (Join-Path -Path $script:LabBuidlerModuleRoot -ChildPath $culture)) { $culture = 'en-US' } @@ -26,7 +26,7 @@ else Import-LocalizedData ` -BindingVariable LocalizedData ` -Filename 'LabBuilder.strings.psd1' ` - -BaseDirectory $script:moduleRoot ` + -BaseDirectory $script:LabBuidlerModuleRoot ` -UICulture $culture #endregion @@ -491,49 +491,49 @@ class LabDSCModule:System.ICloneable { #endregion #region ModuleVariables -[System.String] $Script:WorkingFolder = $ENV:Temp +[System.String] $script:WorkingFolder = $ENV:Temp # Supporting files -[System.String] $Script:SupportConvertWindowsImagePath = Join-Path ` +[System.String] $script:SupportConvertWindowsImagePath = Join-Path ` -Path $PSScriptRoot ` -ChildPath 'support\Convert-WindowsImage.ps1' -[System.String] $Script:SupportGertGenPath = Join-Path ` +[System.String] $script:SupportGertGenPath = Join-Path ` -Path $PSScriptRoot ` -ChildPath 'support\New-SelfSignedCertificateEx.ps1' # DSC Library -[System.String] $Script:DSCLibraryPath = Join-Path ` +[System.String] $script:DSCLibraryPath = Join-Path ` -Path $PSScriptRoot ` -ChildPath 'dsclibrary' # Virtual Networking Parameters -[System.Int32] $Script:DefaultManagementVLan = 99 +[System.Int32] $script:DefaultManagementVLan = 99 # Self-signed Certificate Parameters -[System.Int32] $Script:SelfSignedCertKeyLength = 2048 +[System.Int32] $script:SelfSignedCertKeyLength = 2048 # Warning - using KSP causes the Private Key to not be accessible to PS. -[System.String] $Script:SelfSignedCertProviderName = 'Microsoft Enhanced Cryptographic Provider v1.0' # 'Microsoft Software Key Storage Provider' -[System.String] $Script:SelfSignedCertAlgorithmName = 'RSA' # 'ECDH_P256' Or 'ECDH_P384' Or 'ECDH_P521' -[System.String] $Script:SelfSignedCertSignatureAlgorithm = 'SHA256' # 'SHA1' -[System.String] $Script:DSCEncryptionCert = 'DSCEncryption.cer' -[System.String] $Script:DSCEncryptionPfxCert = 'DSCEncryption.pfx' -[System.String] $Script:DSCCertificateFriendlyName = 'DSC Credential Encryption' -[System.String] $Script:DSCCertificatePassword = 'E3jdNkd903mDn43NEk2nbDENjw' -[System.Int32] $Script:RetryConnectSeconds = 5 -[System.Int32] $Script:RetryHeartbeatSeconds = 1 -[System.Int32] $Script:StartupTimeout = 90 +[System.String] $script:SelfSignedCertProviderName = 'Microsoft Enhanced Cryptographic Provider v1.0' # 'Microsoft Software Key Storage Provider' +[System.String] $script:SelfSignedCertAlgorithmName = 'RSA' # 'ECDH_P256' Or 'ECDH_P384' Or 'ECDH_P521' +[System.String] $script:SelfSignedCertSignatureAlgorithm = 'SHA256' # 'SHA1' +[System.String] $script:DSCEncryptionCert = 'DSCEncryption.cer' +[System.String] $script:DSCEncryptionPfxCert = 'DSCEncryption.pfx' +[System.String] $script:DSCCertificateFriendlyName = 'DSC Credential Encryption' +[System.String] $script:DSCCertificatePassword = 'E3jdNkd903mDn43NEk2nbDENjw' +[System.Int32] $script:RetryConnectSeconds = 5 +[System.Int32] $script:RetryHeartbeatSeconds = 1 +[System.Int32] $script:StartupTimeout = 90 # System Info -[System.Int32] $Script:CurrentBuild = (Get-ItemProperty ` +[System.Int32] $script:currentBuild = (Get-ItemProperty ` -Path 'hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuild # XML Stuff -[System.String] $Script:ConfigurationXMLSchema = Join-Path ` +[System.String] $script:ConfigurationXMLSchema = Join-Path ` -Path $PSScriptRoot ` -ChildPath 'schema\labbuilderconfig-schema.xsd' -[System.String] $Script:ConfigurationXMLTemplate = Join-Path ` +[System.String] $script:ConfigurationXMLTemplate = Join-Path ` -Path $PSScriptRoot ` -ChildPath 'template\labbuilderconfig-template.xml' # Nano Stuff -[System.String] $Script:NanoPackageCulture = 'en-us' +[System.String] $script:NanoPackageCulture = 'en-us' diff --git a/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml b/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml index 348180b5..e625e4b1 100644 --- a/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml +++ b/source/samples/Sample_WS2012R2_DCandDHCPOnly.xml @@ -8,7 +8,7 @@ <settings labid="LABBUILDER-DCANDDHCPONLY.COM " domainname="LABBUILDER-DCANDDHCPONLY.COM" email="admina@LABBUILDER-DCANDDHCPONLY.COM" - labpath="c:\vm\LABBUILDER-DCANDDHCPONLY.COM /> + labpath="c:\vm\LABBUILDER-DCANDDHCPONLY.COM" /> <resources> <msu name="WMF5.1-WS2012R2-W81" diff --git a/tests/Invoke-LabSample.ps1 b/tests/Invoke-LabSample.ps1 index da697e06..57830b6e 100644 --- a/tests/Invoke-LabSample.ps1 +++ b/tests/Invoke-LabSample.ps1 @@ -1,13 +1,13 @@ # Set the name of the sample Lab from the samples folder: -[System.String]$Script:ConfigPath = "$PSScriptRoot\..\src\Samples\Sample_WS2016_DCandDHCPandCA.xml" -[System.String]$Script:ModulePath = "$PSScriptRoot\..\src\LabBuilder.psd1" +[System.String]$script:ConfigPath = "$PSScriptRoot\..\src\Samples\Sample_WS2016_DCandDHCPandCA.xml" +[System.String]$script:ModulePath = "$PSScriptRoot\..\src\LabBuilder.psd1" #################################################################################################### Function Test-StartLabVM { param ( [System.String[]]$StartVMs ) - $Lab = Get-Lab -Config $Script:ConfigPath + $Lab = Get-Lab -Config $script:ConfigPath [array] $VMs = Get-LabVM ` -Lab $Lab ` -Name $StartVMs @@ -20,23 +20,23 @@ Function Test-StartLabVM { } #################################################################################################### Function Test-LabBuilderInstall { - Get-Lab -ConfigPath $Script:ConfigPath | Install-Lab -Verbose + Get-Lab -ConfigPath $script:ConfigPath | Install-Lab -Verbose } # Function Test-LabBuilderInstall #################################################################################################### Function Test-LabBuilderUpdate { - Get-Lab -ConfigPath $Script:ConfigPath | Update-Lab -Verbose + Get-Lab -ConfigPath $script:ConfigPath | Update-Lab -Verbose } # Function Test-LabBuilderInstall #################################################################################################### Function Test-LabBuilderStart { - Get-Lab -ConfigPath $Script:ConfigPath | Start-Lab -Verbose + Get-Lab -ConfigPath $script:ConfigPath | Start-Lab -Verbose } # Function Test-LabBuilderInstall #################################################################################################### Function Test-LabBuilderStop { - Get-Lab -ConfigPath $Script:ConfigPath | Stop-Lab -Verbose + Get-Lab -ConfigPath $script:ConfigPath | Stop-Lab -Verbose } # Function Test-LabBuilderInstall #################################################################################################### Function Test-LabBuilderUninstall { - Get-Lab -ConfigPath $Script:ConfigPath | Uninstall-Lab ` + Get-Lab -ConfigPath $script:ConfigPath | Uninstall-Lab ` -RemoveVMFolder ` -RemoveVMTemplate ` -RemoveLabFolder ` @@ -45,7 +45,7 @@ Function Test-LabBuilderUninstall { } # Function Test-LabBuilderUnnstall #################################################################################################### Function Test-LabBuilderLoadModule { - Import-Module $Script:ModulePath -Verbose -Force + Import-Module $script:ModulePath -Verbose -Force } # Function Test-LabBuilderLoadModule #################################################################################################### diff --git a/tests/testhelper/testhelper.psm1 b/tests/TestHelper/testhelper.psm1 similarity index 100% rename from tests/testhelper/testhelper.psm1 rename to tests/TestHelper/testhelper.psm1 diff --git a/tests/Unit/LabBuilder.tests.ps1 b/tests/Unit/LabBuilder.tests.ps1 deleted file mode 100644 index 94e04ace..00000000 --- a/tests/Unit/LabBuilder.tests.ps1 +++ /dev/null @@ -1,140 +0,0 @@ -$Global:ModuleRoot = Resolve-Path -Path "$($Script:MyInvocation.MyCommand.Path)..\..\..\..\" -$OldPSModulePath = $env:PSModulePath -Push-Location -try -{ - Set-Location -Path $ModuleRoot - if (Get-Module LabBuilder -All) - { - Get-Module LabBuilder -All | Remove-Module - } - - Import-Module (Join-Path -Path $Global:ModuleRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking - $Global:TestConfigPath = Join-Path ` - -Path $Global:ModuleRoot ` - -ChildPath 'test\pestertestconfig' - $Global:TestConfigOKPath = Join-Path ` - -Path $Global:TestConfigPath ` - -ChildPath 'PesterTestConfig.OK.xml' - $Global:ArtifactPath = Join-Path ` - -Path $Global:ModuleRoot ` - -ChildPath 'test\artifacts' - $Global:ExpectedContentPath = Join-Path ` - -Path $Global:TestConfigPath ` - -ChildPath 'expectedcontent' - $null = New-Item ` - -Path $Global:ArtifactPath ` - -ItemType Directory ` - -Force ` - -ErrorAction SilentlyContinue - - # Perform PS Script Analyzer tests on module code only - $null = Set-PackageSource -Name PSGallery -Trusted -Force - $null = Install-Module -Name 'PSScriptAnalyzer' -Confirm:$false - Import-Module -Name 'PSScriptAnalyzer' - - Describe 'PSScriptAnalyzer' { - Context 'LabBuilder Module code and Lib Functions' { - It 'Passes Invoke-ScriptAnalyzer' { - # Perform PSScriptAnalyzer scan. - $PSScriptAnalyzerResult = Invoke-ScriptAnalyzer ` - -path "$ModuleRoot\src\LabBuilder.psm1" ` - -Severity Warning ` - -ErrorAction SilentlyContinue - $PSScriptAnalyzerResult += Invoke-ScriptAnalyzer ` - -path "$ModuleRoot\src\lib\public\*.ps1" ` - -excluderule "PSAvoidUsingUserNameAndPassWordParams" ` - -Severity Warning ` - -ErrorAction SilentlyContinue - $PSScriptAnalyzerResult += Invoke-ScriptAnalyzer ` - -path "$ModuleRoot\src\lib\private\*.ps1" ` - -excluderule "PSAvoidUsingUserNameAndPassWordParams" ` - -Severity Warning ` - -ErrorAction SilentlyContinue - $PSScriptAnalyzerErrors = $PSScriptAnalyzerResult | Where-Object { $_.Severity -eq 'Error' } - $PSScriptAnalyzerWarnings = $PSScriptAnalyzerResult | Where-Object { $_.Severity -eq 'Warning' } - - if ($PSScriptAnalyzerErrors -ne $null) - { - Write-Warning -Message 'There are PSScriptAnalyzer errors that need to be fixed:' - @($PSScriptAnalyzerErrors).Foreach( { Write-Warning -Message "$($_.Scriptname) (Line $($_.Line)): $($_.Message)" } ) - Write-Warning -Message 'For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/psscriptAnalyzer/' - $PSScriptAnalyzerErrors.Count | Should -Be $null - } - - if ($PSScriptAnalyzerWarnings -ne $null) - { - Write-Warning -Message 'There are PSScriptAnalyzer warnings that should be fixed:' - @($PSScriptAnalyzerWarnings).Foreach( { Write-Warning -Message "$($_.Scriptname) (Line $($_.Line)): $($_.Message)" } ) - } - } - } - } - - InModuleScope LabBuilder { - <# - .SYNOPSIS - Helper function that just creates an exception record for testing. - #> - function Get-LabException - { - [CmdLetBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.String] $errorId, - - [Parameter(Mandatory = $true)] - [System.Management.Automation.ErrorCategory] $errorCategory, - - [Parameter(Mandatory = $true)] - [System.String] $errorMessage, - - [Switch] - $terminate - ) - - $exception = New-Object -TypeName System.Exception ` - -ArgumentList $errorMessage - $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord ` - -ArgumentList $exception, $errorId, $errorCategory, $null - return $errorRecord - } - - # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 - - # Perform Configuration XML Schema validation - Describe 'Validate XML schema of lab test files' { - Context 'PesterTestConfig.OK.XML' { - It 'Does not throw an exception' { - { Assert-LabValidConfigurationXMLSchema -ConfigPath $Global:TestConfigOKPath -Verbose } | Should -Not -Throw - } - } - } - - Describe 'Validate XML schema of lab sample files' { - $SampleFiles = Get-ChildItem -Path (Join-Path -Path $Global:ModuleRoot -ChildPath "Samples") -Recurse -Filter 'Sample_*.xml' - - foreach ($SampleFile in $SampleFiles) - { - Context "Samples\$SampleFile" { - It 'Does not throw an exception' { - { Assert-LabValidConfigurationXMLSchema -ConfigPath $($SampleFile.Fullname) -Verbose } | Should -Not -Throw - } - } - } - } - } -} -catch -{ - throw $_ -} -finally -{ - Pop-Location - $env:PSModulePath = $OldPSModulePath -} diff --git a/tests/Unit/Private/dsc.tests.ps1 b/tests/Unit/Private/dsc.tests.ps1 index 1e385d92..49a0d10c 100644 --- a/tests/Unit/Private/dsc.tests.ps1 +++ b/tests/Unit/Private/dsc.tests.ps1 @@ -1,73 +1,76 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath - Describe '\lib\private\Dsc.ps1\Get-LabModulesInDSCConfig' { + Describe 'Get-LabModulesInDSCConfig' { Context 'When Called with Test DSC Resource File' { It 'Returns DSCModules Object that matches Expected Object' { $dscModules = Get-LabModulesInDSCConfig ` - -DSCConfigFile (Join-Path -Path $script:TestConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') ` + -DSCConfigFile (Join-Path -Path $script:testConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') ` -Verbose Set-Content ` - -Path "$script:ArtifactPath\ExpectedDSCModules.json" ` + -Path "$script:artifactPath\ExpectedDSCModules.json" ` -Value ($dscModules | ConvertTo-Json -Depth 4) - $expectedDSCModules = Get-Content -Path "$script:ExpectedContentPath\ExpectedDSCModules.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedDSCModules.json"), $expectedDSCModules, $true) | Should -Be 0 + $expectedDSCModules = Get-Content -Path "$script:expectedContentPath\ExpectedDSCModules.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedDSCModules.json"), $expectedDSCModules, $true) | Should -Be 0 } } Context 'When Called with Test DSC Resource Content' { It 'Returns DSCModules Object that matches Expected Object' { - $content = Get-Content -Path (Join-Path -Path $script:TestConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') -RAW + $content = Get-Content -Path (Join-Path -Path $script:testConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') -RAW $dscModules = Get-LabModulesInDSCConfig ` -DSCConfigContent $content ` -Verbose Set-Content ` - -Path "$script:ArtifactPath\ExpectedDSCModules.json" ` + -Path "$script:artifactPath\ExpectedDSCModules.json" ` -Value ($dscModules | ConvertTo-Json -Depth 4) - $expectedDSCModules = Get-Content -Path "$script:ExpectedContentPath\ExpectedDSCModules.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedDSCModules.json"), $expectedDSCModules, $true) | Should -Be 0 + $expectedDSCModules = Get-Content -Path "$script:expectedContentPath\ExpectedDSCModules.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedDSCModules.json"), $expectedDSCModules, $true) | Should -Be 0 } } } - Describe '\lib\private\Dsc.ps1\Set-LabModulesInDSCConfig' { + Describe 'Set-LabModulesInDSCConfig' { $module1 = [LabDSCModule]::New('PSDesiredStateConfiguration', '1.0') $module2 = [LabDSCModule]::New('xActiveDirectory') $module3 = [LabDSCModule]::New('ComputerManagementDsc', '1.4.0.0') @@ -77,36 +80,36 @@ InModuleScope LabBuilder { Context 'When called with Test DSC Resource File' { It 'Returns DSCConfig Content that matches Expected String' { $dscConfig = Set-LabModulesInDSCConfig ` - -DSCConfigFile (Join-Path -Path $script:TestConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') ` + -DSCConfigFile (Join-Path -Path $script:testConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') ` -Modules $UpdateModules ` -Verbose - Set-Content -Path "$script:ArtifactPath\ExpectedDSCConfig.txt" -Value $dscConfig - $expectedDSCConfig = Get-Content -Path "$script:ExpectedContentPath\ExpectedDSCConfig.txt" + Set-Content -Path "$script:artifactPath\ExpectedDSCConfig.txt" -Value $dscConfig + $expectedDSCConfig = Get-Content -Path "$script:expectedContentPath\ExpectedDSCConfig.txt" @(Compare-Object ` -ReferenceObject $expectedDSCConfig ` - -DifferenceObject (Get-Content -Path "$script:ArtifactPath\ExpectedDSCConfig.txt")).Count | Should -Be 0 + -DifferenceObject (Get-Content -Path "$script:artifactPath\ExpectedDSCConfig.txt")).Count | Should -Be 0 } } Context 'When called with Test DSC Resource Content' { It 'Returns DSCModules Content that matches Expected String' { - $content = Get-Content -Path (Join-Path -Path $script:TestConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') -Raw + $content = Get-Content -Path (Join-Path -Path $script:testConfigPath -ChildPath 'dsclibrary\PesterTest.DSC.ps1') -Raw $dscConfig = Set-LabModulesInDSCConfig ` -DSCConfigContent $Content ` -Modules $UpdateModules ` -Verbose - Set-Content -Path "$script:ArtifactPath\ExpectedDSCConfig.txt" -Value $dscConfig - $expectedDSCConfig = Get-Content -Path "$script:ExpectedContentPath\ExpectedDSCConfig.txt" + Set-Content -Path "$script:artifactPath\ExpectedDSCConfig.txt" -Value $dscConfig + $expectedDSCConfig = Get-Content -Path "$script:expectedContentPath\ExpectedDSCConfig.txt" @(Compare-Object ` -ReferenceObject $expectedDSCConfig ` - -DifferenceObject (Get-Content -Path "$script:ArtifactPath\ExpectedDSCConfig.txt")).Count | Should -Be 0 + -DifferenceObject (Get-Content -Path "$script:artifactPath\ExpectedDSCConfig.txt")).Count | Should -Be 0 } } } - Describe '\lib\private\Dsc.ps1\Update-LabDSC' { + Describe 'Update-LabDSC' { function Get-VM { [CmdletBinding()] @@ -154,7 +157,7 @@ InModuleScope LabBuilder { Mock -CommandName Get-VM - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $switches = Get-LabSwitch -Lab $lab $templates = Get-LabVMTemplate -Lab $lab $vms = Get-LabVM -Lab $lab -VMTemplates $templates -Switches $switches @@ -344,7 +347,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Dsc.ps1\Set-LabDSC' { + Describe 'Set-LabDSC' { # Mock functions function Get-VM { @@ -362,7 +365,7 @@ InModuleScope LabBuilder { Mock -CommandName Get-VM - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $switches = Get-LabSwitch -Lab $lab $templates = Get-LabVMTemplate -Lab $lab $vms = Get-LabVM -Lab $lab -VMTemplates $templates -Switches $switches @@ -428,8 +431,8 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Dsc.ps1\Initialize-LabDSC' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + Describe 'Initialize-LabDSC' { + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $vms = Get-LabVM -Lab $lab Mock -CommandName Update-LabDSC @@ -449,9 +452,9 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Dsc.ps1\Start-LabDSC' -Tags 'Incomplete' { + Describe 'Start-LabDSC' -Tags 'Incomplete' { } - Describe '\lib\private\Dsc.ps1\Get-LabDSCNetworkingConfig' -Tags 'Incomplete' { + Describe 'Get-LabDSCNetworkingConfig' -Tags 'Incomplete' { } } diff --git a/tests/Unit/Private/ManagementSwitch.tests.ps1 b/tests/Unit/Private/managementswitch.tests.ps1 similarity index 92% rename from tests/Unit/Private/ManagementSwitch.tests.ps1 rename to tests/Unit/Private/managementswitch.tests.ps1 index 2dcf1ac7..ca3477f5 100644 --- a/tests/Unit/Private/ManagementSwitch.tests.ps1 +++ b/tests/Unit/Private/managementswitch.tests.ps1 @@ -1,39 +1,41 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue - $script:Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath function Get-VMNetworkAdapter { @@ -170,7 +172,7 @@ InModuleScope LabBuilder { ) } - Describe '\lib\private\Get-LabManagementSwitchName' { + Describe 'Get-LabManagementSwitchName' { Context 'Valid Configuration Passed' { It 'Should return "TestLab Lab Management"' { Get-LabManagementSwitchName -Lab $script:Lab | Should -Be 'TestLab Lab Management' @@ -178,7 +180,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Set-LabSwitchAdapter' { + Describe 'Set-LabSwitchAdapter' { $TestAdapter = @{ Name = 'Adapter Name' SwitchName = 'Switch Name' @@ -320,7 +322,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Initialize-LabManagementSwitch' { + Describe 'Initialize-LabManagementSwitch' { Context 'Valid Configuration Passed and Management Switch does not exist' { Mock -CommandName Get-VMSwitch Mock -CommandName New-VMSwitch diff --git a/tests/Unit/Private/utils.tests.ps1 b/tests/Unit/Private/utils.tests.ps1 index e9a2ae38..b906a86f 100644 --- a/tests/Unit/Private/utils.tests.ps1 +++ b/tests/Unit/Private/utils.tests.ps1 @@ -1,40 +1,43 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath - Describe '\lib\private\Invoke-LabDownloadAndUnzipFile.ps1' { + Describe 'Invoke-LabDownloadAndUnzipFile' { $URL = 'https://raw.githubusercontent.com/PlagueHO/LabBuilder/dev/LICENSE' Context 'When Download folder does not exist' { @@ -144,7 +147,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Invoke-LabDownloadResourceModule.ps1' { + Describe 'Invoke-LabDownloadResourceModule' { $URL = 'https://github.com/PowerShell/NetworkingDsc/archive/dev.zip' Mock -CommandName Get-Module -MockWith { @( New-Object -TypeName PSObject -Property @{ Name = 'NetworkingDsc'; Version = '2.4.0.0'; } ) } @@ -529,7 +532,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\New-LabCredential.ps1' { + Describe 'New-LabCredential' { Context 'When Username and Password provided' { $testUsername = 'testUsername' $testPassword = 'testPassword' @@ -545,8 +548,8 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Install-LabHyperV.ps1' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + Describe 'Install-LabHyperV.ps1' { + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath if ((Get-CimInstance Win32_OperatingSystem).ProductType -eq 1) { Mock -CommandName Get-WindowsOptionalFeature -MockWith { @@ -588,7 +591,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Enable-LabWSMan.ps1' { + Describe 'Enable-LabWSMan' { Context 'When WS-Man is already enabled' { Mock -CommandName Start-Service Mock -CommandName Get-PSProvider -MockWith { @@ -629,10 +632,10 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Assert-LabValidConfigurationXMLSchema.ps1' -Tag 'Incomplete' { + Describe 'Assert-LabValidConfigurationXMLSchema' -Tag 'Incomplete' { } - Describe '\lib\private\Get-NextMacAddress.ps1' { + Describe 'Get-NextMacAddress' { Context 'When MAC address 00155D0106ED is passed' { It 'Returns MAC address 00155D0106EE' { Get-NextMacAddress ` @@ -657,7 +660,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Get-LabNextIpAddress.ps1' { + Describe 'Get-LabNextIpAddress' { Context 'When Invalid IP Address is passed' { It 'Throws a IPAddressError Exception' { $exceptionParameters = @{ @@ -720,7 +723,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Assert-LabValidIpAddress.ps1' { + Describe 'Assert-LabValidIpAddress' { Context 'When IP address 192.168.1.1 is passed' { It 'Returns IP Address' { Assert-LabValidIpAddress ` @@ -754,7 +757,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Install-LabPackageProvider.ps1' { + Describe 'Install-LabPackageProvider' { Context 'When Required package providers already installed' { Mock -CommandName Get-PackageProvider -MockWith { @( @@ -792,7 +795,7 @@ InModuleScope LabBuilder { - Describe '\lib\private\Register-LabPackageSource.ps1' { + Describe 'Register-LabPackageSource' { # Define this function because the built in definition does not # Mock -CommandName properly - the ProviderName parameter is not definied. function Register-PackageSource @@ -900,7 +903,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Write-LabMessage.ps1' { + Describe 'Write-LabMessage' { $script:testMessage = 'Test Message' $script:testMessageTime = Get-Date -UFormat %T $script:testMessageWithTime = ('[{0}]: {1}' -f $script:testMessageTime, $script:testMessage) @@ -993,7 +996,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\ConvertTo-LabAbsolutePath.ps1' { + Describe 'ConvertTo-LabAbsolutePath' { Context 'When absolute Path is passed' { It 'Should return the absolute path' { ConvertTo-LabAbsolutePath -Path 'c:\absolutepath' -BasePath 'c:\mylab' | Should -BeExactly 'c:\absolutepath' @@ -1007,9 +1010,9 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Get-LabBuilderModulePath.ps1' { + Describe 'Get-LabBuilderModulePath' { It 'Should return the path to the LabBuilder Module' { - Get-LabBuilderModulePath | Should -BeExactly (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src') + Get-LabBuilderModulePath | Should -BeExactly (Split-Path -Path (Get-Module -Name LabBuilder).Path -Parent) } } } diff --git a/tests/Unit/Private/vhd.tests.ps1 b/tests/Unit/Private/vhd.tests.ps1 index d02c5fe9..7969c0c0 100644 --- a/tests/Unit/Private/vhd.tests.ps1 +++ b/tests/Unit/Private/vhd.tests.ps1 @@ -1,41 +1,44 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath - Describe '\lib\private\Vhd.ps1\Initialize-LabBootVHD' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + Describe 'Initialize-LabBootVHD' { + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $VMs = Get-LabVM -Lab $Lab $NanoServerPackagesFolder = Join-Path -Path $Lab.labbuilderconfig.settings.labpath -ChildPath 'NanoServerPackages' $ResourceMSUFile = Join-Path -Path $Lab.labbuilderconfig.settings.resourcepathfull -ChildPath "W2K12-KB3191565-x64.msu" @@ -232,7 +235,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Vhd.ps1\Initialize-LabVHD' { + Describe 'Initialize-LabVHD' { function Get-VHD {} function Mount-VHD {} diff --git a/tests/Unit/Private/vm.tests.ps1 b/tests/Unit/Private/vm.tests.ps1 index ab9c2ba3..f1cd5e00 100644 --- a/tests/Unit/Private/vm.tests.ps1 +++ b/tests/Unit/Private/vm.tests.ps1 @@ -1,67 +1,70 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath - Describe '\lib\private\Vm.ps1\New-LabVMInitializationFile' -Tags 'Incomplete' { + Describe 'New-LabVMInitializationFile' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Get-LabUnattendFileContent' -Tags 'Incomplete' { + Describe 'Get-LabUnattendFileContent' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Get-LabCertificatePsFileContent' -Tags 'Incomplete' { + Describe 'Get-LabCertificatePsFileContent' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Recieve-LabSelfSignedCertificate' -Tags 'Incomplete' { + Describe 'Recieve-LabSelfSignedCertificate' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Request-LabSelfSignedCertificate' -Tags 'Incomplete' { + Describe 'Request-LabSelfSignedCertificate' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\New-LabHostSelfSignedCertificate' -Tags 'Incomplete' { + Describe 'New-LabHostSelfSignedCertificate' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Wait-LabVMInitializationComplete' -Tags 'Incomplete' { + Describe 'Wait-LabVMInitializationComplete' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Wait-LabVMStarted' -Tags 'Incomplete' { + Describe 'Wait-LabVMStarted' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Wait-LabVMOff' -Tags 'Incomplete' { + Describe 'Wait-LabVMOff' -Tags 'Incomplete' { } - Describe '\lib\private\Vm.ps1\Get-LabIntegrationServiceName' { + Describe 'Get-LabIntegrationServiceName' { Mock -CommandName Get-CimInstance ` -ParameterFilter { $Class -eq 'Msvm_VssComponentSettingData' } ` -MockWith { @{ Caption = 'VSS' }} @@ -99,7 +102,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Vm.ps1\Update-LabVMIntegrationService' { + Describe 'Update-LabVMIntegrationService' { function Get-VMIntegrationService {} function Enable-VMIntegrationService { [CmdletBinding()] @@ -143,7 +146,7 @@ InModuleScope LabBuilder { Mock -CommandName Enable-VMIntegrationService Mock -CommandName Disable-VMIntegrationService - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Templates = Get-LabVMTemplate -Lab $Lab [array] $Switches = Get-LabSwitch -Lab $Lab @@ -209,7 +212,7 @@ InModuleScope LabBuilder { } - Describe '\lib\private\Vm.ps1\Update-LabVMDataDisk' { + Describe 'Update-LabVMDataDisk' { function Get-VM {} function Get-VHD {} function Resize-VHD {} @@ -237,7 +240,7 @@ InModuleScope LabBuilder { # The same VM will be used for all tests, but a different # DataVHds array will be created/assigned for each test. - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Templates = Get-LabVMTemplate -Lab $Lab [array] $Switches = Get-LabSwitch -Lab $Lab [array] $VMs = Get-LabVM -Lab $Lab -VMTemplates $Templates -Switches $Switches @@ -529,7 +532,7 @@ InModuleScope LabBuilder { $DataVHD.Size = 10GB $DataVHD.PartitionStyle = [LabPartitionStyle]::GPT $DataVHD.FileSystem = [LabFileSystem]::NTFS - $DataVHD.CopyFolders = "$script:TestConfigPath\ExpectedContent" + $DataVHD.CopyFolders = "$script:testConfigPath\ExpectedContent" $VMs[0].DataVHDs = @( $DataVHD ) It 'Does not throw an Exception' { @@ -555,7 +558,7 @@ InModuleScope LabBuilder { $DataVHD = [LabDataVHD]::New('DoesNotExist.vhdx') $DataVHD.VhdType = [LabVHDType]::Dynamic $DataVHD.Size = 10GB - $DataVHD.CopyFolders = "$script:TestConfigPath\ExpectedContent" + $DataVHD.CopyFolders = "$script:testConfigPath\ExpectedContent" $VMs[0].DataVHDs = @( $DataVHD ) It 'Does not throw an Exception' { @@ -700,7 +703,7 @@ InModuleScope LabBuilder { } } - Describe '\lib\private\Vm.ps1\Update-LabVMDvdDrive' { + Describe 'Update-LabVMDvdDrive' { function Get-VMDVDDrive {} function Add-VMDVDDrive {} function Set-VMDVDDrive {} @@ -711,7 +714,7 @@ InModuleScope LabBuilder { # The same VM will be used for all tests, but a different # DVD Drives array will be created/assigned for each test. - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Templates = Get-LabVMTemplate -Lab $Lab [array] $Switches = Get-LabSwitch -Lab $Lab [array] $VMs = Get-LabVM -Lab $Lab -VMTemplates $Templates -Switches $Switches diff --git a/tests/Unit/Public/lab.tests.ps1 b/tests/Unit/Public/lab.tests.ps1 index cc42d26b..bfafa115 100644 --- a/tests/Unit/Public/lab.tests.ps1 +++ b/tests/Unit/Public/lab.tests.ps1 @@ -1,45 +1,48 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath Describe 'Get-Lab' { Context 'When relative path is provided and valid XML file exists' { - Mock Get-Location -MockWith { @{ Path = $script:TestConfigPath} } + Mock Get-Location -MockWith { @{ Path = $script:testConfigPath} } It 'Returns XmlDocument object with valid content' { - $Lab = Get-Lab -ConfigPath (Split-Path -Path $script:TestConfigOKPath -Leaf) + $Lab = Get-Lab -ConfigPath (Split-Path -Path $script:testConfigOKPath -Leaf) $Lab.GetType().Name | Should -Be 'XmlDocument' $Lab.labbuilderconfig | Should -Not -Be $null } @@ -47,7 +50,7 @@ InModuleScope LabBuilder { Context 'When path is provided and valid XML file exists' { It 'Returns XmlDocument object with valid content' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.GetType().Name | Should -Be 'XmlDocument' $Lab.labbuilderconfig | Should -Not -Be $null } @@ -55,7 +58,7 @@ InModuleScope LabBuilder { Context 'When path and LabPath are provided and valid XML file exists' { It 'Returns XmlDocument object with valid content' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath ` + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath ` -LabPath 'c:\Pester Lab' $Lab.GetType().Name | Should -Be 'XmlDocument' $Lab.labbuilderconfig.settings.labpath | Should -Be 'c:\Pester Lab' @@ -100,7 +103,7 @@ InModuleScope LabBuilder { } } - $Script:CurrentBuild = 10000 + $script:currentBuild = 10000 Context 'When path is provided and file exists but host build version requirement not met' { It 'Throws RequiredBuildNotMetError Exception' { @@ -108,20 +111,20 @@ InModuleScope LabBuilder { errorId = 'RequiredBuildNotMetError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.RequiredBuildNotMetError ` - -f $Script:CurrentBuild,'10560') + -f $script:currentBuild,'10560') } $Exception = Get-LabException @exceptionParameters - { Get-Lab -ConfigPath $script:TestConfigOKPath } | Should -Throw $Exception + { Get-Lab -ConfigPath $script:testConfigOKPath } | Should -Throw $Exception } } - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 } Describe 'New-Lab' -Tags 'Incomplete' { } Describe 'Install-Lab' -Tags 'Incomplete' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath Mock Get-VMSwitch Mock New-VMSwitch diff --git a/tests/Unit/Public/resource.tests.ps1 b/tests/Unit/Public/resource.tests.ps1 index 9c98669b..f7c8d197 100644 --- a/tests/Unit/Public/resource.tests.ps1 +++ b/tests/Unit/Public/resource.tests.ps1 @@ -1,43 +1,46 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath Describe 'Get-LabResourceModule' { Context 'When valid configuration passed with resource module missing Name.' { It 'Throws a ResourceModuleNameIsEmptyError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.module[0].RemoveAttribute('name') $exceptionParameters = @{ errorId = 'ResourceModuleNameIsEmptyError' @@ -52,17 +55,17 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed' { It 'Returns Resource Modules Array that matches Expected Array' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $ResourceModules = Get-LabResourceModule -Lab $Lab - Set-Content -Path "$script:ArtifactPath\ExpectedResourceModules.json" -Value ($ResourceModules | ConvertTo-Json -Depth 4) - $ExpectedResourceModules = Get-Content -Path "$script:ExpectedContentPath\ExpectedResourceModules.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedResourceModules.json"),$ExpectedResourceModules,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedResourceModules.json" -Value ($ResourceModules | ConvertTo-Json -Depth 4) + $ExpectedResourceModules = Get-Content -Path "$script:expectedContentPath\ExpectedResourceModules.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedResourceModules.json"),$ExpectedResourceModules,$true) | Should -Be 0 } } } Describe 'Initialize-LabResourceModule' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [LabResourceModule[]]$ResourceModules = Get-LabResourceModule -Lab $Lab Mock Invoke-LabDownloadResourceModule @@ -83,7 +86,7 @@ InModuleScope LabBuilder { Describe 'Get-LabResourceMSU' { Context 'When valid configuration passed with resource MSU missing Name.' { It 'Throws a ResourceMSUNameIsEmptyError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.msu[0].RemoveAttribute('name') $exceptionParameters = @{ errorId = 'ResourceMSUNameIsEmptyError' @@ -98,17 +101,17 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed' { It 'Returns Resource MSU Array that matches Expected Array' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $ResourceMSUs = Get-LabResourceMSU -Lab $Lab - Set-Content -Path "$script:ArtifactPath\ExpectedResourceMSUs.json" -Value ($ResourceMSUs | ConvertTo-Json -Depth 4) - $ExpectedResourceMSUs = Get-Content -Path "$script:ExpectedContentPath\ExpectedResourceMSUs.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedResourceMSUs.json"),$ExpectedResourceMSUs,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedResourceMSUs.json" -Value ($ResourceMSUs | ConvertTo-Json -Depth 4) + $ExpectedResourceMSUs = Get-Content -Path "$script:expectedContentPath\ExpectedResourceMSUs.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedResourceMSUs.json"),$ExpectedResourceMSUs,$true) | Should -Be 0 } } } Describe 'Initialize-LabResourceMSU' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [LabResourceMSU[]]$ResourceMSUs = Get-LabResourceMSU -Lab $Lab Mock Invoke-LabDownloadAndUnzipFile @@ -128,7 +131,7 @@ InModuleScope LabBuilder { Describe 'Get-LabResourceISO' { Context 'When valid configuration passed with resource ISO missing Name.' { It 'Throws a ResourceISONameIsEmptyError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.iso[0].RemoveAttribute('name') $exceptionParameters = @{ errorId = 'ResourceISONameIsEmptyError' @@ -143,7 +146,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with resource ISO with Empty Path' { It 'Throws a ResourceISOPathIsEmptyError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.iso[0].path='' $exceptionParameters = @{ errorId = 'ResourceISOPathIsEmptyError' @@ -159,12 +162,12 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with resource ISO files that do exist.' { It 'Does not throw an Exception' { - $Path = "$script:TestConfigPath\ISOFiles\SQLServer2014SP1-FullSlipstream-x64-ENU.iso" - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Path = "$script:testConfigPath\ISOFiles\SQLServer2014SP1-FullSlipstream-x64-ENU.iso" + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.iso[0].RemoveAttribute('url') - $Lab.labbuilderconfig.resources.iso[0].SetAttribute('path',"$script:TestConfigPath\ISOFiles\SQLServer2014SP1-FullSlipstream-x64-ENU.iso") + $Lab.labbuilderconfig.resources.iso[0].SetAttribute('path',"$script:testConfigPath\ISOFiles\SQLServer2014SP1-FullSlipstream-x64-ENU.iso") $Lab.labbuilderconfig.resources.iso[1].RemoveAttribute('url') - $Lab.labbuilderconfig.resources.iso[1].SetAttribute('path',"$script:TestConfigPath\ISOFiles\SQLFULL_ENU.iso") + $Lab.labbuilderconfig.resources.iso[1].SetAttribute('path',"$script:testConfigPath\ISOFiles\SQLFULL_ENU.iso") { Get-LabResourceISO -Lab $Lab } | Should -Not -Throw } @@ -172,40 +175,40 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed' { It 'Returns Resource ISO Array that matches Expected Array' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $Lab.labbuilderconfig.resources.iso[0].SetAttribute('path',"$($script:TestConfigPath)\ISOFiles\SQLServer2014SP1-FullSlipstream-x64-ENU.iso") - $Lab.labbuilderconfig.resources.iso[1].SetAttribute('path',"$($script:TestConfigPath)\ISOFiles\SQLFULL_ENU.iso") + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath + $Lab.labbuilderconfig.resources.iso[0].SetAttribute('path',"$($script:testConfigPath)\ISOFiles\SQLServer2014SP1-FullSlipstream-x64-ENU.iso") + $Lab.labbuilderconfig.resources.iso[1].SetAttribute('path',"$($script:testConfigPath)\ISOFiles\SQLFULL_ENU.iso") [Array] $ResourceISOs = Get-LabResourceISO -Lab $Lab # Adjust the path to remove machine specific path $ResourceISOs.foreach({ - $_.Path = $_.Path.Replace($script:TestConfigPath,'.') + $_.Path = $_.Path.Replace($script:testConfigPath,'.') }) - Set-Content -Path "$script:ArtifactPath\ExpectedResourceISOs.json" -Value ($ResourceISOs | ConvertTo-Json -Depth 4) - $ExpectedResourceISOs = Get-Content -Path "$script:ExpectedContentPath\ExpectedResourceISOs.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedResourceISOs.json"),$ExpectedResourceISOs,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedResourceISOs.json" -Value ($ResourceISOs | ConvertTo-Json -Depth 4) + $ExpectedResourceISOs = Get-Content -Path "$script:expectedContentPath\ExpectedResourceISOs.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedResourceISOs.json"),$ExpectedResourceISOs,$true) | Should -Be 0 } } Context 'When valid configuration is passed with ISOPath set' { It 'Returns Resource ISO Array that matches Expected Array' { - $Path = "$script:TestConfigPath\ISOFiles" - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Path = "$script:testConfigPath\ISOFiles" + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.SetAttribute('isopath',$Path) [Array] $ResourceISOs = Get-LabResourceISO -Lab $Lab # Adjust the path to remove machine specific path $ResourceISOs.foreach({ - $_.Path = $_.Path.Replace($script:TestConfigPath,'.') + $_.Path = $_.Path.Replace($script:testConfigPath,'.') }) - Set-Content -Path "$script:ArtifactPath\ExpectedResourceISOs.json" -Value ($ResourceISOs | ConvertTo-Json -Depth 4) - $ExpectedResourceISOs = Get-Content -Path "$script:ExpectedContentPath\ExpectedResourceISOs.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedResourceISOs.json"),$ExpectedResourceISOs,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedResourceISOs.json" -Value ($ResourceISOs | ConvertTo-Json -Depth 4) + $ExpectedResourceISOs = Get-Content -Path "$script:expectedContentPath\ExpectedResourceISOs.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedResourceISOs.json"),$ExpectedResourceISOs,$true) | Should -Be 0 } } } Describe 'Initialize-LabResourceISO' { - $Path = "$script:TestConfigPath\ISOFiles" - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Path = "$script:testConfigPath\ISOFiles" + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.resources.SetAttribute('isopath',$Path) [LabResourceISO[]]$ResourceISOs = Get-LabResourceISO -Lab $Lab diff --git a/tests/Unit/Public/switch.tests.ps1 b/tests/Unit/Public/switch.tests.ps1 index 5be28924..22ba09ad 100644 --- a/tests/Unit/Public/switch.tests.ps1 +++ b/tests/Unit/Public/switch.tests.ps1 @@ -1,43 +1,46 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath Describe 'Get-LabSwitch' { Context 'When valid configuration passed with switch missing Switch Name.' { It 'Throws a SwitchNameIsEmptyError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.switches.switch[0].RemoveAttribute('name') $exceptionParameters = @{ errorId = 'SwitchNameIsEmptyError' @@ -52,7 +55,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with switch missing Switch Type.' { It 'Throws a UnknownSwitchTypeError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.switches.switch[0].RemoveAttribute('type') $exceptionParameters = @{ errorId = 'UnknownSwitchTypeError' @@ -68,7 +71,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with switch invalid Switch Type.' { It 'Throws a UnknownSwitchTypeError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.switches.switch[0].type='BadType' $exceptionParameters = @{ errorId = 'UnknownSwitchTypeError' @@ -83,7 +86,7 @@ InModuleScope LabBuilder { } Context 'When valid configuration passed with switch containing adapters but is not External type.' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.switches.switch[0].type='Private' It 'Throws a AdapterSpecifiedError Exception' { $exceptionParameters = @{ @@ -100,7 +103,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with and Name filter set to matching switch' { It 'Returns a Single Switch object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $Switches = Get-LabSwitch -Lab $Lab -Name $Lab.labbuilderconfig.switches.switch[0].name $Switches.Count | Should -Be 1 } @@ -108,7 +111,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with and Name filter set to non-matching switch' { It 'Returns a Single Switch object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $Switches = Get-LabSwitch -Lab $Lab -Name 'Does Not Exist' $Switches.Count | Should -Be 0 } @@ -116,17 +119,17 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed' { It 'Returns Switches Object that matches Expected Object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $Switches = Get-LabSwitch -Lab $Lab - Set-Content -Path "$script:ArtifactPath\ExpectedSwitches.json" -Value ($Switches | ConvertTo-Json -Depth 4) - $ExpectedSwitches = Get-Content -Path "$script:ExpectedContentPath\ExpectedSwitches.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedSwitches.json"),$ExpectedSwitches,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedSwitches.json" -Value ($Switches | ConvertTo-Json -Depth 4) + $ExpectedSwitches = Get-Content -Path "$script:expectedContentPath\ExpectedSwitches.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedSwitches.json"),$ExpectedSwitches,$true) | Should -Be 0 } } } Describe 'Initialize-LabSwitch' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [LabSwitch[]] $Switches = Get-LabSwitch -Lab $Lab function Get-VMSwitch {} @@ -201,7 +204,7 @@ InModuleScope LabBuilder { } } - $script:CurrentBuild = 14295 + $script:currentBuild = 14295 Context 'When valid configuration is passed' { It 'Does not throw an Exception' { @@ -271,7 +274,7 @@ InModuleScope LabBuilder { } } - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 Context 'When valid configuration NAT on unsupported build' { $Switches[0].Type = [LabSwitchType]::NAT @@ -302,7 +305,7 @@ InModuleScope LabBuilder { } } - $script:CurrentBuild = 14295 + $script:currentBuild = 14295 Context 'When valid configuration NAT with invalid NAT Subnet' { $Switches[0].Type = [LabSwitchType]::NAT @@ -527,7 +530,7 @@ InModuleScope LabBuilder { function Remove-VMNetworkAdapter {} function Remove-NetNat {} - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [LabSwitch[]] $Switches = Get-LabSwitch -Lab $Lab Mock Get-VMSwitch -MockWith { $Switches } diff --git a/tests/Unit/Public/templatevhd.tests.ps1 b/tests/Unit/Public/templatevhd.tests.ps1 index ff0146ed..3cd4cbe0 100644 --- a/tests/Unit/Public/templatevhd.tests.ps1 +++ b/tests/Unit/Public/templatevhd.tests.ps1 @@ -1,49 +1,52 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath Describe 'Get-LabVMTemplateVHD' { Context 'When valid configuration passed with rooted ISO Root Path that does not exist' { It 'Throws a VMTemplateVHDISORootPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $Lab.labbuilderconfig.templatevhds.ISOPath = "$script:TestConfigPath\MissingFolder" + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath + $Lab.labbuilderconfig.templatevhds.ISOPath = "$script:testConfigPath\MissingFolder" $exceptionParameters = @{ errorId = 'VMTemplateVHDISORootPathNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.VMTemplateVHDISORootPathNotFoundError ` - -f "$script:TestConfigPath\MissingFolder") + -f "$script:testConfigPath\MissingFolder") } $Exception = Get-LabException @exceptionParameters @@ -53,13 +56,13 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with relative ISO Root Path that does not exist' { It 'Throws a VMTemplateVHDISORootPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.ISOPath = "MissingFolder" $exceptionParameters = @{ errorId = 'VMTemplateVHDISORootPathNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.VMTemplateVHDISORootPathNotFoundError ` - -f "$script:TestConfigPath\MissingFolder") + -f "$script:testConfigPath\MissingFolder") } $Exception = Get-LabException @exceptionParameters @@ -69,13 +72,13 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with rooted VHD Root Path that does not exist' { It 'Throws a VMTemplateVHDRootPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $Lab.labbuilderconfig.templatevhds.VHDPath = "$script:TestConfigPath\MissingFolder" + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath + $Lab.labbuilderconfig.templatevhds.VHDPath = "$script:testConfigPath\MissingFolder" $exceptionParameters = @{ errorId = 'VMTemplateVHDRootPathNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.VMTemplateVHDRootPathNotFoundError ` - -f "$script:TestConfigPath\MissingFolder") + -f "$script:testConfigPath\MissingFolder") } $Exception = Get-LabException @exceptionParameters @@ -85,13 +88,13 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with relative VHD Root Path that does not exist' { It 'Throws a VMTemplateVHDRootPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.VHDPath = "MissingFolder" $exceptionParameters = @{ errorId = 'VMTemplateVHDRootPathNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.VMTemplateVHDRootPathNotFoundError ` - -f "$script:TestConfigPath\MissingFolder") + -f "$script:testConfigPath\MissingFolder") } $Exception = Get-LabException @exceptionParameters @@ -101,7 +104,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with empty template VHD Name' { It 'Throws a EmptyVMTemplateVHDNameError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].RemoveAttribute('name') $exceptionParameters = @{ errorId = 'EmptyVMTemplateVHDNameError' @@ -116,7 +119,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with template ISO Path is empty' { It 'Throws a EmptyVMTemplateVHDISOPathError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].ISO = '' $exceptionParameters = @{ errorId = 'EmptyVMTemplateVHDISOPathError' @@ -132,13 +135,13 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with template ISO Path that does not exist' { It 'Throws a VMTemplateVHDISOPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $Lab.labbuilderconfig.templatevhds.templatevhd[0].ISO = "$script:TestConfigPath\MissingFolder\DoesNotExist.iso" + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath + $Lab.labbuilderconfig.templatevhds.templatevhd[0].ISO = "$script:testConfigPath\MissingFolder\DoesNotExist.iso" $exceptionParameters = @{ errorId = 'VMTemplateVHDISOPathNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.VMTemplateVHDISOPathNotFoundError ` - -f $Lab.labbuilderconfig.templatevhds.templatevhd[0].name,"$script:TestConfigPath\MissingFolder\DoesNotExist.iso") + -f $Lab.labbuilderconfig.templatevhds.templatevhd[0].name,"$script:testConfigPath\MissingFolder\DoesNotExist.iso") } $Exception = Get-LabException @exceptionParameters @@ -148,13 +151,13 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with relative template ISO Path that does not exist' { It 'Throws a VMTemplateVHDISOPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].ISO = "MissingFolder\DoesNotExist.iso" $exceptionParameters = @{ errorId = 'VMTemplateVHDISOPathNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.VMTemplateVHDISOPathNotFoundError ` - -f $Lab.labbuilderconfig.templatevhds.templatevhd[0].name,"$script:TestConfigPath\ISOFiles\MissingFolder\DoesNotExist.iso") + -f $Lab.labbuilderconfig.templatevhds.templatevhd[0].name,"$script:testConfigPath\ISOFiles\MissingFolder\DoesNotExist.iso") } $Exception = Get-LabException @exceptionParameters @@ -164,7 +167,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with invalid OSType' { It 'Throws a InvalidVMTemplateVHDOSTypeError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].OSType = 'invalid' $exceptionParameters = @{ errorId = 'InvalidVMTemplateVHDOSTypeError' @@ -180,7 +183,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with invalid VHDFormat' { It 'Throws a InvalidVMTemplateVHDVHDFormatError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].VHDFormat = 'invalid' $exceptionParameters = @{ errorId = 'InvalidVMTemplateVHDVHDFormatError' @@ -196,7 +199,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with invalid VHDType' { It 'Throws a InvalidVMTemplateVHDVHDTypeError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].VHDType = 'invalid' $exceptionParameters = @{ errorId = 'InvalidVMTemplateVHDVHDTypeError' @@ -212,7 +215,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with invalid VHDType' { It 'Throws a InvalidVMTemplateVHDGenerationError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.templatevhd[0].Generation = '99' $exceptionParameters = @{ errorId = 'InvalidVMTemplateVHDGenerationError' @@ -227,7 +230,7 @@ InModuleScope LabBuilder { } Context 'When valid configuration is passed missing TemplateVHDs Node' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.RemoveChild($Lab.labbuilderconfig.templatevhds) It 'Returns null' { @@ -236,7 +239,7 @@ InModuleScope LabBuilder { } Context 'When valid configuration is passed with no TemplateVHD Nodes' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templatevhds.IsEmpty = $true It 'Returns null' { @@ -246,7 +249,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with and Name filter set to matching switch' { It 'Returns a Single Switch object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $TemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab -Name $Lab.labbuilderconfig.TemplateVHDs.templateVHD[0].Name $TemplateVHDs.Count | Should -Be 1 } @@ -254,7 +257,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with and Name filter set to non-matching switch' { It 'Returns a Single Switch object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $TemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab -Name 'Does Not Exist' $TemplateVHDs.Count | Should -Be 0 } @@ -262,7 +265,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed and template VHD ISOs are found' { It 'Returns VMTemplateVHDs array that matches Expected array' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [Array] $TemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab # Remove the VHDPath and ISOPath values for any VMtemplatesVHD # because they will usually be relative to the test folder and @@ -272,15 +275,15 @@ InModuleScope LabBuilder { $TemplateVHD.VHDPath = 'Intentionally Removed' $TemplateVHD.ISOPath = 'Intentionally Removed' } - Set-Content -Path "$script:ArtifactPath\ExpectedTemplateVHDs.json" -Value ($TemplateVHDs | ConvertTo-Json -Depth 2) - $ExpectedTemplateVHDs = Get-Content -Path "$script:ExpectedContentPath\ExpectedTemplateVHDs.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedTemplateVHDs.json"),$ExpectedTemplateVHDs,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedTemplateVHDs.json" -Value ($TemplateVHDs | ConvertTo-Json -Depth 2) + $ExpectedTemplateVHDs = Get-Content -Path "$script:expectedContentPath\ExpectedTemplateVHDs.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedTemplateVHDs.json"),$ExpectedTemplateVHDs,$true) | Should -Be 0 } } } Describe 'Initialize-LabVMTemplateVHD' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $ResourceMSUFile = Join-Path -Path $Lab.labbuilderconfig.settings.resourcepathfull -ChildPath "W2K12-KB3191565-x64.msu" Mock Mount-DiskImage @@ -318,7 +321,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed but alternate DISM not found' { It 'Throws a FileNotFoundError exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $exceptionParameters = @{ errorId = 'FileNotFoundError' @@ -345,7 +348,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with no VMtemplateVHDs' { It 'Does not throw an Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.RemoveChild($Lab.labbuilderconfig.templatevhds) { Initialize-LabVMTemplateVHD -Lab $Lab } | Should -Not -Throw } @@ -364,7 +367,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed where the template ISO can not be found' { It 'Throws an VMTemplateVHDISOPathNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[0].isopath = 'doesnotexist.iso' $VMTemplateVHDs[0].vhdpath = 'doesnotexist.vhdx' @@ -393,7 +396,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with two VHDx files not generated and no packages' { It 'Does not throw an Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[0].vhdpath = 'doesnotexist.vhdx' $VMTemplateVHDs[1].vhdpath = 'doesnotexist.vhdx' @@ -419,7 +422,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with two VHDx files not generated valid packages' { Mock Test-Path -ParameterFilter { $Path -eq $ResourceMSUFile } -MockWith { $true } It 'Does not throw an Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[0].vhdpath = 'doesnotexist.vhdx' $VMTemplateVHDs[1].vhdpath = 'doesnotexist.vhdx' @@ -440,7 +443,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with an invalid package' { It 'Throws a PackageNotFoundError exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[0].vhdpath = 'doesnotexist.vhdx' foreach ($VMTemplateVHD in $VMTemplateVHDs) @@ -472,7 +475,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with an invalid package' { Mock Test-Path -ParameterFilter { $Path -eq $ResourceMSUFile } -MockWith { $false } It 'Throws a PackageMSUNotFoundError exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[0].vhdpath = 'doesnotexist.vhdx' $exceptionParameters = @{ @@ -497,7 +500,7 @@ InModuleScope LabBuilder { } } - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[5].vhdpath = "$($VMTemplateVHDs[5].vhdpath).NotExist" @@ -575,7 +578,7 @@ InModuleScope LabBuilder { Mock Test-Path -ParameterFilter { $Path -eq $ResourceMSUFile } -MockWith { $true } It 'Throws a ConvertWindowsImageError exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab $VMTemplateVHDs[0].vhdpath = 'doesnotexist.vhdx' $VMTemplateVHDs[1].vhdpath = 'doesnotexist.vhdx' @@ -602,7 +605,7 @@ InModuleScope LabBuilder { } Describe 'Remove-LabVMTemplateVHD' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $VMTemplateVHDs = Get-LabVMTemplateVHD -Lab $Lab Mock Remove-Item Mock Test-Path -MockWith { $false } diff --git a/tests/Unit/Public/vm.tests.ps1 b/tests/Unit/Public/vm.tests.ps1 index a09d1b4c..09f7aa26 100644 --- a/tests/Unit/Public/vm.tests.ps1 +++ b/tests/Unit/Public/vm.tests.ps1 @@ -1,38 +1,41 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking ` - -Verbose:$false -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath Describe 'Get-LabVM' { # Mock functions @@ -43,15 +46,15 @@ InModuleScope LabBuilder { #endregion # Run tests assuming Build 10586 is installed - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 # Figure out the TestVMName (saves typing later on) - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $TestVMName = "$($lab.labbuilderconfig.settings.labid)$($lab.labbuilderconfig.vms.vm.name)" Context 'When valid configuration passed with VM missing VM Name' { It 'Throw VMNameError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.RemoveAttribute('name') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -67,7 +70,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM missing Template' { It 'Throw VMTemplateNameEmptyError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.RemoveAttribute('template') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -84,7 +87,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM invalid Template Name' { It 'Throw VMTemplateNotFoundError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.template = 'BadTemplate' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -101,7 +104,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM missing adapter name' { It 'Throw VMAdapterNameError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.adapters.adapter[0].RemoveAttribute('name') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -118,7 +121,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM missing adapter switch name' { It 'Throw VMAdapterSwitchNameError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.adapters.adapter[0].RemoveAttribute('switchname') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -135,7 +138,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk with empty VHD' { It 'Throw VMDataDiskVHDEmptyError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].vhd = '' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -152,7 +155,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk where ParentVHD can not be found' { It 'Throw VMDataDiskParentVHDNotFoundError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[3].parentvhd = 'c:\ThisFileDoesntExist.vhdx' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -169,7 +172,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk where SourceVHD can not be found' { It 'Throw VMDataDiskSourceVHDNotFoundError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].sourcevhd = 'c:\ThisFileDoesntExist.vhdx' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -186,7 +189,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Differencing Data Disk with empty ParentVHD' { It 'Throw VMDataDiskParentVHDMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[3].RemoveAttribute('parentvhd') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -203,7 +206,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk where it is a Differencing type disk but is shared' { It 'Throw VMDataDiskSharedDifferencingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[3].SetAttribute('Shared','Y') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -220,7 +223,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk where it has an unknown Type' { It 'Throw VMDataDiskUnknownTypeError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].type = 'badtype' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -237,7 +240,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that has an invalid Partition Style' { It 'Throw VMDataDiskPartitionStyleError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].PartitionStyle='Bad' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -254,7 +257,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that has an invalid File System' { It 'Throw VMDataDiskFileSystemError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].FileSystem='Bad' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -271,7 +274,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that has a File System set but not a Partition Style' { It 'Throw VMDataDiskPartitionStyleMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].RemoveAttribute('partitionstyle') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -288,7 +291,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that has a Partition Style set but not a File System' { It 'Throw VMDataDiskFileSystemMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].RemoveAttribute('filesystem') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -305,7 +308,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that has a File System Label set but not a Partition Style or File System' { It 'Throw VMDataDiskPartitionStyleMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[2].RemoveAttribute('partitionstyle') $lab.labbuilderconfig.vms.vm.datavhds.datavhd[2].RemoveAttribute('filesystem') [array] $Switches = Get-LabSwitch -Lab $lab @@ -323,7 +326,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that exists with CopyFolders set to a folder that does not exist' { It 'Throw VMDataDiskCopyFolderMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].CopyFolders='c:\doesnotexist' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -340,7 +343,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that does not exist but Type missing' { It 'Throw VMDataDiskCantBeCreatedError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].RemoveAttribute('type') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -357,7 +360,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that does not exist but Size missing' { It 'Throw VMDataDiskCantBeCreatedError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[1].RemoveAttribute('size') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -374,7 +377,7 @@ InModuleScope LabBuilder { Context 'When valid configuration passed with VM Data Disk that does not exist but SourceVHD missing' { It 'Throw VMDataDiskCantBeCreatedError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].RemoveAttribute('sourcevhd') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -391,7 +394,7 @@ InModuleScope LabBuilder { Context "Configuration passed with VM Data Disk that has MoveSourceVHD flag but SourceVHD missing." { It 'Throw VMDataDiskSourceVHDIfMoveError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[4].RemoveAttribute('sourcevhd') [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -407,19 +410,19 @@ InModuleScope LabBuilder { } Context 'When valid configuration is passed with VM Data Disk with rooted VHD path.' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].vhd = "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $lab = Get-Lab -ConfigPath $script:testConfigOKPath + $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].vhd = "$script:testConfigPath\VhdFiles\DataDisk.vhdx" [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches It 'Returns Template Object containing VHD with correct rooted path' { - $VMs[0].DataVhds[0].vhd | Should -Be "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $VMs[0].DataVhds[0].vhd | Should -Be "$script:testConfigPath\VhdFiles\DataDisk.vhdx" } } Context 'When valid configuration is passed with VM Data Disk with non-rooted VHD path.' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].vhd = "DataDisk.vhdx" [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -431,58 +434,58 @@ InModuleScope LabBuilder { } Context 'When valid configuration is passed with VM Data Disk with rooted Parent VHD path.' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $lab.labbuilderconfig.vms.vm.datavhds.datavhd[3].parentvhd = "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $lab = Get-Lab -ConfigPath $script:testConfigOKPath + $lab.labbuilderconfig.vms.vm.datavhds.datavhd[3].parentvhd = "$script:testConfigPath\VhdFiles\DataDisk.vhdx" [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches It 'Returns Template Object containing Parent VHD with correct rooted path' { - $VMs[0].DataVhds[3].parentvhd | Should -Be "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $VMs[0].DataVhds[3].parentvhd | Should -Be "$script:testConfigPath\VhdFiles\DataDisk.vhdx" } } Context 'When valid configuration is passed with VM Data Disk with non-rooted Parent VHD path.' { Mock Test-Path -MockWith { $true } - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[3].parentvhd = "VhdFiles\DataDisk.vhdx" [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches It 'Returns Template Object containing Parent VHD with correct rooted path' { - $VMs[0].DataVhds[3].parentvhd | Should -Be "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $VMs[0].DataVhds[3].parentvhd | Should -Be "$script:testConfigPath\VhdFiles\DataDisk.vhdx" } } Context 'When valid configuration is passed with VM Data Disk with rooted Source VHD path.' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath - $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].sourcevhd = "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $lab = Get-Lab -ConfigPath $script:testConfigOKPath + $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].sourcevhd = "$script:testConfigPath\VhdFiles\DataDisk.vhdx" [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches It 'Returns Template Object containing Source VHD with correct rooted path' { - $VMs[0].DataVhds[0].sourcevhd | Should -Be "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $VMs[0].DataVhds[0].sourcevhd | Should -Be "$script:testConfigPath\VhdFiles\DataDisk.vhdx" } } Context 'When valid configuration is passed with VM Data Disk with non-rooted Source VHD path.' { Mock Test-Path -MockWith { $true } - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.datavhds.datavhd[0].sourcevhd = "VhdFiles\DataDisk.vhdx" [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches It 'Returns Template Object containing Source VHD with correct rooted path' { - $VMs[0].DataVhds[0].sourcevhd | Should -Be "$script:TestConfigPath\VhdFiles\DataDisk.vhdx" + $VMs[0].DataVhds[0].sourcevhd | Should -Be "$script:testConfigPath\VhdFiles\DataDisk.vhdx" } } Context 'When the configuration passed with VM DVD Drive that has a missing Resource ISO' { It 'Throw VMDataDiskSourceVHDIfMoveError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.dvddrives.dvddrive[0].iso='DoesNotExist' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -499,7 +502,7 @@ InModuleScope LabBuilder { Context 'When the configuration passed with VM unattend file that can not be found' { It 'Throw UnattendFileMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.unattendfile = 'ThisFileDoesntExist.xml' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -507,7 +510,7 @@ InModuleScope LabBuilder { errorId = 'UnattendFileMissingError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.UnattendFileMissingError ` - -f $TestVMName,"$script:TestConfigPath\ThisFileDoesntExist.xml") + -f $TestVMName,"$script:testConfigPath\ThisFileDoesntExist.xml") } $exception = Get-LabException @exceptionParameters { Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches } | Should -Throw $exception @@ -516,7 +519,7 @@ InModuleScope LabBuilder { Context 'When the configuration passed with VM setup complete file that can not be found' { It 'Throw SetupCompleteFileMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.setupcomplete = 'ThisFileDoesntExist.ps1' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -524,7 +527,7 @@ InModuleScope LabBuilder { errorId = 'SetupCompleteFileMissingError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.SetupCompleteFileMissingError ` - -f $TestVMName,"$script:TestConfigPath\ThisFileDoesntExist.ps1") + -f $TestVMName,"$script:testConfigPath\ThisFileDoesntExist.ps1") } $exception = Get-LabException @exceptionParameters { Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches } | Should -Throw $exception @@ -533,7 +536,7 @@ InModuleScope LabBuilder { Context 'When the configuration passed with VM setup complete file with an invalid file extension' { It 'Throw SetupCompleteFileBadTypeError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.setupcomplete = 'ThisFileDoesntExist.abc' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -541,7 +544,7 @@ InModuleScope LabBuilder { errorId = 'SetupCompleteFileBadTypeError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.SetupCompleteFileBadTypeError ` - -f $TestVMName,"$script:TestConfigPath\ThisFileDoesntExist.abc") + -f $TestVMName,"$script:testConfigPath\ThisFileDoesntExist.abc") } $exception = Get-LabException @exceptionParameters { Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches } | Should -Throw $exception @@ -550,7 +553,7 @@ InModuleScope LabBuilder { Context 'When the configuration passed with VM DSC Config File that can not be found' { It 'Throw DSCConfigFileMissingError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.dsc.configfile = 'ThisFileDoesntExist.ps1' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -568,7 +571,7 @@ InModuleScope LabBuilder { Context 'When the configuration passed with VM DSC Config File with an invalid file extension' { It 'Throw DSCConfigFileBadTypeError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.dsc.configfile = 'FileWithBadType.xyz' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -586,7 +589,7 @@ InModuleScope LabBuilder { Context 'When the configuration passed with VM DSC Config File but no DSC Name' { It 'Throw DSCConfigNameIsEmptyError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $lab.labbuilderconfig.vms.vm.dsc.configname = '' [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab @@ -603,7 +606,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with and Name filter set to matching switch' { It 'Returns a Single Switch object' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches -Name $lab.labbuilderconfig.VMs.VM.Name @@ -613,7 +616,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with and Name filter set to non-matching switch' { It 'Returns a Single Switch object' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches -Name 'Does Not Exist' @@ -621,11 +624,11 @@ InModuleScope LabBuilder { } } - $Script:CurrentBuild = 10560 + $script:currentBuild = 10560 Context 'When the configuration passed with ExposeVirtualizationExtensions required but Build is 10560' { It 'Throw DSCConfigNameIsEmptyError Exception' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab $exceptionParameters = @{ @@ -639,10 +642,10 @@ InModuleScope LabBuilder { } } - $Script:CurrentBuild = 10586 + $script:currentBuild = 10586 Context 'When valid configuration is passed but switches and VMTemplates not passed' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath # Set the Instance Count to 2 to check $lab.labbuilderconfig.vms.vm.instancecount = '2' [array] $VMs = Get-LabVM -Lab $lab @@ -660,14 +663,14 @@ InModuleScope LabBuilder { } It 'Returns Template Object that matches Expected Object' { - Set-Content -Path "$script:ArtifactPath\ExpectedVMs.json" -Value ($VMs | ConvertTo-Json -Depth 6) - $ExpectedVMs = Get-Content -Path "$script:ExpectedContentPath\ExpectedVMs.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedVMs.json"),$ExpectedVMs,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedVMs.json" -Value ($VMs | ConvertTo-Json -Depth 6) + $ExpectedVMs = Get-Content -Path "$script:expectedContentPath\ExpectedVMs.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedVMs.json"),$ExpectedVMs,$true) | Should -Be 0 } } Context 'When valid configuration is passed' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Switches = Get-LabSwitch -Lab $lab [array] $Templates = Get-LabVMTemplate -Lab $lab # Set the Instance Count to 2 to check @@ -687,9 +690,9 @@ InModuleScope LabBuilder { } It 'Returns Template Object that matches Expected Object' { - Set-Content -Path "$script:ArtifactPath\ExpectedVMs.json" -Value ($VMs | ConvertTo-Json -Depth 6) - $ExpectedVMs = Get-Content -Path "$script:ExpectedContentPath\ExpectedVMs.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedVMs.json"),$ExpectedVMs,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedVMs.json" -Value ($VMs | ConvertTo-Json -Depth 6) + $ExpectedVMs = Get-Content -Path "$script:expectedContentPath\ExpectedVMs.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedVMs.json"),$ExpectedVMs,$true) | Should -Be 0 } } } @@ -712,7 +715,7 @@ InModuleScope LabBuilder { #endregion Context 'When valid configuration is passed' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $null = New-Item -Path $lab.labbuilderconfig.settings.labpath -ItemType Directory -Force -ErrorAction SilentlyContinue $null = New-Item -Path $lab.labbuilderconfig.settings.vhdparentpath -ItemType Directory -Force -ErrorAction SilentlyContinue @@ -760,7 +763,7 @@ InModuleScope LabBuilder { #endregion Context 'When valid configuration is passed' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $Switches = Get-LabSwitch -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches @@ -780,7 +783,7 @@ InModuleScope LabBuilder { } Context 'When valid configuration is passed but VMs not passed' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath # Create the dummy VM's that the Remove-LabVM function It 'Returns True' { @@ -797,7 +800,7 @@ InModuleScope LabBuilder { } Context 'When valid configuration is passed with RemoveVHDs switch' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Templates = Get-LabVMTemplate -Lab $lab [array] $Switches = Get-LabSwitch -Lab $lab [array] $VMs = Get-LabVM -Lab $lab -VMTemplates $Templates -Switches $Switches @@ -829,7 +832,7 @@ InModuleScope LabBuilder { #endregion Context 'When valid configuration is passed' { - $lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $lab = Get-Lab -ConfigPath $script:testConfigOKPath $null = New-Item -Path $lab.labbuilderconfig.settings.labpath -ItemType Directory -Force -ErrorAction SilentlyContinue $null = New-Item -Path $lab.labbuilderconfig.settings.vhdparentpath -ItemType Directory -Force -ErrorAction SilentlyContinue diff --git a/tests/Unit/Public/vmtemplate.tests.ps1 b/tests/Unit/Public/vmtemplate.tests.ps1 index 9a326d87..f150892a 100644 --- a/tests/Unit/Public/vmtemplate.tests.ps1 +++ b/tests/Unit/Public/vmtemplate.tests.ps1 @@ -1,37 +1,41 @@ -$global:LabBuilderProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent | Split-Path -Parent +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () -if (Get-Module -Name LabBuilder -All) -{ - Get-Module -Name LabBuilder -All | Remove-Module -} +$projectPath = "$PSScriptRoot\..\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'src\LabBuilder.psd1') ` - -Force ` - -DisableNameChecking -Import-Module -Name (Join-Path -Path $global:LabBuilderProjectRoot -ChildPath 'test\testhelper\testhelper.psm1') ` - -Global +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force -InModuleScope LabBuilder { # Run tests assuming Build 10586 is installed - $script:CurrentBuild = 10586 + $script:currentBuild = 10586 - $script:TestConfigPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\pestertestconfig' - $script:TestConfigOKPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'PesterTestConfig.OK.xml' - $script:ArtifactPath = Join-Path ` - -Path $global:LabBuilderProjectRoot ` - -ChildPath 'test\artifacts' - $script:ExpectedContentPath = Join-Path ` - -Path $script:TestConfigPath ` + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` -ChildPath 'expectedcontent' $null = New-Item ` - -Path $script:ArtifactPath ` + -Path $script:artifactPath ` -ItemType Directory ` -Force ` -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath Describe 'Get-LabVMTemplate' { # Mock functions @@ -42,7 +46,7 @@ InModuleScope LabBuilder { Context 'When configuration passed with template missing Template Name.' { It 'Throws a EmptyTemplateNameError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.template[0].RemoveAttribute('name') $exceptionParameters = @{ errorId = 'EmptyTemplateNameError' @@ -57,13 +61,13 @@ InModuleScope LabBuilder { Context 'When configuration passed with template with Source VHD set to relative non-existent file.' { It 'Throws a TemplateSourceVHDNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.template[0].sourcevhd = 'This File Doesnt Exist.vhdx' $exceptionParameters = @{ errorId = 'TemplateSourceVHDNotFoundError' errorCategory = 'InvalidArgument' errorMessage = $($LocalizedData.TemplateSourceVHDNotFoundError ` - -f $Lab.labbuilderconfig.templates.template[0].name,"$script:TestConfigPath\This File Doesnt Exist.vhdx") + -f $Lab.labbuilderconfig.templates.template[0].name,"$script:testConfigPath\This File Doesnt Exist.vhdx") } $Exception = Get-LabException @exceptionParameters @@ -73,7 +77,7 @@ InModuleScope LabBuilder { Context 'When configuration passed with template with Source VHD set to absolute non-existent file.' { It 'Throws a TemplateSourceVHDNotFoundError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.template[0].sourcevhd = 'c:\This File Doesnt Exist.vhdx' $exceptionParameters = @{ errorId = 'TemplateSourceVHDNotFoundError' @@ -89,7 +93,7 @@ InModuleScope LabBuilder { Context 'When configuration passed with template with Source VHD and Template VHD.' { It 'Throws a TemplateSourceVHDAndTemplateVHDConflictError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.template[0].SetAttribute('templatevhd','Windows Server 2012 R2 Datacenter FULL') $exceptionParameters = @{ errorId = 'TemplateSourceVHDAndTemplateVHDConflictError' @@ -105,7 +109,7 @@ InModuleScope LabBuilder { Context 'When configuration passed with template with no Source VHD and no Template VHD.' { It 'Throws a TemplateSourceVHDandTemplateVHDMissingError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.template[0].RemoveAttribute('sourcevhd') $exceptionParameters = @{ errorId = 'TemplateSourceVHDandTemplateVHDMissingError' @@ -121,7 +125,7 @@ InModuleScope LabBuilder { Context 'When configuration passed with template with Template VHD that does not exist.' { It 'Throws a TemplateSourceVHDAndTemplateVHDConflictError Exception' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.template[1].TemplateVHD='Template VHD Does Not Exist' $exceptionParameters = @{ errorId = 'TemplateTemplateVHDNotFoundError' @@ -137,7 +141,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed but no templates found' { It 'Returns Template Object that matches Expected Object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $Templates = Get-LabVMTemplate -Lab $Lab # Remove the SourceVHD values for any templates because they # will usually be relative to the test folder and won't exist @@ -145,9 +149,9 @@ InModuleScope LabBuilder { { $Template.SourceVHD = 'Intentionally Removed' } - Set-Content -Path "$script:ArtifactPath\ExpectedTemplates.json" -Value ($Templates | ConvertTo-Json -Depth 2) - $ExpectedTemplates = Get-Content -Path "$script:ExpectedContentPath\ExpectedTemplates.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedTemplates.json"),$ExpectedTemplates,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedTemplates.json" -Value ($Templates | ConvertTo-Json -Depth 2) + $ExpectedTemplates = Get-Content -Path "$script:expectedContentPath\ExpectedTemplates.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedTemplates.json"),$ExpectedTemplates,$true) | Should -Be 0 } It 'Calls Mocked commands' { @@ -169,7 +173,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with a Name filter set to matching VM' { It 'Returns a Single Template object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.SetAttribute('fromvm','Pester *') [Array] $Templates = Get-LabVMTemplate ` -Lab $Lab ` @@ -180,7 +184,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed with a Name filter set to non-matching VM' { It 'Returns no Template objects' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.SetAttribute('fromvm','Pester *') [Array] $Templates = Get-LabVMTemplate ` -Lab $Lab ` @@ -191,7 +195,7 @@ InModuleScope LabBuilder { Context 'When valid configuration is passed and some templates are found' { It 'Returns Template Object that matches Expected Object' { - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $Lab.labbuilderconfig.templates.SetAttribute('fromvm','Pester *') [array] $Templates = Get-LabVMTemplate -Lab $Lab # Remove the SourceVHD values for any templates because they @@ -200,9 +204,9 @@ InModuleScope LabBuilder { { $Template.SourceVHD = 'Intentionally Removed' } - Set-Content -Path "$script:ArtifactPath\ExpectedTemplates.FromVM.json" -Value ($Templates | ConvertTo-Json -Depth 2) - $ExpectedTemplates = Get-Content -Path "$script:ExpectedContentPath\ExpectedTemplates.FromVM.json" - [System.String]::Compare((Get-Content -Path "$script:ArtifactPath\ExpectedTemplates.FromVM.json"),$ExpectedTemplates,$true) | Should -Be 0 + Set-Content -Path "$script:artifactPath\ExpectedTemplates.FromVM.json" -Value ($Templates | ConvertTo-Json -Depth 2) + $ExpectedTemplates = Get-Content -Path "$script:expectedContentPath\ExpectedTemplates.FromVM.json" + [System.String]::Compare((Get-Content -Path "$script:artifactPath\ExpectedTemplates.FromVM.json"),$ExpectedTemplates,$true) | Should -Be 0 } It 'Calls Mocked commands' { @@ -219,7 +223,7 @@ InModuleScope LabBuilder { function Optimize-VHD {} function Get-VM {} - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath [array] $VMTemplates = Get-LabVMTemplate -Lab $Lab [Int32] $TemplateCount = $Lab.labbuilderconfig.templates.template.count $ResourceWMFMSUFile = Join-Path -Path $Lab.labbuilderconfig.settings.resourcepathfull -ChildPath "W2K12-KB3191565-x64.msu" @@ -315,7 +319,7 @@ InModuleScope LabBuilder { # Mock functions function Get-VM {} - $Lab = Get-Lab -ConfigPath $script:TestConfigOKPath + $Lab = Get-Lab -ConfigPath $script:testConfigOKPath $TemplateCount = $Lab.labbuilderconfig.templates.template.count Mock Set-ItemProperty -ParameterFilter { ($Name -eq 'IsReadOnly') -and ($Value -eq $false) } diff --git a/tests/Unit/labbuilder.tests.ps1 b/tests/Unit/labbuilder.tests.ps1 new file mode 100644 index 00000000..c0deb62a --- /dev/null +++ b/tests/Unit/labbuilder.tests.ps1 @@ -0,0 +1,92 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +[CmdletBinding()] +param () + +$projectPath = "$PSScriptRoot\..\.." | Convert-Path +$projectName = ((Get-ChildItem -Path $projectPath\*\*.psd1).Where{ + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and + $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } ) + }).BaseName + +Import-Module -Name $projectName -Force + +InModuleScope $projectName { + $testRootPath = $PSScriptRoot | Split-Path -Parent + $testHelperPath = $testRootPath | Join-Path -ChildPath 'TestHelper' + Import-Module -Name $testHelperPath -Force + + # Run tests assuming Build 10586 is installed + $script:currentBuild = 10586 + + $script:testConfigPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'pestertestconfig' + $script:testConfigOKPath = Join-Path ` + -Path $script:testConfigPath ` + -ChildPath 'PesterTestConfig.OK.xml' + $script:artifactPath = Join-Path ` + -Path $testRootPath ` + -ChildPath 'artifacts' + $script:expectedContentPath = Join-Path ` + -Path $script:testConfigPath ` + -ChildPath 'expectedcontent' + $null = New-Item ` + -Path $script:artifactPath ` + -ItemType Directory ` + -Force ` + -ErrorAction SilentlyContinue + $script:Lab = Get-Lab -ConfigPath $script:testConfigOKPath + + Import-Module -Name 'PSScriptAnalyzer' + + Describe 'PSScriptAnalyzer' { + Context 'LabBuilder Module code and Lib Functions' { + It 'Passes Invoke-ScriptAnalyzer' { + # Perform PSScriptAnalyzer scan. + $PSScriptAnalyzerResult = Invoke-ScriptAnalyzer ` + -Path ((Get-Module -Name LabBuilder).Path) ` + -Severity Warning ` + -ErrorAction SilentlyContinue + $PSScriptAnalyzerErrors = $PSScriptAnalyzerResult | Where-Object { $_.Severity -eq 'Error' } + $PSScriptAnalyzerWarnings = $PSScriptAnalyzerResult | Where-Object { $_.Severity -eq 'Warning' } + + if ($PSScriptAnalyzerErrors -ne $null) + { + Write-Warning -Message 'There are PSScriptAnalyzer errors that need to be fixed:' + @($PSScriptAnalyzerErrors).Foreach( { Write-Warning -Message "$($_.Scriptname) (Line $($_.Line)): $($_.Message)" } ) + Write-Warning -Message 'For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/psscriptAnalyzer/' + $PSScriptAnalyzerErrors.Count | Should -Be $null + } + + if ($PSScriptAnalyzerWarnings -ne $null) + { + Write-Warning -Message 'There are PSScriptAnalyzer warnings that should be fixed:' + @($PSScriptAnalyzerWarnings).Foreach( { Write-Warning -Message "$($_.Scriptname) (Line $($_.Line)): $($_.Message)" } ) + } + } + } + } + + # Perform Configuration XML Schema validation + Describe 'Validate XML schema of lab test files' { + Context 'When validing lab test file PesterTestConfig.OK.xml' { + It 'Should not throw an exception' { + { Assert-LabValidConfigurationXMLSchema -ConfigPath $script:TestConfigOKPath -Verbose } | Should -Not -Throw + } + } + } + + Describe 'Validate XML schema of lab sample files' { + $samplesFolder = Split-Path -Path ((Get-Module -Name LabBuilder).Path) -Parent | Join-Path -ChildPath 'Samples' + $sampleFiles = Get-ChildItem -Path $samplesFolder -Recurse -Filter 'Sample_*.xml' + + foreach ($SampleFile in $SampleFiles) + { + Context "When validating sample '$SampleFile'" { + It 'Should not throw an exception' { + { Assert-LabValidConfigurationXMLSchema -ConfigPath $($SampleFile.Fullname) -Verbose } | Should -Not -Throw + } + } + } + } +} From dd2b5f95c051723a6084524624d0bbf4aa76101e Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Wed, 22 Apr 2020 07:51:04 +1200 Subject: [PATCH 12/14] Fix badges --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5eda0dde..10409261 100644 --- a/README.md +++ b/README.md @@ -483,8 +483,8 @@ For a list of changes to versions, see the [CHANGELOG.md](CHANGELOG.md) file. - [Blog](https://dscottraynsford.wordpress.com/) [ap-image-master]: https://dev.azure.com/dscottraynsford/GitHub/_apis/build/status/PlagueHO.LabBuilder?branchName=master -[ap-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=5 -[ts-image-master]: https://img.shields.io/azure-devops/tests/dscottraynsford/GitHub/5/master -[ts-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=5&branchName=master +[ap-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=39 +[ts-image-master]: https://img.shields.io/azure-devops/tests/dscottraynsford/GitHub/39/master +[ts-site-master]: https://dev.azure.com/dscottraynsford/GitHub/_build/latest?definitionId=39&branchName=master [cq-image-master]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/master/graph/badge.svg [cq-site-master]: https://codecov.io/gh/PlagueHO/LabBuilder/branch/master From 4d0ba15fa78badeed23ccd5ff8629a826f884001 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Wed, 22 Apr 2020 07:52:57 +1200 Subject: [PATCH 13/14] Update CD Pipeline --- CHANGELOG.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d239bab8..92cad3c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,22 +26,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `dsclibrary\MEMBER_DHCP.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). + - Correct DHCP scope example - fixes [Issue #343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\MEMBER_DHCPDNS.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). + - Correct DHCP scope example - fixes [Issue #343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\MEMBER_DHCPNPAS2016.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). + - Correct DHCP scope example - fixes [Issue #343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\MEMBER_DNS.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - `dsclibrary\STNADALONE_DHCPDNS.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. - - Correct DHCP scope example - fixes [Issue-343](https://github.com/PlagueHO/LabBuilder/issues/343). + - Correct DHCP scope example - fixes [Issue #343](https://github.com/PlagueHO/LabBuilder/issues/343). - `dsclibrary\STNADALONE_INTERNET.DSC.ps1`: - Convert to use xDnsServer version 1.16.0.0. - Clean up code style. +- Remove AppVeyor CI pipeline and updated to new Continuous Delivery + pattern using Azure DevOps - fixes [Issue #355](https://github.com/PlagueHO/LabBuilder/issues/355). From 8df087fc2a04e599ebc38ab994c3765f644b5907 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford <plagueho@gmail.com> Date: Wed, 22 Apr 2020 08:29:03 +1200 Subject: [PATCH 14/14] Reduce code coverage requirement --- build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.yaml b/build.yaml index 7a799e2f..5b85dda8 100644 --- a/build.yaml +++ b/build.yaml @@ -83,7 +83,7 @@ Pester: - tests/Unit ExcludeTag: Incomplete Tag: - CodeCoverageThreshold: 70 + CodeCoverageThreshold: 55 Resolve-Dependency: Gallery: 'PSGallery'