├── .gitignore ├── .vscode └── settings.json ├── ENV.json ├── Lab.Classes.ps1 ├── LabTest.ps1 ├── New-DCUsers.ps1 ├── New-GenerateUsers.ps1 ├── New-LabVHDX.ps1 ├── New-UnattendXml.ps1 ├── New.tests.ps1 ├── NewCAServer.PS1 ├── NewCMSettingfile.ps1 ├── NewENV.Testsold.ps1old ├── NewENV.ps1 ├── NewSCCMServer.PS1 ├── Optimize-AllVHDX.ps1 ├── SVRTemplates.json ├── Unattended.xml ├── WKS-Unattend.xml ├── WriteLogEntry.ps1 ├── new-NDESClientCert.ps1 ├── new-NDESUserCert.ps1 ├── new-ccmwebcert.ps1 ├── new-kdccert.ps1 ├── newCMInstance.ps1 ├── newCMSQLsettingsINI.ps1 ├── newCMWorkStation.ps1 ├── newDCServer.ps1 ├── newRRASServer.ps1 ├── newcmSQLInstance.ps1 ├── readme.md └── v5.Testcases.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | TestExplorerResults.xml 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "powershell.codeFormatting.addWhitespaceAroundPipe": true 3 | } -------------------------------------------------------------------------------- /ENV.json: -------------------------------------------------------------------------------- 1 | { 2 | "ENV": "UsedHouseSalesMan", 3 | "RRASName": "RRAS", 4 | "SQLISO": "D:\\Software\\SQL\\en_sql_server_2019_enterprise_x64_dvd_5e1ecc6b.iso", 5 | "ADKPATH": "D:\\Software\\ADK", 6 | "SCCMPath": "D:\\Software\\ConfigMgr\\ConfigMgr1902", 7 | "WIN16ISO": "D:\\Software\\WindowsServer\\en_windows_server_2019_updated_jan_2021_x64_dvd_5ef22372.iso", 8 | "WINNET35CAB": "D:\\Software\\WindowsServer\\en_windows_server_2019_updated_jan_2021_x64_dvd_5ef22372\\sources\\sxs", 9 | "REFVHDX": "D:\\lab\\ref19c.vhdx", 10 | "SCCMVersion": "PROD", 11 | "SCCMENVType": "PRI", 12 | "SCCMDLPreDown": "1", 13 | "EnableSnapShot": "1", 14 | "ENVConfig": [ 15 | { 16 | "ENV": "UsedHouseSalesMan", 17 | "DomainNetBiosName": "UsedHouse", 18 | "DomainFQDN": "UsedHouse.lab", 19 | "IPSubnet": "172.16.205.", 20 | "VMPath": "D:\\Lab\\UsedHouse", 21 | "SwitchName": "Usedhouse", 22 | "AdminPW": "P@ssw0rd", 23 | "CMSiteCode": "UH1" 24 | }, 25 | { 26 | "ENV": "carlab1", 27 | "DomainNetBiosName": "carlab1", 28 | "DomainFQDN": "carlab1.lab", 29 | "IPSubnet": "172.16.201.", 30 | "VMPath": "D:\\Lab\\carlab1", 31 | "SwitchName": "carlab1", 32 | "AdminPW": "P@ssw0rd", 33 | "CMSiteCode": "CL1" 34 | }, 35 | { 36 | "ENV": "carlab2", 37 | "DomainNetBiosName": "carlab2", 38 | "DomainFQDN": "carlab2.lab", 39 | "IPSubnet": "172.16.202.", 40 | "VMPath": "D:\\Lab\\carlab2", 41 | "SwitchName": "carlab2", 42 | "AdminPW": "P@ssw0rd", 43 | "CMSiteCode": "CL2" 44 | }, 45 | { 46 | "ENV": "IntuneTraining", 47 | "DomainNetBiosName": "IntuneTraining", 48 | "DomainFQDN": "IntuneTraining.lab", 49 | "IPSubnet": "172.16.120.", 50 | "VMPath": "D:\\Lab\\IntuneTraining", 51 | "SwitchName": "IntuneTraining", 52 | "AdminPW": "P@ssw0rd", 53 | "CMSiteCode": "OP1" 54 | }, 55 | { 56 | "ENV": "MMDTraining", 57 | "DomainNetBiosName": "MMDTraining", 58 | "DomainFQDN": "MMDTraining.lab", 59 | "IPSubnet": "172.16.160.", 60 | "VMPath": "D:\\Lab\\MMDTraining", 61 | "SwitchName": "MMDTraining", 62 | "AdminPW": "P@ssw0rd", 63 | "CMSiteCode": "MMD" 64 | }, 65 | { 66 | "ENV": "SHM365LAB", 67 | "DomainNetBiosName": "SHM365LAB", 68 | "DomainFQDN": "SHM365LAB.lab", 69 | "IPSubnet": "172.16.120.", 70 | "VMPath": "D:\\Lab\\SHM365LAB", 71 | "SwitchName": "SHM365LAB", 72 | "AdminPW": "P@ssw0rd", 73 | "CMSiteCode": "OP1" 74 | }, 75 | { 76 | "ENV": "M365", 77 | "DomainNetBiosName": "M365", 78 | "DomainFQDN": "M365.lab", 79 | "IPSubnet": "172.16.130.", 80 | "VMPath": "D:\\Lab\\M365", 81 | "SwitchName": "M365", 82 | "AdminPW": "P@ssw0rd", 83 | "CMSiteCode": "365" 84 | }, 85 | { 86 | "ENV": "MMS", 87 | "DomainNetBiosName": "MMS", 88 | "DomainFQDN": "MMS.lab", 89 | "IPSubnet": "172.16.10.", 90 | "VMPath": "D:\\Lab\\MMS", 91 | "SwitchName": "MMS", 92 | "AdminPW": "P@ssw0rd", 93 | "CMSiteCode": "MMS" 94 | }, 95 | { 96 | "ENV": "TP2", 97 | "DomainNetBiosName": "tp2", 98 | "DomainFQDN": "tp2.corp", 99 | "IPSubnet": "172.16.20.", 100 | "VMPath": "d:\\Lab\\TP2", 101 | "SwitchName": "TP2", 102 | "AdminPW": "P@ssw0rd", 103 | "CMSiteCode": "TP2" 104 | }, 105 | { 106 | "ENV": "TP3", 107 | "DomainNetBiosName": "tp3", 108 | "DomainFQDN": "tp3.lab", 109 | "IPSubnet": "172.16.30.", 110 | "VMPath": "D:\\Lab\\TP3", 111 | "SwitchName": "TP3", 112 | "AdminPW": "P@ssw0rd", 113 | "CMSiteCode": "TP3" 114 | }, 115 | { 116 | "ENV": "TP4", 117 | "DomainNetBiosName": "tp4", 118 | "DomainFQDN": "tp4.lab", 119 | "IPSubnet": "172.16.40.", 120 | "VMPath": "D:\\Lab\\TP4", 121 | "SwitchName": "TP4", 122 | "AdminPW": "P@ssw0rd", 123 | "CMSiteCode": "TP4" 124 | } 125 | ] 126 | } -------------------------------------------------------------------------------- /Lab.Classes.ps1: -------------------------------------------------------------------------------- 1 | class DC { 2 | [string]$Name 3 | [int]$cores 4 | [int]$Ram 5 | [string]$IPAddress 6 | [string]$network 7 | [string]$VHDXpath 8 | [pscredential]$localadmin 9 | [string]$domainFQDN 10 | [string]$AdmPwd 11 | [pscredential]$domainuser 12 | [bool]$VMSnapshotenabled 13 | [string]$refvhdx 14 | Save ([string] $path) { 15 | $this | ConvertTo-Json | Out-File $path 16 | } 17 | load ([string] $path) { 18 | $settings = get-content $path | ConvertFrom-Json 19 | $this.name = $settings.name 20 | $this.cores = $settings.cores 21 | $this.Ram = $settings.ram 22 | $this.IPAddress = $settings.ipaddress 23 | $this.network = $settings.network 24 | $this.VHDXpath = $settings.VHDXpath 25 | $this.domainFQDN = $settings.domainFQDN 26 | $this.AdmPwd = $settings.AdmPwd 27 | $this.VMSnapshotenabled = $settings.vmSnapshotenabled 28 | $this.refvhdx = $settings.refvhdx 29 | } 30 | } 31 | class RRAS { 32 | [string]$Name 33 | [int]$cores 34 | [int]$ram 35 | [string]$ipaddress 36 | [string]$network 37 | [pscredential]$localadmin 38 | [bool]$vmSnapshotenabled 39 | [string]$VHDXpath 40 | [string]$RefVHDX 41 | Save ([string] $path) { 42 | $this | ConvertTo-Json | Out-File $path 43 | } 44 | load ([string]$path) { 45 | $settings = get-content $path | ConvertFrom-Json 46 | $this.name = $settings.name 47 | $this.cores = $settings.cores 48 | $this.ram = $settings.ram 49 | $this.ipaddress = $settings.ipaddress 50 | $this.network = $settings.network 51 | $this.localadmin = $null 52 | $this.vmSnapshotenabled = $settings.vmSnapshotenabled 53 | $this.VHDXpath = $settings.VHDXpath 54 | $this.RefVHDX = $settings.RefVHDX 55 | } 56 | } 57 | class CM { 58 | #14 59 | [string]$name 60 | [int]$cores # 61 | [int]$ram # 62 | [string]$IPAddress # 63 | [string]$network # 64 | [string]$VHDXpath # 65 | [pscredential]$localadmin # 66 | [pscredential]$domainuser # 67 | [string]$AdmPwd # 68 | [string]$domainFQDN # 69 | [bool]$VMSnapshotenabled # 70 | [string]$cmsitecode # 71 | [bool]$SCCMDLPreDownloaded # 72 | [string]$DCIP # 73 | [string]$RefVHDX 74 | [string]$SQLISO 75 | [string]$SCCMPath 76 | [string]$ADKPath 77 | [string]$domainnetbios 78 | [string]$CMServerType 79 | [string]$CASIPAddress 80 | [string]$SCCMVer 81 | [bool]$Built 82 | Save ([string] $path) { 83 | $this | ConvertTo-Json | Out-File $path -Force 84 | } 85 | load ([string] $path) { 86 | $settings = get-content $path | ConvertFrom-Json 87 | $this.name = $settings.name 88 | $this.cores = $settings.cores 89 | $this.ram = $settings.ram 90 | $this.IPAddress = $settings.ipaddress 91 | $this.network = $settings.network 92 | $this.VHDXpath = $settings.VHDXpath 93 | $this.AdmPwd = $settings.AdmPwd 94 | $this.domainFQDN = $settings.domainFQDN 95 | $this.VMSnapshotenabled = $settings.vmSnapshotenabled 96 | $this.cmsitecode = $settings.cmsitecode 97 | $this.SCCMDLPreDownloaded = $settings.SCCMDLPreDownloaded 98 | $this.DCIP = $settings.DCIP 99 | $this.RefVHDX = $settings.refvhdx 100 | $this.SQLISO = $settings.SQLISO 101 | $this.SCCMPath = $settings.SCCMPath 102 | $this.ADKPath = $settings.ADKPath 103 | $this.domainnetbios = $settings.domainnetbios 104 | $this.CMServerType = $settings.CMServerType 105 | $this.CASIPAddress = $settings.CASIPAddress 106 | $this.SCCMVer = $settings.SCCMVer 107 | $this.Built = $settings.built 108 | } 109 | } 110 | class env { 111 | [string]$vmpath 112 | [string]$RefVHDX 113 | [string]$Win16ISOPath 114 | [string]$Win16Net35Cab 115 | [string]$network 116 | [string]$DefaultPwd 117 | Save ([string] $path) { 118 | $this | ConvertTo-Json | Out-File $path 119 | } 120 | } 121 | class CA { 122 | [string]$Name 123 | [int]$cores 124 | [int]$ram 125 | [string]$IPAddress 126 | [string]$network 127 | [string]$VHDXpath 128 | [pscredential]$localadmin 129 | [string]$domainFQDN 130 | [pscredential]$domainuser 131 | [bool]$VMSnapshotenabled 132 | [string]$RefVHDX 133 | [string]$DCIP 134 | save ([string] $path) { 135 | $this | ConvertTo-Json | Out-File $path 136 | } 137 | load ([string] $path) { 138 | $settings = get-content $path | ConvertFrom-Json 139 | $this.name = $settings.name 140 | $this.cores = $settings.cores 141 | $this.Ram = $settings.ram 142 | $this.IPAddress = $settings.ipaddress 143 | $this.network = $settings.network 144 | $this.VHDXpath = $settings.VHDXpath 145 | $this.domainFQDN = $settings.domainFQDN 146 | $this.VMSnapshotenabled = $settings.vmSnapshotenabled 147 | $this.refvhdx = $settings.refvhdx 148 | $this.DCIP = $Settings.DCIP 149 | } 150 | } 151 | class CASP { 152 | 153 | } 154 | class CASC { 155 | 156 | } 157 | class WKS { 158 | 159 | } 160 | 161 | class testframes { 162 | [pscredential]$localadmin 163 | [pscredential]$domuser 164 | [string]$vmpath 165 | [string]$swname 166 | [string]$DomainFQDN 167 | [string]$RefVHDX 168 | } -------------------------------------------------------------------------------- /LabTest.ps1: -------------------------------------------------------------------------------- 1 | function Set-LabSettings { 2 | #process to create x number of workstation clients 3 | #process to create x number of dummy clients 4 | #find a solution to ensure the latest TP is installed 5 | } 6 | 7 | #endregion 8 | #LAZY MODULE TESTING, WILL BE FIXED ONCE COMPLETED 9 | #Who am i kidding this is how it will be for ever :) 10 | foreach ($psfile in get-childitem -Filter *.ps1 | Where-Object { $_.name -notin ("NewENV.Tests.ps1", "labtest.ps1","new.tests.ps1","v5.testcases.ps1") }) { 11 | . $psfile.FullName 12 | } 13 | 14 | #region import JSON Settings 15 | $scriptpath = $PSScriptRoot 16 | $config = Get-Content "$scriptpath\env.json" -Raw | ConvertFrom-Json 17 | $envConfig = $config.ENVConfig | Where-Object { $_.env -eq $config.env } 18 | $script:logfile = "$($envConfig.vmpath)\Build.log" 19 | if (!(Test-Path $envConfig.vmpath)) { new-item -ItemType Directory -Force -Path $envConfig.vmpath | Out-Null } 20 | Write-LogEntry -Type Information -Message "Start of build process for $($config.env) ------" 21 | $admpwd = $envConfig.AdminPW 22 | Write-LogEntry -Type Information -Message "Admin password set to: $admpwd" 23 | $localadmin = new-object -typename System.Management.Automation.PSCredential -argumentlist "administrator", (ConvertTo-SecureString -String $admpwd -AsPlainText -Force) 24 | $domuser = new-object -typename System.Management.Automation.PSCredential -argumentlist "$($envconfig.DomainNetBiosName)\administrator", (ConvertTo-SecureString -String $admpwd -AsPlainText -Force) 25 | $vmpath = $envConfig.VMPath 26 | Write-LogEntry -Type Information -Message "Path for VHDXs set to: $vmpath" 27 | $swname = $envConfig.SwitchName 28 | Write-LogEntry -Type Information -Message "vSwitch name is: $swname" 29 | $ipsub = $envConfig.ipsubnet 30 | Write-LogEntry -type Information -Message "IP Subnet used for this lab is: $ipsub" 31 | $DomainFQDN = $envconfig.DomainFQDN 32 | Write-LogEntry -Type Information -Message "Fully Quilified Domain Name is: $domainfqdn" 33 | $RRASname = $Config.RRASname 34 | Write-LogEntry -Type Information -Message "Routing and Remote Access Services server name is: $RRASName" 35 | $RefVHDX = $config.REFVHDX 36 | Write-LogEntry -Type Information -Message "Path to Reference VHDX is: $RefVHDX" 37 | $domainnetbios = $envconfig.DomainNetBiosName 38 | Write-LogEntry -Type Information -Message "WINNT domain name is: $domainnetbios" 39 | $cmsitecode = $envConfig.CMSiteCode 40 | Write-LogEntry -Type Information -Message "SCCM Site code is: $cmsitecode" 41 | $SCCMDLPreDown = $config.SCCMDLPreDown 42 | Write-LogEntry -Type Information -Message "SCCM Content was Predownloaded: $($sccmdlpredown -eq 1)" 43 | $vmsnapshot = if ($config.Enablesnapshot -eq 1) { $true }else { $false } 44 | Write-LogEntry -Type Information -Message "Snapshots have been: $vmsnapshot" 45 | $unattendpath = $config.REFVHDX -replace ($config.REFVHDX.split('\') | Select-Object -last 1), "Unattended.xml" 46 | Write-LogEntry -Type Information -Message "Windows 2016 unattend file is: $unattendpath" 47 | $SCCMENVType = $Config.SCCMENVType 48 | $SCCMVer = $config.SCCMVersion 49 | #$servertemplates = (Get-Content "$scriptpath\SVRTemplates.json" -Raw | ConvertFrom-Json).ServerTemplates 50 | #endregion 51 | 52 | #region ENVConfig 53 | $envconfig = [env]::new() 54 | $envconfig.vmpath = $vmpath 55 | $envConfig.RefVHDX = $RefVHDX 56 | $envConfig.Win16ISOPath = $config.WIN16ISO 57 | $envConfig.Win16Net35Cab = $config.WINNET35CAB 58 | $envConfig.network = $swname 59 | $envconfig.DefaultPwd = $admpwd 60 | $envConfig.Save("$vmpath`\envconfig.json") 61 | #endregion 62 | 63 | #region RRASConfig 64 | $RRASConfig = [RRAS]::new() 65 | $RRASConfig.name = $RRASname 66 | $RRASConfig.cores = 1 67 | $RRASConfig.ram = 4 68 | $RRASConfig.ipaddress = "$ipsub`1" 69 | $RRASConfig.network = $swname 70 | $RRASConfig.localadmin = $localadmin 71 | $RRASConfig.vmSnapshotenabled = $false 72 | $RRASConfig.VHDXpath = "$(split-path $vmpath)\RRASc.vhdx" 73 | $RRASConfig.RefVHDX = $RefVHDX 74 | $RRASConfig.Save("$(split-path $vmpath)\RRASConfig.json") 75 | #endregion 76 | 77 | #region DCConfig 78 | $DCConfig = [DC]::new() 79 | $DCConfig.Name = "$($config.env)`DC" 80 | $DCConfig.cores = 1 81 | $DCConfig.Ram = 4 82 | $DCConfig.IPAddress = "$ipsub`10" 83 | $DCConfig.network = $swname 84 | $DCConfig.VHDXpath = "$vmpath\$($config.env)`DCc.vhdx" 85 | $DCConfig.localadmin = $localadmin 86 | $DCConfig.domainFQDN = $domainfqdn 87 | $DCConfig.AdmPwd = $admpwd 88 | $DCConfig.domainuser = $domuser 89 | $DCConfig.VMSnapshotenabled = $false 90 | $DCConfig.refvhdx = $RefVHDX 91 | $DCConfig.Save("$vmpath\dcconfig.json") 92 | #endregion 93 | 94 | #region CMConfig - Primary Server 95 | if ($SCCMENVType -eq "PRI") { 96 | $CMConfig = [CM]::new() 97 | $CMConfig.name = "$($config.env)`CM" 98 | $CMConfig.cores = 4 99 | $CMConfig.ram = 12 100 | $CMConfig.IPAddress = "$ipsub`11" 101 | $CMConfig.network = $swname 102 | $CMConfig.VHDXpath = "$vmpath\$($config.env)`CMc.vhdx" 103 | $CMConfig.localadmin = $localadmin 104 | $CMConfig.domainuser = $domuser 105 | $CMConfig.AdmPwd = $admpwd 106 | $CMConfig.domainFQDN = $domainfqdn 107 | $CMConfig.VMSnapshotenabled = $false 108 | $CMConfig.cmsitecode = $cmsitecode 109 | $CMConfig.SCCMDLPreDownloaded = $sccmdlpredown 110 | $CMConfig.DCIP = $DCConfig.IPAddress 111 | $CMConfig.RefVHDX = $RefVHDX 112 | $CMConfig.SQLISO = $config.SQLISO 113 | $CMConfig.SCCMPath = $config.SCCMPath 114 | $CMConfig.ADKPath = $config.ADKPATH 115 | $CMConfig.domainnetbios = $domainnetbios 116 | $CMConfig.CMServerType = "PRI" 117 | $CMConfig.SCCMVer = $SCCMVer 118 | $CMConfig.save("$vmpath\cmconfig.json") 119 | } 120 | #endregion 121 | 122 | #region CMConfig - CAS ENV 123 | else { 124 | $CMCASConfig = [CM]::new() 125 | $CMCASConfig.name = "$($config.env)`CMCAS" 126 | $CMCASConfig.cores = 4 127 | $CMCASConfig.ram = 12 128 | $CMCASConfig.IPAddress = "$ipsub`11" 129 | $CMCASConfig.network = $swname 130 | $CMCASConfig.VHDXpath = "$vmpath\$($config.env)`CMCASc.vhdx" 131 | $CMCASConfig.localadmin = $localadmin 132 | $CMCASConfig.domainuser = $domuser 133 | $CMCASConfig.AdmPwd = $admpwd 134 | $CMCASConfig.domainFQDN = $domainfqdn 135 | $CMCASConfig.VMSnapshotenabled = $false 136 | $CMCASConfig.cmsitecode = $cmsitecode 137 | $CMCASConfig.SCCMDLPreDownloaded = $sccmdlpredown 138 | $CMCASConfig.DCIP = $DCConfig.IPAddress 139 | $CMCASConfig.RefVHDX = $RefVHDX 140 | $CMCASConfig.SQLISO = $config.SQLISO 141 | $CMCASConfig.SCCMPath = $config.SCCMPath 142 | $CMCASConfig.ADKPath = $config.ADKPATH 143 | $CMCASConfig.domainnetbios = $domainnetbios 144 | $CMCASConfig.CMServerType = "CAS" 145 | $CMConfig.SCCMVer = $SCCMVer 146 | $CMCASConfig.save("$vmpath\cmCASconfig.json") 147 | 148 | $CMCASPRIConfig = [CM]::new() 149 | $CMCASPRIConfig.name = "$($config.env)`CMCASPRI" 150 | $CMCASPRIConfig.cores = 4 151 | $CMCASPRIConfig.ram = 12 152 | $CMCASPRIConfig.IPAddress = "$ipsub`12" 153 | $CMCASPRIConfig.network = $swname 154 | $CMCASPRIConfig.VHDXpath = "$vmpath\$($config.env)`CMCASPRIc.vhdx" 155 | $CMCASPRIConfig.localadmin = $localadmin 156 | $CMCASPRIConfig.domainuser = $domuser 157 | $CMCASPRIConfig.AdmPwd = $admpwd 158 | $CMCASPRIConfig.domainFQDN = $domainfqdn 159 | $CMCASPRIConfig.VMSnapshotenabled = $false 160 | $CMCASPRIConfig.cmsitecode = $cmsitecode 161 | $CMCASPRIConfig.SCCMDLPreDownloaded = $sccmdlpredown 162 | $CMCASPRIConfig.DCIP = $DCConfig.IPAddress 163 | $CMCASPRIConfig.RefVHDX = $RefVHDX 164 | $CMCASPRIConfig.SQLISO = $config.SQLISO 165 | $CMCASPRIConfig.SCCMPath = $config.SCCMPath 166 | $CMCASPRIConfig.ADKPath = $config.ADKPATH 167 | $CMCASPRIConfig.domainnetbios = $domainnetbios 168 | $CMCASPRIConfig.CMServerType = "CASPRI" 169 | $CMConfig.SCCMVer = $SCCMVer 170 | $CMCASPRIConfig.CASIPAddress = $CMCASConfig.IPAddress 171 | $CMCASPRIConfig.save("$vmpath\cmCASPRIconfig.json") 172 | } 173 | #endregion 174 | 175 | #region CAConfig 176 | $CAConfig = [CA]::new() 177 | $CAConfig.name = "$($config.env)`CA" 178 | $CAConfig.cores = 1 179 | $CAConfig.Ram = 4 180 | $CAConfig.IPAddress = "$ipsub`20" 181 | $CAConfig.domainuser = $domuser 182 | $CAConfig.localadmin = $localadmin 183 | $CAConfig.network = $swname 184 | $CAConfig.VHDXpath = "$vmpath\$($config.env)`CAc.vhdx" 185 | $CAConfig.domainFQDN = $DomainFQDN 186 | $CAConfig.VMSnapshotenabled = $false 187 | $CAConfig.refvhdx = $RefVHDX 188 | $CAConfig.DCIP = $DCConfig.IPAddress 189 | $CAConfig.save("$vmpath\CAConfig.json") 190 | #endregion 191 | 192 | #region create VMs 193 | new-env -ENVConfig $envconfig 194 | new-RRASServer -RRASConfig $RRASConfig 195 | new-DC -DCConfig $DCConfig 196 | New-CAServer -CAConfig $CAConfig 197 | if ($SCCMENVType -eq "PRI") { 198 | new-SCCMServer -CMConfig $CMConfig 199 | } 200 | else { 201 | new-SCCMServer -CMConfig $CMCASConfig 202 | new-SCCMServer -CMConfig $CMCASPRIConfig 203 | } 204 | #endregion -------------------------------------------------------------------------------- /New-DCUsers.ps1: -------------------------------------------------------------------------------- 1 | Function New-DCUsers { 2 | param( 3 | [Parameter()] 4 | [PSCustomObject] 5 | $UserList, 6 | [Parameter()] 7 | [pscredential] 8 | $DomAdmin, 9 | [Parameter()] 10 | [string] 11 | $DCVM, 12 | [Parameter()] 13 | [string] 14 | $domainname, 15 | [Parameter()] 16 | [string] 17 | $newpwd 18 | ) 19 | $ouname = "AADusers" 20 | $psses = New-PSSession -VMName $DCVM -Credential $DomAdmin 21 | $oucount = (Invoke-Command -Session $psses -ScriptBlock { Param($ou)(Get-ADOrganizationalUnit -filter * | where-Object { $_.name -eq $ou }).Name } -ArgumentList $ouname).count 22 | if ($oucount -eq 0) { 23 | invoke-command -Session $psses -ScriptBlock { Param($ou)new-ADOrganizationalUnit $ou } -ArgumentList $ouname 24 | } 25 | $ou = Invoke-Command -Session $psses -ScriptBlock { Param($ou)Get-ADOrganizationalUnit -filter * | where-Object { $_.name -eq $ou } } -ArgumentList $ouname 26 | foreach ($user in $UserList) { 27 | Invoke-Command -Session $psses -ScriptBlock { Param($fn, $sn, $dn, $ou, $pw)if("$fn.$sn".Length -gt 19){$sam = ("$fn.$sn").substring(0,19)} else {$sam= "$fn.$sn"};new-aduser -name "$fn.$sn" -UserPrincipalName "$fn.$sn@$dn" -path $ou -samaccountname $sam -GivenName $fn -Surname $sn -enabled $true -AccountPassword (ConvertTo-SecureString -String $pw -AsPlainText -Force)} -ArgumentList $user.FirstName.replace("+","").replace(' ',''), $user.lastname.Replace("+","").replace(' ',''), $domainname, $ou.DistinguishedName, $newpwd 28 | } 29 | $mgruser = $UserList | Get-Random 30 | $mgradobj = Invoke-Command -Session $psses -ScriptBlock {Param($fn, $sn) get-aduser -Identity "$fn.$sn"} -ArgumentList $mgruser.FirstName.replace("+","").replace(' ',''), $mgruser.lastname.Replace("+","").replace(' ','') 31 | foreach ($u in ($UserList | Where-Object {$_.firstname -ne $mgruser.FirstName -and $_.lastname -ne $mgruser.lastname})) { 32 | if("$($u.FirstName.replace('+','').replace(' ','')).$($u.lastname.Replace('+','').replace(' ',''))".Length -gt 19) 33 | { 34 | $sam = ("$($u.FirstName.replace('+','').replace(' ','')).$($u.lastname.Replace('+','').replace(' ',''))").substring(0,19) 35 | } else { 36 | $sam= "$($u.FirstName.replace('+','').replace(' ','')).$($u.lastname.Replace('+','').replace(' ',''))" 37 | } 38 | Invoke-Command -Session $psses -ScriptBlock { Param($name,$mgr)set-aduser -Identity $name -Manager (get-aduser -identity $mgr.name)} -ArgumentList $sam, $mgradobj 39 | } 40 | } -------------------------------------------------------------------------------- /New-GenerateUsers.ps1: -------------------------------------------------------------------------------- 1 | function New-GenerateUsers { 2 | param( 3 | [Parameter()] 4 | [int] 5 | $numofusers 6 | ) 7 | $filepath = ".\users.csv" 8 | if (!(test-path $filepath)) { 9 | invoke-webrequest -uri "https://raw.githubusercontent.com/microsoft/sql-server-samples/master/samples/databases/adventure-works/oltp-install-script/Person.csv" -usebasicparsing -OutFile $filepath | Out-Null 10 | } 11 | $rawusers = import-csv -path $filepath -Header 'ID', 'a', 'b', 'title', 'FirstName', 'middlename', 'lastname', 'd', 'e', 'f', 'schema', 'date' -Delimiter '|' 12 | $filteredusers = $rawusers | Get-Random -Count ($numofusers * 2) | Select-object -Property FirstName, middlename, lastname -Unique -First $numofusers 13 | return $filteredusers 14 | } -------------------------------------------------------------------------------- /New-LabVHDX.ps1: -------------------------------------------------------------------------------- 1 | function new-LabVHDX { 2 | param 3 | ( 4 | [parameter(Mandatory)] 5 | [string] 6 | $vhdxpath, 7 | [parameter(Mandatory)] 8 | [string] 9 | $unattend, 10 | [parameter] 11 | [switch] 12 | $core, 13 | [parameter(Mandatory)] 14 | [string] 15 | $WinISO, 16 | [parameter(Mandatory)] 17 | [String] 18 | $WinNet35Cab 19 | ) 20 | $convmod = get-module -ListAvailable -Name 'Hyper-ConvertImage' 21 | if ($convmod.count -ne 1) { 22 | Install-Module -name 'Hyper-ConvertImage' -Scope AllUsers 23 | } 24 | else { 25 | Update-Module -Name 'Hyper-ConvertImage' 26 | } 27 | Import-module -name 'Hyper-ConvertImage' 28 | $cornum = 2 29 | if ($core.IsPresent) { $cornum = 3 }else { $cornum = 4 } 30 | Convert-WindowsImage -SourcePath $WinISO -Edition $cornum -VhdType Dynamic -VhdFormat VHDX -VhdPath $vhdxpath -DiskLayout UEFI -SizeBytes 127gb -UnattendPath $unattend 31 | $drive = (Mount-VHD -Path $vhdxpath -Passthru | Get-Disk | Get-Partition | Where-Object { $_.type -eq 'Basic' }).DriveLetter 32 | new-item "$drive`:\data" -ItemType Directory | Out-Null 33 | $netfiles = get-childitem -Path $winnet35cab -Filter "*netfx*" 34 | foreach ($net in $netfiles) { 35 | Copy-Item -Path $net.fullname -Destination "$drive`:\data\$($net.name)" 36 | } 37 | Dismount-VHD -Path $vhdxpath 38 | } -------------------------------------------------------------------------------- /New-UnattendXml.ps1: -------------------------------------------------------------------------------- 1 | function New-UnattendXml { 2 | [CmdletBinding()] 3 | Param 4 | ( 5 | # The password to have unattnd.xml set the local Administrator to 6 | [Parameter(Mandatory)] 7 | [ValidateNotNull()] 8 | [ValidateNotNullOrEmpty()] 9 | [Alias('password')] 10 | [string] 11 | $admpwd, 12 | [Parameter(Mandatory)] 13 | [string] 14 | $outfile, 15 | [Parameter] 16 | [switch] 17 | $WKS, 18 | [Parameter] 19 | [string] 20 | $domainFQDN, 21 | [Parameter] 22 | [string] 23 | $Adminuname, 24 | [Parameter] 25 | [string] 26 | $domainNetBios 27 | ) 28 | if ($WKS.IsPresent) { 29 | $unattendTemplate = [xml]@" 30 | 31 | 32 | 33 | 34 | 35 | 36 | <> 37 | <> 38 | <> 39 | 40 | <> 41 | 42 | 43 | 44 | 45 | "@ 46 | $unattendTemplate -replace "<>", $admpwd 47 | $unattendTemplate -replace "<>", $domainNetBios 48 | $unattendTemplate -replace "<>", $Adminuname 49 | $unattendTemplate -replace "<>", $domainFQDN 50 | $unattendTemplate | Out-File -FilePath $outfile -Encoding utf8 51 | } 52 | else { 53 | $unattendTemplate = [xml]@" 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | <> 67 | True</PlainText> 68 | </AdministratorPassword> 69 | </UserAccounts> 70 | <OOBE> 71 | <VMModeOptimizations> 72 | <SkipNotifyUILanguageChange>true</SkipNotifyUILanguageChange> 73 | <SkipWinREInitialization>true</SkipWinREInitialization> 74 | </VMModeOptimizations> 75 | <SkipMachineOOBE>true</SkipMachineOOBE> 76 | <HideEULAPage>true</HideEULAPage> 77 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 78 | <ProtectYourPC>3</ProtectYourPC> 79 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 80 | <NetworkLocation>Work</NetworkLocation> 81 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 82 | </OOBE> 83 | </component> 84 | <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"> 85 | <InputLocale>en-au</InputLocale> 86 | <SystemLocale>en-us</SystemLocale> 87 | <UILanguage>en-au</UILanguage> 88 | <UILanguageFallback>en-us</UILanguageFallback> 89 | <UserLocale>en-au</UserLocale> 90 | </component> 91 | </settings> 92 | </unattend> 93 | "@ 94 | $unattendTemplate -replace "<<ADM_PWD>>", $admpwd | Out-File -FilePath $outfile -Encoding utf8 95 | } 96 | } -------------------------------------------------------------------------------- /New.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll { 2 | . .\v5.Testcases.ps1 3 | } 4 | 5 | #region ENV 6 | Describe 'Env' -Tag 'ENV' { 7 | it "ReferenceVHDX" -Tag "RefVHDX" { 8 | get-RefVHDXstate -spath $PSScriptRoot | should -be $true 9 | } 10 | it 'Internet VSwitch should exist' -Tag "ExternalSwitch" { 11 | get-ExternalSwitch | should -be $true 12 | } 13 | it "Lab vSwitch should exist" -Tag "LabSwitch" { 14 | get-labswitch -spath $PSScriptRoot | should -be $true 15 | } 16 | } 17 | #endregion 18 | 19 | #region RRAS 20 | Describe 'RRAS' -Tag 'RRAS' { 21 | it "RRAS VHDX should exist" -Tag "RRASVHDX" { 22 | get-rrasVHDXstate -spath $PSScriptRoot | should -be $true 23 | } 24 | it "RRAS VM should exist" -Tag "RRASVM" { 25 | get-rrasVMexists -spath $PSScriptRoot | should -be 1 26 | } 27 | it "RRAS is running" -Tag "RRASRunning" { 28 | get-rrasVMrunning -spath $PSScriptRoot | should -be 1 29 | } 30 | it "RRAS Features enabled" -Tag "RRASFeatures" { 31 | get-rrasVMfeatures -spath $PSScriptRoot | should -be "Installed" 32 | } 33 | it "RRAS Ext NIC Connected" -Tag "RRASExtNIC" { 34 | (get-rrasVMExternalNIC -spath $PSScriptRoot) | should -be "External" 35 | } 36 | it "RRAS Lab NIC Connected" -Tag "RRASLabNIC" { 37 | get-rrasVMLabNIC -spath $PSScriptRoot | should -be $true 38 | } 39 | it "RRAS Routing" -Tag "RRASVPN" { 40 | get-rrasvmVPN -spath $PSScriptRoot | should -be "RemoteAccessVpn" 41 | } 42 | it "RRAS Lab IP Correct" -Tag "RRASLabIP" { 43 | get-rrasVMIP -spath $PSScriptRoot | should -Be $true 44 | } 45 | it "RRAS has Internet" -Tag "RRASInternet" { 46 | get-rrasvminternet -spath $PSScriptRoot | should -Be $true 47 | } 48 | } 49 | #endregion 50 | 51 | #region DC 52 | describe "DC" -Tag "DC" { 53 | it 'DC VHDX Should Exist' -Tag "DCVHDX" { 54 | get-dcvhdxstate -spath $PSScriptRoot | should -be $true 55 | } 56 | it "DC Should Exist" -Tag "DCVM" { 57 | get-dcvmexists -spath $PSScriptRoot | should -be 1 58 | } 59 | it "DC Should be running" -Tag "DCRunning" { 60 | get-dcvmrunning -spath $PSScriptRoot | should -be 1 61 | } 62 | it "DC IP Set correctly" -Tag "DCIP" { 63 | get-DCVMIP -spath $PSScriptRoot | should -be $true 64 | } 65 | it 'DC Domain Services Installed' -Tag "DCFeatures" { 66 | get-dcvmfeature -spath $PSScriptRoot | should -be $true 67 | } 68 | it 'DC has access to Internet' -Tag "DCInternet" { 69 | get-DCVMInternet -spath $PSScriptRoot | should -be $true 70 | } 71 | it "DC is promoted" -Tag "DCPromo" { 72 | get-DCVMPromoted -spath $PSScriptRoot | should -be $true 73 | } 74 | it "DC DHCP Scope enabled" -Tag "DCDHCP" { 75 | get-DCVMDHCPScope -spath $PSScriptRoot | should -be $true 76 | } 77 | it "DC CM Servers Group exists" -Tag "DCCM" { 78 | Get-DCVMCMGroupExists -spath $PSScriptRoot | should -be $true 79 | } 80 | } 81 | #endregion 82 | 83 | #region CA 84 | Describe "CA" -tag "CA" { 85 | it 'CA VHDX Should Exist' -tag "CAVHDX" { 86 | get-CAvhdxstate -spath $PSScriptRoot | should -be $true 87 | } 88 | it "CA Should Exist" -tag "CAVM" { 89 | get-CAvmexists -spath $PSScriptRoot | should -be 1 90 | } 91 | it "CA Should be running" -tag "CARunning" { 92 | get-CAvmrunning -spath $PSScriptRoot | should -be 1 93 | } 94 | it "CA IP Set correctly" -tag "CAIP" { 95 | get-CAVMIP -spath $PSScriptRoot | should -be $true 96 | } 97 | it 'CA Certificate features Installed' -tag "CAFeatures" { 98 | get-CAvmfeature -spath $PSScriptRoot | should -be $true 99 | } 100 | it 'CA has access to Internet' -tag "CAInternet" { 101 | get-CAVMInternet -spath $PSScriptRoot | should -be $true 102 | } 103 | it 'CA can ping domain' -tag 'CADOM' { 104 | Get-CAVMDomain -spath $PSScriptRoot | should -be $true 105 | } 106 | } 107 | #endregion 108 | 109 | #region CMpri 110 | Describe "CM" -tag "CM" { 111 | it 'CM VHDX Should Exist' -tag "CMVHDX" { 112 | get-CMprivhdxstate -spath $PSScriptRoot | should -be $true 113 | } 114 | it "CM Should Exist" -tag "CMVM" { 115 | get-CMprivmexists -spath $PSScriptRoot | should -be 1 116 | } 117 | it "CM Should be running" -tag "CMRunning" { 118 | get-CMprivmrunning -spath $PSScriptRoot | should -be 1 119 | } 120 | it "CM IP Set correctly" -tag "CMIP" { 121 | get-CMpriVMIP -spath $PSScriptRoot | should -be $true 122 | } 123 | it 'CM Features Installed' -tag "CMFeatures" { 124 | get-CMprivmfeature -spath $PSScriptRoot | should -be $true 125 | } 126 | it 'CM .Net is Installed' -tag "CMNETFeatures" { 127 | get-CMprivmnetfeature -spath $PSScriptRoot | should -be $true 128 | } 129 | it 'CM has access to Internet' -tag "CMInternet" { 130 | get-CMpriVMInternet -spath $PSScriptRoot | should -be $true 131 | } 132 | it 'CM can ping domain' -tag 'CMDOM' { 133 | Get-CMpriVMDomain -spath $PSScriptRoot | should -be $true 134 | } 135 | it 'CM SQL Instance is installed' -tag "CMSQLInst" { 136 | Get-CMPriVMSQLSvc -spath $PSScriptRoot | should -be 1 137 | } 138 | it 'CM ADK Installed' -tag 'CMADK' { 139 | get-CMPriVMADK -spath $PSScriptRoot | should -be $true 140 | } 141 | it 'CM Server in Group' -tag 'CMServerGroup' { 142 | get-CMPriVMSVRGRP -spath $PSScriptRoot | should -be 1 143 | } 144 | it 'CM SCCM Installed' -tag 'CMInstalled' { 145 | get-CMPriVMCMInstalled -spath $PSScriptRoot | should -be 1 146 | } 147 | it 'CM SCCM Console Installed' -tag 'CMConsole' { 148 | get-CMPriVMCMConsoleInstalled -spath $PSScriptRoot | should -be $true 149 | } 150 | it 'CM Site Boundary added' -tag 'CMSiteBound' { 151 | get-CMPriVMCMBoundary -spath $PSScriptRoot | should -be 1 152 | } 153 | it 'CM System Discovery enabled' -tag 'CMSysDisc' { 154 | get-CMPriVMCMDiscovery -spath $PSScriptRoot | should -be 6 155 | } 156 | } 157 | #endregion -------------------------------------------------------------------------------- /NewCAServer.PS1: -------------------------------------------------------------------------------- 1 | function new-CAServer { 2 | param( 3 | [Parameter(ParameterSetName = 'CAClass')] 4 | [CA] 5 | $CAConfig, 6 | [Parameter(ParameterSetName = 'NoClass')] 7 | [string] 8 | $VHDXpath, 9 | [Parameter(ParameterSetName = 'NoClass')] 10 | [pscredential] 11 | $localadmin, 12 | [Parameter(ParameterSetName = 'NoClass')] 13 | [string] 14 | $Network, 15 | [Parameter(ParameterSetName = 'NoClass')] 16 | [string] 17 | $ipAddress, 18 | [Parameter(ParameterSetName = 'NoClass')] 19 | [string] 20 | $DomainFQDN, 21 | [Parameter(ParameterSetName = 'NoClass')] 22 | [pscredential] 23 | $domainuser, 24 | [parameter(ParameterSetName = 'NoClass', Mandatory = $false)] 25 | [switch] 26 | $vmSnapshotenabled, 27 | [Parameter(ParameterSetName = 'NoClass')] 28 | [int] 29 | $cores, 30 | [Parameter(ParameterSetName = 'NoClass')] 31 | [int] 32 | $ram, 33 | [Parameter(ParameterSetName = 'NoClass')] 34 | [string] 35 | $name, 36 | [Parameter(ParameterSetName = 'NoClass')] 37 | [string] 38 | $refvhdx, 39 | [Parameter(ParameterSetName = 'NoClass')] 40 | [string] 41 | $DCIP 42 | ) 43 | if (!$PSBoundParameters.ContainsKey('CAConfig')) { 44 | $CAConfig = [CA]::new() 45 | $CAConfig.Name = $name 46 | $CAConfig.cores = $cores 47 | $CAConfig.ram = $ram 48 | $CAConfig.IPAddress = $ipAddress 49 | $CAConfig.network = $Network 50 | $CAConfig.VHDXpath = $VHDXpath 51 | $CAConfig.localadmin = $localadmin 52 | $CACOnfig.domainFQDN = $DomainFQDN 53 | $CAConfig.domainuser = $domainuser 54 | $CAConfig.VMSnapshotenabled = $vmSnapshotenabled.IsPresent 55 | $CAConfig.RefVHDX = $refvhdx 56 | $CAConfig.DCIP = $DCIP 57 | } 58 | $ipsubnet = $CAConfig.IPAddress.substring(0, ($CAConfig.IPAddress.length - ([ipaddress] $CAConfig.IPAddress).GetAddressBytes()[3].count - 1)) 59 | Write-LogEntry -Message "CA Server Started: $(Get-Date)" -Type Information 60 | Write-LogEntry -Message "CA Settings are: $($CAConfig | ConvertTo-Json)" -Type Information 61 | Write-LogEntry -Message "New CA server name is: $($caconfig.name)" -Type Information 62 | Write-LogEntry -Message "Path for the VHDX for $($CAConfig.name) is: $($CAConfig.VHDXpath)" -Type Information 63 | if (!(Invoke-Pester -tagfilter "CAVM" -passthru -output none).result -ne "Passed") { 64 | if ((Invoke-Pester -tagfilter "CAVHDX" -passthru -output none).result -eq "Passed") { 65 | Write-LogEntry -Message "CA VHDX already exists at path: $($CAConfig.VHDXpath) Please clean up and Rerun. BUILD STOPPED" -Type Error 66 | throw "CA VHDX Already Exists at path: $($CAConfig.VHDXpath) Please clean up and Rerun." 67 | } 68 | else { 69 | Copy-Item -Path $CAConfig.RefVHDX -Destination $CAConfig.VHDXpath 70 | Write-LogEntry -Message "Reference VHDX $($CAConfig.RefVHDX) has been copied to: $($CAConfig.VHDXpath)" -Type Information 71 | } 72 | if ((Invoke-Pester -tagfilter "CAVHDX" -passthru -output none).result -ne "Passed") { 73 | Write-LogEntry -Message "Error creating the VHDX for CA. BUILD STOPPED" -Type Error 74 | throw "Error Creating the VHDX for CA" 75 | } 76 | else { 77 | Write-LogEntry -Message "Starting to create CA Server" -Type Information 78 | $vm = new-vm -name $cAconfig.name -MemoryStartupBytes ($CAConfig.ram * 1gb) -VHDPath $CAConfig.VHDXpath -Generation 2 | Set-VMMemory -DynamicMemoryEnabled:$false 79 | $vm | Set-VMProcessor -Count $CAConfig.cores 80 | if (!($CAConfig.VMSnapshotenabled)) { 81 | set-vm -name $caconfig.name -checkpointtype Disabled 82 | } 83 | Write-LogEntry -Message "$($cAconfig.name) has been created" -Type Information 84 | start-vm -Name $cAconfig.name 85 | Write-LogEntry -Message "CA Server named $($caconfig.name) has been started" -Type Information 86 | Get-VMNetworkAdapter -VMName $cAconfig.name | Connect-VMNetworkAdapter -SwitchName $CAConfig.network 87 | Write-LogEntry -Message "vSwitch named $($CAConfig.network) has been attached to $($cAconfig.name)" -Type Information 88 | } 89 | while ((Invoke-Command -VMName $cAconfig.name -Credential $CAConfig.localadmin { "Test" } -ErrorAction SilentlyContinue) -ne "Test") { Start-Sleep -Seconds 5 } 90 | $cAsessionLA = New-PSSession -vmname $cAconfig.name -credential $CAConfig.localadmin 91 | Write-LogEntry -Message "PowerShell Direct session for $($CAConfig.localadmin.UserName) has been initiated to $($cAconfig.name)" -Type Information 92 | if ($null -eq $casessionLA) { throw "Issue with CA Local User Account" } 93 | $canics = Invoke-Command -session $casessionLA -ScriptBlock { Get-NetAdapter } 94 | Write-LogEntry -Message "Network Adaptor $($canics -join ",") were found on $($cAconfig.name)" -Type Information 95 | if ((Invoke-Pester -tagfilter "CAIP" -passthru -output none).result -ne "Passed") { 96 | $IPGateway = "$ipsubnet`1" 97 | $null = Invoke-Command -session $casessionLA -ScriptBlock { param($t, $i, $g, $d) new-NetIPAddress -InterfaceIndex $t -AddressFamily IPv4 -IPAddress "$i" -PrefixLength 24 -DefaultGateway "$g"; Set-DnsClientServerAddress -ServerAddresses ($d) -InterfaceIndex $t } -ArgumentList $canics.InterfaceIndex, $caconfig.ipaddress, $IPGateway, $CAConfig.DCIP | Out-Null 98 | Write-LogEntry -Message "IP Address $($CAConfig.IPAddress) has been assigned to $($cAconfig.name)" -Type Information 99 | start-sleep 120 100 | } 101 | if ((Invoke-Pester -tagfilter "CADOM" -passthru -output none).result -eq "Passed") { 102 | while ((Invoke-Command -VMName $caconfig.name -Credential $CAConfig.localadmin { param($i)(test-netconnection "$i`10" -ErrorAction SilentlyContinue).pingsucceeded } -ArgumentList $ipsub -ErrorAction SilentlyContinue) -ne $true -and $stop -ne (get-date)) { Start-Sleep -Seconds 5 } 103 | Invoke-Command -session $casessionLA -ErrorAction SilentlyContinue -ScriptBlock { param($env, $DU) Clear-DnsClientCache; Add-Computer -DomainName $env -domainCredential $DU -Restart; Start-Sleep -Seconds 15; Restart-Computer -Force -Delay 0 } -ArgumentList $CACOnfig.domainFQDN, $CAConfig.domainuser 104 | Write-LogEntry -Message "$($cAconfig.name) has been joined to $($CACOnfig.domainFQDN)" -Type Information 105 | $stop = (get-date).AddMinutes(5) 106 | while ((Invoke-Command -VMName $caconfig.name -Credential $CAConfig.domainuser { "Test" } -ErrorAction SilentlyContinue) -ne "Test" -and $stop -ne (get-date)) { Start-Sleep -Seconds 5 } 107 | } 108 | else { 109 | throw "CA Server can't resolve $($CACOnfig.domainFQDN)" 110 | } 111 | $casession = New-PSSession -VMName $cAconfig.name -Credential $CAConfig.domainuser 112 | Write-LogEntry -Message "PowerShell Direct session for user $($CAConfig.domainuser.UserName) has been initiated to $($cAconfig.name)" -Type Information 113 | if ((Invoke-Pester -tagfilter "CAFeatures" -passthru -output none).result -ne "Passed") { 114 | Invoke-Command -session $casession -ScriptBlock { Add-WindowsFeature -Name ADCS-Cert-Authority, ADCS-Web-Enrollment, Storage-Services, Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content, Web-Http-Redirect, Web-Http-Logging, Web-Log-Libraries, Web-Request-Monitor, Web-Http-Tracing, Web-Stat-Compression, Web-Filtering, Web-Windows-Auth, Web-ASP, Web-ISAPI-Ext, Web-Mgmt-Console, Web-Metabase, NET-Framework-45-Core, NET-WCF-TCP-PortSharing45, RSAT-ADCS-Mgmt, RSAT-Online-Responder, RSAT-AD-PowerShell | Out-Null } 115 | Write-LogEntry -Message "Cert Authority feature has been enabled on $($caconfig.name)" -Type Information 116 | } 117 | # add new service account 118 | # grant service account permissions to the CA to manage certs 119 | 120 | Invoke-Command -Session $casession -ScriptBlock { Set-ItemProperty -path HKLM:\SOFTWARE\Microsoft\ServerManager -name DoNotOpenServerManagerAtLogon -Type DWord -value "1" -Force } 121 | Invoke-Command -session $casession -ScriptBlock { param($env)Install-AdcsCertificationAuthority -CAType EnterpriseRootCa -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" -KeyLength 2048 -HashAlgorithmName SHA256 -validityPeriod Months -validityPeriodUnits 6 -CACommonName "$($env)-root" -confirm:$false | Out-Null } -ArgumentList $CAConfig.network 122 | Invoke-Command -session $casession -ScriptBlock { Install-AdcsWebEnrollment -Confirm:$false | Out-Null } 123 | Invoke-Command -session $casession -ScriptBlock { Get-CACrlDistributionPoint | Where-Object { $_.uri -like "http*" } | Remove-CACrlDistributionPoint -Confirm:$false | Out-Null } 124 | Invoke-Command -session $casession -ScriptBlock { Add-CACrlDistributionPoint -Uri "http://<ServerDNSName>/CertEnroll/<CAName><CRLNameSuffix><DeltaCRLAllowed>.crl" -AddToCertificateCdp -AddToFreshestCrl -AddToCrlIdp -Confirm:$false | Out-Null } 125 | Invoke-Command -session $casession -ScriptBlock { Get-CAAuthorityInformationAccess | Where-Object { $_.URI -like 'http:*' } | Remove-CAAuthorityInformationAccess -Confirm:$false | Out-Null } 126 | Invoke-Command -session $casession -ScriptBlock { Add-CAAuthorityInformationAccess -Uri "http://<ServerDNSName>/CertEnroll/<ServerDNSName>_<CAName><CertificateName>.crt" -AddToCertificateAia -Confirm:$false | Out-Null } 127 | Invoke-Command -session $casession -ScriptBlock { Get-Service -Name certsvc | Restart-Service | Out-Null } 128 | $ndessvcuser = "ndessvc" 129 | 130 | $KDCScriptBlock = [ScriptBlock]::create((get-command new-kdccert).Definition.Replace("`$domain", $domainnetbios)) 131 | Invoke-Command -Session $casession -ScriptBlock $KDCScriptBlock 132 | $CCMWebScriptBlock = [ScriptBlock]::create((get-command new-ccmwebcert).Definition.Replace("`$domain", $domainnetbios).Replace("`$ndessvc",$ndessvcuser)) 133 | Invoke-Command -Session $casession -ScriptBlock $CCMWebScriptBlock 134 | $NDESScriptBlock = [scriptblock]::Create((get-command new-NDESUsercert).Definition.Replace("`$domain", $domainnetbios).Replace("`$ndessvc",$ndessvcuser)) 135 | Invoke-Command -Session $casession -ScriptBlock $NDESScriptBlock 136 | Invoke-Command -session $casession -ScriptBlock { Remove-CATemplate DomainControllerAuthentication -Confirm:$false } 137 | Invoke-Command -session $casession -ScriptBlock { Remove-CATemplate KerberosAuthentication -Confirm:$false } 138 | Invoke-Command -session $casession -ScriptBlock { Remove-CATemplate DomainController -Confirm:$false } 139 | Write-LogEntry -Message "Certificate Authority role has been installed on $($cAconfig.name)" -Type Information 140 | Invoke-Command -Session $casession -ScriptBlock { param($env)Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object { $_.subject -like "CN=$env*" } | Select-Object -Unique | Export-Certificate -FilePath c:\rootcert.cer | out-null } -ArgumentList $CAConfig.network 141 | Copy-Item -FromSession $casession -Path "C:\rootcert.cer" -Destination "$(split-path $CAConfig.VHDXpath)\rootcert.cer" | Out-Null 142 | Invoke-Command -Session $casession -ScriptBlock { param($env)Get-ChildItem -Path Cert:\LocalMachine\CA | Where-Object { $_.subject -like "CN=$env*" } | Select-Object -Unique | Export-Certificate -FilePath c:\intcert.cer | out-null } -ArgumentList $CAConfig.network 143 | Copy-Item -FromSession $casession -Path "C:\intcert.cer" -Destination "$(split-path $CAConfig.VHDXpath)\intcert.cer" | Out-Null 144 | $casession | Remove-PSSession 145 | 146 | $rootcert = "$vmpath\rootcert.cer" 147 | $intCert = "$vmpath\intcert.cer" 148 | $dcsessiondom = New-PSSession -VMName "$($caconfig.network)DC" -Credential $CAconfig.domainuser 149 | Copy-Item -ToSession $dcsessiondom -Path $rootcert -Destination "c:\rootcert.cer" | Out-Null 150 | copy-item -ToSession $dcsessiondom -Path $intCert -Destination "c:\intcert.cer" | Out-Null 151 | Invoke-Command -Session $dcsessiondom -ScriptBlock { Import-Certificate -FilePath C:\rootcert.cer -CertStoreLocation Cert:\LocalMachine\Root | out-null } 152 | Invoke-Command -Session $dcsessiondom -ScriptBlock { Import-Certificate -FilePath C:\intcert.cer -CertStoreLocation Cert:\LocalMachine\CA | out-null } 153 | Invoke-Command -Session $dcsessiondom -ScriptBlock { Get-ChildItem Cert:\LocalMachine\My | Remove-Item } 154 | Invoke-Command -Session $dcsessiondom -ScriptBlock { certreq -machine -q -enroll "Domain Controller Authentication (KDC)" | Out-Null } 155 | $dcsessiondom | Remove-PSSession 156 | Write-LogEntry -Message "PowerShell Direct session for $($CAConfig.domainuser.UserName) has been disconnected from $($cAconfig.name)" -Type Information 157 | Invoke-Pester -TagFilter "CA" -Output Detailed 158 | Write-LogEntry -Message "Installation of CA Server named $($cAconfig.name) is completed" -Type Information 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /NewCMSettingfile.ps1: -------------------------------------------------------------------------------- 1 | function New-CMSettingfile { 2 | param( 3 | [Parameter(ParameterSetName = 'NoClass')] 4 | [ValidateSet("CAS", "PRI", "CASPRI")] 5 | [string] 6 | $CMServerType, 7 | $ServerName, 8 | $cmsitecode, 9 | $domainFQDN, 10 | $sqlsettings, 11 | [parameter(Mandatory = $false)] 12 | $CasServerName, 13 | [parameter(Mandatory = $false)] 14 | $CasSiteCode, 15 | [ValidateSet("TP", "Prod")] 16 | [string] 17 | $ver 18 | ) 19 | if ($ver -eq "Prod") { 20 | if ($CMServerType -eq "CAS") { 21 | $hashident = @{'action' = 'InstallCAS' } 22 | } 23 | elseif ($CMServerType -eq "PRI") { 24 | $hashident = @{'action' = 'InstallPrimarySite' } 25 | } 26 | elseif ($CMServerType -eq "CASPRI") { 27 | $hashident = @{'action' = 'InstallPrimarySite'; 28 | 'CDLatest' = "1" 29 | } 30 | } 31 | } 32 | elseif ($ver -eq "TP") { 33 | $hashident = @{'action' = 'InstallPrimarySite'; 34 | 'Preview' = "1" 35 | } 36 | } 37 | if ($CMServerType -eq "CAS") { 38 | $hashoptions = @{'ProductID' = 'EVAL'; 39 | 'SiteCode' = $cmsitecode; 40 | 'SiteName' = "Tech Preview $cmsitecode"; 41 | 'SMSInstallDir' = 'C:\Program Files\Microsoft Configuration Manager'; 42 | 'SDKServer' = "$cmOSName.$DomainFQDN"; 43 | 'PrerequisiteComp' = "$SCCMDLPreDown"; 44 | 'PrerequisitePath' = "C:\DATA\SCCM\DL"; 45 | 'MobileDeviceLanguage' = "0"; 46 | 'AdminConsole' = "1"; 47 | 'JoinCEIP' = "0"; 48 | } 49 | } 50 | elseif ($CMServerType -eq "PRI") { 51 | $hashoptions = @{'ProductID' = 'EVAL'; 52 | 'SiteCode' = $cmsitecode; 53 | 'SiteName' = "Tech Preview $cmsitecode"; 54 | 'SMSInstallDir' = 'C:\Program Files\Microsoft Configuration Manager'; 55 | 'SDKServer' = "$cmOSName.$DomainFQDN"; 56 | 'RoleCommunicationProtocol' = "HTTPorHTTPS"; 57 | 'ClientsUsePKICertificate' = "0"; 58 | 'PrerequisiteComp' = "$SCCMDLPreDown"; 59 | 'PrerequisitePath' = "C:\DATA\SCCM\DL"; 60 | 'ManagementPoint' = "$cmOSName.$DomainFQDN"; 61 | 'ManagementPointProtocol' = "HTTP"; 62 | 'DistributionPoint' = "$cmOSName.$DomainFQDN"; 63 | 'DistributionPointProtocol' = "HTTP"; 64 | 'DistributionPointInstallIIS' = "0"; 65 | 'AdminConsole' = "1"; 66 | 'JoinCEIP' = "0"; 67 | } 68 | } 69 | elseif ($CMServerType -eq "CASPRI") { 70 | $hashoptions = @{'ProductID' = 'EVAL'; 71 | 'SiteCode' = $cmsitecode; 72 | 'SiteName' = "Tech Preview"; 73 | 'SMSInstallDir' = 'C:\Program Files\Microsoft Configuration Manager'; 74 | 'SDKServer' = "$cmOSName.$DomainFQDN"; 75 | 'RoleCommunicationProtocol' = "HTTPorHTTPS"; 76 | 'ClientsUsePKICertificate' = "0"; 77 | 'PrerequisiteComp' = "$SCCMDLPreDown"; 78 | 'PrerequisitePath' = "\\$CasServerName\SMS_$CasSiteCode\cd.latest\DL"; 79 | 'ManagementPoint' = "$cmOSName.$DomainFQDN"; 80 | 'ManagementPointProtocol' = "HTTP"; 81 | 'DistributionPoint' = "$cmOSName.$DomainFQDN"; 82 | 'DistributionPointProtocol' = "HTTP"; 83 | 'DistributionPointInstallIIS' = "0"; 84 | 'AdminConsole' = "1"; 85 | 'JoinCEIP' = "0"; 86 | } 87 | } 88 | $hashSQL = @{'SQLServerName' = "$cmOSName.$DomainFQDN"; 89 | 'SQLServerPort' = '1433'; 90 | 'DatabaseName' = "CM_$cmsitecode"; 91 | 'SQLSSBPort' = '4022'; 92 | 'SQLDataFilePath' = "$($sqlsettings.DefaultFile)"; 93 | 'SQLLogFilePath' = "$($sqlsettings.DefaultLog)" 94 | } 95 | $hashCloud = @{ 96 | 'CloudConnector' = "1"; 97 | 'CloudConnectorServer' = "$cmOSName.$DomainFQDN" 98 | } 99 | $hashSCOpts = @{ 100 | } 101 | if ($CMServerType -eq "CASPRI") { 102 | $hashHierarchy = @{ 103 | 'CCARSiteServer' = "$CasServerName" 104 | } 105 | } 106 | else { 107 | $hashHierarchy = @{ } 108 | } 109 | $HASHCMInstallINI = @{'Identification' = $hashident; 110 | 'Options' = $hashoptions; 111 | 'SQLConfigOptions' = $hashSQL; 112 | 'CloudConnectorOptions' = $hashCloud; 113 | 'SystemCenterOptions' = $hashSCOpts; 114 | 'HierarchyExpansionOption' = $hashHierarchy 115 | } 116 | $CMInstallINI = "" 117 | Foreach ($i in $HASHCMInstallINI.keys) { 118 | $CMInstallINI += "[$i]`r`n" 119 | foreach ($j in $($HASHCMInstallINI[$i].keys | Sort-Object)) { 120 | $CMInstallINI += "$j=$($HASHCMInstallINI[$i][$j])`r`n" 121 | } 122 | $CMInstallINI += "`r`n" 123 | } 124 | return $CMInstallINI 125 | } -------------------------------------------------------------------------------- /NewENV.Testsold.ps1old: -------------------------------------------------------------------------------- 1 | $scriptpath = $PSScriptRoot 2 | $config = Get-Content "$scriptpath\env.json" -Raw | ConvertFrom-Json 3 | $envConfig = $config.ENVConfig | Where-Object { $_.env -eq $config.env } 4 | $admpwd = $envConfig.AdminPW 5 | $localadmin = new-object -typename System.Management.Automation.PSCredential -argumentlist "administrator", (ConvertTo-SecureString -String $admpwd -AsPlainText -Force) 6 | $domuser = new-object -typename System.Management.Automation.PSCredential -argumentlist "$($envconfig.env)\administrator", (ConvertTo-SecureString -String $admpwd -AsPlainText -Force) 7 | $vmpath = $envConfig.VMPath 8 | $swname = $envConfig.SwitchName 9 | #$ipsub = $envConfig.ipsubnet 10 | $DomainFQDN = $envconfig.DomainFQDN 11 | #$RRASname = $Config.RRASname 12 | $RefVHDX = $config.REFVHDX 13 | #$dcname = "$($envconfig.env)`DC" 14 | #if ($Config.SCCMCAS -eq "1") { 15 | # $cmconfig.name = "$($envconfig.ENV)`CMCAS" 16 | #} 17 | #else { 18 | # $cmconfig.name = "$($envconfig.env)`CM" 19 | #} 20 | 21 | . .\Lab.Classes.ps1 22 | Describe "RRAS" -Tag ("Network", "RRAS", "VM") { 23 | $RRASConfig = [RRAS]::new() 24 | $rrasconfig.load("$(split-path $vmpath)\RRASConfig.json") 25 | $tRRASVHDXExists = test-path $RRASConfig.VHDXpath 26 | $tRRASExist = (get-vm -Name $RRASConfig.Name -ErrorAction SilentlyContinue).count 27 | $trrasrunning = (get-vm -Name $RRASConfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).Count 28 | if ($tRRASExist -eq 1 -and $trrasrunning -eq 1) { 29 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $localadmin 30 | $rrasfeat = (Invoke-Command -Session $trrasSession -ScriptBlock { (get-windowsfeature -name routing).installstate }).value 31 | $rrasEXTrename = (Invoke-Command -Session $trrasSession -ScriptBlock { Get-NetAdapter -Physical -Name "External" -ErrorAction SilentlyContinue }).name 32 | $rrasLABrename = (Invoke-Command -Session $trrasSession -ScriptBlock { param($n)Get-NetAdapter -Physical -Name $n -ErrorAction SilentlyContinue } -ArgumentList $RRASConfig.network).name 33 | $rrasVPNStatus = (Invoke-Command -Session $trrasSession -ScriptBlock { if ((get-command Get-RemoteAccess -ErrorAction SilentlyContinue).count -eq 1) { Get-RemoteAccess -ErrorAction SilentlyContinue } }) 34 | $rrasLabIPAddress = (Invoke-Command -Session $trrasSession -ScriptBlock { param($n)(Get-NetIPAddress -interfacealias $n -AddressFamily IPv4 -ErrorAction SilentlyContinue).ipaddress } -ArgumentList $RRASConfig.network) 35 | $tRRASInternet = (Invoke-Command -Session $trrasSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 36 | $trrasSession | Remove-PSSession 37 | } 38 | it 'RRAS VHDX Should exist' { $tRRASVHDXExists | should be $true } 39 | it 'RRAS Server Should exist' { $tRRASExist | should be 1 } 40 | it 'RRAS Server is Running' { $trrasrunning | should be 1 } 41 | it 'RRAS Routing Installed' -Skip:(!($tRRASExist -eq 1 -and $trrasrunning -eq 1)) { $rrasfeat | should be "Installed" } 42 | it 'RRAS External NIC Renamed' -Skip:(!($tRRASExist -eq 1 -and $trrasrunning -eq 1)) { $rrasEXTrename | should be "External" } 43 | it 'RRAS Lab NIC Renamed' -Skip:(!($tRRASExist -eq 1 -and $trrasrunning -eq 1)) { $rrasLABrename | should be $RRASConfig.network } 44 | it 'RRAS Lab IP Address Set' -Skip:(!($tRRASExist -eq 1 -and $trrasrunning -eq 1)) { $rrasLabIPAddress | should be $RRASConfig.ipaddress } 45 | it 'RRAS VPN enabled' -Skip:(!($tRRASExist -eq 1 -and $trrasrunning -eq 1)) { $rrasVPNStatus.VpnStatus | should be 'Installed' } 46 | it 'RRAS has access to Internet' -Skip:(!($tRRASExist -eq 1 -and $trrasrunning -eq 1)) { $tRRASInternet | should be $true } 47 | } 48 | 49 | Describe "DC" -Tag ("Domain", "VM") { 50 | $DCConfig = [DC]::new() 51 | $dcconfig.load("$vmpath\dcconfig.json") 52 | $TDCVHDXExists = (Test-Path -path $DCConfig.VHDXpath) 53 | $TDCExists = (get-vm -name $dcconfig.Name -ErrorAction SilentlyContinue).count 54 | $TDCRunning = (get-vm -name $dcconfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).count 55 | if ($TDCExists -eq 1 -and $TDCRunning -eq 1) { 56 | $TDCSession = new-PSSession -VMName $dcconfig.Name -Credential $localadmin -ErrorAction SilentlyContinue 57 | if (!($TDCSession)) { $TDCSession = new-PSSession -VMName $dcconfig.Name -Credential $domuser; $TDCPromoted = $true } else { $TDCPromoted = $false } 58 | $TDCIPAddress = (Invoke-Command -Session $TDCSession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 59 | $TDCFeat = (Invoke-Command -Session $TDCSession -ScriptBlock { (get-windowsfeature -name AD-Domain-Services).installstate }).value 60 | $TDCTestInternet = (Invoke-Command -Session $TDCSession -ScriptBlock { test-netconnection "steven.hosking.com.au" -CommonTCPPort HTTP -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).TcpTestSucceeded 61 | $TDCPromoted = (Invoke-Command -Session $TDCSession -ScriptBlock { Get-Service -Name "NTDS" -ErrorAction SilentlyContinue }).status 62 | $TDCDHCPScopeexists = (Invoke-Command -Session $TDCSession -ScriptBlock { if (get-command Get-DhcpServerv4Scope -ErrorAction SilentlyContinue) { Get-DhcpServerv4Scope -ErrorAction SilentlyContinue -warningaction SilentlyContinue } }) 63 | $TDCADWSState = (Invoke-Command -session $TDCSession { Get-WmiObject -class Win32_Service -filter 'name="adws"' }).state 64 | if ($TDCADWSState -eq "Running") 65 | { $TDCSCCMGroupExists = (Invoke-Command -Session $TDCSession -ScriptBlock { if (get-command get-adgroup -ErrorAction SilentlyContinue -WarningAction SilentlyContinue) { get-adgroup -Filter "name -eq 'SCCM Servers'" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } }) } 66 | $TDCSession | Remove-PSSession 67 | } 68 | it 'DC VHDX Should Exist' { $TDCVHDXExists | should be $true } 69 | it "DC Should Exist" { $TDCExists | should be 1 } 70 | it "DC Should be running" { $TDCRunning | should be 1 } 71 | it 'DC IP Address' -Skip:(!($TDCExists -eq 1 -and $TDCRunning -eq 1)) { $TDCIPAddress | should be $DCConfig.IPAddress } 72 | it 'DC has access to Internet' -Skip:(!($TDCExists -eq 1 -and $TDCRunning -eq 1)) { $TDCTestInternet | should be $true } 73 | it 'DC Domain Services Installed' -Skip:(!($TDCExists -eq 1 -and $TDCRunning -eq 1)) { $TDCFeat | should be "Installed" } 74 | it 'DC Promoted' -Skip:(!($TDCExists -eq 1 -and $TDCRunning -eq 1)) { $TDCPromoted | should be "Running" } 75 | it 'DC DHCP Scope Active' -Skip:(!($TDCExists -eq 1 -and $TDCRunning -eq 1)) { $TDCDHCPScopeexists[0].State | should be "Active" } 76 | it 'DC SCCM Servers Group' -Skip:(!($TDCExists -eq 1 -and $TDCRunning -eq 1)) { $TDCSCCMGroupExists[0].name | should be "SCCM Servers" } 77 | } 78 | 79 | if ($Config.SCCMENVType -eq "CAS") { 80 | Describe "CMCAS" -tag ("CAS") { 81 | $CMConfig = [CM]::new() 82 | $CMConfig.load("$vmpath\$($config.env)`cmCASconfig.json") 83 | $TCMVHDXExists = (Test-Path -path $CMConfig.VHDXpath) 84 | $TCMExists = (get-vm -name $cmconfig.name -ErrorAction SilentlyContinue).count 85 | $TCMRunning = (get-vm -name $cmconfig.name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).count 86 | if ($TCMExists -eq 1 -and $TCMRunning -eq 1) { 87 | $TCMSession = new-PSSession -VMName $cmconfig.name -Credential $localadmin -ErrorAction SilentlyContinue 88 | if (!($TCMSession)) { $TCMSession = new-PSSession -VMName $cmconfig.name -Credential $domuser } 89 | $TCMIPAddress = (Invoke-Command -Session $TCMSession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 90 | $TCMTestInternet = (Invoke-Command -Session $TCMSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 91 | $TCMTestDomain = (Invoke-Command -Session $TCMSession -ScriptBlock { param($d)Test-NetConnection $d -erroraction SilentlyContinue -WarningAction SilentlyContinue } -ArgumentList $CMConfig.domainFQDN ).PingSucceeded 92 | $TCMFeat = (Invoke-Command -Session $TCMSession -ScriptBlock { (get-windowsfeature -name 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-Ext, 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) | Where-Object { $_.installstate -eq "Installed" } }).count 93 | $TCMNetFeat = (Invoke-Command -Session $TCMSession -ScriptBlock { (get-windowsfeature -name NET-Framework-Features, NET-Framework-Core) | Where-Object { $_.installstate -eq "Installed" } }).count 94 | $TCMSQLInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { get-service -name "MSSQLSERVER" -ErrorAction SilentlyContinue }).name.count 95 | $TCMADKInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { test-path "C:\Program Files (x86)\Windows Kits\10\Assessment and deployment kit" -ErrorAction SilentlyContinue }) 96 | $TCMSCCMServerinGRP = (Invoke-Command -Session $TCMSession -ScriptBlock { Get-ADGroupMember "SCCM Servers" | Where-Object { $_.name -eq $env:computername } } -ErrorAction SilentlyContinue).name.count 97 | $TCMSCCMInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { get-service -name "SMS_EXECUTIVE" -ErrorAction SilentlyContinue }).name.count 98 | $TCMSCCMConsoleInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { test-path "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.exe" }) 99 | if ($TCMSCCMConsoleInstalled) { 100 | Invoke-Command -Session $TCMSession -ScriptBlock { param ($sitecode) import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME }; Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:" } -ArgumentList $CMConfig.cmsitecode 101 | $TCMBoundary = (Invoke-Command -Session $TCMSession -ScriptBlock { param($subname) Get-CMBoundary -name $subname } -ArgumentList $CMConfig.network).displayname.count 102 | $TCMDiscovery = (Invoke-Command -Session $TCMSession -ScriptBlock { Get-CMDiscoveryMethod -Name ActiveDirectorySystemDiscovery }).flag 103 | } 104 | $TCMSession | Remove-PSSession 105 | } 106 | it 'CM VHDX Should Exist' { $TCMVHDXExists | should be $true } 107 | it 'CM Should Exist' { $TCMExists | should be 1 } 108 | it 'CM Should be running' { $TCMRunning | should be 1 } 109 | it 'CM IP Address' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMIPAddress | should be $CMConfig.IPAddress } 110 | it 'CM has access to Internet' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMTestInternet | should be $true } 111 | it "CM has access to $DomainFQDN" -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMTestDomain | Should be $true } 112 | it 'CM .Net Feature installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMNetFeat | should be 2 } 113 | it 'CM Features are installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMFeat | should be 44 } 114 | it 'CM SQL Instance is installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSQLInstalled | should be 1 } 115 | it 'CM ADK Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMADKInstalled | should be $true } 116 | it 'CM Server in Group' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMServerinGRP | should be 1 } 117 | it 'CM SCCM Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMInstalled | should be 1 } 118 | it 'CM SCCM Console Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMConsoleInstalled | should be $true } 119 | it 'CM Site Boundary added' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1 -and $TCMSCCMConsoleInstalled)) { $TCMBoundary | should be 1 } 120 | it 'CM System Discovery enabled' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1 -and $TCMSCCMConsoleInstalled)) { $TCMDiscovery | should be 6 } 121 | } 122 | Describe "CMCAS" -tag ("CASPRI") { 123 | $CMConfig = [CM]::new() 124 | $CMConfig.load("$vmpath\$($config.env)`cmCASPRIconfig.json") 125 | $TCMVHDXExists = (Test-Path -path $CMConfig.VHDXpath) 126 | $TCMExists = (get-vm -name $cmconfig.name -ErrorAction SilentlyContinue).count 127 | $TCMRunning = (get-vm -name $cmconfig.name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).count 128 | if ($TCMExists -eq 1 -and $TCMRunning -eq 1) { 129 | $TCMSession = new-PSSession -VMName $cmconfig.name -Credential $localadmin -ErrorAction SilentlyContinue 130 | if (!($TCMSession)) { $TCMSession = new-PSSession -VMName $cmconfig.name -Credential $domuser } 131 | $TCMIPAddress = (Invoke-Command -Session $TCMSession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 132 | $TCMTestInternet = (Invoke-Command -Session $TCMSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 133 | $TCMTestDomain = (Invoke-Command -Session $TCMSession -ScriptBlock { param($d)Test-NetConnection $d -erroraction SilentlyContinue -WarningAction SilentlyContinue } -ArgumentList $CMConfig.domainFQDN ).PingSucceeded 134 | $TCMFeat = (Invoke-Command -Session $TCMSession -ScriptBlock { (get-windowsfeature -name 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-Ext, 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) | Where-Object { $_.installstate -eq "Installed" } }).count 135 | $TCMNetFeat = (Invoke-Command -Session $TCMSession -ScriptBlock { (get-windowsfeature -name NET-Framework-Features, NET-Framework-Core) | Where-Object { $_.installstate -eq "Installed" } }).count 136 | $TCMSQLInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { get-service -name "MSSQLSERVER" -ErrorAction SilentlyContinue }).name.count 137 | $TCMADKInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { test-path "C:\Program Files (x86)\Windows Kits\10\Assessment and deployment kit" -ErrorAction SilentlyContinue }) 138 | $TCMSCCMServerinGRP = (Invoke-Command -Session $TCMSession -ScriptBlock { Get-ADGroupMember "SCCM Servers" | Where-Object { $_.name -eq $env:computername } } -ErrorAction SilentlyContinue).name.count 139 | $TCMSCCMInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { get-service -name "SMS_EXECUTIVE" -ErrorAction SilentlyContinue }).name.count 140 | $TCMSCCMConsoleInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { test-path "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.exe" }) 141 | if ($TCMSCCMConsoleInstalled) { 142 | Invoke-Command -Session $TCMSession -ScriptBlock { param ($sitecode) import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME }; Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:" } -ArgumentList $CMConfig.cmsitecode 143 | $TCMBoundary = (Invoke-Command -Session $TCMSession -ScriptBlock { param($subname) Get-CMBoundary -name $subname } -ArgumentList $CMConfig.network).displayname.count 144 | $TCMDiscovery = (Invoke-Command -Session $TCMSession -ScriptBlock { Get-CMDiscoveryMethod -Name ActiveDirectorySystemDiscovery }).flag 145 | } 146 | $TCMSession | Remove-PSSession 147 | } 148 | it 'CM VHDX Should Exist' { $TCMVHDXExists | should be $true } 149 | it 'CM Should Exist' { $TCMExists | should be 1 } 150 | it 'CM Should be running' { $TCMRunning | should be 1 } 151 | it 'CM IP Address' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMIPAddress | should be $CMConfig.IPAddress } 152 | it 'CM has access to Internet' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMTestInternet | should be $true } 153 | it "CM has access to $DomainFQDN" -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMTestDomain | Should be $true } 154 | it 'CM .Net Feature installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMNetFeat | should be 2 } 155 | it 'CM Features are installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMFeat | should be 44 } 156 | it 'CM SQL Instance is installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSQLInstalled | should be 1 } 157 | it 'CM ADK Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMADKInstalled | should be $true } 158 | it 'CM Server in Group' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMServerinGRP | should be 1 } 159 | it 'CM SCCM Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMInstalled | should be 1 } 160 | it 'CM SCCM Console Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMConsoleInstalled | should be $true } 161 | it 'CM Site Boundary added' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1 -and $TCMSCCMConsoleInstalled)) { $TCMBoundary | should be 1 } 162 | it 'CM System Discovery enabled' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1 -and $TCMSCCMConsoleInstalled)) { $TCMDiscovery | should be 6 } 163 | } 164 | } 165 | else { 166 | Describe "CM" -tag ("PRI") { 167 | $CMConfig = [CM]::new() 168 | $CMConfig.load("$vmpath\$($config.env)`cmconfig.json") 169 | $TCMVHDXExists = (Test-Path -path $CMConfig.VHDXpath) 170 | $TCMExists = (get-vm -name $cmconfig.name -ErrorAction SilentlyContinue).count 171 | $TCMRunning = (get-vm -name $cmconfig.name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).count 172 | if ($TCMExists -eq 1 -and $TCMRunning -eq 1) { 173 | $TCMSession = new-PSSession -VMName $cmconfig.name -Credential $localadmin -ErrorAction SilentlyContinue 174 | if (!($TCMSession)) { $TCMSession = new-PSSession -VMName $cmconfig.name -Credential $domuser } 175 | $TCMIPAddress = (Invoke-Command -Session $TCMSession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 176 | $TCMTestInternet = (Invoke-Command -Session $TCMSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 177 | $TCMTestDomain = (Invoke-Command -Session $TCMSession -ScriptBlock { param($d)Test-NetConnection $d -erroraction SilentlyContinue -WarningAction SilentlyContinue } -ArgumentList $CMConfig.domainFQDN ).PingSucceeded 178 | $TCMFeat = (Invoke-Command -Session $TCMSession -ScriptBlock { (get-windowsfeature -name 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-Ext, 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) | Where-Object { $_.installstate -eq "Installed" } }).count 179 | $TCMNetFeat = (Invoke-Command -Session $TCMSession -ScriptBlock { (get-windowsfeature -name NET-Framework-Features, NET-Framework-Core) | Where-Object { $_.installstate -eq "Installed" } }).count 180 | $TCMSQLInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { get-service -name "MSSQLSERVER" -ErrorAction SilentlyContinue }).name.count 181 | $TCMADKInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { test-path "C:\Program Files (x86)\Windows Kits\10\Assessment and deployment kit" -ErrorAction SilentlyContinue }) 182 | $TCMSCCMServerinGRP = (Invoke-Command -Session $TCMSession -ScriptBlock { Get-ADGroupMember "SCCM Servers" | Where-Object { $_.name -eq $env:computername } } -ErrorAction SilentlyContinue).name.count 183 | $TCMSCCMInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { get-service -name "SMS_EXECUTIVE" -ErrorAction SilentlyContinue }).name.count 184 | $TCMSCCMConsoleInstalled = (Invoke-Command -Session $TCMSession -ScriptBlock { test-path "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.exe" }) 185 | if ($TCMSCCMConsoleInstalled) { 186 | Invoke-Command -Session $TCMSession -ScriptBlock { param ($sitecode) import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME }; Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:" } -ArgumentList $CMConfig.cmsitecode 187 | $TCMBoundary = (Invoke-Command -Session $TCMSession -ScriptBlock { param($subname) Get-CMBoundary -name $subname } -ArgumentList $CMConfig.network).displayname.count 188 | $TCMDiscovery = (Invoke-Command -Session $TCMSession -ScriptBlock { Get-CMDiscoveryMethod -Name ActiveDirectorySystemDiscovery }).flag 189 | } 190 | $TCMSession | Remove-PSSession 191 | } 192 | it 'CM VHDX Should Exist' { $TCMVHDXExists | should be $true } 193 | it 'CM Should Exist' { $TCMExists | should be 1 } 194 | it 'CM Should be running' { $TCMRunning | should be 1 } 195 | it 'CM IP Address' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMIPAddress | should be $CMConfig.IPAddress } 196 | it 'CM has access to Internet' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMTestInternet | should be $true } 197 | it "CM has access to $DomainFQDN" -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMTestDomain | Should be $true } 198 | it 'CM .Net Feature installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMNetFeat | should be 2 } 199 | it 'CM Features are installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMFeat | should be 44 } 200 | it 'CM SQL Instance is installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSQLInstalled | should be 1 } 201 | it 'CM ADK Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMADKInstalled | should be $true } 202 | it 'CM Server in Group' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMServerinGRP | should be 1 } 203 | it 'CM SCCM Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMInstalled | should be 1 } 204 | it 'CM SCCM Console Installed' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1)) { $TCMSCCMConsoleInstalled | should be $true } 205 | it 'CM Site Boundary added' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1 -and $TCMSCCMConsoleInstalled)) { $TCMBoundary | should be 1 } 206 | it 'CM System Discovery enabled' -Skip:(!($TCMExists -eq 1 -and $TCMRunning -eq 1 -and $TCMSCCMConsoleInstalled)) { $TCMDiscovery | should be 6 } 207 | } 208 | } 209 | 210 | 211 | Describe "CA" -tag ("CA", "VM") { 212 | $CAConfig = [CA]::new() 213 | $CAConfig.load("$VMpath\CAconfig.json") 214 | $TCAVHDXExists = (Test-Path -path $CAConfig.VHDXpath) 215 | $TCAExists = (get-vm -name $CAConfig.Name -ErrorAction SilentlyContinue).count 216 | $TCARunning = (get-vm -name $CAConfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).count 217 | if ($TCAExists -eq 1 -and $TCARunning -eq 1) { 218 | $TCASession = new-PSSession -VMName $CAConfig.Name -Credential $localadmin -ErrorAction SilentlyContinue 219 | if (!($TCASession)) { $TCASession = new-PSSession -VMName $CAConfig.Name -Credential $domuser } 220 | $TCAIPAddress = (Invoke-Command -Session $TCASession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 221 | $TCATestInternet = (Invoke-Command -Session $TCASession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 222 | $TCATestDomain = (Invoke-Command -Session $TCASession -ScriptBlock { param($d)Test-NetConnection $d -erroraction SilentlyContinue -WarningAction SilentlyContinue } -ArgumentList $CAConfig.domainFQDN ).PingSucceeded 223 | $TCAFeat = (Invoke-Command -Session $TCASession -ScriptBlock { (get-windowsfeature -name Adcs-Cert-Authority | Where-Object { $_.installstate -eq "Installed" }).count }) 224 | $TCASession | Remove-PSSession 225 | } 226 | it 'CA VHDX Should Exist' { $TCAVHDXExists | should be $true } 227 | it 'CA Should Exist' { $TCAExists | should be 1 } 228 | it 'CA Should be running' { $TCARunning | should be 1 } 229 | it 'CA IP Address' -Skip:(!($TCAExists -eq 1 -and $TCARunning -eq 1)) { $TCAIPAddress | should be $CAConfig.IPAddress } 230 | it 'CA has access to Internet' -Skip:(!($TCAExists -eq 1 -and $TCARunning -eq 1)) { $TCATestInternet | should be $true } 231 | it "CA has access to $DomainFQDN" -Skip:(!($TCAExists -eq 1 -and $TCARunning -eq 1)) { $TCATestDomain | Should be $true } 232 | it 'CA Feature is installed' -Skip:(!($TCAExists -eq 1 -and $TCARunning -eq 1)) { $TCAFeat | should be 1 } 233 | 234 | } 235 | 236 | Describe "vSwitch" -tag ("Network", "ENV") { 237 | $lresult = (Get-VMSwitch -Name $swname -ErrorAction SilentlyContinue).count 238 | $Iresult = (Get-VMSwitch -Name "Internet" -ErrorAction SilentlyContinue).count 239 | it "Lab VMSwitch Should exist" { $lresult | should be 1 } 240 | it 'Internet VSwitch should exist' { $Iresult | should be 1 } 241 | } 242 | 243 | Describe "Reference-VHDX" -Tag ("VM", "Template") { 244 | $result = (Test-Path -Path "$RefVHDX") 245 | it 'VHDX Should exist' { $result | should be $true } 246 | } 247 | 248 | Describe "Test Source Media" -tag ("VM", "ENV") { 249 | $Win16iso = (Test-path -Path "$($config.WIN16ISO)") 250 | $SQLMedia = (Test-Path -Path "$($config.SQLISO)") 251 | $adkmedia = (Test-Path -Path "$($config.ADKPATH)") 252 | $SCCMMedia = (Test-Path -Path "$($config.SCCMPath)") 253 | $SCCMDLMedia = (Test-Path -Path "$($config.SCCMPath)\DL") 254 | $net35path = (Test-Path -Path "$($config.WINNET35CAB)") 255 | $unattendpath = $config.REFVHDX -replace ($config.REFVHDX.split('\') | Select-Object -last 1), "Unattended.xml" ## is wrong, need to change to correct path 256 | $win16unattend = (test-path "$($unattendpath)") 257 | it 'Windows 2016 source media' { $Win16iso | should be $true } 258 | it 'Windows 2016 Unattend' { $win16unattend | should be $true } 259 | it 'SQL 2016 Media' { $SQLMedia | Should be $true } 260 | it 'ADK Content' { $adkmedia | should be $true } 261 | it 'SCCM Media' { $SCCMMedia | should be $true } 262 | it 'SCCM Download Media' { $SCCMDLMedia | should be $true } 263 | it '.net 3.5 Media' { $net35path | should be $true } 264 | } -------------------------------------------------------------------------------- /NewENV.ps1: -------------------------------------------------------------------------------- 1 | function new-ENV { 2 | param( 3 | [Parameter(ParameterSetName = 'ENVClass')] 4 | [env] 5 | $ENVConfig, 6 | [Parameter(ParameterSetName = 'NoClass')] 7 | [string] 8 | $vmpath, 9 | [Parameter(ParameterSetName = 'NoClass')] 10 | [string] 11 | $RefVHDX, 12 | [Parameter(ParameterSetName = 'NoClass')] 13 | [string] 14 | $Win16ISOPath, 15 | [Parameter(ParameterSetName = 'NoClass')] 16 | [string] 17 | $Win16Net35Cab, 18 | [Parameter(ParameterSetName = 'NoClass')] 19 | [string] 20 | $network, 21 | [Parameter(ParameterSetName = 'NoClass')] 22 | [string] 23 | $DefaultPwd 24 | ) 25 | if (!$PSBoundParameters.ContainsKey('ENVConfig')) { 26 | $ENVConfig = [env]::new() 27 | $ENVConfig.vmpath = $vmpath 28 | $ENVConfig.RefVHDX = $RefVHDX 29 | $ENVConfig.Win16ISOPath = $Win16ISOPath 30 | $ENVConfig.Win16Net35Cab = $Win16Net35Cab 31 | $ENVConfig.network = $network 32 | $ENVConfig.DefaultPwd = $DefaultPwd 33 | } 34 | Write-LogEntry -Type Information -Message "Creating the base requirements for Lab Environment" 35 | Write-LogEntry -Type Information -Message "ENV Settings are: $($envconfig | ConvertTo-Json)" 36 | if ((Invoke-Pester -TagFilter "RefVHDX" -PassThru -Output None).result -eq "Passed") { 37 | Write-LogEntry -Type Information -Message "Reference image already exists in: $($ENVConfig.RefVHDX)" 38 | } 39 | else { 40 | if (!(Test-Path $ENVConfig.vmpath)) { New-Item -ItemType Directory -Force -Path $ENVConfig.vmpath } 41 | else { 42 | if (!(Test-Path "$($scriptpath)\unattended.xml")) { 43 | New-UnattendXml -admpwd $ENVConfig.DefaultPwd -outfile "$($scriptpath)\unattended.xml" 44 | } 45 | Write-LogEntry -Type Information -Message "Reference image doesn't exist, will create it now" 46 | new-LabVHDX -VHDXPath $ENVConfig.RefVHDX -Unattend "$($scriptpath)\unattended.xml" -WinISO $ENVConfig.Win16ISOPath -WinNet35Cab $ENVConfig.Win16Net35Cab 47 | Write-LogEntry -Type Information -Message "Reference image has been created in: $($ENVConfig.RefVHDX)" 48 | } 49 | } 50 | if ((invoke-pester -TagFilter "ExternalSwitch" -PassThru -Output None).result -ne 'Passed') { 51 | Write-LogEntry -Type Information -Message "vSwitch named Internet does not exist" 52 | $nic = Get-NetAdapter -Physical 53 | Write-LogEntry -Type Information -Message "Following physical network adaptors found: $($nic.Name -join ",")" 54 | if ($nic.count -gt 1) { 55 | Write-Verbose "Multiple Network Adptors found. " 56 | $i = 1 57 | $oOptions = @() 58 | $nic | ForEach-Object { 59 | $oOptions += [pscustomobject]@{ 60 | Item = $i 61 | Name = $_.Name 62 | } 63 | $i++ 64 | } 65 | $oOptions | Out-Host 66 | $selection = Read-Host -Prompt "Please make a selection" 67 | Write-LogEntry -Type Information -Message "The following physical network adaptor has been selected for Internet access: $selection" 68 | $Selected = $oOptions | Where-Object { $_.Item -eq $selection } 69 | New-VMSwitch -Name 'Internet' -NetAdapterName $selected.name -AllowManagementOS:$true | Out-Null 70 | Write-LogEntry -Type Information -Message "Internet vSwitch has been created." 71 | } 72 | } 73 | if ((invoke-pester -TagFilter "LabSwitch" -PassThru -Output None).result -ne 'Passed') { 74 | Write-LogEntry -Type Information -Message "Private vSwitch named $($ENVConfig.network) does not exist" 75 | New-VMSwitch -Name $ENVConfig.network -SwitchType Private | Out-Null 76 | Write-LogEntry -Type Information -Message "Private vSwitch named $($ENVConfig.network) has been created." 77 | } 78 | Write-LogEntry -Type Information -Message "Base requirements for Lab Environment has been met" 79 | } -------------------------------------------------------------------------------- /NewSCCMServer.PS1: -------------------------------------------------------------------------------- 1 | function new-SCCMServer { 2 | param( 3 | [Parameter(ParameterSetName = 'CMClass')] 4 | [CM] 5 | $CMConfig, 6 | [Parameter(ParameterSetName = 'NoClass')] 7 | [psobject] 8 | $envconfig, 9 | [Parameter(ParameterSetName = 'NoClass')] 10 | [string] 11 | $VHDXpath, 12 | [Parameter(ParameterSetName = 'NoClass')] 13 | [pscredential] 14 | $localadmin, 15 | [Parameter(ParameterSetName = 'NoClass')] 16 | [string] 17 | $IPAddress, 18 | [Parameter(ParameterSetName = 'NoClass')] 19 | [string] 20 | $DomainFQDN, 21 | [Parameter(ParameterSetName = 'NoClass')] 22 | [pscredential] 23 | $DomainUser, 24 | [Parameter(ParameterSetName = 'NoClass')] 25 | [psobject] 26 | $config, 27 | [Parameter(ParameterSetName = 'NoClass')] 28 | [string] 29 | $admpwd, 30 | [Parameter(ParameterSetName = 'NoClass')] 31 | [string] 32 | $domainnetbios, 33 | [Parameter(ParameterSetName = 'NoClass')] 34 | [string] 35 | $cmsitecode, 36 | [Parameter(ParameterSetName = 'NoClass')] 37 | [string] 38 | $SCCMDLPreDownloaded, 39 | [parameter(ParameterSetName = 'NoClass', Mandatory = $false)] 40 | [switch] 41 | $vmSnapshotenabled, 42 | [parameter(ParameterSetName = 'NoClass', Mandatory = $false)] 43 | [switch] 44 | $CAS, 45 | [Parameter(ParameterSetName = 'NoClass')] 46 | [switch] 47 | $PRI, 48 | [Parameter(ParameterSetName = 'NoClass')] 49 | [string] 50 | $casservername, 51 | [Parameter(ParameterSetName = 'NoClass')] 52 | [int] 53 | $cores, 54 | [Parameter(ParameterSetName = 'NoClass')] 55 | [int] 56 | $ram, 57 | [Parameter(ParameterSetName = 'NoClass')] 58 | [string] 59 | $name, 60 | [Parameter(ParameterSetName = 'NoClass')] 61 | [string] 62 | $DCIP, 63 | [Parameter(ParameterSetName = 'NoClass')] 64 | [string] 65 | $RefVHDX, 66 | [Parameter(ParameterSetName = 'NoClass')] 67 | [string] 68 | $SQLISO, 69 | [Parameter(ParameterSetName = 'NoClass')] 70 | [string] 71 | $SCCMPath, 72 | [Parameter(ParameterSetName = 'NoClass')] 73 | [string] 74 | $ADKPath, 75 | [Parameter(ParameterSetName = 'NoClass')] 76 | [ValidateSet("CAS", "PRI", "CASPRI")] 77 | [string] 78 | $CMServerType = "PRI", 79 | [Parameter(ParameterSetName = 'NoClass')] 80 | [string] 81 | $CASIPAddress, 82 | [Parameter(ParameterSetName = 'NoClass')] 83 | [string] 84 | $SCCMVer 85 | ) 86 | if (!$PSBoundParameters.ContainsKey('CMConfig')) { 87 | $CMConfig = [CM]::new() 88 | $CMConfig.AdmPwd = $admpwd 89 | $CMConfig.cmsitecode = $cmsitecode 90 | $CMConfig.cores = $Cores 91 | $CMConfig.ram = $ram 92 | $CMConfig.domainFQDN = $DomainFQDN 93 | $CMConfig.domainuser = $DomainUser 94 | $CMConfig.IPAddress = $IPAddress 95 | $CMConfig.DCIP = $DCIP 96 | $CMConfig.localadmin = $localadmin 97 | $CMConfig.network = $network 98 | $CMConfig.VHDXpath = $VHDXpath 99 | $CMConfig.VMSnapshotenabled = $vmSnapshotenabled.IsPresent 100 | $CMConfig.SCCMDLPreDownloaded = $SCCMDLPreDownloaded 101 | $CMConfig.name = $name 102 | $CMConfig.RefVHDX = $RefVHDX 103 | $CMConfig.SQLISO = $SQLISO 104 | $CMConfig.SCCMPath = $SCCMPath 105 | $CMConfig.ADKPath = $ADKPath 106 | $CMConfig.domainnetbios = $domainnetbios 107 | $CMConfig.CMServerType = $CMServerType 108 | $CMConfig.CASIPAddress = $CASIPAddress 109 | $CMConfig.SCCMVer = $SCCMVer 110 | } 111 | $ipsubnet = $CMConfig.IPAddress.substring(0, ($CMConfig.IPAddress.length - ([ipaddress] $CMConfig.IPAddress).GetAddressBytes()[3].count - 1)) 112 | Write-logentry -message "CM Server Started: $(Get-Date)" -type information 113 | #if ($cas.ispresent) { 114 | # $cmname = "$($envconfig.env)`CMCAS" 115 | #} 116 | #else { 117 | # $cmname = "$($envconfig.env)`CM" 118 | #} 119 | write-logentry -message "VM for CM will be named: $($CMConfig.name)" -type information 120 | Write-LogEntry -Message "CM Settings are: $($CMConfig | ConvertTo-Json)" -Type Information 121 | write-logentry -message "Path for the VHDX for $($CMConfig.name) is: $($CMConfig.VHDXpath)" -type information 122 | if (!(Invoke-Pester -tagfilter "CMVM" -passthru -output none).result -ne "Passed") { 123 | write-logentry -message "CM for env:$($envconfig.env) doesn't exist, creating now" -type information 124 | if ((Invoke-Pester -tagfilter "CMVHDX" -passthru -output none).result -eq "Passed") { 125 | write-logentry -message "CM VHDX Already exists at path: $($CMConfig.VHDXpath) Please clean up and ReRun" -type error 126 | throw "CM VHDX Already Exists at path: $($CMConfig.VHDXpath) Please clean up and Rerun." 127 | } 128 | else { 129 | Copy-Item -Path $cmconfig.RefVHDX -Destination $CMConfig.VHDXpath 130 | write-logentry -message "Reference VHDX: $($CMConfig.refvhdx) has been copied to: $($CMConfig.VHDXpath)" -type information 131 | $disk = (mount-vhd -Path $CMConfig.VHDXpath -Passthru | Get-disk | Get-Partition | Where-Object { $_.type -eq 'Basic' }).DriveLetter 132 | write-logentry -message "$($CMConfig.VHDXpath) has been mounted to allow for file copy to $disk" -type information 133 | Copy-Item -Path $cmconfig.SCCMPath -Destination "$disk`:\data\SCCM" -Recurse 134 | write-logentry -message "SCCM Media copied to $disk`:\data\SCCM" -type information 135 | Copy-Item -Path $cmconfig.ADKPath -Destination "$disk`:\data\adk" -Recurse 136 | write-logentry -message "ADK Media copied to $disk`:\data\adk" -type information 137 | Dismount-VHD $CMConfig.VHDXpath 138 | write-logentry -message "$disk has been dismounted" -type information 139 | } 140 | if ((Invoke-Pester -tagfilter "CMVHDX" -passthru -output none).result -ne "Passed") { 141 | write-logentry -message "Error creating VHDX for CM. BUILD STOPPED" -type error 142 | throw "Error Creating the VHDX for CM" 143 | } 144 | else { 145 | write-logentry -message "Starting to create $($CMConfig.name)" -type information 146 | new-vm -name $CMConfig.name -MemoryStartupBytes ($cmconfig.ram * 1gb) -VHDPath $CMConfig.VHDXpath -Generation 2 | Set-VMMemory -DynamicMemoryEnabled:$false 147 | write-logentry -message "Setting vCPU for $($CMConfig.name) to 4" -type information 148 | get-vm -name $CMConfig.name | Set-VMProcessor -Count $CMConfig.cores 149 | if (!($cmconfig.vmSnapshotenabled)) { 150 | set-vm -name $CMConfig.name -checkpointtype Disabled 151 | } 152 | write-logentry -message "$($CMConfig.name) has been created" -type information 153 | start-vm -Name $CMConfig.name 154 | write-logentry -message "CM Server named $($CMConfig.name) has been started" -type information 155 | Get-VMNetworkAdapter -VMName $CMConfig.name | Connect-VMNetworkAdapter -SwitchName $cmconfig.network 156 | write-logentry -message "vSwitch $($cmconfig.network) has been attached to $($CMConfig.name)" -type information 157 | } 158 | while ((Invoke-Command -VMName $CMConfig.name -Credential $CMConfig.localadmin { "Test" } -ErrorAction SilentlyContinue) -ne "Test") { Start-Sleep -Seconds 5 } 159 | $cmsessionLA = New-PSSession -vmname $CMConfig.name -credential $CMConfig.localadmin 160 | if ($null -eq $cmsessionLA) { throw "Issue with CM Local User Account" } 161 | write-logentry -message "PowerShell Direct session for $($CMConfig.localadmin.username) has been initated with CM server named: $($CMConfig.name)" -type information 162 | $cmnics = Invoke-Command -session $cmsessionLA -ScriptBlock { Get-NetAdapter } 163 | write-logentry -message "The following network adaptors $($cmnics -join ",") have been found on: $($CMConfig.name)" -type information 164 | if ((Invoke-Pester -tagfilter "CMIP" -passthru -output none).result -ne "Passed") { 165 | $IPGateway = "$ipsubnet`1" 166 | $null = Invoke-Command -session $cmsessionLA -ScriptBlock { param($t, $i, $g, $d) new-NetIPAddress -InterfaceIndex $t -AddressFamily IPv4 -IPAddress "$i" -PrefixLength 24 -DefaultGateway "$g"; Set-DnsClientServerAddress -ServerAddresses ($d) -InterfaceIndex $t } -ArgumentList $cmnics.InterfaceIndex, $CMConfig.IPAddress, $IPGateway, $CMConfig.DCIP 167 | write-logentry -message "IP Address $($CMConfig.IPAddress) has been assigned to $($CMConfig.name)" -type information 168 | start-sleep 300 169 | } 170 | if ((Invoke-Pester -tagfilter "CMDOM" -passthru -output none).result -eq "Passed") { 171 | while ((Invoke-Command -VMName $CMConfig.name -Credential $CMConfig.localadmin { param($i)(test-netconnection "$i" -ErrorAction SilentlyContinue).pingsucceeded } -ArgumentList $cmconfig.DCIP -ErrorAction SilentlyContinue) -ne $true -and $stop -ne (get-date)) { Start-Sleep -Seconds 5 } 172 | Invoke-Command -session $cmsessionLA -ErrorAction SilentlyContinue -ScriptBlock { param($env, $domuser) Clear-DnsClientCache; Add-Computer -DomainName $env -domainCredential $domuser -Restart; Start-Sleep -Seconds 15; Restart-Computer -Force -Delay 0 } -ArgumentList $cmconfig.domainFQDN, $CMConfig.domainuser 173 | write-logentry -message "Joined $($CMConfig.name) to domain $($cmconfig.domainFQDN)" -type information 174 | $stop = (get-date).AddMinutes(5) 175 | while ((Invoke-Command -VMName $CMConfig.name -Credential $CMConfig.domainuser { "Test" } -ErrorAction SilentlyContinue) -ne "Test" -and $stop -ne (get-date)) { Start-Sleep -Seconds 5 } 176 | } 177 | else { 178 | write-logentry -message "Couldn't find $($cmconfig.domainFQDN)" -type error 179 | throw "CM Server can't resolve $($cmconfig.domainFQDN)" 180 | } 181 | $cmsession = New-PSSession -VMName $CMConfig.name -Credential $CMConfig.domainuser 182 | write-logentry -message "PowerShell Direct session for $($CMConfig.domainuser.username) has been initated with CM Server named: $($CMConfig.name)" -type information 183 | if ($null -eq $cmsession) { throw "Issue with CM Domain User Account" } 184 | if ((Invoke-Pester -tagfilter "CMNETFeatures" -passthru -output none).result -ne "Passed") { 185 | Invoke-Command -session $cmsession -ScriptBlock { Add-WindowsFeature -Name NET-Framework-Features, NET-Framework-Core -Source "C:\data" } | Out-Null 186 | write-logentry -message ".Net 3.5 enabled on $($CMConfig.name)" -type information 187 | } 188 | #invoke-command -Session $cmsession -ScriptBlock { Restart-Computer} 189 | #$stop = (get-date).AddMinutes(5) 190 | #while ((Invoke-Command -VMName $CMConfig.name -Credential $CMConfig.domainuser { "Test" } -ErrorAction SilentlyContinue) -ne "Test" -and $stop -ne (get-date)) { Start-Sleep -Seconds 5 } 191 | if ((Invoke-Pester -tagfilter "CMFeatures" -passthru -output none).result -ne "Passed") { 192 | invoke-command -session $cmsession -scriptblock { $feature = @('BITS', 'BITSExtensions-Upload', 'LightweightServer', 'IIS-WebServerRole', 'IIS-WebServer', 'IIS-CommonHttpFeatures', 'IIS-DefaultDocument', 'IIS-DirectoryBrowsing', 'IIS-HttpErrors', 'IIS-StaticContent', 'IIS-HttpRedirect', 'IIS-ApplicationDevelopment', 'IIS-NetFxExtensibility', 'IIS-NetFxExtensibility45', 'IIS-ASP', 'IIS-ASPNET', 'IIS-ASPNET45', 'IIS-CGI', 'IIS-ISAPIExtensions', 'IIS-ISAPIFilter', 'IIS-HealthAndDiagnostics', 'IIS-HttpLogging', 'IIS-CustomLogging', 'IIS-LoggingLibraries', 'IIS-BasicAuthentication', 'IIS-IPSecurity', 'IIS-URLAuthorization', 'IIS-WindowsAuthentication', 'IIS-WebServerManagementTools', 'IIS-ManagementConsole', 'IIS-IIS6ManagementCompatibility', 'IIS-Metabase', 'IIS-LegacySnapIn', 'IIS-LegacyScripts', 'IIS-WMICompatibility', 'IIS-ManagementScriptingTools', 'IIS-ManagementService', 'MSRDC-Infrastructure', 'ActiveDirectory-PowerShell', 'IIS-RequestMonitor', 'IIS-HttpTracing', 'IIS-Performance', 'IIS-HttpCompressionStatic', 'IIS-Security', 'IIS-RequestFiltering'); 193 | $feature | ForEach-Object { dism /online /quiet /enable-feature /FeatureName:$_ /all } 194 | } 195 | write-logentry -message "Windows Features enabled on $($CMConfig.name)" -type information 196 | } 197 | if ((Invoke-Pester -tagfilter "CMSQLInst" -passthru -output none).result -ne "Passed") { 198 | write-logentry -message "Start SQL Install on $($CMConfig.name)" -type information 199 | new-CMSQLInstance -cmname $CMConfig.name -cmsession $cmsession -SQLISO $CMConfig.SQLISO -domainnetbios $CMConfig.domainnetbios -admpwd $cmconfig.admpwd 200 | } 201 | if ((Invoke-Pester -tagfilter "CMADK" -passthru -output none).result -ne "Passed") { 202 | write-logentry -message "ADK is installing on $($CMConfig.name)" -type information 203 | Invoke-Command -Session $cmsession -ScriptBlock { Start-Process -FilePath "c:\data\adk\adksetup.exe" -Wait -ArgumentList " /Features OptionId.DeploymentTools OptionId.WindowsPreinstallationEnvironment OptionId.ImagingAndConfigurationDesigner OptionId.UserStateMigrationTool /norestart /quiet /ceip off" } 204 | write-logentry -message "ADK is installed on $($CMConfig.name)" -type information 205 | } 206 | if ($CMConfig.CMServerType -eq "CAS") { 207 | invoke-command -Session $cmsession -ScriptBlock { new-netfirewallrule -DisplayName "CAS Inbound" -Action Allow -Profile Any -Enabled true -Direction Inbound -Protocol TCP -LocalPort 1433, 4022 } 208 | } 209 | if ((Invoke-Pester -tagfilter "CMInstalled" -passthru -output none).result -ne "Passed") { 210 | if ($CMConfig.CMServerType -eq "CASPRI") { 211 | new-CMInstance -CMServerType $CMConfig.CMServerType -cmsession $cmsession -cmname $CMConfig.name -cmsitecode $cmconfig.cmsitecode -domainfqdn $CMConfig.domainFQDN -ver $CMConfig.SCCMVer -ipsub $ipsubnet -domainnetbios $CMConfig.domainnetbios -CASIPAddress $CMConfig.CASIPAddress 212 | } 213 | else { 214 | new-CMInstance -CMServerType $CMConfig.CMServerType -cmsession $cmsession -cmname $CMConfig.name -cmsitecode $cmconfig.cmsitecode -domainfqdn $CMConfig.domainFQDN -ver $CMConfig.SCCMVer -ipsub $ipsubnet -domainnetbios $CMConfig.domainnetbios #-cas:($cas.IsPresent) -pri:($pri.ispresent) -casservername $casservername 215 | } 216 | 217 | } 218 | Invoke-Command -Session $cmsession -ScriptBlock { Set-ItemProperty -path HKLM:\SOFTWARE\Microsoft\ServerManager -name DoNotOpenServerManagerAtLogon -Type DWord -value "1" -Force } 219 | Invoke-Command -Session $cmsession -ScriptBlock { New-NetFirewallRule -DisplayName "SQL1433" -Direction Inbound -Protocol TCP -Action Allow -LocalPort 1433 -Profile any } 220 | Invoke-Command -Session $cmsession -ScriptBlock { Set-ExecutionPolicy Bypass -Scope Process -Force 221 | Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 222 | choco install git.install -y -f -x --no-progress -r 223 | choco install vscode-insiders -y -f -x --no-progress -r 224 | choco install sql-server-management-studio -y -f -x --no-progress -r 225 | } 226 | #TODO: still need to work on checking to see if there is an update available 227 | write-logentry -message "Checking for updates from Microsoft for SCCM, this might take some time depending upon your internet" -type information 228 | Invoke-Command -Session $cmsession -ScriptBlock { param($sitecode) 229 | Get-Service SMS_EXECUTIVE | Restart-Service 230 | start-sleep -seconds 120 231 | $update = $null 232 | $update = Get-WmiObject -Namespace "root\sms\site_$sitecode" -Class sms_cm_updatepackages -ErrorAction SilentlyContinue 233 | if($null -eq $update){ 234 | Start-Sleep -Seconds 120 235 | $update = Get-WmiObject -Namespace "root\sms\site_$sitecode" -Class sms_cm_updatepackages -ErrorAction SilentlyContinue 236 | } 237 | if (!($update.PackageExistsInToBeDownloadedList()).PackageExists) { 238 | while (!(Get-WmiObject -Namespace "root\sms\site_$sitecode" -Query "select * from sms_cm_updatepackages where State = 262146")) { "downloading"; Start-Sleep -Seconds 60 } 239 | $update.InitiateUpgrade($update.PrereqFlag) 240 | while ((Get-WmiObject -namespace "root\sms\site_$sitecode" ` 241 | -Query "select substagename, IsComplete from SMS_CM_UpdatePackDetailedMonitoring where substagename = 'Updating Client folder on Site Server' and packageguid = '$($update.packageguid)'" ` 242 | -erroraction SilentlyContinue ).IsComplete -ne 2) { "wait"; start-sleep -Seconds 60 } 243 | } 244 | 245 | } -ArgumentList $CMConfig.cmsitecode 246 | write-logentry -message "Installation of SCCM updates have been installed" -type Information 247 | $cmsession | remove-PSSession 248 | write-logentry -message "Powershell Direct session for $($CMConfig.domainuser.username) on $($CMConfig.name) has been disposed" -type information 249 | } 250 | $CMConfig.Built = $true 251 | $CMConfig.save("$vmpath\config.json") 252 | Invoke-Pester -tagfilter "CM" -output detailed 253 | Write-Output "CM Server Completed: $(Get-Date)" 254 | write-logentry -message "SCCM Server installation has completed on $($CMConfig.name)" -type information 255 | } 256 | -------------------------------------------------------------------------------- /Optimize-AllVHDX.ps1: -------------------------------------------------------------------------------- 1 | function Optimize-VHDs { 2 | $vms = get-vm 3 | foreach ($vm in $vms) { 4 | if ($vm.state -eq "off") { 5 | foreach ($disk in $vm.harddrives) { 6 | Optimize-VHD -Path $disk.path -Mode Full 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /SVRTemplates.json: -------------------------------------------------------------------------------- 1 | { 2 | "ServerTemplates": [ 3 | { 4 | "NameSuffix": "CM", 5 | "Cores": 4, 6 | "RAM": 12, 7 | "IPSuffix": 11, 8 | "ServerType": "PRI", 9 | "SQLMax": 8, 10 | "SQLMin": 4 11 | }, 12 | { 13 | "NameSuffix": "DC", 14 | "Cores": 1, 15 | "RAM": 4, 16 | "IPSuffix": 10, 17 | "ServerType": "DC" 18 | }, 19 | { 20 | "NameSuffix": "CA", 21 | "Cores": 1, 22 | "RAM": 4, 23 | "IPSuffix": 13, 24 | "ServerType": "CA" 25 | }, 26 | { 27 | "NameSuffix": "RRAS", 28 | "Cores": 1, 29 | "RAM": 4, 30 | "IPSuffix": 10, 31 | "ServerType": "RRAS" 32 | }, 33 | { 34 | "NameSuffix": "WKS", 35 | "Cores": 1, 36 | "RAM": 4, 37 | "ServerType": "WKS", 38 | "IPSuffix": 0 39 | }, 40 | { 41 | "NameSuffix": "CAS", 42 | "SVRS": [ 43 | { 44 | "NameSuffix": "CMCAS", 45 | "Cores": 4, 46 | "RAM": 12, 47 | "IPSuffix": 11, 48 | "ServerType": "CAS", 49 | "SQLMax": 8, 50 | "SQLMin": 4, 51 | "CAS": 1 52 | }, 53 | { 54 | "NameSuffix": "CMCP", 55 | "Cores": 4, 56 | "RAM": 12, 57 | "IPSuffix": 12, 58 | "ServerType": "CPRI", 59 | "SQLMax": 8, 60 | "SQLMin": 4, 61 | "PRI": 1 62 | } 63 | ] 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /Unattended.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <unattend xmlns="urn:schemas-microsoft-com:unattend"> 3 | <servicing> 4 | <package action="install" permanence="removable"> 5 | <assemblyIdentity name="Microsoft-Windows-NetFx3-OnDemand-Package" version="10.0.14393.0" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" /> 6 | <source location="c:\data\microsoft-windows-netfx3-ondemand-package.cab" /> 7 | </package> 8 | </servicing> 9 | <settings pass="oobeSystem"> 10 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 11 | <UserAccounts> 12 | <AdministratorPassword> 13 | <Value>P@ssw0rd</Value> 14 | <PlainText>True</PlainText> 15 | </AdministratorPassword> 16 | </UserAccounts> 17 | <OOBE> 18 | <VMModeOptimizations> 19 | <SkipNotifyUILanguageChange>true</SkipNotifyUILanguageChange> 20 | <SkipWinREInitialization>true</SkipWinREInitialization> 21 | </VMModeOptimizations> 22 | <SkipMachineOOBE>true</SkipMachineOOBE> 23 | <HideEULAPage>true</HideEULAPage> 24 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 25 | <ProtectYourPC>3</ProtectYourPC> 26 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 27 | <NetworkLocation>Work</NetworkLocation> 28 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 29 | </OOBE> 30 | </component> 31 | <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"> 32 | <InputLocale>en-au</InputLocale> 33 | <SystemLocale>en-us</SystemLocale> 34 | <UILanguage>en-au</UILanguage> 35 | <UILanguageFallback>en-us</UILanguageFallback> 36 | <UserLocale>en-au</UserLocale> 37 | </component> 38 | </settings> 39 | <cpi:offlineImage cpi:source="catalog:d:/data/software/microsoft/windows2016/extracted/sources/install_windows server 2016 serverdatacenter.clg" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> 40 | </unattend> 41 | -------------------------------------------------------------------------------- /WKS-Unattend.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <unattend xmlns="urn:schemas-microsoft-com:unattend"> 3 | <settings pass="specialize"> 4 | <component name="Microsoft-Windows-UnattendedJoin" 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"> 5 | <Identification> 6 | <Credentials> 7 | <Domain>TP1</Domain> 8 | <Password>P@ssw0rd</Password> 9 | <Username>administrator</Username> 10 | </Credentials> 11 | <JoinDomain>TP1.lab</JoinDomain> 12 | </Identification> 13 | </component> 14 | </settings> 15 | </unattend> -------------------------------------------------------------------------------- /WriteLogEntry.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Write-LogEntry { 3 | [cmdletBinding()] 4 | param ( 5 | [ValidateSet("Information", "Error")] 6 | $Type = "Information", 7 | [parameter(Mandatory = $true)] 8 | $Message 9 | ) 10 | switch ($Type) { 11 | 'Error' { 12 | $Severity = 3 13 | break; 14 | } 15 | 'Information' { 16 | $Severity = 6 17 | break; 18 | } 19 | } 20 | $DateTime = New-Object -ComObject WbemScripting.SWbemDateTime 21 | $DateTime.SetVarDate($(Get-Date)) 22 | $UtcValue = $DateTime.Value 23 | $UtcOffset = $UtcValue.Substring(21, $UtcValue.Length - 21) 24 | $scriptname = (Get-PSCallStack)[1] 25 | $logline = ` 26 | "<![LOG[$message]LOG]!>" + ` 27 | "<time=`"$(Get-Date -Format HH:mm:ss.fff)$($UtcOffset)`" " + ` 28 | "date=`"$(Get-Date -Format M-d-yyyy)`" " + ` 29 | "component=`"$($scriptname.Command)`" " + ` 30 | "context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " + ` 31 | "type=`"$Severity`" " + ` 32 | "thread=`"$PID`" " + ` 33 | "file=`"$($Scriptname.ScriptName)`">"; 34 | 35 | $logline | Out-File -Append -Encoding utf8 -FilePath $Logfile -Force 36 | Write-Verbose $Message 37 | } -------------------------------------------------------------------------------- /new-NDESClientCert.ps1: -------------------------------------------------------------------------------- 1 | function new-NDESClientcert { 2 | param( 3 | $Domain, 4 | $ndessvc 5 | ) 6 | $ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext 7 | $ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" 8 | $newcert = $adsi.create("pKICertificateTemplate", "CN=ConfigMgrWebServer") 9 | $newcert.put("distinguishedName", "CN=ConfigMgrWebServer,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext") 10 | $newcert.put("flags", "131649") 11 | $newcert.put("displayName", "ConfigMgr Web Server") 12 | $newcert.put("revision", "100") 13 | $newcert.put("pKIDefaultKeySpec", "1") 14 | $newcert.put("pKIMaxIssuingDepth", "0") 15 | $newcert.put("pKICriticalExtensions", "2.5.29.15") 16 | $newcert.put("pKIExtendedKeyUsage", "1.3.6.1.5.5.7.3.1") 17 | $newcert.put("msPKI-RA-Signature", "0") 18 | $newcert.put("msPKI-Enrollment-Flag", "8") 19 | $newcert.put("msPKI-Private-Key-Flag", "16842752") 20 | $newcert.put("msPKI-Certificate-Name-Flag", "1") 21 | $newcert.put("msPKI-Minimal-Key-Size", "2048") 22 | $newcert.put("msPKI-Template-Schema-Version", "2") 23 | $newcert.put("msPKI-Template-Minor-Revision", "1") 24 | $newcert.put("msPKI-Cert-Template-OID", "1.3.6.1.4.1.311.21.8.9297300.10481922.2378919.4036973.687234.60.11634013.16673656") 25 | $newcert.put("msPKI-Certificate-Application-Policy", "1.3.6.1.5.5.7.3.1") 26 | $newcert.put("pKIKeyUsage", [Byte[]]( "160", "0" )) 27 | $newcert.put("pKIExpirationPeriod", ([Byte[]](0, 192, 171, 149, 139, 163, 252, 255))) 28 | $newcert.put("pKIOverlapPeriod", ([Byte[]](0, 128, 166, 10, 255, 222, 255, 255))) 29 | $newcert.setinfo() | Out-Null 30 | $AdObj = New-Object System.Security.Principal.NTAccount("$domain\SCCM Servers") 31 | $identity = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 32 | $adRights = "ReadProperty, WriteProperty, ExtendedRight,GenericExecute" 33 | $type = "Allow" 34 | $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 35 | $newcert.psbase.ObjectSecurity.SetAccessRule($ACE) 36 | $AdObj = New-Object System.Security.Principal.NTAccount("$domain\$ndessvc") 37 | $identity = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 38 | $adRights = "ReadProperty, WriteProperty, ExtendedRight,GenericExecute" 39 | $type = "Allow" 40 | $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 41 | $newcert.psbase.ObjectSecurity.SetAccessRule($ACE) 42 | $newcert.psbase.commitchanges() 43 | $p = Start-Process "C:\Windows\System32\certtmpl.msc" -PassThru 44 | Start-Sleep 2 45 | $p | Stop-Process 46 | Add-CATemplate -name "ConfigMgrWebServer" -ErrorAction SilentlyContinue -Force 47 | } -------------------------------------------------------------------------------- /new-NDESUserCert.ps1: -------------------------------------------------------------------------------- 1 | function new-NDESUsercert { 2 | param( 3 | $Domain, 4 | $ndessvc 5 | ) 6 | $ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext 7 | $ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" 8 | $newcert = $adsi.create("pKICertificateTemplate", "CN=NDESUser") 9 | $newcert.put("distinguishedName", "CN=NDESUser,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext") 10 | $newcert.put("flags", "131642") 11 | $newcert.put("displayName", "NDES User") 12 | $newcert.put("revision", "100") 13 | $newcert.put("pKIDefaultKeySpec", "1") 14 | $newcert.put("pKIMaxIssuingDepth", "0") 15 | $newcert.put("pKICriticalExtensions", @("2.5.29.7", "2.5.29.15")) 16 | $newcert.put("pKIExtendedKeyUsage", @("1.3.6.1.5.5.7.3.4", "1.3.6.1.4.1.311.10.3.4", "1.3.6.1.5.5.7.3.2", "2.5.29.37.0")) 17 | $newcert.put("msPKI-RA-Signature", "0") 18 | $newcert.put("msPKI-Enrollment-Flag", "1") 19 | $newcert.put("msPKI-Private-Key-Flag", "16842752") 20 | $newcert.put("msPKI-Certificate-Name-Flag", "1") 21 | $newcert.put("msPKI-Minimal-Key-Size", "2048") 22 | $newcert.put("msPKI-Template-Schema-Version", "2") 23 | $newcert.put("msPKI-Template-Minor-Revision", "2") 24 | $newcert.put("msPKI-Cert-Template-OID", "1.3.6.1.4.1.311.21.8.12344063.8726395.2247602.10184630.9318525.198.2328079.11028052") 25 | $newcert.put("msPKI-Certificate-Application-Policy", @("1.3.6.1.5.5.7.3.4", "1.3.6.1.4.1.311.10.3.4", "1.3.6.1.5.5.7.3.2", "2.5.29.37.0")) 26 | $newcert.put("pKIKeyUsage", [Byte[]]( "32" )) 27 | $newcert.put("pKIExpirationPeriod", ([Byte[]](0, 64, 57, 135, 46, 225, 254, 255))) 28 | $newcert.put("pKIOverlapPeriod", ([Byte[]](0, 128, 166, 10, 255, 222, 255, 255))) 29 | $newcert.setinfo() | Out-Null 30 | $AdObj = New-Object System.Security.Principal.NTAccount("$domain\SCCM Servers") 31 | $identity = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 32 | $adRights = "ReadProperty, WriteProperty, ExtendedRight,GenericExecute" 33 | $type = "Allow" 34 | $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 35 | $newcert.psbase.ObjectSecurity.SetAccessRule($ACE) 36 | $AdObj = New-Object System.Security.Principal.NTAccount("$domain\$ndessvc") 37 | $identity = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 38 | $adRights = "ReadProperty, WriteProperty, ExtendedRight,GenericExecute" 39 | $type = "Allow" 40 | $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 41 | $newcert.psbase.ObjectSecurity.SetAccessRule($ACE) 42 | $newcert.psbase.commitchanges() 43 | $p = Start-Process "C:\Windows\System32\certtmpl.msc" -PassThru 44 | Start-Sleep 2 45 | $p | Stop-Process 46 | Add-CATemplate -name "NDESUser" -ErrorAction SilentlyContinue -Force 47 | } -------------------------------------------------------------------------------- /new-ccmwebcert.ps1: -------------------------------------------------------------------------------- 1 | function new-CCMWebcert { 2 | param( 3 | $Domain 4 | ) 5 | $ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext 6 | $ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" 7 | $newcert = $adsi.create("pKICertificateTemplate", "CN=ConfigMgr Web Server") 8 | $newcert.put("distinguishedName", "CN=ConfigMgrWebServer,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext") 9 | $newcert.put("flags", "131649") 10 | $newcert.put("displayName", "ConfigMgr Web Server") 11 | $newcert.put("revision", "100") 12 | $newcert.put("pKIDefaultKeySpec", "1") 13 | $newcert.put("pKIMaxIssuingDepth", "0") 14 | $newcert.put("pKICriticalExtensions", "2.5.29.15") 15 | $newcert.put("pKIExtendedKeyUsage", "1.3.6.1.5.5.7.3.1") 16 | $newcert.put("msPKI-RA-Signature", "0") 17 | $newcert.put("msPKI-Enrollment-Flag", "8") 18 | $newcert.put("msPKI-Private-Key-Flag", "16842752") 19 | $newcert.put("msPKI-Certificate-Name-Flag", "1") 20 | $newcert.put("msPKI-Minimal-Key-Size", "2048") 21 | $newcert.put("msPKI-Template-Schema-Version", "2") 22 | $newcert.put("msPKI-Template-Minor-Revision", "1") 23 | $newcert.put("msPKI-Cert-Template-OID", "1.3.6.1.4.1.311.21.8.9297300.10481922.2378919.4036973.687234.60.11634013.16673656") 24 | $newcert.put("msPKI-Certificate-Application-Policy", "1.3.6.1.5.5.7.3.1") 25 | $newcert.put("pKIKeyUsage", [Byte[]]( "160", "0" )) 26 | $newcert.put("pKIExpirationPeriod", ([Byte[]](0, 192, 171, 149, 139, 163, 252, 255))) 27 | $newcert.put("pKIOverlapPeriod", ([Byte[]](0, 128, 166, 10, 255, 222, 255, 255))) 28 | $newcert.setinfo() | Out-Null 29 | $AdObj = New-Object System.Security.Principal.NTAccount("$domain\SCCM Servers") 30 | $identity = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 31 | $adRights = "ReadProperty, WriteProperty, ExtendedRight,GenericExecute" 32 | $type = "Allow" 33 | $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 34 | $newcert.psbase.ObjectSecurity.SetAccessRule($ACE) 35 | $newcert.psbase.commitchanges() 36 | $p = Start-Process "C:\Windows\System32\certtmpl.msc" -PassThru 37 | Start-Sleep 2 38 | $p | Stop-Process 39 | Add-CATemplate -name "ConfigMgr Web Server" -ErrorAction SilentlyContinue -Force 40 | } -------------------------------------------------------------------------------- /new-kdccert.ps1: -------------------------------------------------------------------------------- 1 | function new-kdccert { 2 | param( 3 | $Domain 4 | ) 5 | $ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext 6 | $ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" 7 | $newcert = $adsi.create("pKICertificateTemplate", "CN=Domain Controller Authentication (KDC)") 8 | $newcert.put("distinguishedName", "CN=DomainControllerAuthentication(KDC),CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext") 9 | $newcert.put("flags", "131168") 10 | $newcert.put("displayName", "Domain Controller Authentication (KDC)") 11 | $newcert.put("revision", "100") 12 | $newcert.put("pKIDefaultKeySpec", "1") 13 | $newcert.put("pKIMaxIssuingDepth", "0") 14 | $newcert.put("pKICriticalExtensions", @("2.5.29.17", "2.5.29.15")) 15 | $newcert.put("pKIExtendedKeyUsage", @("1.3.6.1.4.1.311.20.2.2", "1.3.6.1.5.5.7.3.1", "1.3.6.1.5.2.3.5", "1.3.6.1.5.5.7.3.2")) 16 | $newcert.put("msPKI-RA-Signature", "0") 17 | $newcert.put("msPKI-Enrollment-Flag", "32") 18 | $newcert.put("msPKI-Private-Key-Flag", "84213760") 19 | $newcert.put("msPKI-Certificate-Name-Flag", "406847488") 20 | $newcert.put("msPKI-Minimal-Key-Size", "2048") 21 | $newcert.put("msPKI-Template-Schema-Version", "4") 22 | $newcert.put("msPKI-Template-Minor-Revision", "3") 23 | $newcert.put("msPKI-Cert-Template-OID", "1.3.6.1.4.1.311.21.8.1228481.4920039.15089813.8619623.7815189.17.10339185.7330727") 24 | $newcert.put("msPKI-Certificate-Application-Policy", @("1.3.6.1.4.1.311.20.2.2", "1.3.6.1.5.5.7.3.1", "1.3.6.1.5.2.3.5", "1.3.6.1.5.5.7.3.2")) 25 | $newcert.put("msPKI-Supersede-Templates", @("DomainController", "DomainControllerAuthentication", "KerberosAuthentication")) 26 | $newcert.put("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``") 27 | $newcert.put("pKIKeyUsage", [Byte[]]( "160", "0" )) 28 | $newcert.put("pKIExpirationPeriod", ([Byte[]](0, 192, 171, 149, 139, 163, 252, 255))) 29 | $newcert.put("pKIOverlapPeriod", ([Byte[]](0, 128, 166, 10, 255, 222, 255, 255))) 30 | $newcert.setinfo() | Out-Null 31 | $AdObj = New-Object System.Security.Principal.NTAccount("$domain\Domain Controllers") 32 | $identity = $AdObj.Translate([System.Security.Principal.SecurityIdentifier]) 33 | $adRights = "ReadProperty, WriteProperty, ExtendedRight" 34 | $type = "Allow" 35 | $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity, $adRights, $type) 36 | $newcert.psbase.ObjectSecurity.SetAccessRule($ACE) 37 | $newcert.psbase.commitchanges() 38 | $p = Start-Process "C:\Windows\System32\certtmpl.msc" -PassThru 39 | Start-Sleep 2 40 | $p | Stop-Process 41 | Add-CATemplate -name "Domain Controller Authentication (KDC)" -ErrorAction SilentlyContinue -Force 42 | } 43 | -------------------------------------------------------------------------------- /newCMInstance.ps1: -------------------------------------------------------------------------------- 1 | function new-CMInstance { 2 | param( 3 | $cmsession, 4 | $cmname, 5 | $cmsitecode, 6 | $domainfqdn, 7 | [ValidateSet("TP", "Prod")] 8 | [string] 9 | $ver, 10 | $ipsub, 11 | $domainnetbios, 12 | [ValidateSet("CAS", "PRI", "CASPRI")] 13 | [string] 14 | $CMServerType, 15 | [string] 16 | $CASIPAddress 17 | ) 18 | if ($CMServerType -eq "CASPRI") { 19 | $casservername = Invoke-Command -Session $cmsession -ScriptBlock { param($i) [System.Net.Dns]::GetHostEntry("$i").hostname } -ArgumentList $CASIPAddress 20 | } 21 | $cmOSName = Invoke-Command -Session $cmsession -ScriptBlock { $env:COMPUTERNAME } 22 | $sqlsettings = invoke-command -Session $cmsession -ScriptBlock { (new-object ('Microsoft.SqlServer.Management.Smo.Server') $env:COMPUTERNAME).Settings | Select-Object DefaultFile, Defaultlog } 23 | write-logentry -message "Host name for $cmname is: $cmosname" 24 | $cmsitecode = "$cmsitecode" 25 | if ($CMServerType -eq "CASPRI") { 26 | $cminstallini = New-CMSettingfile -CMServerType $CMServerType -ServerName $cmOSName -cmsitecode "PRI" -domainFQDN $DomainFQDN -sqlsettings $sqlsettings -ver $ver -CasServerName $casservername -CasSiteCode $cmsitecode 27 | } 28 | else { 29 | $cminstallini = New-CMSettingfile -CMServerType $CMServerType -ServerName $cmOSName -cmsitecode $cmsitecode -domainFQDN $DomainFQDN -sqlsettings $sqlsettings -ver $ver 30 | } 31 | 32 | write-logentry -message "CM install ini for $cmname is: $cminstallini" -type information 33 | Invoke-Command -Session $cmsession -ScriptBlock { param($ini) new-item -ItemType file -Path c:\CMinstall.ini -Value $INI -Force } -ArgumentList $CMInstallINI | out-null 34 | Invoke-Command -Session $cmsession -ScriptBlock { Add-ADGroupMember "SCCM Servers" -Members "$($env:computername)$" } 35 | if (!($CMServerType -eq "CASPRI")) { 36 | invoke-command -Session $cmsession -scriptblock { start-process -filepath "c:\data\sccm\smssetup\bin\x64\extadsch.exe" -wait } 37 | write-logentry -message "AD Schema has been exteded for SCCM on $domainfqdn" 38 | } 39 | write-logentry -message "SCCM installation process has started on $cmname this will take some time so grab a coffee" -type information 40 | Invoke-Command -Session $cmsession -ScriptBlock { Start-Process -FilePath "C:\DATA\SCCM\SMSSETUP\bin\x64\setup.exe" -ArgumentList "/script c:\CMinstall.ini" -wait } 41 | if (!($cas.IsPresent)) { 42 | write-logentry -message "SCCM has been installed on $cmname" -type information 43 | invoke-command -session $cmsession -scriptblock { 44 | param($ipsub, $sitecode, $Subnetname, $DomainDN) 45 | while (!(Test-Path "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)) { 46 | start-sleep -Seconds 20 47 | } 48 | import-module "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"; 49 | if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME | Out-Null } 50 | Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:"; 51 | New-CMBoundary -Type IPSubnet -Value "$($ipsub)0/24" -name $Subnetname | Out-Null; 52 | New-CMBoundaryGroup -name $Subnetname -DefaultSiteCode "$((Get-PSDrive -PSProvider CMSite).name)" | Out-Null; 53 | Add-CMBoundaryToGroup -BoundaryName $Subnetname -BoundaryGroupName $Subnetname; 54 | $Schedule = New-CMSchedule -RecurInterval Minutes -Start "2012/10/20 00:00:00" -End "2013/10/20 00:00:00" -RecurCount 10; 55 | Set-CMDiscoveryMethod -ActiveDirectorySystemDiscovery -SiteCode $sitecode -Enabled $True -EnableDeltaDiscovery $True -PollingSchedule $Schedule -AddActiveDirectoryContainer "LDAP://$domaindn" -Recursive -ErrorAction SilentlyContinue; 56 | Get-CMDevice | Where-Object { $_.ADSiteName -eq "Default-First-Site-Name" } | Install-CMClient -IncludeDomainController $true -AlwaysInstallClient $true -SiteCode $sitecode; 57 | } -ArgumentList $ipsub, $cmsitecode, $domainnetbios, ("dc=" + ($DomainFQDN.Split('.') -join ",dc=")) 58 | } 59 | return $cmOSName 60 | } -------------------------------------------------------------------------------- /newCMSQLsettingsINI.ps1: -------------------------------------------------------------------------------- 1 | function new-CMSQLsettingsINI { 2 | param( 3 | $domainnetbios, 4 | $admpwd 5 | ) 6 | $SQLHash = @{'ACTION' = '"Install"'; 7 | 'SUPPRESSPRIVACYSTATEMENTNOTICE' = '"TRUE"'; 8 | 'IACCEPTROPENLICENSETERMS' = '"TRUE"'; 9 | 'ENU' = '"TRUE"'; 10 | 'QUIET' = '"TRUE"'; 11 | 'UpdateEnabled' = '"TRUE"'; 12 | 'USEMICROSOFTUPDATE' = '"TRUE"'; 13 | 'FEATURES' = 'SQLENGINE,RS'; 14 | 'UpdateSource' = '"MU"'; 15 | 'HELP' = '"FALSE"'; 16 | 'INDICATEPROGRESS' = '"FALSE"'; 17 | 'X86' = '"FALSE"'; 18 | 'INSTANCENAME' = '"MSSQLSERVER"'; 19 | 'INSTALLSHAREDDIR' = '"C:\Program Files\Microsoft SQL Server"'; 20 | 'INSTALLSHAREDWOWDIR' = '"C:\Program Files (x86)\Microsoft SQL Server"'; 21 | 'INSTANCEID' = '"MSSQLSERVER"'; 22 | 'RSINSTALLMODE' = '"DefaultNativeMode"'; 23 | 'SQLTELSVCACCT' = '"NT Service\SQLTELEMETRY"'; 24 | 'SQLTELSVCSTARTUPTYPE' = '"Automatic"'; 25 | 'INSTANCEDIR' = '"C:\Program Files\Microsoft SQL Server"'; 26 | 'AGTSVCACCOUNT' = '"NT Service\SQLSERVERAGENT"'; 27 | 'AGTSVCSTARTUPTYPE' = '"Manual"'; 28 | 'COMMFABRICPORT' = '"0"'; 29 | 'COMMFABRICNETWORKLEVEL' = '"0"'; 30 | 'COMMFABRICENCRYPTION' = '"0"'; 31 | 'MATRIXCMBRICKCOMMPORT' = '"0"'; 32 | 'SQLSVCSTARTUPTYPE' = '"Automatic"'; 33 | 'FILESTREAMLEVEL' = '"0"'; 34 | 'ENABLERANU' = '"FALSE"'; 35 | 'SQLCOLLATION' = '"SQL_Latin1_General_CP1_CI_AS"'; 36 | 'SQLSVCACCOUNT' = """$domainnetbios\administrator"""; 37 | 'SQLSVCPASSWORD' = """$admpwd""" 38 | 'SQLSVCINSTANTFILEINIT' = '"FALSE"'; 39 | 'SQLSYSADMINACCOUNTS' = """$domainnetbios\administrator"" ""$domainnetbios\Domain Users"""; 40 | 'SQLTEMPDBFILECOUNT' = '"1"'; 41 | 'SQLTEMPDBFILESIZE' = '"8"'; 42 | 'SQLTEMPDBFILEGROWTH' = '"64"'; 43 | 'SQLTEMPDBLOGFILESIZE' = '"8"'; 44 | 'SQLTEMPDBLOGFILEGROWTH' = '"64"'; 45 | 'ADDCURRENTUSERASSQLADMIN' = '"FALSE"'; 46 | 'TCPENABLED' = '"1"'; 47 | 'NPENABLED' = '"1"'; 48 | 'BROWSERSVCSTARTUPTYPE' = '"Disabled"'; 49 | 'RSSVCACCOUNT' = '"NT Service\ReportServer"'; 50 | 'RSSVCSTARTUPTYPE' = '"Automatic"'; 51 | } 52 | $SQLHASHINI = @{'OPTIONS' = $SQLHash } 53 | $SQLInstallINI = "" 54 | Foreach ($i in $SQLHASHINI.keys) { 55 | $SQLInstallINI += "[$i]`r`n" 56 | foreach ($j in $($SQLHASHINI[$i].keys | Sort-Object)) { 57 | $SQLInstallINI += "$j=$($SQLHASHINI[$i][$j])`r`n" 58 | } 59 | $SQLInstallINI += "`r`n" 60 | } 61 | return $SQLInstallINI 62 | } -------------------------------------------------------------------------------- /newCMWorkStation.ps1: -------------------------------------------------------------------------------- 1 | function new-CMWorkstation 2 | { 3 | $vmname = "TP1WKS1" 4 | $Cores = 2 5 | $RAM = 4Gb 6 | $RefVHDX = "" 7 | 8 | } -------------------------------------------------------------------------------- /newDCServer.ps1: -------------------------------------------------------------------------------- 1 | function new-DC { 2 | param( 3 | [Parameter(ParameterSetName = 'DCClass')] 4 | [DC] 5 | $DCConfig, 6 | [Parameter(ParameterSetName = 'NoClass')] 7 | [string] 8 | $VHDXpath, 9 | [Parameter(ParameterSetName = 'NoClass')] 10 | [pscredential] 11 | $LocalAdmin, 12 | [Parameter(ParameterSetName = 'NoClass')] 13 | [string] 14 | $Network, 15 | [Parameter(ParameterSetName = 'NoClass')] 16 | [string] 17 | $IPAddress, 18 | [Parameter(ParameterSetName = 'NoClass')] 19 | [string] 20 | $DomainFQDN, 21 | [Parameter(ParameterSetName = 'NoClass')] 22 | [string] 23 | $ADMPwd, 24 | [Parameter(ParameterSetName = 'NoClass')] 25 | [pscredential] 26 | $DomainUser, 27 | [parameter(ParameterSetName = 'NoClass', Mandatory = $false)] 28 | [switch] 29 | $VMSnapshotEnabled, 30 | [Parameter(ParameterSetName = 'NoClass')] 31 | [int] 32 | $Cores, 33 | [Parameter(ParameterSetName = 'NoClass')] 34 | [int] 35 | $RAM, 36 | [Parameter(ParameterSetName = 'NoClass')] 37 | [string] 38 | $Name, 39 | [Parameter(ParameterSetName = 'NoClass')] 40 | [string] 41 | $RefVHDX 42 | ) 43 | if (!$PSBoundParameters.ContainsKey('DCConfig')) { 44 | $DCconfig = [DC]::new() 45 | $dcconfig.AdmPwd = $admpwd 46 | $dcconfig.cores = $cores 47 | $dcconfig.Ram = $ram 48 | $dcconfig.IPAddress = $ipAddress 49 | $dcconfig.Name = $name 50 | $dcconfig.network = $Network 51 | $dcconfig.VHDXpath = $VHDXpath 52 | $dcconfig.localadmin = $localadmin 53 | $dcconfig.domainFQDN = $DomainFQDN 54 | $dcconfig.domainuser = $domainuser 55 | $dcconfig.VMSnapshotenabled = $vmSnapshotenabled.IsPresent 56 | $DCConfig.refvhdx = $refvhdx 57 | } 58 | $ipsubnet = $dcconfig.IPAddress.substring(0, ($dcconfig.IPAddress.length - ([ipaddress] $dcconfig.IPAddress).GetAddressBytes()[3].count - 1)) 59 | Write-LogEntry -Message "DC Server Started: $(Get-Date)" -Type Information 60 | Write-LogEntry -Message "DC Settings are: $($DCConfig | ConvertTo-Json)" -Type Information 61 | Write-LogEntry -Message "VM for DC will be named: $($dcconfig.name)" -type Information 62 | Write-LogEntry -Message "Path for the VHDX for $($dcconfig.name) is: $($dcconfig.VHDXpath)" -type information 63 | if (!(Invoke-Pester -tagfilter "DCVM" -passthru -output none).result -ne "Passed") { 64 | write-logentry -message "DC for env: $($DCConfig.Network) doesn't exist, creating now" -Type Information 65 | if ((Invoke-Pester -tagfilter "DCVHDX" -passthru -output none).result -eq "Passed") { 66 | Write-LogEntry -Message "DC VHDX Already Exists at path: $($dcconfig.VHDXpath) Please clean up and Rerun." -Type Error 67 | throw "DC VHDX Already Exists at path: $($dcconfig.VHDXpath) Please clean up and Rerun." 68 | } 69 | else { 70 | Copy-Item -Path $dcconfig.RefVHDX -Destination $dcconfig.VHDXpath 71 | Write-LogEntry -Message "Reference VHDX: $($dcconfig.refvhdx) has been copied to: $($dcconfig.VHDXpath)" -Type Information 72 | } 73 | if ((Invoke-Pester -tagfilter "DCVHDX" -passthru -output none).result -ne "Passed") { 74 | Write-LogEntry -Message "Error Creating the VHDX for $($dcconfig.name). Build STOPPED" -Type Error 75 | throw "Error Creating the VHDX for DC" 76 | } 77 | else { 78 | Write-LogEntry -Message "Starting to create $($dcconfig.name) server" -Type Information 79 | $vm = new-vm -Name $dcconfig.name -MemoryStartupBytes ($dcconfig.ram * 1Gb) -VHDPath $dcconfig.VHDXpath -Generation 2 80 | $vm | Set-VMProcessor -Count $dcconfig.cores 81 | Enable-VMIntegrationService -VMName $dcconfig.name -Name "Guest Service Interface" 82 | if (!($dcconfig.vmSnapshotenabled)) { 83 | set-vm -name $dcconfig.name -checkpointtype Disabled 84 | } 85 | Write-LogEntry -Message "$($dcconfig.name) has been created" -Type Information 86 | start-vm -Name $dcconfig.name 87 | Write-LogEntry -Message "DC server named $($dcconfig.name) has been started" 88 | } 89 | Get-VMNetworkAdapter -vmname $dcconfig.name | Connect-VMNetworkAdapter -SwitchName $dcconfig.Network 90 | Write-LogEntry -Message "vSwitch $Network has been attached to $($dcconfig.name)" -Type Information 91 | while ((Invoke-Command -VMName $dcconfig.name -Credential $dcconfig.localadmin { "Test" } -ErrorAction SilentlyContinue) -ne "Test") { Start-Sleep -Seconds 5 } 92 | $dcsession = New-PSSession -VMName $dcconfig.name -Credential $dcconfig.localadmin 93 | Write-LogEntry -Message "PowerShell Direct session for $($dcconfig.localadmin.UserName) has been initated with DC Service named: $($dcconfig.name)" -Type Information 94 | $dcnics = Invoke-Command -VMName $dcconfig.name -Credential $dcconfig.localadmin -ScriptBlock { Get-NetAdapter } 95 | Write-LogEntry -Message "The following network adaptors $($dcnics -join ",") have been found on: $($dcconfig.name)" -Type Information 96 | if ((Invoke-Pester -tagfilter "DCIP" -passthru -output none).result -ne "Passed") { 97 | $IPGateway = "$ipsubnet`1" 98 | Invoke-Command -Session $dcsession -ScriptBlock { param($t, $i, $g) new-NetIPAddress -InterfaceIndex $t -AddressFamily IPv4 -IPAddress "$i" -PrefixLength 24 -DefaultGateway "$g"; Set-DnsClientServerAddress -ServerAddresses ('8.8.8.8') -InterfaceIndex $t } -ArgumentList $dcnics.InterfaceIndex, $dcconfig.ipaddress, $IPGateway | Out-Null 99 | Write-LogEntry -Message "IP Address $($dcconfig.IPAddress) has been assigned to $($dcconfig.name)" -Type Information 100 | } 101 | if ((Invoke-Pester -tagfilter "DCFeatures" -passthru -output none).result -ne "Passed") { 102 | Invoke-Command -Session $dcsession -ScriptBlock { Install-WindowsFeature -Name DHCP, DNS, AD-Domain-Services } | Out-Null 103 | Write-LogEntry -Message "Domain Services roles have been enabled on $($dcconfig.name)" -Type Information 104 | } 105 | if ((Invoke-Pester -tagfilter "DCPromo" -passthru -output none).result -ne "Passed") { 106 | Invoke-Command -Session $dcsession -ScriptBlock { param($d, $p)Install-ADDSForest -DomainName $d -SafeModeAdministratorPassword (ConvertTo-SecureString -string $p -asplaintext -Force) -confirm:$false -WarningAction SilentlyContinue } -ArgumentList $dcconfig.DomainFQDN, $dcconfig.admpwd | out-null 107 | Write-LogEntry -Message "Forrest $($dcconfig.DomainFQDN) has been promoted on $($dcconfig.name)" -Type Information 108 | } 109 | $dcsession | Remove-PSSession 110 | Write-LogEntry -Type Information -Message "PowerShell Direct Session for $($dcconfig.name) has been disconnected" 111 | start-sleep -Seconds 360 112 | while (!(Invoke-Command -VMName $dcconfig.name -Credential $dcconfig.domainuser { test-netconnection $env:computername -port 9389 }).TcpTestSucceeded) { 113 | (Invoke-Command -VMName $dcconfig.name -Credential $dcconfig.domainuser { Get-WmiObject -Class Win32_Service -Filter 'name="adws"' }).state 114 | Start-Sleep -Seconds 5 115 | } 116 | while ((Invoke-Pester -TagFilter "DCCM" -PassThru -Output None).result -ne "Passed") { 117 | $dcsessiondom = New-PSSession -VMName $dcconfig.name -Credential $dcconfig.domainuser -ErrorAction SilentlyContinue 118 | #Write-LogEntry -Message "PowerShell Direct session for $($dcconfig.domainuser.UserName) has been initated with DC Service named: $($dcconfig.name)" -Type Information 119 | $null = Invoke-Command -Session $dcsessiondom -ScriptBlock { Import-Module ActiveDirectory -ErrorAction SilentlyContinue -WarningAction SilentlyContinue; New-ADGroup -Name "SCCM Servers" -GroupScope 1 -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } 120 | } 121 | Invoke-Command -Session $dcsessiondom -ScriptBlock { $root = (Get-ADRootDSE).defaultNamingContext; if (!([adsi]::Exists("LDAP://CN=System Management,CN=System,$root"))) { $null = New-ADObject -Type Container -name "System Management" -Path "CN=System,$root" -Passthru }; $acl = get-acl "ad:CN=System Management,CN=System,$root"; $objGroup = Get-ADGroup -filter { Name -eq "SCCM Servers" }; $All = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::SelfAndChildren; $ace = new-object System.DirectoryServices.ActiveDirectoryAccessRule $objGroup.SID, "GenericAll", "Allow", $All; $acl.AddAccessRule($ace); Set-acl -aclobject $acl "ad:CN=System Management,CN=System,$root" } 122 | Write-LogEntry -Message "System Management Container created in $($dcconfig.DomainFQDN) forrest on $($dcconfig.name)" -type Information 123 | Write-LogEntry -Type Information -Message "Configuring DHCP Server" 124 | ### need to add DNS and Default Gateway addresses too the DHCP Scope. 125 | Invoke-Command -Session $dcsessiondom -ScriptBlock { param($domname, $iprange) 126 | Add-DhcpServerInDC; 127 | Add-DhcpServerv4Scope -name "$domname" -StartRange "$($iprange)100" -EndRange "$($iprange)150" -SubnetMask "255.255.255.0" 128 | Set-DhcpServerv4OptionValue -ComputerName $env:COMPUTERNAME -OptionId 003 -Value "$($iprange)1" 129 | Set-DhcpServerv4OptionValue -ComputerName $env:COMPUTERNAME -OptionId 006 -Value "$($iprange)10" 130 | } -ArgumentList $domainnetbios, $ipsubnet | Out-Null 131 | Write-LogEntry -Type Information -Message "DHCP Scope has been configured for $($ipsubnet)100 to $($ipsubnet)150 with a mask of 255.255.255.0" 132 | Invoke-Command -Session $dcsessiondom -ScriptBlock { Set-aduser -identity "Administrator" -PasswordNeverExpires $true } 133 | Write-LogEntry -type Information -message "Domain admin account set to not expire" 134 | Invoke-Command -Session $dcsessiondom -ScriptBlock { Set-ItemProperty -path HKLM:\SOFTWARE\Microsoft\ServerManager -name DoNotOpenServerManagerAtLogon -Type DWord -value "1" -Force } 135 | $dcsessiondom | Remove-PSSession 136 | } 137 | Write-LogEntry -Message "DC Server Completed: $(Get-Date)" -Type Information 138 | Invoke-Pester -TagFilter "DC" -Output Detailed 139 | } 140 | -------------------------------------------------------------------------------- /newRRASServer.ps1: -------------------------------------------------------------------------------- 1 | function new-RRASServer { 2 | param( 3 | [Parameter(ParameterSetName = 'RRASClass')] 4 | [RRAS] 5 | $RRASConfig, 6 | [Parameter(ParameterSetName = 'NoClass')] 7 | [string] 8 | $VHDXPath, 9 | [Parameter(ParameterSetName = 'NoClass')] 10 | [string] 11 | $RefVHDX, 12 | [Parameter(ParameterSetName = 'NoClass')] 13 | [pscredential] 14 | $localadmin, 15 | [parameter(ParameterSetName = 'NoClass', Mandatory = $false)] 16 | [switch] 17 | $vmSnapshotenabled, 18 | [Parameter(ParameterSetName = 'NoClass')] 19 | [string] 20 | $Name, 21 | [Parameter(ParameterSetName = 'NoClass')] 22 | [int] 23 | $cores, 24 | [Parameter(ParameterSetName = 'NoClass')] 25 | [int] 26 | $RAM, 27 | [Parameter(ParameterSetName = 'NoClass')] 28 | [string] 29 | $IPaddress, 30 | [Parameter(ParameterSetName = 'NoClass')] 31 | [string] 32 | $network 33 | ) 34 | if (!$PSBoundParameters.ContainsKey('RRASConfig')) { 35 | $RRASConfig = [RRAS]::new() 36 | $RRASConfig.Name = $name 37 | $RRASConfig.Cores = $cores 38 | $RRASConfig.Ram = $RAM 39 | $RRASConfig.ipaddress = $IPaddress 40 | $RRASConfig.Network = $network 41 | $RRASConfig.localadmin = $localadmin 42 | $RRASConfig.vmSnapshotenabled = $vmSnapshotenabled 43 | $RRASConfig.VHDXpath = $VHDXPath 44 | $RRASConfig.RefVHDX = $RefVHDX 45 | } 46 | Write-LogEntry -Message "RRAS Server started $(Get-Date)" -type Information 47 | Write-LogEntry -Message "RRAS Settings are: $($RRASConfig | ConvertTo-Json)" -Type Information 48 | if ((Invoke-Pester -TagFilter "RRASVM" -PassThru -Output None).result -ne "Passed") { 49 | Write-LogEntry -Type Information -Message "Path for the VHDX for RRAS is: $($RRASConfig.VHDXpath)" 50 | if ((Invoke-Pester -TagFilter "RRASVHDX" -PassThru -Output None).result -eq "Passed") { 51 | Write-LogEntry -Type Error -Message "RRAS VHDX Already Exists at path: $($RRASConfig.VHDXpath) Please clean up and Rerun. Build STOPPED" 52 | throw "RRAS VHDX Already Exists at path: $($RRASConfig.VHDXpath) Please clean up and Rerun." 53 | } 54 | else { 55 | Copy-Item -Path $RRASConfig.RefVHDX -Destination $RRASConfig.VHDXpath 56 | Write-LogEntry -Type Information -Message "Reference VHDX: $($RRASConfig.RefVHDX) has been copied to: $($RRASConfig.VHDXpath)" 57 | } 58 | if ((Invoke-Pester -TagFilter "RRASVHDX" -PassThru -Output None).result -ne "Passed") { 59 | Write-LogEntry -Type Error -Message "Error Creating the VHDX for RRAS. Build STOPPED" 60 | throw "Error Creating the VHDX for RRAS" 61 | } 62 | else { 63 | Write-LogEntry -Type Information -Message "Starting to create RRAS Server" 64 | $vm = new-vm -Name $RRASConfig.name -MemoryStartupBytes ($RRASConfig.RAM * 1Gb) -VHDPath $RRASConfig.VHDXpath -Generation 2 | out-null # | Set-VMMemory -DynamicMemoryEnabled:$false 65 | $vm | Set-VMProcessor -Count $RRASConfig.cores 66 | Enable-VMIntegrationService -VMName $RRASConfig.name -Name "Guest Service Interface" 67 | if (!$RRASConfig.vmSnapshotenabled) { 68 | set-vm -Name $RRASConfig.name -CheckpointType Disabled 69 | } 70 | Write-LogEntry -Type Information -Message "RRAS Server has been created" 71 | if ((Invoke-Pester -TagFilter "RRASVM" -PassThru -Output None).result -ne "Passed") 72 | { 73 | Write-LogEntry -Type Error -message "Error Creating the VHDX for RRAS"; 74 | throw "Error Creating the VHDX for RRAS" 75 | } 76 | } 77 | start-vm -Name $RRASConfig.name 78 | Write-LogEntry -Type Information -Message "RRAS Server named $($RRASConfig.Name) has been started" 79 | Get-VMNetworkAdapter -vmname $RRASConfig.name | Connect-VMNetworkAdapter -SwitchName 'Internet' | Set-VMNetworkAdapter -Name 'Internet' -DeviceNaming On 80 | Write-LogEntry -Type Information -Message "vSwitch named Internet has been connected to the RRAS Server" 81 | while ((Invoke-Command -VMName $RRASConfig.name -Credential $RRASConfig.localadmin { "Test" } -ErrorAction SilentlyContinue) -ne "Test") { Start-Sleep -Seconds 5 } 82 | $RRASConfigSession = New-PSSession -VMName $RRASConfig.name -Credential $RRASConfig.localadmin 83 | Write-LogEntry -Type Information -Message "PowerShell Direct session for $($RRASConfig.localadmin.UserName) has been initated with RRAS Server named: $($RRASConfig.name)" 84 | if ((Invoke-Pester -TagFilter "RRASVPN" -PassThru -Output None).result -eq "Passed") { 85 | Write-Verbose "RRAS Routing Already installed" 86 | } 87 | else { 88 | $null = Invoke-Command -Session $RRASConfigSession -ScriptBlock { Install-WindowsFeature Routing -IncludeManagementTools } 89 | Write-LogEntry -Type Information -Message "Routing and Remote Access services role now installed on: $($RRASConfig.name)" 90 | if (((Invoke-Pester -TestName "RRAS" -PassThru -show None).TestResult | Where-Object { $_.name -match "RRAS Routing Installed" }).Result -notmatch "Passed") { Write-LogEntry -Type Error -Message "Error installing RRAS Routing, Build STOPPED"; throw "Error installing RRAS Routing" } 91 | } 92 | while ((Invoke-Command -VMName $RRASConfig.name -Credential $RRASConfig.localadmin { "Test" } -ErrorAction SilentlyContinue) -ne "Test") { Start-Sleep -Seconds 5 } 93 | $RRASConfigSession = New-PSSession -VMName $RRASConfig.name -Credential $RRASConfig.localadmin 94 | Write-LogEntry -Type Information -Message "PowerShell Direct session for $($RRASConfig.localadmin.UserName) has been initated with RRAS Server named: $($RRASConfig.name)" 95 | if ((Invoke-Pester -TagFilter "RRASExtNIC" -PassThru -Output None).result -eq "Passed") { 96 | Write-Verbose "RRAS NIC Already Named external" 97 | } 98 | else { 99 | Invoke-Command -Session $RRASConfigSession -ScriptBlock { Get-NetAdapter -Physical -name Ethernet | rename-netadapter -newname "External" } 100 | Write-LogEntry -Type Information -Message "Renamed Network Adaptor to 'External'" 101 | if (((Invoke-Pester -TestName "RRAS" -PassThru -show None).TestResult | Where-Object { $_.name -match "RRAS External NIC Renamed" }).Result -notmatch "Passed") { write-logentry -Type Error -Message "RRAS NIC not renamed. Build STOPPED"; throw "RRAS NIC not renamed" } 102 | } 103 | Invoke-Command -Session $RRASConfigSession -ScriptBlock { Install-RemoteAccess -VpnType Vpn; netsh routing ip nat install; netsh routing ip nat add interface "External"; netsh routing ip nat set interface "External" mode=full } 104 | Write-LogEntry -Type Information -Message "Routing configured for External Network adapter" 105 | $RRASConfigSession | Remove-PSSession 106 | Write-LogEntry -Type Information -Message "PowerShell Direct Session for $($RRASConfig.name) has been disconnected" 107 | } 108 | else { 109 | Start-VM $RRASConfig.name -WarningAction SilentlyContinue -ErrorAction SilentlyContinue 110 | Write-LogEntry -Type Information -Message "Starting Routing and Remote Access Services server named: $($RRASConfig.Name)" 111 | while ((Invoke-Command -VMName $RRASConfig.name -Credential $RRASConfig.localadmin { "Test" } -ErrorAction SilentlyContinue) -ne "Test") { Start-Sleep -Seconds 5 } 112 | } 113 | 114 | if ((Get-VMNetworkAdapter -VMName $RRASConfig.name | Where-Object { $_.switchname -eq $RRASConfig.network }).count -eq 0) { 115 | if ((Invoke-Pester -TagFilter "RRASLabIP" -PassThru -Output None).result -eq "Passed") { 116 | Write-Verbose "RRAS NIC Already Named $($RRASConfig.Network)" 117 | } 118 | else { 119 | $RRASConfigSession = New-PSSession -VMName $RRASConfig.name -Credential $RRASConfig.localadmin 120 | Write-LogEntry -Type Information -Message "PowerShell Direct session for $($RRASConfig.localadmin.UserName) has been initated with RRAS Server named: $($RRASConfig.name)" 121 | $RRASConfignics = Invoke-Command -Session $RRASConfigSession -ScriptBlock { Get-NetAdapter } 122 | Write-LogEntry -Type Information -Message "The following Network Adaptors $($RRASConfignics -join ",") have been found on: $($RRASConfig.name)" 123 | get-vm -Name $RRASConfig.name | Add-VMNetworkAdapter -SwitchName $RRASConfig.Network 124 | Write-LogEntry -Type Information -Message "Network adaptor for switch: $($RRASConfig.Network) has been added to: $($RRASConfig.Name)" 125 | Start-Sleep -Seconds 10 126 | $RRASConfignewnics = Invoke-Command -Session $RRASConfigSession -ScriptBlock { Get-NetAdapter } 127 | Write-LogEntry -Type Information -Message "The following Network Adaptors $($RRASConfignewnics -join ",") have been found on: $($RRASConfig.name)" 128 | $t = Compare-Object -ReferenceObject $RRASConfignics -DifferenceObject $RRASConfignewnics -PassThru 129 | $null = Invoke-Command -Session $RRASConfigSession -ScriptBlock { param($t, $i) new-NetIPAddress -InterfaceIndex $t -AddressFamily IPv4 -IPAddress "$i" -PrefixLength 24 } -ArgumentList $t.InterfaceIndex, $rrasconfig.IPaddress 130 | Write-LogEntry -Type Information -Message "Ip address of $rrasconfig.IPAddress has been set on Network Adaptor $($RRASConfig.Network) for VM $($RRASConfig.Name)" 131 | Invoke-Command -Session $RRASConfigSession -ScriptBlock { param($n, $t)Get-NetAdapter -InterfaceIndex $n | rename-netadapter -newname $t } -ArgumentList $t.InterfaceIndex, $RRASConfig.Network 132 | Invoke-Command -Session $RRASConfigSession -ScriptBlock { param($n)get-service -name "remoteaccess" | Restart-Service -WarningAction SilentlyContinue; netsh routing ip nat add interface $n } -ArgumentList $RRASConfig.Network 133 | Write-LogEntry -Type Information -Message "Network adaptor renamed to: $($RRASConfig.Network) and Routing configured." 134 | if ((Invoke-Pester -TagFilter "RRASLabIP" -PassThru -Output None).result -ne "Passed") 135 | { 136 | Write-LogEntry -Type Error -Message "Lab IP address not added. Build STOPPED"; 137 | throw "Lab IP address not added" 138 | } 139 | } 140 | Invoke-Command -Session $RRASConfigSession -ScriptBlock { Set-LocalUser -Name "Administrator" -PasswordNeverExpires 1 } 141 | Write-LogEntry -type Information -message "Local admin account set to not expire" 142 | Invoke-Command -Session $RRASConfigSession -ScriptBlock { Set-ItemProperty -path HKLM:\SOFTWARE\Microsoft\ServerManager -name DoNotOpenServerManagerAtLogon -Type DWord -value "1" -Force } 143 | $RRASConfigSession | Remove-PSSession 144 | Write-LogEntry -Type Information -Message "PowerShell Direct Session for $($RRASConfig.name) has been disconnected" 145 | } 146 | write-logentry -Type Information -Message "RRAS Server Completed: $(Get-Date)" 147 | invoke-pester -TagFilter "RRAS" -Output Detailed 148 | } -------------------------------------------------------------------------------- /newcmSQLInstance.ps1: -------------------------------------------------------------------------------- 1 | function New-CMSQLInstance { 2 | param( 3 | $cmname, 4 | $cmsession, 5 | $SQLISO, 6 | $domainnetbios, 7 | $admpwd 8 | ) 9 | Add-VMDvdDrive -VMName $cmname -ControllerNumber 0 -ControllerLocation 1 10 | Set-VMDvdDrive -Path $SQLISO -VMName $cmname -ControllerNumber 0 -ControllerLocation 1 11 | $sqldisk = Invoke-Command -session $cmsession -ScriptBlock { (Get-PSDrive -PSProvider FileSystem | where-object { $_.name -ne "c" }).root } 12 | write-logentry -message "$SQLISO mounted as $sqldisk to $cmname" -type information 13 | $sqlinstallini = new-CMSQLsettingsINI -domainnetbios $domainnetbios -admpwd $admpwd 14 | write-logentry -message "SQL Configuration for $cmname is: $sqlinstallini" -type information 15 | Invoke-Command -Session $cmsession -ScriptBlock { param($ini) new-item -ItemType file -Path c:\ConfigurationFile.INI -Value $INI -Force } -ArgumentList $SQLInstallINI | out-null 16 | write-logentry -message "SQL installation has started on $cmname this can take some time" -type information 17 | Invoke-Command -Session $cmsession -ScriptBlock { param($drive)start-process -FilePath "$drive`Setup.exe" -Wait -ArgumentList "/ConfigurationFile=c:\ConfigurationFile.INI /IACCEPTSQLSERVERLICENSETERMS" } -ArgumentList $sqldisk 18 | write-logentry -message "SQL installation has completed on $cmname told you it would take some time" -type information 19 | Invoke-Command -Session $cmsession -ScriptBlock { 20 | [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null 21 | $srv = New-Object Microsoft.SQLServer.Management.Smo.Server($env:COMPUTERNAME) 22 | if ($srv.status) { 23 | $srv.Configuration.MaxServerMemory.ConfigValue = 8kb 24 | $srv.Configuration.MinServerMemory.ConfigValue = 4kb 25 | $srv.Configuration.Alter() 26 | } 27 | } 28 | Set-VMDvdDrive -VMName $cmname -Path $null 29 | Invoke-Command -session $cmsession -ScriptBlock { Add-WindowsFeature UpdateServices-Services, UpdateServices-db -warningaction silentlycontinue } | Out-Null 30 | invoke-command -session $cmsession -scriptblock { start-process -filepath "C:\Program Files\Update Services\Tools\WsusUtil.exe" -ArgumentList "postinstall CONTENT_DIR=C:\WSUS SQL_INSTANCE_NAME=$env:COMPUTERNAME" -Wait } 31 | invoke-command -session $cmsession -ScriptBlock { start-process -FilePath "C:\windows\system32\msiexec.exe" -ArgumentList "/I c:\data\sccm\dl\sqlncli.msi /QN REBOOT=ReallySuppress" } 32 | write-logentry -message "SQL ISO dismounted from $cmname" -type information 33 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SCCM Lab Environment PowerShell script # 2 | 3 | This script has been a few months in the making, I created it for 2 reasons in order. 4 | 5 | > 1. To learn how to use Pester in PowerShell, 6 | > 2. Build multiple Isolated SCCM labs for testing using a single external IP address (RRAS Server) 7 | 8 | As a result of the script being written to learn Pester it doesn't always use the cleanest code to build the servers for the SCCM environment. 9 | 10 | The solution is designed to run on a Hyper-V host and provision the following virtual machines 11 | 12 | > - RRAS (one per Hyper-V host) 13 | > - Domain Controller (one per environment) 14 | > - SCCM Server (one per environment) 15 | > - Certificate Authority (one per environment) 16 | 17 | To complete this task I'm using PowerShell Direct so there is a requirement to use either Windows 10 or Server 2016 at the host OS for the solution. 18 | 19 | the solution was developed on a higher end laptop so it does have some high number for RAM on each of the devices update as suits your lab hardware. 20 | 21 | ## Instructions ## 22 | 23 | Update the env.json file to reflect the location for SQL, ADK, SCCM, Windows Server 2016 & the Sources directory for .Net 3.5 installation media for Server 2016. 24 | 25 | The script has been setup to use either TP or Production version of SCCM, and you can see in the code where this is handled. 26 | 27 | Execute LabTest.ps1 to spin up a Lab. 28 | 29 | ### Known issues ### 30 | 31 | CM System Discovery setting does't auto configure. 32 | 33 | Tweet me on @onpremcloudguy if there are anything you think should be included. 34 | 35 | big thanks to @ncbrady from @windowsnoob, and the Guys & Girls from @SCConfigMgr for there previous work in documenting the installation of SCCM and the dependent components. -------------------------------------------------------------------------------- /v5.Testcases.ps1: -------------------------------------------------------------------------------- 1 | . .\Lab.Classes.ps1 2 | function Get-EnvSettings { 3 | param ( 4 | $scriptpath 5 | ) 6 | $testconfig = [testframes]::new() 7 | $config = Get-Content "$scriptpath\env.json" -Raw | ConvertFrom-Json 8 | $envConfig = $config.ENVConfig | Where-Object { $_.env -eq $config.env } 9 | $admpwd = $envConfig.AdminPW 10 | $testconfig.localadmin = new-object -typename System.Management.Automation.PSCredential -argumentlist ".\administrator", (ConvertTo-SecureString -String $admpwd -AsPlainText -Force) 11 | $testconfig.domuser = new-object -typename System.Management.Automation.PSCredential -argumentlist "$($envconfig.DomainNetBiosName)\administrator", (ConvertTo-SecureString -String $admpwd -AsPlainText -Force) 12 | $testconfig.vmpath = $envConfig.VMPath 13 | $testconfig.swname = $envConfig.SwitchName 14 | $testconfig.DomainFQDN = $envconfig.DomainFQDN 15 | $testconfig.RefVHDX = $config.REFVHDX 16 | return $testconfig 17 | } 18 | #region RRAS 19 | function Get-rrasVHDXstate { 20 | param ( 21 | $spath 22 | ) 23 | $rrassettings = Get-EnvSettings -scriptpath $spath 24 | $RRASConfig = [RRAS]::new() 25 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 26 | return test-path $RRASConfig.VHDXpath 27 | } 28 | function Get-rrasVMexists { 29 | param ( 30 | $spath 31 | ) 32 | $rrassettings = Get-EnvSettings -scriptpath $spath 33 | $RRASConfig = [RRAS]::new() 34 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 35 | return (Get-vm -Name $RRASConfig.Name -ErrorAction SilentlyContinue).count 36 | } 37 | function Get-rrasVMrunning { 38 | param ( 39 | $spath 40 | ) 41 | $rrassettings = Get-EnvSettings -scriptpath $spath 42 | $RRASConfig = [RRAS]::new() 43 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 44 | return (Get-vm -Name $RRASConfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).Count 45 | } 46 | function Get-rrasVMfeatures { 47 | param ( 48 | $spath 49 | ) 50 | $rrassettings = Get-EnvSettings -scriptpath $spath 51 | $RRASConfig = [RRAS]::new() 52 | $rrasfeat = "Not Installed" 53 | if ((Get-rrasVMexists -spath $spath) -and (Get-rrasVMrunning -spath $spath)) { 54 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 55 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $rrassettings.localadmin 56 | $rrasfeat = (Invoke-Command -Session $trrasSession -ScriptBlock { (Get-windowsfeature -name routing).installstate }).value 57 | $trrasSession | Remove-PSSession 58 | } 59 | return $rrasfeat 60 | } 61 | function Get-rrasVMExternalNIC { 62 | param ( 63 | $spath 64 | ) 65 | $rrassettings = Get-EnvSettings -scriptpath $spath 66 | $RRASConfig = [RRAS]::new() 67 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 68 | $rrasEXTrename = $false 69 | if ((Get-rrasVMexists -spath $spath) -and (Get-rrasVMrunning -spath $spath)) { 70 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $rrassettings.localadmin 71 | $rrasEXTrename = (Invoke-Command -Session $trrasSession -ScriptBlock { Get-NetAdapter -Physical -Name "External" -ErrorAction SilentlyContinue }).name 72 | $trrasSession | Remove-PSSession 73 | } 74 | return $rrasEXTrename 75 | } 76 | function Get-rrasVMLabNIC { 77 | param ( 78 | $spath 79 | ) 80 | $rrassettings = Get-EnvSettings -scriptpath $spath 81 | $RRASConfig = [RRAS]::new() 82 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 83 | $rrasLabrename = $false 84 | if ((Get-rrasVMexists -spath $spath) -and (Get-rrasVMrunning -spath $spath)) { 85 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $rrassettings.localadmin 86 | $rrasLabrename = ((Invoke-Command -Session $trrasSession -ScriptBlock { param($n)Get-NetAdapter -Physical -Name $n -ErrorAction SilentlyContinue } -ArgumentList $RRASConfig.network).name -eq $RRASConfig.network) 87 | $trrasSession | Remove-PSSession 88 | } 89 | return $rrasLabrename 90 | } 91 | function Get-rrasVMVPN { 92 | param ( 93 | $spath 94 | ) 95 | $rrassettings = Get-EnvSettings -scriptpath $spath 96 | $RRASConfig = [RRAS]::new() 97 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 98 | $rrasVPN = $false 99 | if ((Get-rrasVMexists -spath $spath) -and (Get-rrasVMrunning -spath $spath)) { 100 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $rrassettings.localadmin 101 | $rrasVPN = (Invoke-Command -Session $trrasSession -ScriptBlock { if ((Get-command Get-RemoteAccess -ErrorAction SilentlyContinue).count -eq 1) { Get-RemoteAccess -ErrorAction SilentlyContinue } }) 102 | $trrasSession | Remove-PSSession 103 | } 104 | return $rrasVPN 105 | } 106 | function Get-rrasVMIP { 107 | param ( 108 | $spath 109 | ) 110 | $rrassettings = Get-EnvSettings -scriptpath $spath 111 | $RRASConfig = [RRAS]::new() 112 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 113 | $rrasVMIP = $false 114 | if ((Get-rrasVMexists -spath $spath) -and (Get-rrasVMrunning -spath $spath)) { 115 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $rrassettings.localadmin 116 | $rrasVMIP = (Invoke-Command -Session $trrasSession -ScriptBlock { param($n)(Get-NetIPAddress -interfacealias $n -AddressFamily IPv4 -ErrorAction SilentlyContinue).ipaddress } -ArgumentList $RRASConfig.network)# -eq $RRASConfig.ipaddress) 117 | $trrasSession | Remove-PSSession 118 | } 119 | return $rrasVMIP 120 | } 121 | function Get-rrasVMInternet { 122 | param ( 123 | $spath 124 | ) 125 | $rrassettings = Get-EnvSettings -scriptpath $spath 126 | $RRASConfig = [RRAS]::new() 127 | $rrasconfig.load("$(split-path $rrassettings.vmpath)\RRASConfig.json") 128 | $rrasvmip = $false 129 | if ((Get-rrasVMexists -spath $spath) -and (Get-rrasVMrunning -spath $spath)) { 130 | $trrasSession = New-PSSession -VMName $RRASConfig.Name -Credential $rrassettings.localadmin 131 | $rrasVMIP = (Invoke-Command -Session $trrasSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 132 | $trrasSession | Remove-PSSession 133 | } 134 | return $rrasVMIP 135 | } 136 | #endregion 137 | 138 | #region ENV 139 | function Get-RefVHDXstate { 140 | param ( 141 | $spath 142 | ) 143 | $EnvSettings = Get-EnvSettings -scriptpath $spath 144 | return test-path -path $EnvSettings.refvhdx 145 | } 146 | function Get-ExternalSwitch { 147 | if ((Get-VMSwitch -Name "Internet" -ErrorAction SilentlyContinue).count -eq 1) { 148 | return $true 149 | } 150 | else { 151 | return $false 152 | } 153 | } 154 | function Get-LabSwitch { 155 | param( 156 | $spath 157 | ) 158 | $EnvSettings = Get-EnvSettings -scriptpath $spath 159 | if ((Get-VMSwitch -Name $EnvSettings.swname -ErrorAction SilentlyContinue).count -eq 1) { 160 | return $true 161 | } 162 | else { 163 | return $false 164 | } 165 | } 166 | #endregion 167 | 168 | #region DC 169 | function Get-DCVHDXstate { 170 | param ( 171 | $spath 172 | ) 173 | $DCsettings = Get-EnvSettings -scriptpath $spath 174 | $DCConfig = [DC]::new() 175 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 176 | return test-path $DCConfig.VHDXpath 177 | } 178 | function Get-DCVMexists { 179 | param ( 180 | $spath 181 | ) 182 | $DCsettings = Get-EnvSettings -scriptpath $spath 183 | $DCConfig = [DC]::new() 184 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 185 | return (Get-vm -Name $DCConfig.Name -ErrorAction SilentlyContinue).count 186 | } 187 | function Get-DCVMrunning { 188 | param ( 189 | $spath 190 | ) 191 | $DCsettings = Get-EnvSettings -scriptpath $spath 192 | $DCConfig = [DC]::new() 193 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 194 | return (Get-vm -Name $DCConfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).Count 195 | } 196 | function Get-DCVMIP { 197 | param ( 198 | $spath 199 | ) 200 | $DCsettings = Get-EnvSettings -scriptpath $spath 201 | $DCConfig = [DC]::new() 202 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 203 | $DCVMIP = $false 204 | if ((Get-DCVMexists -spath $spath) -and (Get-DCVMrunning -spath $spath)) { 205 | $tDCSession = New-PSSession -VMName $DCConfig.Name -Credential $DCsettings.domuser 206 | $DCVMIP = Invoke-Command -Session $tDCSession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress } 207 | $tDCSession | Remove-PSSession 208 | } 209 | return ($dcvmip -eq $DCconfig.IPAddress) 210 | } 211 | function Get-DCVMFeature { 212 | param ( 213 | $spath 214 | ) 215 | $DCsettings = Get-EnvSettings -scriptpath $spath 216 | $DCConfig = [DC]::new() 217 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 218 | if ((Get-DCVMexists -spath $spath) -and (Get-DCVMrunning -spath $spath)) { 219 | $tDCSession = New-PSSession -VMName $DCConfig.Name -Credential $DCsettings.domuser 220 | $DCFeat = (Invoke-Command -Session $TDCSession -ScriptBlock { (Get-WindowsFeature -name AD-Domain-Services).installstate }).value 221 | $tDCSession | Remove-PSSession 222 | } 223 | return ($DCFeat -eq "Installed") 224 | } 225 | function Get-DCVMInternet { 226 | param ( 227 | $spath 228 | ) 229 | $DCsettings = Get-EnvSettings -scriptpath $spath 230 | $DCConfig = [DC]::new() 231 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 232 | $DCvmip = $false 233 | if ((Get-DCVMexists -spath $spath) -and (Get-DCVMrunning -spath $spath)) { 234 | $tDCSession = New-PSSession -VMName $DCConfig.Name -Credential $DCsettings.domuser 235 | $DCVMIP = (Invoke-Command -Session $tDCSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 236 | $tDCSession | Remove-PSSession 237 | } 238 | return $DCVMIP 239 | } 240 | function Get-DCVMPromoted { 241 | param ( 242 | $spath 243 | ) 244 | $DCsettings = Get-EnvSettings -scriptpath $spath 245 | $DCConfig = [DC]::new() 246 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 247 | $DCvmPromoted = $false 248 | if ((Get-DCVMexists -spath $spath) -and (Get-DCVMrunning -spath $spath)) { 249 | $tDCSession = New-PSSession -VMName $DCConfig.Name -Credential $DCsettings.domuser 250 | $DCvmPromoted = (Invoke-Command -Session $TDCSession -ScriptBlock { Get-Service -Name "NTDS" -ErrorAction SilentlyContinue }).status 251 | $tDCSession | Remove-PSSession 252 | } 253 | return ($DCvmPromoted -eq "running") 254 | } 255 | function Get-DCVMDHCPScope { 256 | param ( 257 | $spath 258 | ) 259 | $DCsettings = Get-EnvSettings -scriptpath $spath 260 | $DCConfig = [DC]::new() 261 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 262 | $DCVMDHCPScope = $false 263 | if ((Get-DCVMexists -spath $spath) -and (Get-DCVMrunning -spath $spath)) { 264 | $tDCSession = New-PSSession -VMName $DCConfig.Name -Credential $DCsettings.domuser 265 | $DCVMDHCPScope = Invoke-Command -Session $TDCSession -ScriptBlock { 266 | if (Get-command Get-DhcpServerv4Scope -ErrorAction SilentlyContinue) { 267 | Get-DhcpServerv4Scope -ErrorAction SilentlyContinue -warningaction SilentlyContinue 268 | } 269 | } 270 | $tDCSession | Remove-PSSession 271 | } 272 | return ($DCVMDHCPScope[0].State -eq "Active") 273 | } 274 | function Get-DCVMCMGroupExists { 275 | [cmdletbinding()] 276 | param ( 277 | $spath 278 | ) 279 | $DCsettings = Get-EnvSettings -scriptpath $spath 280 | $DCConfig = [DC]::new() 281 | $DCconfig.load("$($DCsettings.vmpath)\DCConfig.json") 282 | $DCVMCMGroupExists = $false 283 | if ((Get-DCVMexists -spath $spath) -and (Get-DCVMrunning -spath $spath)) { 284 | $tDCSession = New-PSSession -VMName $DCConfig.Name -Credential $DCsettings.domuser 285 | $DCVMCMGroupExists = Invoke-Command -Session $TDCSession -ScriptBlock { 286 | if (Get-Command Get-ADGroup -ErrorAction SilentlyContinue -WarningAction SilentlyContinue) { 287 | Get-ADGroup -Filter "name -eq 'SCCM Servers'" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue 288 | } 289 | } 290 | $tDCSession | Remove-PSSession 291 | } 292 | if ($DCVMCMGroupExists | Where-Object {$_.Name -eq 'SCCM Servers'}) { 293 | return $true 294 | } 295 | else { 296 | return $false 297 | } 298 | } 299 | #endregion 300 | 301 | #region CA 302 | function Get-CAVHDXstate { 303 | param ( 304 | $spath 305 | ) 306 | $CAsettings = Get-EnvSettings -scriptpath $spath 307 | $CAConfig = [CA]::new() 308 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 309 | return test-path $CAConfig.VHDXpath 310 | } 311 | function Get-CAVMexists { 312 | param ( 313 | $spath 314 | ) 315 | $CAsettings = Get-EnvSettings -scriptpath $spath 316 | $CAConfig = [CA]::new() 317 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 318 | return (Get-vm -Name $CAConfig.Name -ErrorAction SilentlyContinue).count 319 | } 320 | function Get-CAVMrunning { 321 | param ( 322 | $spath 323 | ) 324 | $CAsettings = Get-EnvSettings -scriptpath $spath 325 | $CAConfig = [CA]::new() 326 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 327 | return (Get-vm -Name $CAConfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).Count 328 | } 329 | function Get-CAVMIP { 330 | param ( 331 | $spath 332 | ) 333 | $CAsettings = Get-EnvSettings -scriptpath $spath 334 | $CAConfig = [CA]::new() 335 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 336 | $CAVMIP = $false 337 | if ((Get-CAVMexists -spath $spath) -and (Get-CAVMrunning -spath $spath)) { 338 | $tCASession = New-PSSession -VMName $CAConfig.Name -Credential $CAsettings.localadmin 339 | $CAVMIP = (Invoke-Command -Session $tCASession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 340 | $tCASession | Remove-PSSession 341 | } 342 | return ($CAvmip -eq $CAconfig.IPAddress) 343 | } 344 | function Get-CAVMFeature { 345 | param ( 346 | $spath 347 | ) 348 | $CAsettings = Get-EnvSettings -scriptpath $spath 349 | $CAConfig = [CA]::new() 350 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 351 | if ((Get-CAVMexists -spath $spath) -and (Get-CAVMrunning -spath $spath)) { 352 | $tCASession = New-PSSession -VMName $CAConfig.Name -Credential $CAsettings.localadmin 353 | $CAFeat = (Invoke-Command -Session $TCASession -ScriptBlock { (Get-WindowsFeature -name Adcs-Cert-Authority).installstate }).value 354 | $tCASession | Remove-PSSession 355 | } 356 | return ($CAFeat -eq "Installed") 357 | } 358 | function Get-CAVMInternet { 359 | param ( 360 | $spath 361 | ) 362 | $CAsettings = Get-EnvSettings -scriptpath $spath 363 | $CAConfig = [CA]::new() 364 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 365 | $CAvmip = $false 366 | if ((Get-CAVMexists -spath $spath) -and (Get-CAVMrunning -spath $spath)) { 367 | $tCASession = New-PSSession -VMName $CAConfig.Name -Credential $CAsettings.localadmin 368 | $CAVMIP = (Invoke-Command -Session $tCASession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 369 | $tCASession | Remove-PSSession 370 | } 371 | return $CAVMIP 372 | } 373 | function Get-CAVMDomain { 374 | param ( 375 | $spath 376 | ) 377 | $CAsettings = Get-EnvSettings -scriptpath $spath 378 | $CAConfig = [CA]::new() 379 | $CAconfig.load("$($CAsettings.vmpath)\CAConfig.json") 380 | $CAvmDom = $false 381 | if ((Get-CAVMexists -spath $spath) -and (Get-CAVMrunning -spath $spath)) { 382 | $tCASession = New-PSSession -VMName $CAConfig.Name -Credential $CAsettings.localadmin 383 | $CAvmDom = (Invoke-Command -Session $TCASession -ScriptBlock { param($d)Test-NetConnection $d -erroraction SilentlyContinue -WarningAction SilentlyContinue } -ArgumentList $CAConfig.domainFQDN ).PingSucceeded 384 | $tCASession | Remove-PSSession 385 | } 386 | return $CAvmDom 387 | } 388 | 389 | #endregion 390 | 391 | #region CMPri 392 | function Get-CMPriVHDXstate { 393 | param ( 394 | $spath 395 | ) 396 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 397 | $CMConfig = [CM]::new() 398 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 399 | return test-path $CMConfig.VHDXpath 400 | } 401 | function Get-CMPriVMexists { 402 | param ( 403 | $spath 404 | ) 405 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 406 | $CMConfig = [CM]::new() 407 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 408 | return (Get-vm -Name $CMConfig.Name -ErrorAction SilentlyContinue).count 409 | } 410 | function Get-CMPriVMrunning { 411 | param ( 412 | $spath 413 | ) 414 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 415 | $CMConfig = [CM]::new() 416 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 417 | return (Get-vm -Name $CMConfig.Name -ErrorAction SilentlyContinue | Where-Object { $_.State -match "Running" }).Count 418 | } 419 | function Get-CMPriVMIP { 420 | param ( 421 | $spath 422 | ) 423 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 424 | $CMConfig = [CM]::new() 425 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 426 | $CMPriVMIP = $false 427 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 428 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 429 | $CMPriVMIP = (Invoke-Command -Session $tCMPriSession -ScriptBlock { (Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual -ErrorAction SilentlyContinue).ipaddress }) 430 | $tCMPriSession | Remove-PSSession 431 | } 432 | return ($CMPrivmip -eq $CMConfig.IPAddress) 433 | } 434 | function Get-CMPriVMFeature { 435 | param ( 436 | $spath 437 | ) 438 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 439 | $CMConfig = [CM]::new() 440 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 441 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 442 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 443 | $CMPriFeat = (Invoke-Command -Session $TCMPriSession -ScriptBlock { (Get-WindowsFeature -name 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-Ext, 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).installstate }).value 444 | $tCMPriSession | Remove-PSSession 445 | } 446 | $res = ($CMPriFeat -ne "Installed").count 447 | return ($res -eq 0) 448 | } 449 | function Get-CMPriVMnetFeature { 450 | param ( 451 | $spath 452 | ) 453 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 454 | $CMConfig = [CM]::new() 455 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 456 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 457 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 458 | $CMPriFeat = (Invoke-Command -Session $TCMPriSession -ScriptBlock { (Get-WindowsFeature -name NET-Framework-Features, NET-Framework-Core).installstate }).value 459 | $tCMPriSession | Remove-PSSession 460 | } 461 | $res = ($CMPriFeat -ne "Installed").count 462 | return ($res -eq 0) 463 | } 464 | function Get-CMPriVMInternet { 465 | param ( 466 | $spath 467 | ) 468 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 469 | $CMConfig = [CM]::new() 470 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 471 | $CMPrivmip = $false 472 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 473 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 474 | $CMPriVMIP = (Invoke-Command -Session $tCMPriSession -ScriptBlock { test-netconnection "8.8.8.8" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }).PingSucceeded 475 | $tCMPriSession | Remove-PSSession 476 | } 477 | return $CMPriVMIP 478 | } 479 | function Get-CMPriVMDomain { 480 | param ( 481 | $spath 482 | ) 483 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 484 | $CMConfig = [CM]::new() 485 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 486 | $CMPrivmDom = $false 487 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 488 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 489 | $CMPrivmDom = (Invoke-Command -Session $TCMPriSession -ScriptBlock { param($d)Test-NetConnection $d -erroraction SilentlyContinue -WarningAction SilentlyContinue } -ArgumentList $CMConfig.domainFQDN ).PingSucceeded 490 | $tCMPriSession | Remove-PSSession 491 | } 492 | return $CMPrivmDom 493 | } 494 | function Get-CMPriVMSQLSvc { 495 | param ( 496 | $spath 497 | ) 498 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 499 | $CMConfig = [CM]::new() 500 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 501 | $CMPriVMSQLSvc = $false 502 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 503 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 504 | $CMPriVMSQLSvc = (Invoke-Command -Session $tCMPriSession -ScriptBlock { get-service -name "MSSQLSERVER" -ErrorAction SilentlyContinue }).name.count 505 | $tCMPriSession | Remove-PSSession 506 | } 507 | return $CMPriVMSQLSvc 508 | } 509 | function Get-CMPriVMADK { 510 | param ( 511 | $spath 512 | ) 513 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 514 | $CMConfig = [CM]::new() 515 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 516 | $CMPriVMADK = $false 517 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 518 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.localadmin 519 | $CMPriVMADK = (Invoke-Command -Session $tCMPriSession -ScriptBlock { test-path "C:\Program Files (x86)\Windows Kits\10\Assessment and deployment kit" -ErrorAction SilentlyContinue }) 520 | $tCMPriSession | Remove-PSSession 521 | } 522 | return $CMPriVMADK 523 | } 524 | function Get-CMPriVMSVRGRP { 525 | param ( 526 | $spath 527 | ) 528 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 529 | $CMConfig = [CM]::new() 530 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 531 | $CMPriVMSVRGRP = $false 532 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 533 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.domuser 534 | $CMPriVMSVRGRP = (Invoke-Command -Session $tCMPriSession -ScriptBlock { Get-ADGroupMember "SCCM Servers" | Where-Object { $_.name -eq $env:computername } } -ErrorAction SilentlyContinue).name.count 535 | $tCMPriSession | Remove-PSSession 536 | } 537 | return $CMPriVMSVRGRP 538 | } 539 | function Get-CMPriVMCMInstalled { 540 | param ( 541 | $spath 542 | ) 543 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 544 | $CMConfig = [CM]::new() 545 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 546 | $CMPriVMCMInstalled = $false 547 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 548 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.domuser 549 | $CMPriVMCMInstalled = (Invoke-Command -Session $tCMPriSession -ScriptBlock { get-service -name "SMS_EXECUTIVE" -ErrorAction SilentlyContinue }).name.count 550 | $tCMPriSession | Remove-PSSession 551 | } 552 | return $CMPriVMCMInstalled 553 | } 554 | function Get-CMPriVMCMConsoleInstalled { 555 | param ( 556 | $spath 557 | ) 558 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 559 | $CMConfig = [CM]::new() 560 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 561 | $CMPriVMCMConsoleInstalled = $false 562 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath)) { 563 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.domuser 564 | $CMPriVMCMConsoleInstalled = (Invoke-Command -Session $tCMPriSession -ScriptBlock { test-path "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.exe" }) 565 | $tCMPriSession | Remove-PSSession 566 | } 567 | return $CMPriVMCMConsoleInstalled 568 | } 569 | function Get-CMPriVMCMBoundary { 570 | param ( 571 | $spath 572 | ) 573 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 574 | $CMConfig = [CM]::new() 575 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 576 | $CMPriVMCMBoundary = $false 577 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath -and (Get-CMPriVMCMConsoleInstalled -spath $spath))) { 578 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.domuser 579 | Invoke-Command -Session $tCMPriSession -ScriptBlock { param ($sitecode) import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME }; Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:" } -ArgumentList $CMConfig.cmsitecode 580 | $CMPriVMCMBoundary = (Invoke-Command -Session $tCMPriSession -ScriptBlock { param($subname) Get-CMBoundary -name $subname } -ArgumentList $CMConfig.network).displayname.count 581 | $tCMPriSession | Remove-PSSession 582 | } 583 | return $CMPriVMCMBoundary 584 | } 585 | function Get-CMPriVMCMDiscovery { 586 | param ( 587 | $spath 588 | ) 589 | $CMPrisettings = Get-EnvSettings -scriptpath $spath 590 | $CMConfig = [CM]::new() 591 | $CMConfig.load("$($CMPrisettings.vmpath)\CMConfig.json") 592 | $CMPriVMCMDiscovery = $false 593 | if ((Get-CMPriVMexists -spath $spath) -and (Get-CMPriVMrunning -spath $spath -and (Get-CMPriVMCMConsoleInstalled -spath $spath))) { 594 | $tCMPriSession = New-PSSession -VMName $CMConfig.Name -Credential $CMPrisettings.domuser 595 | Invoke-Command -Session $tCMPriSession -ScriptBlock { param ($sitecode) import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME }; Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:" } -ArgumentList $CMConfig.cmsitecode 596 | $CMPriVMCMDiscovery = (Invoke-Command -Session $tCMPriSession -ScriptBlock { Get-CMDiscoveryMethod -Name ActiveDirectorySystemDiscovery }).flag 597 | $tCMPriSession | Remove-PSSession 598 | } 599 | return $CMPriVMCMDiscovery 600 | } 601 | #if ($TCMSCCMConsoleInstalled) { 602 | # Invoke-Command -Session $tCMPriSession -ScriptBlock { param ($sitecode) import-module "$(($env:SMS_ADMIN_UI_PATH).remove(($env:SMS_ADMIN_UI_PATH).Length -4, 4))ConfigurationManager.psd1"; if ($null -eq (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $env:COMPUTERNAME }; Set-Location "$((Get-PSDrive -PSProvider CMSite).name)`:" } -ArgumentList $CMConfig.cmsitecode 603 | # $TCMBoundary = (Invoke-Command -Session $tCMPriSession -ScriptBlock { param($subname) Get-CMBoundary -name $subname } -ArgumentList $CMConfig.network).displayname.count 604 | # $TCMDiscovery = (Invoke-Command -Session $tCMPriSession -ScriptBlock { Get-CMDiscoveryMethod -Name ActiveDirectorySystemDiscovery }).flag 605 | #} 606 | #endregion --------------------------------------------------------------------------------