├── .gitignore ├── .vscode └── settings.json ├── CMHyperHydrate.psd1 ├── CMHyperHydrate.psm1 ├── Certificates ├── Create-ConfigMgrCertTemplates.ps1 └── Generate-CACertTemplateConfigs.ps1 ├── Create-ModuleManifest.ps1 ├── Create-NewLab.ps1 ├── LICENSE ├── NewEnv.ASDLabTP1.json ├── NewEnv.Template.json ├── Private ├── Invoke-LabCommand.ps1 ├── New-LabCMSQLSettingsINI.ps1 ├── New-LabUnattendXML.ps1 └── Test-LabConnection.ps1 ├── Public ├── Add-LabAdditionalApps.ps1 ├── Add-LabRoleCA.ps1 ├── Add-LabRoleCM.ps1 ├── Add-LabRoleDC.ps1 ├── Add-LabRoleRRAS.ps1 ├── Add-LabRoleSQL.ps1 ├── Get-LabConfig.ps1 ├── Join-LabDomain.ps1 ├── New-LabEnv.ps1 ├── New-LabRefVHDX.ps1 ├── New-LabSwitch.ps1 ├── New-LabVM.ps1 ├── Update-LabRRAS.ps1 ├── UpgradeCM └── Write-LogEntry.ps1 └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | NewEnv.CMCB_CPC.json 2 | TestFiles/ 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "markdownlint.config": { 3 | "MD028": false, 4 | "MD025": { 5 | "front_matter_title": "" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /CMHyperHydrate.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'CMHyperHydrate' 3 | # 4 | # Generated by: Adam Gross (@AdamGrossTX) & Steven Hosking (@OnPremCloudGuy) 5 | # 6 | # Generated on: 9/11/2021 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'CMHyperHydrate.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '2.0' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = 'd80ae962-a46c-4181-9568-dbeb12a9f9fe' 22 | 23 | # Author of this module 24 | Author = 'Adam Gross (@AdamGrossTX) & Steven Hosking (@OnPremCloudGuy)' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Unknown' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Adam Gross (@AdamGrossTX) & Steven Hosking (@OnPremCloudGuy). All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | # Description = '' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | # PowerShellVersion = '' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # ClrVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # 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. 72 | FunctionsToExport = @() 73 | 74 | # 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. 75 | CmdletsToExport = @() 76 | 77 | # Variables to export from this module 78 | # VariablesToExport = @() 79 | 80 | # 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. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # 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. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | # LicenseUri = '' 102 | 103 | # A URL to the main website for this project. 104 | # ProjectUri = '' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | # ExternalModuleDependencies = @() 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | 133 | -------------------------------------------------------------------------------- /CMHyperHydrate.psm1: -------------------------------------------------------------------------------- 1 | #Imports all PS1 files from the Public and Private folders to create the module. 2 | 3 | $Public = @( Get-ChildItem -Path .\Public\*.ps1 -Recurse ) 4 | $Private = @( Get-ChildItem -Path .\Private\*.ps1 -Recurse) 5 | foreach ($import in @($Public + $Private)) { 6 | . $import.fullname 7 | } -------------------------------------------------------------------------------- /Certificates/Create-ConfigMgrCertTemplates.ps1: -------------------------------------------------------------------------------- 1 | 2 | #https://github.com/PowerShell/CertificateDsc/issues/54 3 | $Main = { 4 | foreach ($Template in $Configs) { 5 | Write-Host $Template.displayName 6 | Create-PKICertTemplate -TemplateDisplayName $Template.DisplayName -PKIConfig $Template.Config -SecurityConfig $Template.Security 7 | } 8 | } 9 | 10 | $Configs = @( 11 | @{ 12 | "DisplayName" = "Domain Controller Authentication (KDC)" 13 | "Config" = [hashtable]@{ 14 | "flags" = "131168" 15 | "pKIDefaultKeySpec" = "1" 16 | "pKIKeyUsage" = [Byte[]]( "160","0" ) 17 | "pKIMaxIssuingDepth" = "0" 18 | "pKICriticalExtensions" = @( "2.5.29.15","2.5.29.17" ) 19 | "pKIExpirationPeriod" = ([Byte[]](0,64,57,135,46,225,254,255)) 20 | "pKIOverlapPeriod" = ([Byte[]](0,128,166,10,255,222,255,255)) 21 | "pKIExtendedKeyUsage" = @( "1.3.6.1.5.5.7.3.2","1.3.6.1.5.5.7.3.1","1.3.6.1.4.1.311.20.2.2","1.3.6.1.5.2.3.5" ) 22 | "msPKI-RA-Signature" = "0" 23 | "msPKI-Enrollment-Flag" = "32" 24 | "msPKI-Private-Key-Flag" = "67436544" 25 | "msPKI-Certificate-Name-Flag" = "138412032" 26 | "msPKI-Minimal-Key-Size" = "2048" 27 | "msPKI-Template-Schema-Version" = "4" 28 | "msPKI-Template-Minor-Revision" = "3" 29 | "msPKI-Cert-Template-OID" = "1.3.6.1.4.1.311.21.8.9297300.10481922.2378919.4036973.687234.60.15267975.11339196" 30 | "msPKI-Supersede-Templates" = @( "KerberosAuthentication","DomainControllerAuthentication","DomainController" ) 31 | "msPKI-Certificate-Application-Policy" = @( "1.3.6.1.5.5.7.3.2","1.3.6.1.5.5.7.3.1","1.3.6.1.4.1.311.20.2.2","1.3.6.1.5.2.3.5" ) 32 | "msPKI-RA-Application-Policies" = "msPKI-Asymmetric-Algorithm``PZPWSTR``RSA``msPKI-Hash-Algorithm``PZPWSTR``SHA256``msPKI-Key-Usage``DWORD``16777215``msPKI-Symmetric-Algorithm``PZPWSTR``3DES``msPKI-Symmetric-Key-Length``DWORD``168``" 33 | } 34 | "Security" = @( 35 | @{ 36 | "IdentityReference" = "NT AUTHORITY\Authenticated Users" 37 | "ActiveDirectoryRights" = "GenericRead" 38 | "AccessControlType" = "Allow" 39 | "ObjectType" = "00000000-0000-0000-0000-000000000000" 40 | "InheritanceType" = "None" 41 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 42 | }, 43 | @{ 44 | "IdentityReference" = "CPDesk\Domain Admins" 45 | "ActiveDirectoryRights" = "CreateChild, DeleteChild, Self, WriteProperty, DeleteTree, Delete, GenericRead, WriteDacl, WriteOwner" 46 | "AccessControlType" = "Allow" 47 | "ObjectType" = "00000000-0000-0000-0000-000000000000" 48 | "InheritanceType" = "None" 49 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 50 | }, 51 | @{ 52 | "IdentityReference" = "CPDesk\Enterprise Admins" 53 | "ActiveDirectoryRights" = "CreateChild, DeleteChild, Self, WriteProperty, DeleteTree, Delete, GenericRead, WriteDacl, WriteOwner" 54 | "AccessControlType" = "Allow" 55 | "ObjectType" = "00000000-0000-0000-0000-000000000000" 56 | "InheritanceType" = "None" 57 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 58 | }, 59 | @{ 60 | "IdentityReference" = "CPDesk\Desktop Admins" 61 | "ActiveDirectoryRights" = "CreateChild, DeleteChild, Self, WriteProperty, DeleteTree, Delete, GenericRead, WriteDacl, WriteOwner" 62 | "AccessControlType" = "Allow" 63 | "ObjectType" = "00000000-0000-0000-0000-000000000000" 64 | "InheritanceType" = "None" 65 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 66 | }, 67 | @{ 68 | "IdentityReference" = "NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS" 69 | "ActiveDirectoryRights" = "ReadProperty, WriteProperty, ExtendedRight" 70 | "AccessControlType" = "Allow" 71 | "ObjectType" = "0e10c968-78fb-11d2-90d4-00c04f79dc55" 72 | "InheritanceType" = "None" 73 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 74 | }, 75 | @{ 76 | "IdentityReference" = "NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS" 77 | "ActiveDirectoryRights" = "ReadProperty, WriteProperty, ExtendedRight" 78 | "AccessControlType" = "Allow" 79 | "ObjectType" = "a05b8cc2-17bc-4802-a710-e7c15ab866a2" 80 | "InheritanceType" = "None" 81 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 82 | }, 83 | @{ 84 | "IdentityReference" = "CPDesk\Enterprise Read-only Domain Controllers" 85 | "ActiveDirectoryRights" = "ReadProperty, ExtendedRight" 86 | "AccessControlType" = "Allow" 87 | "ObjectType" = "a05b8cc2-17bc-4802-a710-e7c15ab866a2" 88 | "InheritanceType" = "None" 89 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 90 | }, 91 | @{ 92 | "IdentityReference" = "CPDesk\Enterprise Read-only Domain Controllers" 93 | "ActiveDirectoryRights" = "ReadProperty, ExtendedRight" 94 | "AccessControlType" = "Allow" 95 | "ObjectType" = "0e10c968-78fb-11d2-90d4-00c04f79dc55" 96 | "InheritanceType" = "None" 97 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 98 | }, 99 | @{ 100 | "IdentityReference" = "CPDesk\Domain Admins" 101 | "ActiveDirectoryRights" = "ReadProperty, WriteProperty, ExtendedRight" 102 | "AccessControlType" = "Allow" 103 | "ObjectType" = "0e10c968-78fb-11d2-90d4-00c04f79dc55" 104 | "InheritanceType" = "None" 105 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 106 | }, 107 | @{ 108 | "IdentityReference" = "CPDesk\Domain Controllers" 109 | "ActiveDirectoryRights" = "ReadProperty, WriteProperty, ExtendedRight" 110 | "AccessControlType" = "Allow" 111 | "ObjectType" = "a05b8cc2-17bc-4802-a710-e7c15ab866a2" 112 | "InheritanceType" = "None" 113 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 114 | }, 115 | @{ 116 | "IdentityReference" = "CPDesk\Domain Controllers" 117 | "ActiveDirectoryRights" = "ReadProperty, WriteProperty, ExtendedRight" 118 | "AccessControlType" = "Allow" 119 | "ObjectType" = "0e10c968-78fb-11d2-90d4-00c04f79dc55" 120 | "InheritanceType" = "None" 121 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 122 | }, 123 | @{ 124 | "IdentityReference" = "CPDesk\Enterprise Admins" 125 | "ActiveDirectoryRights" = "ReadProperty, WriteProperty, ExtendedRight" 126 | "AccessControlType" = "Allow" 127 | "ObjectType" = "0e10c968-78fb-11d2-90d4-00c04f79dc55" 128 | "InheritanceType" = "None" 129 | "InheritedObjectType" = "00000000-0000-0000-0000-000000000000" 130 | } 131 | ) 132 | } 133 | ) 134 | 135 | 136 | 137 | function Create-PKICertTemplate { 138 | param ( 139 | [Parameter()] 140 | [ValidateNotNullOrEmpty()] 141 | [string] 142 | $TemplateDisplayName, 143 | 144 | [Parameter()] 145 | [ValidateNotNullOrEmpty()] 146 | [hashtable] 147 | $PKIConfig, 148 | 149 | [Parameter()] 150 | [ValidateNotNullOrEmpty()] 151 | $SecurityConfig 152 | 153 | ) 154 | 155 | $CN = $TemplateDisplayName.Replace(" ","") 156 | $PKIConfig.revision = "100" 157 | 158 | $ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext 159 | $ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$($ConfigContext)" 160 | 161 | $Template = [ADSI]"LDAP://CN=$($CN),CN=Certificate Templates,CN=Public Key Services,CN=Services,$($ConfigContext)" 162 | if ([string]::IsNullOrEmpty($Template.Name)){ 163 | # create if not exists 164 | $Template = $ADSI.Create("pKICertificateTemplate", "CN=$($CN)") 165 | } 166 | 167 | $Template.Put("displayName", $TemplateDisplayName) 168 | $Template.SetInfo() 169 | 170 | foreach ($key in $PKIConfig.Keys){ 171 | $Template.Put($key, $PKIConfig[$key]) 172 | } 173 | 174 | $Template.InvokeSet("pKIKeyUsage", $PKIConfig.pKIKeyUsage) 175 | $Template.SetInfo() 176 | 177 | #ResetPerms 178 | $Template.ObjectSecurity.Access | foreach-Object {$Template.ObjectSecurity.RemoveAccessRule(($_))} 179 | #Where-Object InheritanceFlags -ne "ContainerInherit" | 180 | foreach ($Permission in $SecurityConfig) { 181 | $ID = $Permission.IdentityReference.Split("\") 182 | $ACC = [System.Security.Principal.NTAccount]::new($ID[0], $ID[1]) 183 | $IdentityReference = $ACC.Translate([System.Security.Principal.SecurityIdentifier]) 184 | $Rule = [System.DirectoryServices.ActiveDirectoryAccessRule]::New($IdentityReference,$Permission.ActiveDirectoryRights,$Permission.AccessControlType,$Permission.ObjectType,$Permission.InheritanceType,$Permission.InheritedObjectType) 185 | 186 | $Template.ObjectSecurity.AddAccessRule($Rule) 187 | $Template.commitchanges() 188 | } 189 | 190 | 191 | #Allow Computers to Enroll 192 | 193 | #$DomainName = Get-WMIObject Win32_NTDomain | Select -ExpandProperty DomainName 194 | #$Template.ObjectSecurity.GetAccessRules($true, $true, [System.Security.Principal.NTAccount]) 195 | #$ADObj = New-Object System.Security.Principal.NTAccount("$env\Domain Controllers") 196 | #$Identity = $ADObj.Translate([System.Security.Principal.SecurityIdentifier]) 197 | #$ADRights = [System.DirectoryServices.ActiveDirectoryRights]::ReadProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight 198 | #$Type = "Allow" 199 | #$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 200 | #$Template.psbase.ObjectSecurity.SetAccessRule($ACE) 201 | #$Template.psbase.commitchanges() 202 | #$P = Start-Process "C:\Windows\System32\certtmpl.msc" -PassThru 203 | #Start-Sleep 2 204 | #$P | Stop-Process 205 | ##Add-CATemplate -name "$TemplateDisplayName" -ErrorAction SilentlyContinue -Force 206 | 207 | 208 | #$ACC = [System.Security.Principal.NTAccount]::new($DomainName, "Domain Computers") 209 | #$Identity = $ACC.Translate([System.Security.Principal.SecurityIdentifier]) 210 | #$EnrollObjectType = [Guid]::Parse("0e10c968-78fb-11d2-90d4-00c04f79dc55") 211 | #$ADRights = [System.DirectoryServices.ActiveDirectoryRights]::ReadProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight 212 | #$Type = [System.Security.AccessControl.AccessControlType]::Allow 213 | #$Rule = [System.DirectoryServices.ActiveDirectoryAccessRule]::New($Identity, $ADRights, $Type, $EnrollObjectType) 214 | #$Template.ObjectSecurity.AddAccessRule($Rule) 215 | #$Template.commitchanges() 216 | } 217 | 218 | & $Main 219 | -------------------------------------------------------------------------------- /Certificates/Generate-CACertTemplateConfigs.ps1: -------------------------------------------------------------------------------- 1 | 2 | $TargetDomain = "ASD" 3 | 4 | #Need to add logic to manage which servers or groups need perms 5 | $SourceTemplates = @( 6 | "DomainControllerAuthentication(KDC)", 7 | "ConfigMgrWebServerCertificate", 8 | "ConfigMgrDistributionPointCertificate", 9 | "ConfigMgrClientCertificate" 10 | ) 11 | Start-Transcript .\settings.txt -Force | Out-Null; 12 | Write-Host ""`$Configs" = @(" 13 | foreach ($SourceTemplateCN in $SourceTemplates) { 14 | $ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext 15 | $ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" 16 | $Template = [ADSI]"LDAP://CN=$SourceTemplateCN,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" 17 | $PropertyList = $Template.Properties.PropertyNames | Where-Object {$_.StartsWith("pKI") -or $_.StartsWith("flags") -or $_.StartsWith("msPKI-")} 18 | 19 | Write-Host " @{" 20 | Write-Host " ""DisplayName"" = ""$($Template.displayName.ToString()) - Test""" 21 | Write-Host " ""Config"" = [hashtable]@{" 22 | foreach ($Property in $PropertyList) { 23 | $Value = $Template.psbase.Properties.Item($Property).Value 24 | if ($Property -eq "pKIExpirationPeriod" -or $Property -eq "pKIOverlapPeriod") { 25 | $b = $Value -join ',' 26 | Write-Host " ""$Property"" = ([Byte[]]($b))" 27 | 28 | } 29 | elseif ($Value -is [byte[]]) { 30 | $b = '"{0}"' -f ($Value -join '","') 31 | Write-Host " ""$Property"" = [Byte[]]("$($b.ToString())")" 32 | } 33 | elseif ($Value -is [Object[]]) { 34 | $b = '"{0}"' -f ($Value -join '","') 35 | Write-Host " ""$Property"" = @("$($b.ToString())")" 36 | } 37 | elseif ($Value -match '`') { 38 | $NewVal = $Value.Replace('`','``') 39 | Write-Host " ""$Property"" = ""$($NewVal.ToString())""" 40 | } 41 | else { 42 | Write-Host " ""$Property"" = ""$($Value.ToString())""" 43 | } 44 | } 45 | Write-Host " }" 46 | 47 | #GetPermissions 48 | $TemplateSecurityConfig = $Template.ObjectSecurity.Access | Select-Object IdentityReference,ActiveDirectoryRights,AccessControlType,ObjectType,InheritanceType,InheritedObjectType 49 | 50 | Write-Host " ""Security"" = @(" 51 | $Count = 0 52 | foreach ($Config in $TemplateSecurityConfig) { 53 | $Count++ 54 | Write-Host " @{" 55 | foreach ($Prop in $Config.PSObject.Properties) { 56 | $Value = $Prop.Value 57 | if ($Prop.Name.ToString() -Match "IdentityReference" -and $Value -notmatch 'NT AUTHORITY') { 58 | $Value = "{0}\{1}" -f $TargetDomain,($Value.ToString().Split("\"))[1] 59 | } 60 | Write-Host " ""$($Prop.Name.ToString())"" = ""$($Value.ToString())""" 61 | } 62 | if ($Count -eq $TemplateSecurityConfig.Count) { 63 | Write-Host " }" 64 | } 65 | else { 66 | Write-Host " }," 67 | } 68 | } 69 | Write-Host " )" 70 | Write-Host " }" 71 | } 72 | 73 | Write-Host ")" 74 | 75 | 76 | Stop-Transcript -------------------------------------------------------------------------------- /Create-ModuleManifest.ps1: -------------------------------------------------------------------------------- 1 | $Manifest = @{ 2 | Path = '.\CMHyperHydrate.psd1' 3 | RootModule = 'CMHyperHydrate.psm1' 4 | Author = 'Adam Gross (@AdamGrossTX) & Steven Hosking (@OnPremCloudGuy)' 5 | CompanyName = 'A Square Dozen' 6 | ModuleVersion = '2.0' 7 | functionsToExport = @() 8 | CmdletsToExport = @() 9 | VariablesToExport = @() 10 | AliasesToExport = @() 11 | DscResourcesToExport = @() 12 | } 13 | 14 | New-ModuleManifest @Manifest -------------------------------------------------------------------------------- /Create-NewLab.ps1: -------------------------------------------------------------------------------- 1 | 2 | Import-Module "$($PSScriptRoot)\CMHyperHydrate.psm1" -Force 3 | 4 | $ConfigFileName = "$($PSScriptRoot)\NewEnv.ASDLabTP1.json" 5 | 6 | New-LabEnv -ConfigFileName $ConfigFileName -verbose -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Adam Gross 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NewEnv.ASDLabTP1.json: -------------------------------------------------------------------------------- 1 | { 2 | "ENVToBuild" : "ASDLabTP1", 3 | "LabPath" : "D:\\Labs", 4 | "SourcePath" : "C:\\Labs", 5 | "PathSQL" : "\\Media\\ISO\\SQL", 6 | "PathSvr" : "\\Media\\ISO\\Server", 7 | "PathWin10" : "\\Media\\ISO\\Windows10", 8 | "PathADK" : "\\Media\\ADK", 9 | "PathADKWinPE" : "\\Media\\ADKWinPE", 10 | "PathConfigMgrTP" : "\\Media\\ConfigMgrTP", 11 | "PathConfigMgrCB" : "\\Media\\ConfigMgrCB", 12 | "PathPackages" : "\\Media\\Packages", 13 | "PathDrivers" : "\\Media\\Drivers", 14 | "PathRefImage" : "\\ReferenceImage", 15 | "VMDataPath" : "\\Data", 16 | "VMLogPath" : "\\Data\\Logs", 17 | "VMScriptPath" : "\\Data\\Scripts", 18 | "ServerRef" : [ 19 | { 20 | "RefID" : "2019", 21 | "RefName" : "Windows Server 2019", 22 | "RefVHDXName" : "Server2019Ref.vhdx", 23 | "RefIndex" : "2", 24 | "RefSrcType" : "ISO", 25 | "RefHVDSize" : "25gb", 26 | "RefFeature" : "NetFx3" 27 | } 28 | ], 29 | "ENVConfig" : [ 30 | { 31 | "Env" : "ASDLabTP1", 32 | "EnvSwitchName" : "ASDLabTP1", 33 | "EnvNetBios" : "ASD", 34 | "EnvFQDN" : "ASD.lab", 35 | "EnvIPSubnet" : "172.100.10.", 36 | "EnvAdminName" : "Administrator", 37 | "EnvAdminPW" : "P@ssw0rd", 38 | "EnvTimeZone" : "Central Standard Time", 39 | "InputLocale" : "0409:00000409", 40 | "SystemLocale" : "en-us", 41 | "UILanguage" : "en-us", 42 | "UILanguageFB" : "en-us", 43 | "UserLocale" : "en-us", 44 | "VLANID" : "4", 45 | "RRASName" : "ASDLabTP1-RRAS", 46 | "InternetSwitchName" : "LAB - Realtek USB", 47 | "ServerVMList": [ 48 | { 49 | "VMID" : "VM1", 50 | "VMName" : "RRAS", 51 | "VMOS" : "Server", 52 | "VMRoles" : "RRAS", 53 | "VMHDDSize" : "25gb", 54 | "VMIPLastOctet" : "1", 55 | "EnableSnapShot": "1", 56 | "StartupMemory" : "4gb", 57 | "ProcessorCount" : "1", 58 | "Generation" : "2", 59 | "AutoStartup" : "1" 60 | }, 61 | { 62 | "VMID" : "VM2", 63 | "VMName" : "DC01", 64 | "VMOS" : "Server", 65 | "VMRoles" : "DC,CA", 66 | "VMHDDSize" : "25gb", 67 | "VMIPLastOctet" : "12", 68 | "EnableSnapShot": "1", 69 | "StartupMemory" : "4gb", 70 | "ProcessorCount" : "1", 71 | "Generation" : "2", 72 | "AutoStartup" : "1" 73 | }, 74 | { 75 | "VMID" : "VM3", 76 | "VMName" : "CM01", 77 | "VMOS" : "Server", 78 | "VMRoles" : "SQL,ADK,PE,CM", 79 | "VMHDDSize" : "150gb", 80 | "VMIPLastOctet" : "13", 81 | "CMSiteCode" : "PS1", 82 | "CMVersion" : "TP", 83 | "CMPreReqPreDL" : "1", 84 | "CMPreReqPath" : "1", 85 | "EnableSnapShot": "1", 86 | "StartupMemory" : "16gb", 87 | "ProcessorCount" : "4", 88 | "Generation" : "2", 89 | "AutoStartup" : "1", 90 | "AddWSUS" : "0" 91 | } 92 | 93 | ] 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /NewEnv.Template.json: -------------------------------------------------------------------------------- 1 | { 2 | "ENVToBuild" : "ASDLab1", 3 | "LabPath" : "D:\\Labs", 4 | "SourcePath" : "C:\\Labs", 5 | "PathSQL" : "\\Media\\ISO\\SQL", 6 | "PathSvr" : "\\Media\\ISO\\Server", 7 | "PathWin10" : "\\Media\\ISO\\Windows10", 8 | "PathADK" : "\\Media\\ADK", 9 | "PathADKWinPE" : "\\Media\\ADKWinPE", 10 | "PathConfigMgrTP" : "\\Media\\ConfigMgrTP", 11 | "PathConfigMgrCB" : "\\Media\\ConfigMgrCB", 12 | "PathPackages" : "\\Media\\Packages", 13 | "PathDrivers" : "\\Media\\Drivers", 14 | "PathRefImage" : "\\ReferenceImage", 15 | "VMDataPath" : "\\Data", 16 | "VMLogPath" : "\\Data\\Logs", 17 | "VMScriptPath" : "\\Data\\Scripts", 18 | "ServerRef" : [ 19 | { 20 | "RefID" : "2019", 21 | "RefName" : "Windows Server 2019", 22 | "RefVHDXName" : "Server2019Ref.vhdx", 23 | "RefIndex" : "2", 24 | "RefSrcType" : "ISO", 25 | "RefHVDSize" : "25gb", 26 | "RefHFeature" : "NetFx3", 27 | "RefPackage" : "", 28 | "RefDriver" : "" 29 | } 30 | ], 31 | "ENVConfig" : [ 32 | { 33 | "Env" : "ASDLab1", 34 | "EnvSwitchName" : "ASDLab1", 35 | "EnvNetBios" : "ASD", 36 | "EnvFQDN" : "ASD.lab", 37 | "EnvIPSubnet" : "172.90.10.", 38 | "EnvAdminName" : "Administrator", 39 | "EnvAdminPW" : "P@ssw0rd", 40 | "EnvTimeZone" : "Central Standard Time", 41 | "InputLocale" : "0409:00000409", 42 | "SystemLocale" : "en-us", 43 | "UILanguage" : "en-us", 44 | "UILanguageFB" : "en-us", 45 | "UserLocale" : "en-us", 46 | "VLANID" : "4", 47 | "RRASName" : "ASDLab1-RRAS", 48 | "InternetSwitchName" : "LAB - Realtek USB", 49 | "ServerVMList": [ 50 | { 51 | "VMID" : "VM1", 52 | "VMName" : "RRAS", 53 | "VMOS" : "Server", 54 | "VMRoles" : "RRAS", 55 | "VMHDDSize" : "25gb", 56 | "VMIPLastOctet" : "1", 57 | "EnableSnapShot": "1", 58 | "StartupMemory" : "4gb", 59 | "ProcessorCount" : "1", 60 | "Generation" : "2", 61 | "AutoStartup" : "1" 62 | }, 63 | { 64 | "VMID" : "VM2", 65 | "VMName" : "DC01", 66 | "VMOS" : "Server", 67 | "VMRoles" : "DC", 68 | "VMHDDSize" : "25gb", 69 | "VMIPLastOctet" : "10", 70 | "EnableSnapShot": "1", 71 | "StartupMemory" : "4gb", 72 | "ProcessorCount" : "1", 73 | "Generation" : "2", 74 | "AutoStartup" : "1" 75 | }, 76 | { 77 | "VMID" : "VM3", 78 | "VMName" : "CA01", 79 | "VMOS" : "Server", 80 | "VMRoles" : "CA", 81 | "VMHDDSize" : "25gb", 82 | "VMIPLastOctet" : "11", 83 | "EnableSnapShot": "1", 84 | "StartupMemory" : "4gb", 85 | "ProcessorCount" : "1", 86 | "Generation" : "2", 87 | "AutoStartup" : "1" 88 | }, 89 | { 90 | "VMID" : "VM4", 91 | "VMName" : "CM01", 92 | "VMOS" : "Server", 93 | "VMRoles" : "SQL,ADK,PE,CM", 94 | "VMHDDSize" : "150gb", 95 | "VMIPLastOctet" : "12", 96 | "CMSiteCode" : "PS1", 97 | "CMVersion" : "Prod", 98 | "CMPreReqPreDL" : "1", 99 | "CMPreReqPath" : "1", 100 | "EnableSnapShot": "1", 101 | "StartupMemory" : "16gb", 102 | "ProcessorCount" : "4", 103 | "Generation" : "2", 104 | "AutoStartup" : "1", 105 | "AddWSUS" : "0" 106 | } 107 | ] 108 | } 109 | ] 110 | } -------------------------------------------------------------------------------- /Private/Invoke-LabCommand.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-LabCommand { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [string] 7 | $MessageText, 8 | 9 | [Parameter()] 10 | [scriptblock] 11 | $ScriptBlock, 12 | 13 | [Parameter()] 14 | [string] 15 | $FilePath, 16 | 17 | [Parameter()] 18 | [Guid] 19 | $VMId, 20 | 21 | [Parameter()] 22 | [System.Object[]] 23 | $ArgumentList, 24 | 25 | [parameter()] 26 | [ValidateSet('Local','Domain')] 27 | [string] 28 | $SessionType = 'Domain', 29 | 30 | [Parameter()] 31 | [ValidateNotNullOrEmpty()] 32 | [pscredential] 33 | $LocalAdminCreds = $Script:LocalAdminCreds, 34 | 35 | [Parameter()] 36 | [ValidateNotNullOrEmpty()] 37 | [pscredential] 38 | $DomainAdminCreds = $Script:DomainAdminCreds 39 | 40 | ) 41 | 42 | Write-Host "##########################" | out-null 43 | Write-Host "Started - $($MessageText)" | out-null 44 | $Result = $Null 45 | 46 | if (Test-LabConnection -Type $SessionType -VMId $VMId) { 47 | $Creds = Switch($SessionType) { 48 | "Local" {$LocalAdminCreds;break;} 49 | "Domain" {$DomainAdminCreds;break;} 50 | default {break;} 51 | } 52 | 53 | Write-Host "Invoking Remote Command" | out-null 54 | try { 55 | if ($ScriptBlock) { 56 | $Result = Invoke-Command -VMID $VMID -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList -Credential $Creds 57 | } 58 | else { 59 | $Result = Invoke-Command -VMId $VMID -Credential $Creds -FilePath $FilePath 60 | } 61 | } 62 | catch { 63 | Write-Warning $_.Exception.Message | out-null 64 | break; 65 | } 66 | 67 | Write-Host "Remote Command Completed" | out-null 68 | } 69 | else { 70 | $Result = "Error" 71 | } 72 | Write-Host "Completed - $($MessageText)" | out-null 73 | return $Result 74 | } 75 | -------------------------------------------------------------------------------- /Private/New-LabCMSQLSettingsINI.ps1: -------------------------------------------------------------------------------- 1 | function New-LabCMSQLSettingsINI { 2 | param ( 3 | [Parameter()] 4 | [ValidateNotNullOrEmpty()] 5 | [string] 6 | $DomainNetBiosName, 7 | 8 | [Parameter()] 9 | [ValidateNotNullOrEmpty()] 10 | [string] 11 | $UserName, 12 | 13 | [Parameter()] 14 | [ValidateNotNullOrEmpty()] 15 | [string] 16 | $Password 17 | ) 18 | 19 | $SQLHash = @{'ACTION' = '"Install"'; 20 | 'SUPPRESSPRIVACYSTATEMENTNOTICE' = '"TRUE"'; 21 | 'IACCEPTROPENLICENSETERMS' = '"TRUE"'; 22 | 'ENU' = '"TRUE"'; 23 | 'QUIET' = '"TRUE"'; 24 | 'UpdateEnabled' = '"TRUE"'; 25 | 'USEMICROSOFTUPDATE' = '"TRUE"'; 26 | 'FEATURES' = 'SQLENGINE,RS'; 27 | 'UpdateSource' = '"MU"'; 28 | 'HELP' = '"FALSE"'; 29 | 'INDICATEPROGRESS' = '"FALSE"'; 30 | 'X86' = '"FALSE"'; 31 | 'INSTANCENAME' = '"MSSQLSERVER"'; 32 | 'INSTALLSHAREDDIR' = '"C:\Program Files\Microsoft SQL Server"'; 33 | 'INSTALLSHAREDWOWDIR' = '"C:\Program Files (x86)\Microsoft SQL Server"'; 34 | 'INSTANCEID' = '"MSSQLSERVER"'; 35 | 'RSINSTALLMODE' = '"DefaultNativeMode"'; 36 | 'SQLTELSVCACCT' = '"NT Service\SQLTELEMETRY"'; 37 | 'SQLTELSVCSTARTUPTYPE' = '"Automatic"'; 38 | 'INSTANCEDIR' = '"C:\Program Files\Microsoft SQL Server"'; 39 | 'AGTSVCACCOUNT' = '"NT Service\SQLSERVERAGENT"'; 40 | 'AGTSVCSTARTUPTYPE' = '"Manual"'; 41 | 'COMMFABRICPORT' = '"0"'; 42 | 'COMMFABRICNETWORKLEVEL' = '"0"'; 43 | 'COMMFABRICENCRYPTION' = '"0"'; 44 | 'MATRIXCMBRICKCOMMPORT' = '"0"'; 45 | 'SQLSVCSTARTUPTYPE' = '"Automatic"'; 46 | 'FILESTREAMLEVEL' = '"0"'; 47 | 'ENABLERANU' = '"FALSE"'; 48 | 'SQLCOLLATION' = '"SQL_Latin1_General_CP1_CI_AS"'; 49 | 'SQLSVCACCOUNT' = """$($UserName)"""; 50 | 'SQLSVCPASSWORD' = """$($Password)""" 51 | 'SQLSVCINSTANTFILEINIT' = '"FALSE"'; 52 | 'SQLSYSADMINACCOUNTS' = """$($UserName)"" ""$($DomainNetBiosName)\Domain Users"""; 53 | 'SQLTEMPDBFILECOUNT' = '"1"'; 54 | 'SQLTEMPDBFILESIZE' = '"8"'; 55 | 'SQLTEMPDBFILEGROWTH' = '"64"'; 56 | 'SQLTEMPDBLOGFILESIZE' = '"8"'; 57 | 'SQLTEMPDBLOGFILEGROWTH' = '"64"'; 58 | 'ADDCURRENTUSERASSQLADMIN' = '"FALSE"'; 59 | 'TCPENABLED' = '"1"'; 60 | 'NPENABLED' = '"1"'; 61 | 'BROWSERSVCSTARTUPTYPE' = '"Disabled"'; 62 | 'RSSVCACCOUNT' = '"NT Service\ReportServer"'; 63 | 'RSSVCSTARTUPTYPE' = '"Automatic"'; 64 | } 65 | $SQLHASHINI = @{'OPTIONS' = $SQLHash} 66 | $SQLInstallINI = "" 67 | foreach ($i in $SQLHASHINI.keys) { 68 | $SQLInstallINI += "[$i]`r`n" 69 | foreach ($j in $($SQLHASHINI[$i].keys | Sort-Object)) { 70 | $SQLInstallINI += "$j=$($SQLHASHINI[$i][$j])`r`n" 71 | } 72 | $SQLInstallINI += "`r`n" 73 | } 74 | return $SQLInstallINI 75 | } -------------------------------------------------------------------------------- /Private/New-LabUnattendXML.ps1: -------------------------------------------------------------------------------- 1 | function New-LabUnattendXML { 2 | [cmdletbinding()] 3 | param ( 4 | [Parameter()] 5 | [hashtable] 6 | $BaseConfig, 7 | 8 | [Parameter()] 9 | [hashtable] 10 | $LabEnvConfig, 11 | 12 | [Parameter()] 13 | [ValidateNotNullOrEmpty()] 14 | [string] 15 | $AdministratorPassword = $LabEnvConfig.EnvAdminPW, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $TimeZone = $LabEnvConfig.EnvTimeZone, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $InputLocale = $LabEnvConfig.InputLocale, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [string] 30 | $SystemLocale = $LabEnvConfig.SystemLocale, 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [string] 35 | $UILanguage = $LabEnvConfig.UILanguage, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $UILanguageFallback = $LabEnvConfig.UILanguageFB, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $UserLocale = $LabEnvConfig.UserLocale, 46 | 47 | [Parameter()] 48 | [ValidateNotNullOrEmpty()] 49 | [string] 50 | $OutputFile = "Unattend.XML", 51 | 52 | [Parameter()] 53 | [ValidateNotNullOrEmpty()] 54 | [string] 55 | $OutputPath = $BaseConfig.VMPath 56 | 57 | ) 58 | 59 | New-Item -Path $OutputPath -ItemType Directory -Force 60 | 61 | $unattendTemplate = [xml]@" 62 | 63 | 64 | 65 | 66 | 67 | 68 | $($AdministratorPassword) 69 | True</PlainText> 70 | </AdministratorPassword> 71 | </UserAccounts> 72 | <OOBE> 73 | <VMModeOptimizations> 74 | <SkipNotifyUILanguageChange>true</SkipNotifyUILanguageChange> 75 | <SkipWinREInitialization>true</SkipWinREInitialization> 76 | </VMModeOptimizations> 77 | <SkipMachineOOBE>true</SkipMachineOOBE> 78 | <HideEULAPage>true</HideEULAPage> 79 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 80 | <ProtectYourPC>3</ProtectYourPC> 81 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 82 | <NetworkLocation>Work</NetworkLocation> 83 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 84 | </OOBE> 85 | <Timezone>$($TimeZone)</Timezone> 86 | </component> 87 | <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"> 88 | <InputLocale>$($InputLocale)</InputLocale> 89 | <SystemLocale>$($SystemLocale)</SystemLocale> 90 | <UILanguage>$($UILanguage)</UILanguage> 91 | <UILanguageFallback>$($UILanguageFallback)</UILanguageFallback> 92 | <UserLocale>$($UserLocale)</UserLocale> 93 | </component> 94 | </settings> 95 | </unattend> 96 | "@ 97 | $unattendTemplate.Save("$($OutputPath)\$($OutputFile)") 98 | } -------------------------------------------------------------------------------- /Private/Test-LabConnection.ps1: -------------------------------------------------------------------------------- 1 | #https://blogs.technet.microsoft.com/virtualization/2016/10/11/waiting-for-vms-to-restart-in-a-complex-configuration-script-with-powershell-direct/ 2 | function Test-LabConnection { 3 | [cmdletbinding()] 4 | param ( 5 | [parameter()] 6 | [ValidateSet('Local','Domain')] 7 | [string] 8 | $Type = 'Domain', 9 | 10 | [Parameter()] 11 | [Guid] 12 | $VMId, 13 | 14 | [Parameter()] 15 | [ValidateNotNullOrEmpty()] 16 | [pscredential] 17 | $LocalAdminCreds = $Script:LocalAdminCreds, 18 | 19 | [Parameter()] 20 | [ValidateNotNullOrEmpty()] 21 | [pscredential] 22 | $DomainAdminCreds = $Script:DomainAdminCreds 23 | ) 24 | 25 | $Password = ConvertTo-SecureString -String $LabEnvConfig.EnvAdminPW -AsPlainText -Force 26 | $LocalAdminCreds = new-object -typename System.Management.Automation.PSCredential($BaseConfig.LocalAdminName, $Password) 27 | 28 | $VM = Get-VM -Id $VMId 29 | 30 | $Creds = Switch($Type) { 31 | "Local" {$LocalAdminCreds; break;} 32 | "Domain" {$DomainAdminCreds; break;} 33 | default {break;} 34 | } 35 | 36 | try { 37 | if ($VM.State -eq "Off") { 38 | write-Host "VM Not Running. Starting VM." 39 | $VM | Start-VM -WarningAction SilentlyContinue 40 | } 41 | 42 | # Wait for the VM's heartbeat integration component to come up if it is enabled 43 | $heartbeatic = (Get-VMIntegrationService -VM $VM | Where-Object Id -match "84EAAE65-2F2E-45F5-9BB5-0E857DC8EB47") 44 | if ($heartbeatic -and ($heartbeatic.Enabled -eq $true)) { 45 | $startTime = Get-Date 46 | do { 47 | $timeElapsed = $(Get-Date) - $startTime 48 | if ($($timeElapsed).TotalMinutes -ge 10) { 49 | Write-Host "Integration components did not come up after 10 minutes" -MessageType Error 50 | throw 51 | } 52 | Start-Sleep -sec 1 53 | } 54 | until ($heartbeatic.PrimaryStatusDescription -eq "OK") 55 | Write-Host "Heartbeat IC connected." 56 | } 57 | do { 58 | Write-Host "Testing $($Type) Connection." 59 | 60 | $timeElapsed = $(Get-Date) - $startTime 61 | if ($($timeElapsed).TotalMinutes -ge 10) { 62 | Write-Host "Could not connect to PS Direct after 10 minutes" 63 | throw 64 | } 65 | Start-Sleep -sec 1 66 | $psReady = Invoke-Command -VMId $VM.VMId -Credential $Creds -ErrorAction SilentlyContinue -ScriptBlock { $True } 67 | if ($Type -eq 'Domain') { 68 | Invoke-Command -VMId $VM.VMId -Credential $Creds -ErrorAction SilentlyContinue -ScriptBlock { 69 | do { 70 | Write-Host "." -NoNewline -ForegroundColor Gray 71 | Start-Sleep -Seconds 5 72 | # query AD for the local computer 73 | #Get-ADComputer $env:COMPUTERNAME | Out-Null 74 | Get-WMIObject Win32_ComputerSystem | Out-Null 75 | } until ($?) # exits the loop if last call was successful 76 | Write-Host "succeeded." 77 | } 78 | } 79 | } 80 | until ($psReady) 81 | } 82 | 83 | catch { 84 | Write-Warning $_.Exception.Message 85 | break; 86 | } 87 | 88 | return $psReady 89 | } -------------------------------------------------------------------------------- /Public/Add-LabAdditionalApps.ps1: -------------------------------------------------------------------------------- 1 | function Add-LabAdditionalApps { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $ClientDriveRoot = "c:", 26 | 27 | [Parameter()] 28 | [ValidateSet("sql-server-management-studio","vscode","snagit","postman")] 29 | [string[]] 30 | $AppList, 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [pscredential] 35 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $ScriptPath = $BaseConfig.VMScriptPath, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $LogPath = $BaseConfig.VMLogPath, 46 | 47 | [Parameter()] 48 | [ValidateNotNullOrEmpty()] 49 | [string] 50 | $LabPath = $BaseConfig.LabPath 51 | 52 | ) 53 | 54 | #region Standard Setup 55 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 56 | $ClientScriptPath = "$($ClientDriveRoot)$($ScriptPath)" 57 | 58 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 59 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 60 | } 61 | 62 | $LogPath = "C:$($LogPath)" 63 | $SBDefaultParams = @" 64 | param 65 | ( 66 | `$_LogPath = "$($LogPath)" 67 | ) 68 | "@ 69 | 70 | $SBScriptTemplateBegin = { 71 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 72 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 73 | } 74 | 75 | $_LogFile = "$($_LogPath)\Transcript.log"; 76 | 77 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 78 | Write-Output "Logging to $_LogFile"; 79 | 80 | #region Do Stuff Here 81 | } 82 | 83 | $SBScriptTemplateEnd = { 84 | #endregion 85 | Stop-Transcript 86 | } 87 | 88 | #endregion 89 | 90 | $_AppList = $AppList -join '","' 91 | $SBDefaultParams = @" 92 | param 93 | ( 94 | `$_LogPath = "$($LogPath)", 95 | `$_AppList = @("$($_AppList)") 96 | ) 97 | "@ 98 | 99 | #region Script Blocks 100 | $SBInstallAdditionalApps = { 101 | Set-ExecutionPolicy Bypass -Scope Process -Force; 102 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 103 | choco upgrade chocolatey -y -f 104 | ForEach($app in $_AppList) { 105 | choco install "$($app)" -y -f 106 | } 107 | } 108 | 109 | $InstallAdditionalApps += $SBDefaultParams 110 | $InstallAdditionalApps += $SBScriptTemplateBegin.ToString() 111 | $InstallAdditionalApps += $SBInstallAdditionalApps.ToString() 112 | $InstallAdditionalApps += $SBScriptTemplateEnd.ToString() 113 | $InstallAdditionalApps | Out-File "$($LabScriptPath)\InstallAdditionalApps.ps1" 114 | 115 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 116 | 117 | #endregion 118 | 119 | $VM = Get-VM -VM $VMName 120 | $VM | Start-VM -WarningAction SilentlyContinue 121 | start-sleep 10 122 | 123 | while (-not (Invoke-Command -VMName $VMName -Credential $DomainAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 124 | 125 | foreach ($Script in $Scripts) { 126 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "$($ClientScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 127 | } 128 | 129 | Invoke-LabCommand -FilePath "$($LabScriptPath)\InstallAdditionalApps.ps1" -MessageText "InstallAdditionalApps" -SessionType Domain -VMID $VM.VMId 130 | 131 | Write-Host "Install Additional Apps Complete!" 132 | 133 | } -------------------------------------------------------------------------------- /Public/Add-LabRoleCA.ps1: -------------------------------------------------------------------------------- 1 | function Add-LabRoleCA { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $DomainFQDN = $LabEnvConfig.EnvFQDN, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [pscredential] 30 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [string] 35 | $ScriptPath = $BaseConfig.VMScriptPath, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $LogPath = $BaseConfig.VMLogPath, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $LabPath = $BaseConfig.LabPath 46 | 47 | ) 48 | 49 | #region Standard Setup 50 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 51 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 52 | $ClientScriptPath = "C:$($ScriptPath)" 53 | 54 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 55 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 56 | } 57 | 58 | $LogPath = "C:$($LogPath)" 59 | $SBDefaultParams = @" 60 | param 61 | ( 62 | `$_LogPath = "$($LogPath)" 63 | ) 64 | "@ 65 | 66 | $SBScriptTemplateBegin = { 67 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 68 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 69 | } 70 | 71 | $_LogFile = "$($_LogPath)\Transcript.log"; 72 | 73 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 74 | Write-Output "Logging to $_LogFile"; 75 | 76 | #region Do Stuff Here 77 | } 78 | 79 | $SBScriptTemplateEnd = { 80 | #endregion 81 | Stop-Transcript 82 | } 83 | 84 | #endregion 85 | 86 | #region Script Blocks 87 | $SBInstallCA = { 88 | Add-WindowsFeature -Name Adcs-Cert-Authority -IncludeManagementTools; 89 | Install-AdcsCertificationAuthority -CAType EnterpriseRootCA -KeyLength 2048 -HashAlgorithm SHA1 -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" -ValidityPeriod Years -ValidityPeriodUnits 5 -Force -confirm:$false 90 | } 91 | 92 | $InstallCA += $SBDefaultParams 93 | $InstallCA += $SBScriptTemplateBegin.ToString() 94 | $InstallCA += $SBInstallCA.ToString() 95 | $InstallCA += $SBScriptTemplateEnd.ToString() 96 | $InstallCA | Out-File "$($LabScriptPath)\InstallCA.ps1" 97 | 98 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 99 | 100 | #endregion 101 | 102 | $VM = Get-VM -VM $VMName 103 | $VM | Start-VM -WarningAction SilentlyContinue 104 | start-sleep 10 105 | 106 | while (-not (Invoke-Command -VMName $VMName -Credential $DomainAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 107 | 108 | foreach ($Script in $Scripts) { 109 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "C:$($ScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 110 | } 111 | 112 | Invoke-LabCommand -FilePath "$($LabScriptPath)\InstallCA.ps1" -MessageText "InstallCA" -SessionType Domain -VMID $VM.VMId 113 | 114 | #TODO Create-PKICertTemplate 115 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 116 | } -------------------------------------------------------------------------------- /Public/Add-LabRoleCM.ps1: -------------------------------------------------------------------------------- 1 | function Add-LabRoleCM { 2 | [cmdletBinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $VMWinName = $VMConfig.VMWinName, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [string] 30 | $VMPath = ("$($VMConfig.VMHDPath)\$($VMConfig.VMHDName)"), 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [string] 35 | $VMDataPath = $BaseConfig.VMDataPath, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $ADKMediaPath = $BaseConfig.PathADK, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $ADKWinPEMediaPath = $BaseConfig.PathADKWinPE, 46 | 47 | [Parameter()] 48 | [ValidateNotNullOrEmpty()] 49 | [object[]] 50 | $PackageMediaPath = $BaseConfig.Packages, 51 | 52 | [Parameter()] 53 | [ValidateNotNullOrEmpty()] 54 | [string] 55 | $ClientDriveRoot = "c:", 56 | 57 | [Parameter()] 58 | [ValidateNotNullOrEmpty()] 59 | [string] 60 | $DomainNetBiosName = $LabEnvConfig.EnvNetBios, 61 | 62 | [Parameter()] 63 | [ValidateNotNullOrEmpty()] 64 | [string] 65 | $DomainFQDN = $LabEnvConfig.EnvFQDN, 66 | 67 | [Parameter()] 68 | [ValidateNotNullOrEmpty()] 69 | [string] 70 | $ConfigMgrSiteCode = $VMConfig.CMSiteCode, 71 | 72 | [Parameter()] 73 | [ValidateNotNullOrEmpty()] 74 | [ValidateSet("Prod","TP")] 75 | [string] 76 | $ConfigMgrVersion = $VMConfig.CMVersion, 77 | 78 | [Parameter()] 79 | [int] 80 | $ConfigMgrPrereqsPreDL = $VMConfig.CMPreReqPreDL, 81 | 82 | [Parameter()] 83 | [ValidateNotNullOrEmpty()] 84 | [pscredential] 85 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 86 | 87 | [Parameter()] 88 | [ValidateNotNullOrEmpty()] 89 | [string] 90 | $ScriptPath = $BaseConfig.VMScriptPath, 91 | 92 | [Parameter()] 93 | [ValidateNotNullOrEmpty()] 94 | [string] 95 | $LogPath = $BaseConfig.VMLogPath, 96 | 97 | [Parameter()] 98 | [ValidateNotNullOrEmpty()] 99 | [string] 100 | $LabPath = $BaseConfig.LabPath 101 | 102 | ) 103 | 104 | #region Standard Setup 105 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 106 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 107 | $ClientScriptPath = "$($ClientDriveRoot)$($ScriptPath)" 108 | 109 | $ConfigMgrMediaPath = Switch($VMConfig.CMVersion) {"TP" {$BaseConfig.PathConfigMgrTP; break;} "Prod" {$BaseConfig.PathConfigMgrCB; break;}} 110 | 111 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 112 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 113 | } 114 | 115 | $LogPath = "$($ClientDriveRoot)$($LogPath)" 116 | $SBDefaultParams = @" 117 | param 118 | ( 119 | `$_LogPath = "$($LogPath)" 120 | ) 121 | "@ 122 | 123 | $SBScriptTemplateBegin = { 124 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 125 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 126 | } 127 | 128 | $_LogFile = "$($_LogPath)\Transcript.log"; 129 | 130 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 131 | Write-Output "Logging to $_LogFile"; 132 | 133 | #region Do Stuff Here 134 | } 135 | 136 | $SBScriptTemplateEnd = { 137 | #endregion 138 | Stop-Transcript | Out-Null 139 | } 140 | 141 | #endregion 142 | 143 | #region Copy media to VM 144 | $VM = Get-VM -Name $VMName 145 | if ((Get-VM -Name $VMName).State -eq "Running") { 146 | Stop-VM -Name $VMName -WarningAction SilentlyContinue 147 | } 148 | 149 | $Disk = (Mount-VHD -Path $VMPath -Passthru | Get-Disk | Get-Partition | Where-Object {$_.type -eq 'Basic'}).DriveLetter 150 | $ConfigMgrPath = "$($disk):$($VMDataPath)\ConfigMgr" 151 | $ConfigMgrPrereqsPath = "$($disk):$($VMDataPath)\ConfigMgr\Prereqs" 152 | $ADKPath = "$($disk):$($VMDataPath)\adk" 153 | $ADKWinPEPath = "$($disk):$($VMDataPath)\adkwinpe" 154 | $PackagePath = "$($disk):$($VMDataPath)\Packages" 155 | 156 | Copy-Item -Path $ConfigMgrMediaPath -Destination $ConfigMgrPath -Recurse -ErrorAction SilentlyContinue 157 | Copy-Item -Path $ADKMediaPath -Destination $ADKPath -Recurse -ErrorAction SilentlyContinue 158 | Copy-Item -Path $ADKWinPEMediaPath -Destination $ADKWinPEPath -Recurse -ErrorAction SilentlyContinue 159 | New-Item -Path $PackagePath -ItemType Directory -Force 160 | Copy-Item -Path $PackageMediaPath -Destination $PackagePath -Recurse -ErrorAction SilentlyContinue 161 | Dismount-VHD $VMPath 162 | #endregion 163 | 164 | $ConfigMgrPath = $ConfigMgrPath -replace "$($disk):", "$($ClientDriveRoot)" 165 | $ConfigMgrPrereqsPath = $ConfigMgrPrereqsPath -replace "$($disk):", "$($ClientDriveRoot)" 166 | $ADKPath = $ADKPath -replace "$($disk):", "$($ClientDriveRoot)" 167 | $ADKWinPEPath = $ADKWinPEPath -replace "$($disk):", "$($ClientDriveRoot)" 168 | $PackagePath = $PackagePath -replace "$($disk):", "$($ClientDriveRoot)" 169 | 170 | #region Script Blocks 171 | $SBInstallSQLNativeClientParams = @" 172 | param 173 | ( 174 | `$_LogPath = "$($LogPath)", 175 | `$_ConfigMgrPath = "$($ConfigMgrPath)" 176 | ) 177 | "@ 178 | 179 | $SBInstallSQLNativeClient = { 180 | start-process -FilePath "C:\windows\system32\msiexec.exe" -ArgumentList "/I $($_ConfigMgrPath)\Prereqs\sqlncli.msi /QN REBOOT=ReallySuppress /l*v $($ConfigMgrPath)\SQLLog.log" -Wait 181 | } 182 | 183 | $SBGetSQLSettings = { 184 | Start-Service MSSQLSERVER -ErrorAction SilentlyContinue | Out-Null 185 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null 186 | $Settings = (new-object ('Microsoft.SqlServer.Management.Smo.Server') $env:COMPUTERNAME).Settings | Select-Object DefaultFile, Defaultlog 187 | } 188 | 189 | $SBAddFeaturesParams = @" 190 | param 191 | ( 192 | `$_LogPath = "$($LogPath)", 193 | `$_PackagePath = "$($PackagePath)" 194 | ) 195 | "@ 196 | 197 | $SBAddFeatures = { 198 | $FeatureList = @("BITS","BITS-IIS-Ext","BITS-Compact-Server","Web-Server","Web-WebServer","Web-Common-Http","Web-Default-Doc","Web-Dir-Browsing","Web-Http-Errors","Web-Static-Content","Web-Http-Redirect","Web-App-Dev","Web-Net-Ext45","Web-ASP","Web-Asp-Net","Web-Asp-Net45","Web-CGI","Web-ISAPI-Ext","Web-ISAPI-Filter","Web-Health","Web-Http-Logging","Web-Custom-Logging","Web-Log-Libraries","Web-Request-Monitor","Web-Http-Tracing","Web-Performance","Web-Stat-Compression","Web-Security","Web-Filtering","Web-Basic-Auth","Web-IP-Security","Web-Url-Auth","Web-Windows-Auth","Web-Mgmt-Tools","Web-Mgmt-Console","Web-Mgmt-Compat","Web-Metabase","Web-Lgcy-Mgmt-Console","Web-Lgcy-Scripting","Web-WMI","Web-Scripting-Tools","Web-Mgmt-Service","RDC") 199 | foreach ($Feature in $FeatureList) { 200 | Write-Output "Adding Feature: $($Feature)" 201 | Add-WindowsFeature -Name $Feature -Source $_PackagePath 202 | } 203 | } 204 | 205 | $SBADKParams = @" 206 | param 207 | ( 208 | `$_LogPath = "$($LogPath)", 209 | `$_ADKPath = "$($ADKPath)" 210 | ) 211 | "@ 212 | $SBADK = { 213 | $_SetupSwitches = "/Features OptionId.DeploymentTools OptionId.ImagingAndConfigurationDesigner OptionId.ICDConfigurationDesigner OptionId.UserStateMigrationTool /norestart /quiet /ceip off" 214 | Start-Process -FilePath "$($_ADKPath)\adksetup.exe" -ArgumentList $_SetupSwitches -NoNewWindow -Wait 215 | } 216 | 217 | $SBWinPEADKParams = @" 218 | param 219 | ( 220 | `$_LogPath = "$($LogPath)", 221 | `$_ADKWinPEPath = "$($ADKWinPEPath)" 222 | ) 223 | "@ 224 | $SBWinPEADK = { 225 | $_SetupSwitches = "/Features OptionId.WindowsPreinstallationEnvironment /norestart /quiet /ceip off" 226 | Start-Process -FilePath "$($_ADKWinPEPath)\adkwinpesetup.exe" -ArgumentList $_SetupSwitches -NoNewWindow -Wait 227 | Restart-Computer -Force 228 | } 229 | 230 | $SBExtSchemaParams = @" 231 | param 232 | ( 233 | `$_LogPath = "$($LogPath)", 234 | `$_ConfigMgrPath = "$($ConfigMgrPath)" 235 | ) 236 | "@ 237 | $SBExtSchema = { 238 | Start-Process -FilePath "$($_ConfigMgrPath)\SMSSETUP\bin\x64\extadsch.exe" -wait 239 | } 240 | 241 | $SBInstallCMParams = @" 242 | param 243 | ( 244 | `$_LogPath = "$($LogPath)", 245 | `$_ConfigMgrPath = "$($ConfigMgrPath)", 246 | `$_ClientScriptPath = "$($ClientScriptPath)" 247 | ) 248 | "@ 249 | $SBInstallCM = { 250 | $SQLService = Get-Service -Name MSSQLSERVER -ErrorAction SilentlyContinue 251 | if ($SQLService) { 252 | $SQLService | Where-Object {$_.Status -ne 'Running'} | Start-Service 253 | Start-Process -FilePath "$($_ConfigMgrPath)\SMSSETUP\bin\x64\setup.exe" -ArgumentList "/script $($_ClientScriptPath)\CMinstall.ini" -Wait 254 | } 255 | else { 256 | Write-Output "SQL Server Not Running" 257 | } 258 | } 259 | 260 | $SBCMExtrasParams = @" 261 | param 262 | ( 263 | `$_LogPath = "$($LogPath)", 264 | `$_ConfigMgrSiteCode = "$($ConfigMgrSiteCode)", 265 | `$_DomainFQDN = "$($DomainFQDN)" 266 | ) 267 | "@ 268 | $SBCMExtras = { 269 | import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; 270 | if ($null -eq (Get-PSDrive -Name $_ConfigMgrSiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) {New-PSDrive -Name $_ConfigMgrSiteCode -PSProvider CMSite -Root $env:COMPUTERNAME} 271 | Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:"; 272 | #New-CMBoundary -Type IPSubnet -Value "$($ipsub)/24" -name $Subnetname; 273 | New-CMBoundary -DisplayName "DefaultBoundary" -BoundaryType ADSite -Value "Default-First-Site-Name" 274 | New-CMBoundaryGroup -name "DefaultBoundary" -DefaultSiteCode "$((Get-PSDrive -PSProvider CMSite).name)"; 275 | Add-CMBoundaryToGroup -BoundaryName "DefaultBoundary" -BoundaryGroupName "DefaultBoundary"; 276 | $_Schedule = New-CMSchedule -RecurInterval Minutes -Start "2012/10/20 00:00:00" -End "2013/10/20 00:00:00" -RecurCount 10; 277 | Set-CMDiscoveryMethod -ActiveDirectorySystemDiscovery -SiteCode $_ConfigMgrSiteCode -Enabled $True -EnableDeltaDiscovery $True -PollingSchedule $_Schedule -AddActiveDirectoryContainer "LDAP://$_DomainFQDN" -Recursive; 278 | Get-CMDevice | Where-Object {$_.ADSiteName -eq "Default-First-Site-Name"} | Install-CMClient -IncludeDomainController $true -AlwaysInstallClient $true -SiteCode $_ConfigMgrSiteCode; 279 | } 280 | 281 | 282 | $InstallSQLNativeClient += $SBInstallSQLNativeClientParams 283 | $InstallSQLNativeClient += $SBScriptTemplateBegin.ToString() 284 | $InstallSQLNativeClient += $SBInstallSQLNativeClient.ToString() 285 | $InstallSQLNativeClient += $SBScriptTemplateEnd.ToString() 286 | $InstallSQLNativeClient | Out-File "$($LabScriptPath)\InstallSQLNativeClient.ps1" 287 | 288 | $GetSQLSettings += $SBDefaultParams 289 | $GetSQLSettings += $SBScriptTemplateBegin.ToString() 290 | $GetSQLSettings += $SBGetSQLSettings.ToString() 291 | $GetSQLSettings += $SBScriptTemplateEnd.ToString() 292 | $GetSQLSettings += 'Return $Settings' 293 | $GetSQLSettings | Out-File "$($LabScriptPath)\GetSQLSettings.ps1" 294 | 295 | $AddFeatures += $SBAddFeaturesParams 296 | $AddFeatures += $SBScriptTemplateBegin.ToString() 297 | $AddFeatures += $SBAddFeatures.ToString() 298 | $AddFeatures += $SBScriptTemplateEnd.ToString() 299 | $AddFeatures | Out-File "$($LabScriptPath)\AddFeatures.ps1" 300 | 301 | $ADK += $SBADKParams 302 | $ADK += $SBScriptTemplateBegin.ToString() 303 | $ADK += $SBADK.ToString() 304 | $ADK += $SBScriptTemplateEnd.ToString() 305 | $ADK | Out-File "$($LabScriptPath)\ADK.ps1" 306 | 307 | $WinPEADK += $SBWinPEADKParams 308 | $WinPEADK += $SBScriptTemplateBegin.ToString() 309 | $WinPEADK += $SBWinPEADK.ToString() 310 | $WinPEADK += $SBScriptTemplateEnd.ToString() 311 | $WinPEADK | Out-File "$($LabScriptPath)\WinPEADK.ps1" 312 | 313 | $ExtSchema += $SBExtSchemaParams 314 | $ExtSchema += $SBScriptTemplateBegin.ToString() 315 | $ExtSchema += $SBExtSchema.ToString() 316 | $ExtSchema += $SBScriptTemplateEnd.ToString() 317 | $ExtSchema | Out-File "$($LabScriptPath)\ExtSchema.ps1" 318 | 319 | $InstallCM += $SBInstallCMParams 320 | $InstallCM += $SBScriptTemplateBegin.ToString() 321 | $InstallCM += $SBInstallCM.ToString() 322 | $InstallCM += $SBScriptTemplateEnd.ToString() 323 | $InstallCM | Out-File "$($LabScriptPath)\InstallCM.ps1" 324 | 325 | $CMExtras += $SBCMExtrasParams 326 | $CMExtras += $SBScriptTemplateBegin.ToString() 327 | $CMExtras += $SBCMExtras.ToString() 328 | $CMExtras += $SBScriptTemplateEnd.ToString() 329 | $CMExtras | Out-File "$($LabScriptPath)\CMExtras.ps1" 330 | 331 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 332 | 333 | #endregion 334 | 335 | $VM = Get-VM -Name $VMName 336 | $VM | Start-VM -WarningAction SilentlyContinue 337 | start-sleep 10 338 | 339 | while (-not (Invoke-Command -VMName $VMName -Credential $DomainAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 340 | 341 | foreach ($Script in $Scripts) { 342 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "$($ClientScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 343 | } 344 | 345 | Invoke-LabCommand -FilePath "$($LabScriptPath)\InstallSQLNativeClient.ps1" -MessageText "InstallSQLNativeClient" -SessionType Domain -VMID $VM.VMId 346 | $SQLSettings = Invoke-LabCommand -FilePath "$($LabScriptPath)\GetSQLSettings.ps1" -MessageText "GetSQLSettings" -SessionType Domain -VMID $VM.VMId 347 | 348 | #region INIFile 349 | if ($ConfigMgrVersion -eq "Prod") { 350 | $HashIdent = @{'Action' = 'InstallPrimarySite' 351 | } 352 | $SiteName = "Current Branch $($ConfigMgrSiteCode)" 353 | } 354 | else { 355 | $HashIdent = @{ 356 | 'Action' = 'InstallPrimarySite'; 357 | 'Preview' = "1" 358 | } 359 | $SiteName = "Tech Preview $($ConfigMgrSiteCode)" 360 | } 361 | $HashOptions = @{ 362 | 'ProductID' = 'EVAL'; 363 | 'SiteCode' = "$($ConfigMgrSiteCode)"; 364 | 'SiteName' = "$($SiteName)"; 365 | 'SMSInstallDir' = 'C:\Program Files\Microsoft Configuration Manager'; 366 | 'SDKServer' = "$($VMWinName).$($DomainFQDN)"; 367 | 'RoleCommunicationProtocol' = "HTTPorHTTPS"; 368 | 'ClientsUsePKICertificate' = "0"; 369 | 'PrerequisiteComp' = "$($ConfigMgrPrereqsPreDL)"; 370 | 'PrerequisitePath' = "$($ConfigMgrPrereqsPath)"; 371 | 'ManagementPoint' = "$($VMWinName).$($DomainFQDN)"; 372 | 'ManagementPointProtocol' = "HTTP"; 373 | 'DistributionPoint' = "$($VMWinName).$($DomainFQDN)"; 374 | 'DistributionPointProtocol' = "HTTP"; 375 | 'DistributionPointInstallIIS' = "0"; 376 | 'AdminConsole' = "1"; 377 | 'JoinCEIP' = "0"; 378 | } 379 | 380 | $hashSQL = @{ 381 | 'SQLServerName' = "$($VMWinName).$($DomainFQDN)"; 382 | 'SQLServerPort' = '1433'; 383 | 'DatabaseName' = "CM_$($ConfigMgrSiteCode)"; 384 | 'SQLSSBPort' = '4022' 385 | 'SQLDataFilePath' = "$($sqlsettings.DefaultFile)"; 386 | 'SQLLogFilePath' = "$($sqlsettings.DefaultLog)" 387 | } 388 | 389 | $hashCloud = @{ 390 | 'CloudConnector' = "1"; 391 | 'CloudConnectorServer' = "$($VMWinName).$($DomainFQDN)" 392 | } 393 | 394 | $hashSCOpts = @{ 395 | } 396 | 397 | $hashHierarchy = @{ 398 | 399 | } 400 | 401 | $HASHCMInstallINI = @{ 402 | 'Identification' = $hashident; 403 | 'Options' = $hashoptions; 404 | 'SQLConfigOptions' = $hashSQL; 405 | 'CloudConnectorOptions' = $hashCloud; 406 | 'SystemCenterOptions' = $hashSCOpts; 407 | 'HierarchyExpansionOption' = $hashHierarchy 408 | } 409 | $CMInstallINI = "" 410 | foreach ($i in $HASHCMInstallINI.keys) { 411 | $CMInstallINI += "[$i]`r`n" 412 | foreach ($j in $($HASHCMInstallINI[$i].keys | Sort-Object)) { 413 | $CMInstallINI += "$j=$($HASHCMInstallINI[$i][$j])`r`n" 414 | } 415 | $CMInstallINI += "`r`n" 416 | } 417 | #endregion 418 | 419 | $CreateINI += $CMInstallINI.ToString() 420 | $CreateINI | Out-File "$($LabScriptPath)\CMinstall.ini" -Force 421 | Copy-VMFile -VM $VM -SourcePath "$($LabScriptPath)\CMinstall.ini" -DestinationPath "$($ClientScriptPath)\CMinstall.ini" -CreateFullPath -FileSource Host -Force 422 | 423 | Invoke-LabCommand -FilePath "$($LabScriptPath)\AddFeatures.ps1" -MessageText "AddFeatures" -SessionType Domain -VMID $VM.VMId 424 | Start-Sleep -seconds 120 425 | Invoke-LabCommand -FilePath "$($LabScriptPath)\ADK.ps1" -MessageText "ADK" -SessionType Domain -VMID $VM.VMId 426 | Invoke-LabCommand -FilePath "$($LabScriptPath)\WinPEADK.ps1" -MessageText "WinPEADK" -SessionType Domain -VMID $VM.VMId 427 | Start-Sleep -seconds 120 428 | Invoke-LabCommand -FilePath "$($LabScriptPath)\ExtSchema.ps1" -MessageText "ExtSchema" -SessionType Domain -VMID $VM.VMId 429 | Invoke-LabCommand -FilePath "$($LabScriptPath)\InstallCM.ps1" -MessageText "InstallCM" -SessionType Domain -VMID $VM.VMId 430 | Invoke-LabCommand -FilePath "$($LabScriptPath)\CMExtras.ps1" -MessageText "CMExtras" -SessionType Domain -VMID $VM.VMId 431 | 432 | #Checkpoint-VM -VM $VM -SnapshotName "ConfigMgr Configuration Complete" 433 | 434 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 435 | } -------------------------------------------------------------------------------- /Public/Add-LabRoleDC.ps1: -------------------------------------------------------------------------------- 1 | function Add-LabRoleDC { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $VMWinName = $VMConfig.VMWinName, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [string] 30 | $IPAddress = $VMConfig.VMIPAddress, 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [string] 35 | $IPSubNet = $LabEnvConfig.EnvIPSubnet, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $RRASMac = $LabEnvConfig.RRASMAC, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $RRASName = $LabEnvConfig.RRASName, 46 | 47 | [Parameter()] 48 | [ValidateNotNullOrEmpty()] 49 | [string] 50 | $DomainFQDN = $LabEnvConfig.EnvFQDN, 51 | 52 | [Parameter()] 53 | [ValidateNotNullOrEmpty()] 54 | [pscredential] 55 | $LocalAdminCreds = $BaseConfig.LocalAdminCreds, 56 | 57 | [Parameter()] 58 | [ValidateNotNullOrEmpty()] 59 | [pscredential] 60 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 61 | 62 | [Parameter()] 63 | [ValidateNotNullOrEmpty()] 64 | [string] 65 | $ScriptPath = $BaseConfig.VMScriptPath, 66 | 67 | [Parameter()] 68 | [ValidateNotNullOrEmpty()] 69 | [string] 70 | $LogPath = $BaseConfig.VMLogPath, 71 | 72 | [Parameter()] 73 | [ValidateNotNullOrEmpty()] 74 | [string] 75 | $LabPath = $BaseConfig.LabPath 76 | 77 | ) 78 | 79 | #region Standard Setup 80 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 81 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 82 | $ClientScriptPath = "C:$($ScriptPath)" 83 | 84 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 85 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 86 | } 87 | 88 | $LogPath = "C:$($LogPath)" 89 | $SBDefaultParams = @" 90 | param 91 | ( 92 | `$_LogPath = "$($LogPath)" 93 | ) 94 | "@ 95 | 96 | $SBScriptTemplateBegin = { 97 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 98 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 99 | } 100 | 101 | $_LogFile = "$($_LogPath)\Transcript.log"; 102 | 103 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader; 104 | Write-Output "Logging to $_LogFile"; 105 | 106 | #region Do Stuff Here 107 | } 108 | 109 | $SBScriptTemplateEnd = { 110 | #endregion 111 | Stop-Transcript 112 | } 113 | 114 | #endregion 115 | 116 | #region Script Blocks 117 | $SBSetDCIPParams = @" 118 | param 119 | ( 120 | `$_LogPath = "$($LogPath)", 121 | `$_IPAddress = "$($IPAddress)", 122 | `$_DefaultGateway = "$($IPSubNet)1", 123 | `$_InterfaceID = (Get-NetAdapter).InterfaceIndex, 124 | `$_DNSAddress = ('127.0.0.1') 125 | ) 126 | "@ 127 | 128 | $SBSetDCIP = { 129 | New-NetIPAddress -InterfaceIndex $_InterfaceID -AddressFamily IPv4 -IPAddress $_IPAddress -PrefixLength 24 -DefaultGateway $_DefaultGateway; 130 | Set-DnsClientServerAddress -ServerAddresses $_DNSAddress -InterfaceIndex $_InterfaceID; 131 | #Restart-Computer -Force; 132 | } 133 | 134 | $SBAddDCFeatures = { 135 | Install-WindowsFeature -Name DHCP, DNS, AD-Domain-Services -IncludeManagementTools; 136 | } 137 | 138 | $SBDCPromoParams = @" 139 | param 140 | ( 141 | `$_LogPath = "$($LogPath)", 142 | `$_FQDN = "$($DomainFQDN)", 143 | `$_DomainAdminName = "$($LabEnvConfig.EnvNetBios)\Administrator", 144 | `$_DomainAdminPassword = "$($LabEnvConfig.EnvAdminPW)" 145 | ) 146 | "@ 147 | 148 | $SBDCPromo = { 149 | $_Password = ConvertTo-SecureString -String $_DomainAdminPassword -AsPlainText -Force 150 | $_Creds = new-object -typename System.Management.Automation.PSCredential($_DomainAdminName,$_Password) 151 | Install-ADDSForest -DomainName $_FQDN -SafeModeAdministratorPassword $_Creds.Password -confirm:$false -WarningAction SilentlyContinue; 152 | } 153 | 154 | $SBConfigureDHCPParams = @" 155 | param 156 | ( 157 | `$_LogPath = "$($LogPath)", 158 | `$_DomainFQDN = "$($DomainFQDN)", 159 | `$_ServerName = "$($VMWinName)", 160 | `$_IPAddress = "$($IPAddress)", 161 | `$_IPSubnet = "$($IPSubnet)", 162 | [ipaddress]`$_ScopeID = "$($IPSubnet)0", 163 | `$_RRASMAC = "$($RRASMAC)" 164 | ) 165 | "@ 166 | 167 | $SBConfigureDHCP = { 168 | & netsh dhcp add securitygroups; 169 | Restart-Service dhcpserver; 170 | Restart-Service dhcpserver; 171 | start-sleep -seconds 60 172 | Add-DhcpServerInDC # -DnsName "$($_ServerName).$($_DomainFQDN)" -IPAddress $_IPAddress; 173 | Set-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ServerManager\Roles\12 -Name ConfigurationState -Value 2; 174 | Add-DhcpServerv4Scope -name $_DomainFQDN -StartRange "$($_IPSubnet)100" -EndRange "$($_IPSubnet)150" -SubnetMask "255.255.255.0"; 175 | Set-DhcpServerv4OptionValue -Router "$($_IPSubnet)`1" -DNSDomain $_DomainFQDN -DNSServer $_IPAddress -ScopeId $_ScopeID 176 | Add-DhcpServerv4Reservation -IPAddress "$($_IPSubnet)`1" -ClientId $_RRASMAC -ScopeId "$($_IPSubnet)0" -Name "RRAS" 177 | } 178 | 179 | $SBCreateDCLabDomain = { 180 | Import-Module ActiveDirectory; 181 | $root = (Get-ADRootDSE).defaultNamingContext; 182 | if (-not ([adsi]::Exists("LDAP://CN=System Management,CN=System,$root"))) { 183 | $null = New-ADObject -Type Container -name "System Management" -Path "CN=System,$root" -Passthru 184 | }; 185 | $acl = get-acl "ad:CN=System Management,CN=System,$root"; 186 | New-ADGroup -name "SCCM Servers" -groupscope Global; 187 | $objGroup = Get-ADGroup -filter {Name -eq "SCCM Servers"}; 188 | $All = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::SelfAndChildren; 189 | $ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $objGroup.SID, "GenericAll", "Allow", $All; 190 | $acl.AddAccessRule($ace); 191 | Set-acl -aclobject $acl "ad:CN=System Management,CN=System,$root" 192 | } 193 | 194 | $SetDCIP += $SBSetDCIPParams 195 | $SetDCIP += $SBScriptTemplateBegin.ToString() 196 | $SetDCIP += $SBSetDCIP.ToString() 197 | $SetDCIP += $SBScriptTemplateEnd.ToString() 198 | $SetDCIP | Out-File "$($LabScriptPath)\SetDCIP.ps1" 199 | 200 | $AddDCFeatures += $SBDefaultParams 201 | $AddDCFeatures += $SBScriptTemplateBegin.ToString() 202 | $AddDCFeatures += $SBAddDCFeatures.ToString() 203 | $AddDCFeatures += $SBScriptTemplateEnd.ToString() 204 | $AddDCFeatures | Out-File "$($LabScriptPath)\AddDCFeatures.ps1" 205 | 206 | $DCPromo += $SBDCPromoParams 207 | $DCPromo += $SBScriptTemplateBegin.ToString() 208 | $DCPromo += $SBDCPromo.ToString() 209 | $DCPromo += $SBScriptTemplateEnd.ToString() 210 | $DCPromo | Out-File "$($LabScriptPath)\DCPromo.ps1" 211 | 212 | $ConfigureDHCP += $SBConfigureDHCPParams 213 | $ConfigureDHCP += $SBScriptTemplateBegin.ToString() 214 | $ConfigureDHCP += $SBConfigureDHCP.ToString() 215 | $ConfigureDHCP += $SBScriptTemplateEnd.ToString() 216 | $ConfigureDHCP | Out-File "$($LabScriptPath)\ConfigureDHCP.ps1" 217 | 218 | $CreateDCLabDomain += $SBDefaultParams 219 | $CreateDCLabDomain += $SBScriptTemplateBegin.ToString() 220 | $CreateDCLabDomain += $SBCreateDCLabDomain.ToString() 221 | $CreateDCLabDomain += $SBScriptTemplateEnd.ToString() 222 | $CreateDCLabDomain | Out-File "$($LabScriptPath)\CreateDCLabDomain.ps1" 223 | 224 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 225 | 226 | #endregion 227 | 228 | $VM = Get-VM -VM $VMName 229 | $VM | Start-VM -WarningAction SilentlyContinue 230 | start-sleep 10 231 | 232 | while (-not (Invoke-Command -VMName $VMName -Credential $LocalAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 233 | 234 | foreach ($Script in $Scripts) { 235 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "C:$($ScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 236 | } 237 | 238 | Invoke-LabCommand -FilePath "$($LabScriptPath)\SetDCIP.ps1" -MessageText "SetDCIP" -SessionType Local -VMID $VM.VMId 239 | $VM | Stop-VM -Force -WarningAction SilentlyContinue 240 | Invoke-LabCommand -FilePath "$($LabScriptPath)\AddDCFeatures.ps1" -MessageText "AddDCFeatures" -SessionType Local -VMID $VM.VMId 241 | Invoke-LabCommand -FilePath "$($LabScriptPath)\DCPromo.ps1" -MessageText "DCPromo" -SessionType Local -VMID $VM.VMId 242 | #Checkpoint-VM -VM $VM -SnapshotName "DC Promo Complete" 243 | #endregion 244 | start-sleep -seconds 360 245 | while (-not (Invoke-Command -VMName $VMName -Credential $DomainAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 246 | while ((Invoke-Command -VMName $VM.VMName -Credential $DomainAdminCreds {(get-command get-adgroup).count} -ErrorAction Continue) -ne 1) {Start-Sleep -Seconds 5} 247 | Invoke-LabCommand -FilePath "$($LabScriptPath)\ConfigureDHCP.ps1" -MessageText "ConfigureDHCP" -SessionType Domain -VMID $VM.VMId 248 | #Checkpoint-VM -VM $VM -SnapshotName "DHCP Configured" 249 | 250 | while ((Invoke-Command -VMName $VM.VMName -Credential $DomainAdminCreds {(get-command get-adgroup).count} -ErrorAction Continue) -ne 1) {Start-Sleep -Seconds 5} 251 | Invoke-LabCommand -FilePath "$($LabScriptPath)\CreateDCLabDomain.ps1" -MessageText "CreateDCLabDomain" -SessionType Domain -VMID $VM.VMId 252 | 253 | $VM | Stop-VM -Force -WarningAction SilentlyContinue 254 | Get-VM -VM $RRASName | Restart-VM -Force -ErrorAction SilentlyContinue 255 | $VM | Start-VM -WarningAction SilentlyContinue 256 | start-sleep -seconds 120 257 | 258 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 259 | } -------------------------------------------------------------------------------- /Public/Add-LabRoleRRAS.ps1: -------------------------------------------------------------------------------- 1 | function Add-LabRoleRRAS { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $InternetSwitchName = $LabEnvConfig.InternetSwitchName, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [string] 30 | $DomainFQDN = $LabEnvConfig.EnvFQDN, 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [pscredential] 35 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $ScriptPath = $BaseConfig.VMScriptPath, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $LogPath = $BaseConfig.VMLogPath, 46 | 47 | [Parameter()] 48 | [ValidateNotNullOrEmpty()] 49 | [string] 50 | $LabPath = $BaseConfig.LabPath 51 | 52 | ) 53 | 54 | #region Standard Setup 55 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 56 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 57 | $ClientScriptPath = "C:$($ScriptPath)" 58 | 59 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 60 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 61 | } 62 | 63 | $LogPath = "C:$($LogPath)" 64 | $SBDefaultParams = @" 65 | param 66 | ( 67 | `$_LogPath = "$($LogPath)" 68 | ) 69 | "@ 70 | 71 | $SBScriptTemplateBegin = { 72 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 73 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 74 | } 75 | 76 | $_LogFile = "$($_LogPath)\Transcript.log"; 77 | 78 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 79 | Write-Output "Logging to $_LogFile"; 80 | 81 | #region Do Stuff Here 82 | } 83 | 84 | $SBScriptTemplateEnd = { 85 | #endregion 86 | Stop-Transcript 87 | } 88 | 89 | #endregion 90 | 91 | #region Script Blocks 92 | $SBInstallRRAS = { 93 | Install-WindowsFeature Routing,RSAT-RemoteAccess-Mgmt -IncludeManagementTools 94 | Get-NetAdapter -Physical -Name "Ethernet" | Rename-NetAdapter -newname "External" 95 | Install-RemoteAccess -VpnType RoutingOnly 96 | netsh routing ip nat install 97 | netsh routing ip nat add interface "External" 98 | netsh routing ip nat set interface "External" mode=full 99 | Set-LocalUser -Name "Administrator" -PasswordNeverExpires 1 100 | Set-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -Type DWord -Value 1 -Force 101 | Set-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RemoteAccess\Parameters -Name ModernStackEnabled -Value 0 -Type DWord -Force 102 | } 103 | 104 | $InstallRRAS += $SBDefaultParams 105 | $InstallRRAS += $SBScriptTemplateBegin.ToString() 106 | $InstallRRAS += $SBInstallRRAS.ToString() 107 | $InstallRRAS += $SBScriptTemplateEnd.ToString() 108 | $InstallRRAS | Out-File "$($LabScriptPath)\InstallRRAS.ps1" 109 | 110 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 111 | 112 | #endregion 113 | 114 | $VM = Get-VM -VM $VMName 115 | $VM | Start-VM -WarningAction SilentlyContinue 116 | start-sleep 10 117 | 118 | while (-not (Invoke-Command -VMName $VMName -Credential $DomainAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 119 | 120 | foreach ($Script in $Scripts) { 121 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "$($ClientScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 122 | } 123 | 124 | $Adapter = Get-VMNetworkAdapter -VMName $VMName 125 | $Adapter | Connect-VMNetworkAdapter -SwitchName $InternetSwitchName 126 | $Adapter | Set-VMNetworkAdapter -DeviceNaming On 127 | $Adapter | Set-VMNetworkAdapterVlan -Untagged 128 | $Adapter | Rename-VMNetworkAdapter -NewName "Internet" 129 | 130 | Invoke-LabCommand -FilePath "$($LabScriptPath)\InstallRRAS.ps1" -MessageText "InstallRRAS" -SessionType Local -VMID $VM.VMId 131 | 132 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 133 | } -------------------------------------------------------------------------------- /Public/Add-LabRoleSQL.ps1: -------------------------------------------------------------------------------- 1 | function Add-LabRoleSQL { 2 | [cmdletBinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $SQLISO = $BaseConfig.SQLISO, 26 | 27 | [Parameter()] 28 | [string] 29 | $AddWSUS = $VMConfig.AddWSUS, 30 | 31 | [Parameter()] 32 | [ValidateNotNullOrEmpty()] 33 | [string] 34 | $VMDataPath = $BaseConfig.VMDataPath, 35 | 36 | [Parameter()] 37 | [ValidateNotNullOrEmpty()] 38 | [pscredential] 39 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 40 | 41 | [Parameter()] 42 | [ValidateNotNullOrEmpty()] 43 | [string] 44 | $ScriptPath = $BaseConfig.VMScriptPath, 45 | 46 | [Parameter()] 47 | [ValidateNotNullOrEmpty()] 48 | [string] 49 | $LogPath = $BaseConfig.VMLogPath, 50 | 51 | [Parameter()] 52 | [ValidateNotNullOrEmpty()] 53 | [string] 54 | $LabPath = $BaseConfig.LabPath 55 | 56 | ) 57 | 58 | #region Standard Setup 59 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 60 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 61 | $ClientScriptPath = "C:$($ScriptPath)" 62 | 63 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 64 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 65 | } 66 | 67 | $LogPath = "C:$($LogPath)" 68 | $SBDefaultParams = @" 69 | param 70 | ( 71 | `$_LogPath = "$($LogPath)" 72 | ) 73 | "@ 74 | 75 | $SBScriptTemplateBegin = { 76 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 77 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 78 | } 79 | 80 | $_LogFile = "$($_LogPath)\Transcript.log"; 81 | 82 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 83 | Write-Output "Logging to $_LogFile"; 84 | 85 | #region Do Stuff Here 86 | } 87 | 88 | $SBScriptTemplateEnd = { 89 | #endregion 90 | Stop-Transcript 91 | } 92 | 93 | #endregion 94 | 95 | Get-VMDvdDrive -VMName $VMName 96 | Add-VMDvdDrive -VMName $VMName -ControllerNumber 0 -ControllerLocation 1 97 | Set-VMDvdDrive -Path $SQLISO -VMName $VMName -ControllerNumber 0 -ControllerLocation 1 98 | 99 | $SQLInstallINI = New-LabCMSQLSettingsINI -DomainNetBiosName $LabEnvConfig.EnvFQDN -UserName $BaseConfig.DomainAdminName -Password $LabEnvConfig.EnvAdminPW 100 | 101 | #region Script Blocks 102 | $SBSQLInstallCmdParams = @" 103 | param ( 104 | `$_LogPath = "$($LogPath)", 105 | `$_ClientScriptPath = "$($ClientScriptPath)" 106 | ) 107 | "@ 108 | 109 | $SBSQLInstallCmd = { 110 | $_SQLDisk = (Get-PSDrive -PSProvider FileSystem | where-object {$_.name -ne "c"}).root 111 | Start-Process -FilePath "$($_SQLDisk)Setup.exe" -Wait -ArgumentList "/ConfigurationFile=$($_ClientScriptPath)\ConfigurationFile.INI /IACCEPTSQLSERVERLICENSETERMS" 112 | } 113 | 114 | $SBSQLMemory = { 115 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null 116 | $srv = New-Object Microsoft.SQLServer.Management.Smo.Server($env:COMPUTERNAME) 117 | if ($srv.status) { 118 | $srv.Configuration.MaxServerMemory.ConfigValue = 8kb 119 | $srv.Configuration.MinServerMemory.ConfigValue = 4kb 120 | $srv.Configuration.Alter() 121 | } 122 | } 123 | 124 | $SBAddWSUS = { 125 | Add-WindowsFeature UpdateServices-Services, UpdateServices-db 126 | } 127 | 128 | $SBWSUSPostInstall = { 129 | Start-Process -filepath "C:\Program Files\Update Services\Tools\WsusUtil.exe" -ArgumentList "postinstall CONTENT_DIR=C:\WSUS SQL_INSTANCE_NAME=$env:COMPUTERNAME" -Wait 130 | } 131 | 132 | $SQLIni += $SQLInstallINI.ToString() 133 | $SQLIni | Out-File "$($LabScriptPath)\ConfigurationFile.INI" -Force 134 | 135 | $SQLInstallCmd += $SBSQLInstallCmdParams 136 | $SQLInstallCmd += $SBScriptTemplateBegin.ToString() 137 | $SQLInstallCmd += $SBSQLInstallCmd.ToString() 138 | $SQLInstallCmd += $SBScriptTemplateEnd.ToString() 139 | $SQLInstallCmd | Out-File "$($LabScriptPath)\SQLInstallCmd.ps1" 140 | 141 | $SQLMemory += $SBDefaultParams 142 | $SQLMemory += $SBScriptTemplateBegin.ToString() 143 | $SQLMemory += $SBSQLMemory.ToString() 144 | $SQLMemory += $SBScriptTemplateEnd.ToString() 145 | $SQLMemory | Out-File "$($LabScriptPath)\SQLMemory.ps1" 146 | 147 | $AddWSUS += $SBDefaultParams 148 | $AddWSUS += $SBScriptTemplateBegin.ToString() 149 | $AddWSUS += $SBAddWSUS.ToString() 150 | $AddWSUS += $SBScriptTemplateEnd.ToString() 151 | $AddWSUS | Out-File "$($LabScriptPath)\AddWSUS.ps1" 152 | 153 | $WSUSPostInstall += $SBDefaultParams 154 | $WSUSPostInstall += $SBScriptTemplateBegin.ToString() 155 | $WSUSPostInstall += $SBWSUSPostInstall.ToString() 156 | $WSUSPostInstall += $SBScriptTemplateEnd.ToString() 157 | $WSUSPostInstall | Out-File "$($LabScriptPath)\WSUSPostInstall.ps1" 158 | 159 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 160 | 161 | #endregion 162 | 163 | $VM = Get-VM -Name $VMName 164 | $VM | Start-VM -WarningAction SilentlyContinue 165 | start-sleep 10 166 | 167 | while (-not (Invoke-Command -VMName $VMName -Credential $DomainAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 168 | 169 | foreach ($Script in $Scripts) { 170 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "$($ClientScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 171 | } 172 | 173 | #Invoke-LabCommand -FilePath "$($LabScriptPath)\SQLIni.ps1" -MessageText "SQLIni" -SessionType Domain -VMID $VM.VMId 174 | Invoke-LabCommand -FilePath "$($LabScriptPath)\SQLInstallCmd.ps1" -MessageText "SQLInstallCmd" -SessionType Domain -VMID $VM.VMId 175 | Invoke-LabCommand -FilePath "$($LabScriptPath)\SQLMemory.ps1" -MessageText "SQLMemory" -SessionType Domain -VMID $VM.VMId 176 | if($AddWSUS = 1) { 177 | Invoke-LabCommand -FilePath "$($LabScriptPath)\AddWSUS.ps1" -MessageText "AddWSUS" -SessionType Domain -VMID $VM.VMId 178 | Invoke-LabCommand -FilePath "$($LabScriptPath)\WSUSPostInstall.ps1" -MessageText "WSUSPostInstall" -SessionType Domain -VMID $VM.VMId 179 | } 180 | Set-VMDvdDrive -VMName $VMName -Path $null 181 | 182 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 183 | } -------------------------------------------------------------------------------- /Public/Get-LabConfig.ps1: -------------------------------------------------------------------------------- 1 | function Get-LabConfig { 2 | [cmdletBinding()] 3 | [OutputType([PSCustomObject])] 4 | param ( 5 | [Parameter()] 6 | [ValidateNotNullOrEmpty()] 7 | [ValidateScript({ Test-Path $(Resolve-Path $_) })] 8 | [string] 9 | $ConfigFileName, 10 | 11 | [Parameter()] 12 | [switch] 13 | $CreateFolders 14 | ) 15 | 16 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 17 | 18 | $RawConfig = Get-Content $ConfigFileName -Raw | ConvertFrom-Json 19 | $RawENVConfig = $RawConfig.ENVConfig | Where-Object {$_.ENV -eq $RawConfig.ENVToBuild} 20 | $RawSvrRefConfig = $RawConfig.ServerRef 21 | $RawWksRefConfig = $RawConfig.WorkstationRef 22 | 23 | $BaseConfig = @{} 24 | $LabEnvConfig = @{} 25 | $ServerRefConfig = @{} 26 | $WorkstationRefConfig = @{} 27 | 28 | ($RawConfig | Select-Object -Property * -ExcludeProperty "ENVConfig", "VMList", "ServerRef", "WorkstationRef").psobject.properties | foreach-Object {$BaseConfig[$_.Name] = $_.Value} 29 | ($RawENVConfig | Select-Object -Property * -ExcludeProperty "ServerVMList", "WorkstationVMList").psobject.properties | foreach-Object {$LabEnvConfig[$_.Name] = $_.Value} 30 | 31 | foreach ($Img in $RawSvrRefConfig) { 32 | ($Img).psobject.properties | foreach-Object {$ServerRefConfig[$_.Name] = $_.Value} 33 | } 34 | 35 | foreach ($Img in $RawWksRefConfig) { 36 | ($Img).psobject.properties | foreach-Object {$WorkstationRefConfig[$_.Name] = $_.Value} 37 | } 38 | 39 | $BaseConfig["ConfigMgrCBPrereqsPath"] = "$($BaseConfig.PathConfigMgr)\Prereqs" 40 | $BaseConfig["ConfigMgrTPPrereqsPath"] = "$($BaseConfig.PathConfigMgrTP)\Prereqs" 41 | 42 | foreach ($key in @($BaseConfig.keys)) { 43 | If ($key -like "Path*") 44 | { 45 | $BaseConfig[$key] = $BaseConfig.SourcePath + $BaseConfig[$key] 46 | if ($CreateFolders) { 47 | if (-not (Test-Path -path $BaseConfig[$key])) {New-Item -path $BaseConfig[$key] -ItemType Directory;} 48 | } 49 | } 50 | } 51 | 52 | $BaseConfig["VMPath"] = Join-Path -Path $BaseConfig.LabPath -ChildPath $BaseConfig.ENVToBuild 53 | $BaseConfig["SQLISO"] = Get-ChildItem -Path $BaseConfig.PathSQL -Filter "*.ISO" | Select-Object -First 1 -ExpandProperty FullName 54 | $BaseConfig["SvrISO"] = Get-ChildItem -Path $BaseConfig.PathSvr -Filter "*.ISO" | Select-Object -First 1 -ExpandProperty FullName 55 | $BaseConfig["WinISO"] = Get-ChildItem -Path $BaseConfig.PathWin10 -Filter "*.ISO" | Select-Object -First 1 -ExpandProperty FullName 56 | $BaseConfig["Packages"] = Get-ChildItem -Path $BaseConfig.PathPackages -Filter "*.CAB" | Select-Object -ExpandProperty FullName 57 | $BaseConfig["Drivers"] = Get-ChildItem -Path $BaseConfig.PathDrivers -Filter "*.*" | Select-Object -ExpandProperty FullName 58 | $BaseConfig["SvrVHDX"] = Get-ChildItem -Path $BaseConfig.PathRefImage | Where-Object {$_.Name -eq $ServerRefConfig.RefVHDXName} | Select-Object -ExpandProperty FullName 59 | $BaseConfig["WksVHDX"] = Get-ChildItem -Path $BaseConfig.PathRefImage | Where-Object {$_.Name -eq $WorkstationRefConfig.RefVHDXName} | Select-Object -ExpandProperty FullName 60 | 61 | $BaseConfig["LocalAdminName"] = ".\$($LabEnvConfig.EnvAdminName)" 62 | $BaseConfig["LocalAdminPassword"] = ConvertTo-SecureString -String $LabEnvConfig.EnvAdminPW -AsPlainText -Force 63 | $BaseConfig["LocalAdminCreds"] = new-object -typename System.Management.Automation.PSCredential($BaseConfig.LocalAdminName, $BaseConfig.LocalAdminPassword) 64 | 65 | $BaseConfig["DomainAdminName"] = "$($LabEnvConfig.EnvNetBios)\$($LabEnvConfig.EnvAdminName)" 66 | $BaseConfig["DomainAdminPassword"] = ConvertTo-SecureString -String $LabEnvConfig.EnvAdminPW -AsPlainText -Force 67 | $BaseConfig["DomainAdminCreds"] = new-object -typename System.Management.Automation.PSCredential($BaseConfig.DomainAdminName,$BaseConfig.DomainAdminPassword) 68 | 69 | $ServerVMs = $RawENVConfig.ServerVMList | ForEach-Object { 70 | $VMConfig = [PSCustomObject]@{} 71 | ($_[0]).psobject.properties | foreach-Object {$VMConfig | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -ErrorAction SilentlyContinue} 72 | $VMConfig | Add-Member -NotePropertyName "SvrVHDX" -NotePropertyValue $Null -ErrorAction SilentlyContinue 73 | $VMConfig | Add-Member -NotePropertyName "VMIPAddress" -NotePropertyValue $Null -ErrorAction SilentlyContinue 74 | $VMConfig | Add-Member -NotePropertyName "VMWinName" -NotePropertyValue $Null -ErrorAction SilentlyContinue 75 | $VMConfig | Add-Member -NotePropertyName "VMName" -NotePropertyValue $Null -ErrorAction SilentlyContinue 76 | $VMConfig | Add-Member -NotePropertyName "VMHDPath" -NotePropertyValue $Null -ErrorAction SilentlyContinue 77 | $VMConfig | Add-Member -NotePropertyName "VMHDName" -NotePropertyValue $Null -ErrorAction SilentlyContinue 78 | $VMConfig | Add-Member -NotePropertyName "EnableSnapshot" -NotePropertyValue $Null -ErrorAction SilentlyContinue 79 | $VMConfig | Add-Member -NotePropertyName "AutoStartup" -NotePropertyValue $Null -ErrorAction SilentlyContinue 80 | $VMConfig | Add-Member -NotePropertyName "StartupMemory" -NotePropertyValue $Null -ErrorAction SilentlyContinue 81 | 82 | $VMConfig.SvrVHDX = $BaseConfig.SvrVHDX 83 | $VMConfig.VMIPAddress = "$($LabEnvConfig.EnvIPSubnet)$($_.VMIPLastOctet)" 84 | $VMConfig.VMWinName = "$($_.VMName)" 85 | $VMConfig.VMName = "$($LabEnvConfig.Env)-$($_.VMName)" 86 | $VMConfig.VMHDPath = "$($BaseConfig.VMPath)\$($VMConfig.VMName)\Virtual Hard Disks" 87 | $VMConfig.VMHDName = "$($VMConfig.VMName)c.vhdx" 88 | $VMConfig.EnableSnapshot = if ($_.EnableSnapshot -eq 1) {$true} else {$false} 89 | $VMConfig.AutoStartup = if ($_.AutoStartup -eq 1) {$true} else {$false} 90 | $VMConfig.StartupMemory = [int64]$_.StartupMemory.Replace('gb','') * 1GB 91 | $VMConfig 92 | } 93 | 94 | #TODO 95 | <# 96 | $WksVMs = $RawENVConfig.WorkstationVMList | ForEach-Object { 97 | $VMConfig = [PSCustomObject]@{} 98 | ($_[0]).psobject.properties | foreach-Object {$VMConfig | Add-Member -NotePropertyName $_.Name -NotePropertyValue $_.Value -ErrorAction SilentlyContinue} 99 | $VMConfig | Add-Member -NotePropertyName "WksVHDX" -NotePropertyValue $Null -ErrorAction SilentlyContinue 100 | $VMConfig | Add-Member -NotePropertyName "VMIPAddress" -NotePropertyValue $Null -ErrorAction SilentlyContinue 101 | $VMConfig | Add-Member -NotePropertyName "VMWinName" -NotePropertyValue $Null -ErrorAction SilentlyContinue 102 | $VMConfig | Add-Member -NotePropertyName "VMName" -NotePropertyValue $Null -ErrorAction SilentlyContinue 103 | $VMConfig | Add-Member -NotePropertyName "VMHDPath" -NotePropertyValue $Null -ErrorAction SilentlyContinue 104 | $VMConfig | Add-Member -NotePropertyName "VMHDName" -NotePropertyValue $Null -ErrorAction SilentlyContinue 105 | $VMConfig | Add-Member -NotePropertyName "EnableSnapshot" -NotePropertyValue $Null -ErrorAction SilentlyContinue 106 | $VMConfig | Add-Member -NotePropertyName "AutoStartup" -NotePropertyValue $Null -ErrorAction SilentlyContinue 107 | $VMConfig | Add-Member -NotePropertyName "StartupMemory" -NotePropertyValue $Null -ErrorAction SilentlyContinue 108 | 109 | $VMConfig.SvrVHDX = $BaseConfig.SvrVHDX 110 | $VMConfig.VMIPAddress = "$($LabEnvConfig.EnvIPSubnet)$($_.VMIPLastOctet)" 111 | $VMConfig.VMWinName = "$($_.VMName)" 112 | $VMConfig.VMName = "$($LabEnvConfig.Env)-$($_.VMName)" 113 | $VMConfig.VMHDPath = "$($BaseConfig.VMPath)\$($_.VMName)\Virtual Hard Disks" 114 | $VMConfig.VMHDName = "$($_.VMName)c.vhdx" 115 | $VMConfig.EnableSnapshot = if ($_.EnableSnapshot -eq 1) {$true} else {$false} 116 | $VMConfig.AutoStartup = if ($_.AutoStartup -eq 1) {$true} else {$false} 117 | $VMConfig.StartupMemory = [int64]$_.StartupMemory.Replace('gb','') * 1GB 118 | $VMConfig 119 | } 120 | 121 | 122 | $VMs = Get-VM 123 | $VLans = foreach ($VM in $VMs) { 124 | $VM | Get-VMNetworkAdapterVlan | Select -ExpandProperty AccessVlanId 125 | } 126 | 127 | $LabEnvConfig["VLanID"] = if ($VLans) { 128 | ($VLans | Measure-Object -Maximum).Maximum + 1 129 | } 130 | else { 131 | 1 132 | } 133 | #> 134 | 135 | #Setting these to avoiding needing to pass in creds with each lab connection test 136 | $Script:LocalAdminCreds = $BaseConfig.LocalAdminCreds 137 | $Script:DomainAdminCreds = $BaseConfig.DomainAdminCreds 138 | 139 | $Config = @{} 140 | $Config["BaseConfig"] = $BaseConfig 141 | $Config.BaseConfig["ServerRef"] = $ServerRefConfig 142 | $Config.BaseConfig["WorkstationRef"] = $WorkstationRefConfig 143 | 144 | $Config["LabEnvConfig"] = $LabEnvConfig 145 | $Config.LabEnvConfig["ServerVMs"] = $ServerVMs 146 | $Config.LabEnvConfig["WorkstationVMs"] = $WorkstationVMs 147 | return $Config 148 | } -------------------------------------------------------------------------------- /Public/Join-LabDomain.ps1: -------------------------------------------------------------------------------- 1 | function Join-LabDomain { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $VMConfig.VMName, 21 | 22 | [Parameter()] 23 | [ValidateNotNullOrEmpty()] 24 | [string] 25 | $DomainFQDN = $LabEnvConfig.EnvFQDN, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [pscredential] 30 | $LocalAdminCreds = $BaseConfig.LocalAdminCreds, 31 | 32 | [Parameter()] 33 | [ValidateNotNullOrEmpty()] 34 | [string] 35 | $ScriptPath = $BaseConfig.VMScriptPath, 36 | 37 | [Parameter()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $LogPath = $BaseConfig.VMLogPath, 41 | 42 | [Parameter()] 43 | [ValidateNotNullOrEmpty()] 44 | [string] 45 | $LabPath = $BaseConfig.LabPath, 46 | 47 | [Parameter()] 48 | [ValidateNotNullOrEmpty()] 49 | [string] 50 | $DomainAdminName = "$($LabEnvConfig.EnvNetBios)\Administrator", 51 | 52 | [Parameter()] 53 | [ValidateNotNullOrEmpty()] 54 | [string] 55 | $DomainAdminPassword = $LabEnvConfig.EnvAdminPW 56 | 57 | ) 58 | 59 | #region Standard Setup 60 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 61 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 62 | $ClientScriptPath = "C:$($ScriptPath)" 63 | 64 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 65 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 66 | } 67 | 68 | $LogPath = "C:$($LogPath)" 69 | $SBDefaultParams = @" 70 | param 71 | ( 72 | `$_LogPath = "$($LogPath)" 73 | ) 74 | "@ 75 | 76 | $SBScriptTemplateBegin = { 77 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 78 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 79 | } 80 | 81 | $_LogFile = "$($_LogPath)\Transcript.log"; 82 | 83 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 84 | Write-Output "Logging to $_LogFile"; 85 | 86 | #region Do Stuff Here 87 | } 88 | 89 | $SBScriptTemplateEnd = { 90 | #endregion 91 | Stop-Transcript 92 | } 93 | 94 | #endregion 95 | 96 | #region Script Blocks 97 | $SBJoinDomainParams = @" 98 | param 99 | ( 100 | `$_LogPath = "$($LogPath)", 101 | `$_FQDN = "$($DomainFQDN)", 102 | `$_DomainAdminName = "$($DomainAdminName)", 103 | `$_DomainAdminPassword = "$($DomainAdminPassword)" 104 | ) 105 | "@ 106 | 107 | $SBJoinDomain = { 108 | $_Password = ConvertTo-SecureString -String $_DomainAdminPassword -AsPlainText -Force 109 | $_Creds = new-object -typename System.Management.Automation.PSCredential($_DomainAdminName,$_Password) 110 | Add-Computer -Credential $_Creds -DomainName $_FQDN -Restart 111 | $DomainJoined = (((Get-ComputerInfo).CsDomainRole -eq "PrimaryDomainController") -or (Test-ComputerSecureChannel -ErrorAction SilentlyContinue)) 112 | Return $DomainJoined 113 | } 114 | 115 | $JoinDomain += $SBJoinDomainParams 116 | $JoinDomain += $SBScriptTemplateBegin.ToString() 117 | $JoinDomain += $SBJoinDomain.ToString() 118 | $JoinDomain += $SBScriptTemplateEnd.ToString() 119 | $JoinDomain | Out-File "$($LabScriptPath)\JoinDomain.ps1" 120 | 121 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 122 | 123 | #endregion 124 | 125 | $VM = Get-VM -VM $VMName 126 | $VM | Start-VM -WarningAction SilentlyContinue 127 | start-sleep 20 128 | 129 | $DomainJoined = $False 130 | while (-not (Invoke-Command -VMName $VMName -Credential $LocalAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 131 | do { 132 | foreach ($Script in $Scripts) { 133 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "C:$($ScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 134 | } 135 | $DomainJoined = Invoke-LabCommand -FilePath "$($LabScriptPath)\JoinDomain.ps1" -MessageText "JoinDomain" -SessionType Local -VMID $VM.VMId 136 | } until ($DomainJoined) 137 | 138 | Start-Sleep -Seconds 60 139 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 140 | } -------------------------------------------------------------------------------- /Public/New-LabEnv.ps1: -------------------------------------------------------------------------------- 1 | #TODO Add params to this function to allow customizing what we need. CreateVHDX, CreateFolders, ETC, ReUseSwitch 2 | function New-LabEnv { 3 | [cmdletbinding()] 4 | param ( 5 | [Parameter()] 6 | [String] 7 | $ConfigFileName 8 | ) 9 | 10 | $OriginalPref = $ProgressPreference # Default is 'Continue' 11 | $ProgressPreference = "SilentlyContinue" 12 | 13 | try { 14 | Write-Host "Starting Lab Build" -ForegroundColor Yellow 15 | 16 | $ModulePath = $script:PSCommandPath 17 | $Config = Get-LabConfig -ConfigFileName $ConfigFileName -CreateFolders 18 | 19 | if ($Config.BaseConfig.ServerRef.RefVHDXName -and -not $Config.BaseConfig.SvrVHDX) { 20 | New-LabUnattendXML -BaseConfig $Config.BaseConfig -LabEnvConfig $Config.LabEnvConfig 21 | $Config.BaseConfig.SvrVHDX = New-LabRefVHDX -BuildType Server -BaseConfig $Config.BaseConfig 22 | } 23 | if ($Config.BaseConfig.WorkstationRef.RefVHDXName -and -not $Base.WksVHDX) { 24 | New-LabUnattendXML -BaseConfig $Config.BaseConfig -LabEnvConfig $Config.LabEnvConfig 25 | $Config.BaseConfig.WksVHDX = New-LabRefVHDX -BuildType Workstation -BaseConfig $Config.BaseConfig 26 | } 27 | 28 | New-LabSwitch -LabEnvConfig $Config.LabEnvConfig 29 | 30 | Write-Host "Starting Batch Job to Create VMs." -ForegroundColor Cyan 31 | Write-Host " - No status will be returned while VMs are being created. Please wait." -ForegroundColor Cyan 32 | Write-Host " - Logs can be viewed in $($BaseConfig.LabPath)$($BaseConfig.VMScriptPath)\<VMNAME>\<VMName>.log" -ForegroundColor Cyan 33 | $Job = $Config.LabEnvConfig.ServerVMs | ForEach-Object -AsJob -ThrottleLimit 5 -Parallel { 34 | Import-Module $using:ModulePath -Force 35 | Write-Host "Creating New VM: $($_.VMName)" -ForegroundColor Cyan 36 | $ConfigSplat = @{ 37 | VMConfig = $_ 38 | BaseConfig = $Using:Config.BaseConfig 39 | LabEnvConfig = $Using:Config.LabEnvConfig 40 | } 41 | 42 | New-LabVM @ConfigSplat | Out-Null 43 | } 44 | 45 | $job | Wait-Job | Receive-Job 46 | 47 | foreach ($VM in $Config.LabEnvConfig.ServerVMs) { 48 | Write-Host "Installing Roles for: $($VM.VMName)" 49 | $VMRoles = $VM.VMRoles.Split(",") 50 | foreach($role in $VMRoles) { 51 | $ConfigSplat = @{ 52 | VMConfig = $VM 53 | BaseConfig = $Config.BaseConfig 54 | LabEnvConfig = $Config.LabEnvConfig 55 | } 56 | 57 | Write-Host " - Found Role $($role) for $($VM.VMName)" -ForegroundColor Cyan 58 | Switch($role) { 59 | "RRAS" { 60 | Add-LabRoleRRAS @ConfigSplat 61 | break 62 | } 63 | "DC" { 64 | Update-LabRRAS @ConfigSplat 65 | Add-LabRoleDC @ConfigSplat 66 | break 67 | } 68 | "CA" { 69 | Join-LabDomain @ConfigSplat 70 | Add-LabRoleCA @ConfigSplat 71 | Break 72 | } 73 | "SQL" { 74 | Join-LabDomain @ConfigSplat 75 | Add-LabRoleSQL @ConfigSplat 76 | Add-LabAdditionalApps @ConfigSplat -AppList @("sql-server-management-studio") 77 | Break 78 | } 79 | "CM" { 80 | Join-LabDomain @ConfigSplat 81 | Add-LabRoleCM @ConfigSplat 82 | Add-LabAdditionalApps @ConfigSplat -AppList @("sql-server-management-studio","vscode","snagit") 83 | Break 84 | } 85 | Default {Write-Host "No Role Found"; Break;} 86 | } 87 | } 88 | } 89 | 90 | <# 91 | foreach ($VM in $Script:WksVMs) { 92 | $Script:VMConfig = @{} 93 | ($VM[0]).psobject.properties | foreach-Object {$VMConfig[$_.Name] = $_.Value} 94 | $VMConfig["WksVHDX"] = $Config.BaseConfig["WksVHDX"] 95 | $VMConfig["VMIPAddress"] = "$($Config.LabEnvConfig["EnvIPSubnet"])$($script:VMConfig.VMIPLastOctet)" 96 | $VMConfig["VMWinName"] = "$($script:VMConfig.VMName)" 97 | $VMConfig["VMName"] = "$($Config.LabEnvConfig.Env)-$($script:VMConfig.VMName)" 98 | $VMConfig["VMHDPath"] = "$($Config.BaseConfig.VMPath)\$($script:VMConfig.VMName)\Virtual Hard Disks" 99 | $VMConfig["VMHDName"] = "$($script:VMConfig.VMName)c.vhdx" 100 | $VMConfig["EnableSnapshot"] = if ($Script:VMConfig.EnableSnapshot -eq 1) {$true} else {$false} 101 | $VMConfig["AutoStartup"] = if ($Script:VMConfig.AutoStartup -eq 1) {$true} else {$false} 102 | $VMConfig["StartupMemory"] = [int64]$Script:VMConfig.StartupMemory.Replace('gb','') * 1GB 103 | 104 | Write-Host $VM.VMName 105 | New-LabVM 106 | } 107 | #> 108 | 109 | Write-Host "New lab build Finished." -ForegroundColor Cyan 110 | (Get-date).DateTime 111 | 112 | } 113 | catch { 114 | 115 | } 116 | finally { 117 | $ProgressPreference = $OriginalPref 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /Public/New-LabRefVHDX.ps1: -------------------------------------------------------------------------------- 1 | function New-LabRefVHDX { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [hashtable] 7 | $BaseConfig, 8 | 9 | [Parameter()] 10 | [ValidateNotNullOrEmpty()] 11 | [ValidateSet("Server","Workstation")] 12 | [string] 13 | $BuildType="Server" 14 | 15 | ) 16 | 17 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 18 | 19 | $params = Switch($BuildType) { 20 | "Server" { 21 | @{ 22 | SourcePath = switch($BaseConfig.ServerRef.RefSrcType) 23 | { 24 | "ISO" {$BaseConfig.SvrISO; break;} 25 | "WIM" {$BaseConfig.SvrWim;break;} 26 | default {$BaseConfig.SvrISO;break;} 27 | } 28 | Edition = $BaseConfig.ServerRef.RefIndex 29 | VhdPath = Join-Path -Path $BaseConfig.PathRefImage -ChildPath $BaseConfig.ServerRef.RefVHDXName 30 | SizeBytes = [uint64]($BaseConfig.ServerRef.RefHVDSize/1) 31 | UnattendPath = Join-Path -Path $BaseConfig.VMPath -ChildPath "Unattend.xml" 32 | VhdType = "Dynamic" 33 | VhdFormat = "VHDX" 34 | DiskLayout = "UEFI" 35 | Feature = if (-not ([string]::IsNullOrEmpty($BaseConfig.ServerRef.RefFeature))) {$BaseConfig.ServerRef.RefFeature} else {$null} 36 | } 37 | } 38 | "Workstation" { 39 | @{ 40 | SourcePath = switch($BaseConfig.WorkstationRef.RefSrcType) 41 | { 42 | "ISO" {$BaseConfig.WksISO; break;} 43 | "WIM" {$BaseConfig.WksWim;break;} 44 | default {$BaseConfig.WksISO;break;} 45 | } 46 | Edition = $BaseConfig.WorkstationRef.RefIndex 47 | VhdPath = Join-Path -Path $BaseConfig.PathRefImage -ChildPath $BaseConfig.ServerRef.RefVHDXName 48 | SizeBytes = [uint64]($BaseConfig.WorkstationRef.RefHVDSize/1) 49 | UnattendPath = Join-Path -Path $BaseConfig.VMPath -ChildPath "Unattend.xml" 50 | VhdType = "Dynamic" 51 | VhdFormat = "VHDX" 52 | DiskLayout = "UEFI" 53 | Feature = if (-not ([string]::IsNullOrEmpty($BaseConfig.WorkstationRef.RefFeature))) {$BaseConfig.WorkstationRef.RefFeature} else {$null} 54 | } 55 | } 56 | default {$null; break;} 57 | } 58 | 59 | if (-not (Test-Path -Path $params.VhdPath -ErrorAction SilentlyContinue)) { 60 | $module = Get-Module -ListAvailable -Name 'Hyper-ConvertImage' 61 | if ($module.count -lt 1) { 62 | Install-Module -Name 'Hyper-ConvertImage' 63 | $module = Get-Module -ListAvailable -Name 'Hyper-ConvertImage' 64 | } 65 | #Hyper-ConvertImage doesn't play nice with PowerShell 7 so we need to force it to use Powershell 5 with the -UseWindowsPowerShell parameter 66 | if ($PSVersionTable.PSVersion.Major -eq 7) { 67 | Import-Module -Name (Split-Path $module.ModuleBase -Parent) -UseWindowsPowerShell -ErrorAction SilentlyContinue 3>$null 68 | } 69 | else { 70 | Import-Module -Name 'Hyper-ConvertImage' 71 | } 72 | Convert-WindowsImage @Params 73 | } 74 | 75 | if(Test-Path $params.VhdPath) { 76 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 77 | Return $params.VhdPath 78 | } 79 | else { 80 | Throw "Failed to create VHDX." 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /Public/New-LabSwitch.ps1: -------------------------------------------------------------------------------- 1 | function New-LabSwitch { 2 | [cmdletbinding()] 3 | Param 4 | ( 5 | 6 | [Parameter()] 7 | [hashtable] 8 | $LabEnvConfig, 9 | 10 | [Parameter()] 11 | [String] 12 | $SwitchName = $LabEnvConfig.EnvSwitchName, 13 | 14 | [Parameter()] 15 | [String] 16 | $IPSubnet = $LabEnvConfig.EnvIPSubnet, 17 | 18 | [Parameter()] 19 | [String] 20 | $RRASName = $LabEnvConfig.RRASName, 21 | 22 | [Parameter()] 23 | [Switch] 24 | $RemoveExisting 25 | 26 | ) 27 | 28 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 29 | 30 | $ExistingSwitch = Get-VMSwitch -Name $SwitchName -ErrorAction SilentlyContinue 31 | if (-not $ExistingSwitch) { 32 | New-VMSwitch -Name $SwitchName -SwitchType Internal | Out-Null 33 | } 34 | 35 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 36 | } -------------------------------------------------------------------------------- /Public/New-LabVM.ps1: -------------------------------------------------------------------------------- 1 | 2 | function New-LabVM { 3 | [cmdletbinding()] 4 | param ( 5 | 6 | [Parameter()] 7 | [PSCustomObject] 8 | $VMConfig, 9 | 10 | [Parameter()] 11 | [hashtable] 12 | $BaseConfig, 13 | 14 | [Parameter()] 15 | [hashtable] 16 | $LabEnvConfig, 17 | 18 | [Parameter()] 19 | [ValidateNotNullOrEmpty()] 20 | [string] 21 | $ENVName = $LabEnvConfig.Env, 22 | 23 | [Parameter()] 24 | [ValidateNotNullOrEmpty()] 25 | [string] 26 | $VMName = $VMConfig.VMName, 27 | 28 | [Parameter()] 29 | [ValidateNotNullOrEmpty()] 30 | [string] 31 | $VMWinName = $VMConfig.VMWinName, 32 | 33 | [Parameter()] 34 | [ValidateNotNullOrEmpty()] 35 | [string] 36 | $ReferenceVHDX = $BaseConfig.SvrVHDX, 37 | 38 | [Parameter()] 39 | [ValidateNotNullOrEmpty()] 40 | [string] 41 | $VMPath = $BaseConfig.VMPath, 42 | 43 | [Parameter()] 44 | [ValidateNotNullOrEmpty()] 45 | [string] 46 | $VMHDPath = $VMConfig.VMHDPath, 47 | 48 | [Parameter()] 49 | [ValidateNotNullOrEmpty()] 50 | [string] 51 | $VMHDName = $VMConfig.VMHDName, 52 | 53 | [Parameter()] 54 | [UInt64] 55 | [ValidateNotNullOrEmpty()] 56 | [ValidateRange(512MB, 64TB)] 57 | $StartupMemory = ($VMConfig.StartupMemory /1), 58 | 59 | [Parameter()] 60 | [UInt64] 61 | $HDDSize = ($VMConfig.VMHDDSize / 1), 62 | 63 | [Parameter()] 64 | [int] 65 | $ProcessorCount = $VMConfig.ProcessorCount, 66 | 67 | [ValidateNotNullOrEmpty()] 68 | [int] 69 | $Generation = $VMConfig.Generation, 70 | 71 | [Parameter()] 72 | [switch] 73 | $EnableSnapshot = $VMConfig.EnableSnapshot, 74 | 75 | [Parameter()] 76 | [switch] 77 | $StartUp = $VMConfig.AutoStartup, 78 | 79 | [Parameter()] 80 | [string] 81 | $DomainName = $LabEnvConfig.EnvFQDN, 82 | 83 | [Parameter()] 84 | [ValidateNotNullOrEmpty()] 85 | [string] 86 | $IPAddress = $VMConfig.IPAddress, 87 | 88 | [Parameter()] 89 | [ValidateNotNullOrEmpty()] 90 | [string] 91 | $SwitchName = $LabEnvConfig.EnvSwitchName, 92 | 93 | [Parameter()] 94 | [ValidateNotNullOrEmpty()] 95 | [int] 96 | $VLanID = $LabEnvConfig.VLanID, 97 | 98 | [Parameter()] 99 | [pscredential] 100 | $LocalAdminCreds = $BaseConfig.LocalAdminCreds, 101 | 102 | [Parameter()] 103 | [ValidateNotNullOrEmpty()] 104 | [string] 105 | $ScriptPath = $BaseConfig.VMScriptPath, 106 | 107 | [Parameter()] 108 | [ValidateNotNullOrEmpty()] 109 | [string] 110 | $LogPath = $BaseConfig.VMLogPath, 111 | 112 | [Parameter()] 113 | [ValidateNotNullOrEmpty()] 114 | [string] 115 | $LabPath = $BaseConfig.LabPath 116 | 117 | ) 118 | 119 | #Setting these to avoiding needing to pass in creds with each lab connection test 120 | $Script:LocalAdminCreds = $BaseConfig.LocalAdminCreds 121 | $Script:DomainAdminCreds = $BaseConfig.DomainAdminCreds 122 | 123 | #region Standard Setup 124 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 125 | $ClientScriptPath = "$($ClientDriveRoot)$($ScriptPath)" 126 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 127 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 128 | } 129 | 130 | Start-Transcript -Path "$($LabScriptPath)\$($VMName).log" -Force -ErrorAction SilentlyContinue | Out-Null 131 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 132 | Write-Host "BaseConfig: $($BaseConfig)" 133 | Write-Host "ReferenceVHDX: $($ReferenceVHDX)" 134 | 135 | $LogPath = "$($ClientDriveRoot)$($LogPath)" 136 | $SBDefaultParams = @" 137 | param 138 | ( 139 | `$_LogPath = "$($LogPath)" 140 | ) 141 | "@ 142 | 143 | $SBScriptTemplateBegin = { 144 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 145 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 146 | } 147 | 148 | $_LogFile = "$($_LogPath)\Transcript.log"; 149 | 150 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 151 | Write-Output "Logging to $_LogFile"; 152 | 153 | #region Do Stuff Here 154 | } 155 | 156 | $SBScriptTemplateEnd = { 157 | #endregion 158 | Stop-Transcript | Out-Null 159 | } 160 | 161 | #endregion 162 | 163 | #$Password = ConvertTo-SecureString -String $LabEnvConfig.EnvAdminPW -AsPlainText -Force 164 | #$LocalAdminCreds = new-object -typename System.Management.Automation.PSCredential($BaseConfig.LocalAdminName, $Password) 165 | 166 | if (Get-VM -Name $VMName -ErrorAction SilentlyContinue) { 167 | Write-Host "VM Already Exists. Skipping Creating VM." 168 | } 169 | else { 170 | #region Script Blocks 171 | 172 | $SBResizeRenameComputerParams = @" 173 | param 174 | ( 175 | `$_LogPath = "$($LogPath)", 176 | `$_VMWinName = "$($VMWinName)" 177 | ) 178 | "@ 179 | $SBResizeRenameComputer = { 180 | #Disable IE Enhanced Security and UAC 181 | $AdminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" 182 | $UserKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" 183 | Set-ItemProperty -Path $AdminKey -Name "IsInstalled" -Value 0 184 | Set-ItemProperty -Path $UserKey -Name "IsInstalled" -Value 0 185 | Stop-Process -Name Explorer -Erroraction SilentlyContinue 186 | Write-Host "IE Enhanced Security Configuration (ESC) has been disabled." 187 | Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorAdmin" -Value 00000000 188 | Set-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -Type DWord -Value 1 -Force 189 | Write-Host "User Access Control (UAC) has been disabled." 190 | 191 | $MaxSize = (Get-PartitionSupportedSize -DriveLetter c).SizeMax 192 | $CurrentSize = (Get-Disk).AllocatedSize 193 | if($CurrentSize -lt $MaxSize) { 194 | Resize-Partition -DriveLetter c -Size $MaxSize 195 | } 196 | Rename-Computer -NewName "$($_VMWinName)"; 197 | } 198 | 199 | $ResizeRenameComputer += $SBResizeRenameComputerParams 200 | $ResizeRenameComputer += $SBScriptTemplateBegin.ToString() 201 | $ResizeRenameComputer += $SBResizeRenameComputer.ToString() 202 | $ResizeRenameComputer += $SBScriptTemplateEnd.ToString() 203 | $ResizeRenameComputer | Out-File "$($LabScriptPath)\ResizeRenameComputer.ps1" 204 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" -Exclude "*.log" 205 | #endregion 206 | 207 | if (-not (Test-Path "$($VMHDPath)\$($VMHDName)" -ErrorAction SilentlyContinue)) { 208 | New-Item -Path $VMHDPath -ItemType Directory 209 | Copy-Item -Path $ReferenceVHDX -Destination "$($VMHDPath)\$($VMHDName)" 210 | } 211 | 212 | $VM = Get-VM -Name $VMName -ErrorAction SilentlyContinue 213 | if (-not $VM) { 214 | $VM = New-VM -Name $VMName -MemoryStartupBytes $StartupMemory -VHDPath "$($VMHDPath)\$($VMHDName)" -Generation $Generation -Path $VMPath 215 | if ($EnableSnapshot) { 216 | Set-VM -VM $VM -checkpointtype Production -AutomaticCheckpointsEnabled $False 217 | } 218 | } 219 | 220 | if(-not $HDDSize) {$HDDSize = 150gb /1 } 221 | if(($VM.VMId | Get-VHD).Size -lt $HDDSize ) { 222 | Resize-VHD -Path "$($VMHDPath)\$($VMHDName)" -SizeBytes $HDDSize 223 | } 224 | Enable-VMIntegrationService -VMName $VMName -Name "Guest Service Interface" 225 | Set-VM -name $VMName -checkpointtype Disabled -ProcessorCount $ProcessorCount 226 | $Adapter = Get-VMNetworkAdapter -VMName $VMName 227 | $Adapter | Connect-VMNetworkAdapter -SwitchName $SwitchName 228 | $Adapter | Set-VMNetworkAdapterVlan -VlanId $VLanID -Access 229 | 230 | if ($VM.State -eq "Off") { 231 | write-Host "VM Not Running. Starting VM." 232 | Start-VM -Name $VMName -WarningAction SilentlyContinue 233 | Start-Sleep -Seconds 120 234 | } 235 | 236 | Write-Host $LabEnvConfig.EnvAdminPW 237 | Write-Host $BaseConfig.LocalAdminName 238 | 239 | while (-not (Invoke-Command -VMName $VMName -Credential $LocalAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 240 | foreach ($Script in $Scripts) { 241 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "$($ClientScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 242 | } 243 | 244 | Invoke-LabCommand -FilePath "$($LabScriptPath)\ResizeRenameComputer.ps1" -MessageText "ResizeRenameComputer" -SessionType Local -VMID $VM.VMId 245 | $VMName | Restart-VM -Force -ErrorAction SilentlyContinue 246 | start-sleep -Seconds 120 247 | } 248 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 249 | 250 | Stop-Transcript | Out-Null 251 | } -------------------------------------------------------------------------------- /Public/Update-LabRRAS.ps1: -------------------------------------------------------------------------------- 1 | function Update-LabRRAS { 2 | [cmdletbinding()] 3 | param ( 4 | 5 | [Parameter()] 6 | [PSCustomObject] 7 | $VMConfig, 8 | 9 | [Parameter()] 10 | [hashtable] 11 | $BaseConfig, 12 | 13 | [Parameter()] 14 | [hashtable] 15 | $LabEnvConfig, 16 | 17 | [Parameter()] 18 | [ValidateNotNullOrEmpty()] 19 | [string] 20 | $VMName = $LabEnvConfig.RRASName, 21 | 22 | [Parameter()] 23 | [String] 24 | $SwitchName = $LabEnvConfig.EnvSwitchName, 25 | 26 | [Parameter()] 27 | [ValidateNotNullOrEmpty()] 28 | [string] 29 | $RRASName = $LabEnvConfig.RRASName, 30 | 31 | [Parameter()] 32 | [ValidateNotNullOrEmpty()] 33 | [string] 34 | $ENVName = $LabEnvConfig.Env, 35 | 36 | [Parameter()] 37 | [ValidateNotNullOrEmpty()] 38 | [pscredential] 39 | $LocalAdminCreds = $BaseConfig.LocalAdminCreds, 40 | 41 | [Parameter()] 42 | [ValidateNotNullOrEmpty()] 43 | [pscredential] 44 | $DomainAdminCreds = $BaseConfig.DomainAdminCreds, 45 | 46 | [Parameter()] 47 | [ValidateNotNullOrEmpty()] 48 | [string] 49 | $ScriptPath = $BaseConfig.VMScriptPath, 50 | 51 | [Parameter()] 52 | [ValidateNotNullOrEmpty()] 53 | [string] 54 | $LogPath = $BaseConfig.VMLogPath, 55 | 56 | [Parameter()] 57 | [ValidateNotNullOrEmpty()] 58 | [string] 59 | $LabPath = $BaseConfig.LabPath 60 | 61 | ) 62 | 63 | #region Standard Setup 64 | Write-Host "Starting $($MyInvocation.MyCommand)" -ForegroundColor Cyan 65 | $LabScriptPath = "$($LabPath)$($ScriptPath)\$($VMName)" 66 | $ClientScriptPath = "C:$($ScriptPath)" 67 | 68 | if (-not (Test-Path -Path "$($LabScriptPath)")) { 69 | New-Item -Path "$($LabScriptPath)" -ItemType Directory -ErrorAction SilentlyContinue 70 | } 71 | 72 | $LogPath = "C:$($LogPath)" 73 | $SBDefaultParams = @" 74 | param 75 | ( 76 | `$_LogPath = "$($LogPath)" 77 | ) 78 | "@ 79 | 80 | $SBScriptTemplateBegin = { 81 | if (-not (Test-Path -Path $_LogPath -ErrorAction SilentlyContinue)) { 82 | New-Item -Path $_LogPath -ItemType Directory -ErrorAction SilentlyContinue; 83 | } 84 | 85 | $_LogFile = "$($_LogPath)\Transcript.log"; 86 | 87 | Start-Transcript $_LogFile -Append -IncludeInvocationHeader | Out-Null; 88 | Write-Output "Logging to $_LogFile"; 89 | 90 | #region Do Stuff Here 91 | } 92 | 93 | $SBScriptTemplateEnd = { 94 | #endregion 95 | Stop-Transcript 96 | } 97 | 98 | #endregion 99 | 100 | $VM = Get-VM -VM $VMName 101 | $VM | Get-VMNetworkAdapter | Where-Object {$_.SwitchName -eq $SwitchName} | Remove-VMNetworkAdapter 102 | $NewRRASAdapter = $VM | Add-VMNetworkAdapter -SwitchName $SwitchName -Name $ENVName -Passthru 103 | $NewRRASAdapter | Set-VMNetworkAdapterVlan -VlanId 4 -Access 104 | $NewRRASAdapter | Set-VMNetworkAdapter -DeviceNaming On -Passthru | Rename-VMNetworkAdapter -NewName $ENVName 105 | $LabEnvConfig["RRASMAC"] = $NewRRASAdapter.MACAddress 106 | $RRASMAC = $LabEnvConfig["RRASMAC"] 107 | 108 | #region Script Blocks 109 | 110 | $SBUpdateRRASParams = @" 111 | param 112 | ( 113 | `$_LogPath = "$($LogPath)", 114 | `$_RRASMAC = "$($RRASMAC)", 115 | `$_ENVName = "$($ENVName)" 116 | ) 117 | "@ 118 | 119 | $SBUpdateRRAS = { 120 | $ExistingNIC = Get-ChildItem -Path "HKLM:SYSTEM\CurrentControlSet\Control\Network" -Recurse -ErrorAction 0 | Get-ItemProperty -Name "Name" -ErrorAction 0 | Where-Object {$_.Name -eq $_ENVName} | ForEach-Object { Get-ItemPropertyValue -Path $_.PSPath -Name PnPInstanceId} 121 | if($ExistingNIC) { 122 | $Dev = Get-PnpDevice -class net | Where-Object { $_.Status -eq "Unknown" -and $_.InstanceId -eq $ExistingNIC} 123 | $RemoveKey = "HKLM:\SYSTEM\CurrentControlSet\Enum\$($Dev.InstanceId)" 124 | Get-Item $RemoveKey | Select-Object -ExpandProperty Property | ForEach-Object { Remove-ItemProperty -Path $RemoveKey -Name $_} 125 | } 126 | 127 | $NewNIC = Get-NetAdapter | Where-Object {$_.MACAddress.Replace("-","") -eq $_RRASMAC} 128 | $NewNIC | Rename-NetAdapter -NewName $_ENVName 129 | Get-Service -name "remoteaccess" | Restart-Service -WarningAction SilentlyContinue; 130 | netsh routing ip nat add interface "$($_ENVName)" 131 | $NewNIC | Restart-NetAdapter 132 | Start-Sleep -Seconds 60 133 | Get-Service -name "remoteaccess" | Restart-Service -WarningAction SilentlyContinue; 134 | } 135 | 136 | $UpdateRRAS += $SBUpdateRRASParams 137 | $UpdateRRAS += $SBScriptTemplateBegin.ToString() 138 | $UpdateRRAS += $SBUpdateRRAS.ToString() 139 | $UpdateRRAS += $SBScriptTemplateEnd.ToString() 140 | $UpdateRRAS | Out-File "$($LabScriptPath)\$($ENVName).ps1" 141 | 142 | $Scripts = Get-Item -Path "$($LabScriptPath)\*.*" 143 | 144 | #endregion 145 | 146 | $VM = Get-VM -VM $RRASName 147 | $VM | Start-VM -WarningAction SilentlyContinue 148 | start-sleep 10 149 | 150 | while (-not (Invoke-Command -VMName $VMName -Credential $LocalAdminCreds {Get-Process "LogonUI" -ErrorAction SilentlyContinue;})) {Start-Sleep -seconds 5} 151 | 152 | foreach ($Script in $Scripts) { 153 | Copy-VMFile -VM $VM -SourcePath $Script.FullName -DestinationPath "C:$($ScriptPath)\$($Script.Name)" -CreateFullPath -FileSource Host -Force 154 | } 155 | 156 | Invoke-LabCommand -FilePath "$($LabScriptPath)\$($ENVName).ps1" -MessageText "Update RRAS" -SessionType Local -VMID $VM.VMId 157 | 158 | Write-Host "$($MyInvocation.MyCommand) Complete!" -ForegroundColor Cyan 159 | } -------------------------------------------------------------------------------- /Public/UpgradeCM: -------------------------------------------------------------------------------- 1 | # Import the ConfigurationManager module 2 | Import-Module -FullyQualifiedName "$env:SMS_ADMIN_UI_PATH\..\ConfigurationManager.psd1" 3 | # Get the site code 4 | $SiteCode = (Get-CimInstance -Namespace ROOT/SMS -ClassName SMS_ProviderLocation).SiteCode 5 | # Change to the site psdrive 6 | Set-Location -Path "$($SiteCode):" 7 | # Find the latest update 8 | $LatestSiteUpdate = (Get-CMSiteUpdate -Fast | Sort-Object -Property LastUpdateTime | Select-Object -First 1).Name 9 | # Install the update 10 | Install-CMSiteUpdate -Name $LatestSiteUpdate -Confirm:$false -Force -------------------------------------------------------------------------------- /Public/Write-LogEntry.ps1: -------------------------------------------------------------------------------- 1 | function Write-LogEntry { 2 | [cmdletBinding()] 3 | param ( 4 | [parameter()] 5 | [ValidateSet("Information", "Error")] 6 | [string] 7 | $Type = "Information", 8 | 9 | [parameter(Mandatory = $true)] 10 | [string] 11 | $Message, 12 | 13 | [parameter()] 14 | [ValidateScript({ Test-Path $(Resolve-Path $_) })] 15 | [string] 16 | $Logfile = "$($PSScriptRoot)\Build.log" 17 | ) 18 | switch ($Type) { 19 | 'Error' { 20 | $Severity = 3 21 | break; 22 | } 23 | 'Information' { 24 | $Severity = 6 25 | break; 26 | } 27 | } 28 | $DateTime = New-Object -ComObject WbemScripting.SWbemDateTime 29 | $DateTime.SetVarDate($(Get-Date)) 30 | $UtcValue = $DateTime.Value 31 | $UtcOffset = $UtcValue.Substring(21, $UtcValue.Length - 21) 32 | $scriptname = (Get-PSCallStack)[1] 33 | $logline = ` 34 | "<-not [LOG[$message]LOG]-not >" + ` 35 | "<time=`"$(Get-Date -Format HH:mm:ss.fff)$($UtcOffset)`" " + ` 36 | "date=`"$(Get-Date -Format M-d-yyyy)`" " + ` 37 | "component=`"$($scriptname.Command)`" " + ` 38 | "context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " + ` 39 | "type=`"$Severity`" " + ` 40 | "thread=`"$PID`" " + ` 41 | "file=`"$($Scriptname.ScriptName)`">"; 42 | 43 | $logline | Out-File -Append -Encoding utf8 -FilePath $Logfile -Force 44 | Write-Verbose $Message 45 | 46 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CMHyperHydrate 2 | A PowerShell module designed to rapidly hydrate a ConfigMgr lab on Hyper-V. 3 | 4 | THIS IS A WORK IN PROGRESS. SEVERAL KNOWN ISSUES. USE AT YOUR OWN RISK! 5 | --------------------------------------------------------------------------------