├── Enable-NestedVirtualization.ps1 ├── LICENSE ├── New-LabADSite.ps1 ├── New-LabCert.ps1 ├── New-LabData.ps1 ├── New-LabForest.ps1 ├── New-LabIIS.ps1 ├── New-LabNCSI.ps1 ├── New-LabUser.ps1 ├── New-LabVM.ps1 ├── README.md ├── Test-IntSvcGuest.ps1 ├── ad_sites_subnets.csv └── lab_user.csv /Enable-NestedVirtualization.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Enable-NestedVirtualization configures virtual machines to support nested virtualization. 4 | .DESCRIPTION 5 | Enable-NestedVirtualization configures all needed prerequisites if you want to install 6 | the Hyper-V-Role inside a virtual machine. It 7 | - shuts down the VM 8 | - enables virtualization extensions of the VMProcessor 9 | - disables dynamic memory 10 | - allows MAC-address spoofing on the virtual NIC 11 | - restarts the VM 12 | .EXAMPLE 13 | PS C:\> Enable-NestedVirtualizaion -VMName vm1,vm2 14 | This command configures the prerequisites for nested virtualization in all given virtual machines. 15 | .NOTES 16 | FileName: Enable-NestedVirtualization.ps1 17 | Author: Oliver Jaekel 18 | www: github.com/JaekelEDV 19 | Twitter: @JaekelEDV 20 | #> 21 | [CmdletBinding(SupportsShouldProcess)] 22 | param ( 23 | [Parameter( 24 | Mandatory = $True, 25 | ValueFromPipeline=$true)] 26 | [string[]]$VMName 27 | ) 28 | 29 | foreach ($VM in $VMName){ 30 | 31 | try { 32 | $VMState = (Get-VM -Name $VMName).State 33 | 34 | if ($VMState -eq "Running") { 35 | Write-Host "Stopping VMs." -ForegroundColor Yellow 36 | Stop-VM -Name $VMName 37 | } 38 | } 39 | 40 | catch { 41 | Write-Warning $_.Exception.Message 42 | throw "That's all folks, something went terribly wrong. Cannot configure $VM." 43 | } 44 | 45 | try { 46 | $VMProc = (Get-VMProcessor -VMName $VM).ExposeVirtualizationExtensions 47 | $VMMem = (Get-VMMemory -VMName $VM).DynamicMemoryEnabled 48 | $VMNIC = (Get-VMNetworkAdapter -VMName $VM).MacAddressSpoofing 49 | 50 | if ($VMProc -eq $false) { 51 | Write-Host "Setting ExposeVirtualizationExtensions on $VM to 'true'." -ForegroundColor Yellow 52 | Set-VMProcessor -VMName $VM -ExposeVirtualizationExtensions $true 53 | } 54 | else { 55 | Write-Host "Setting ExposeVirtualizationExtensions on $VM is already 'true'." -ForegroundColor Green 56 | } 57 | 58 | if ($VMMem -eq $true) { 59 | Write-Host "Setting DynamicMemory on $VM to 'Off'." -ForegroundColor Yellow 60 | Set-VMMemory -VMName $VM -DynamicMemoryEnabled $false 61 | } 62 | else { 63 | Write-Host "Setting DynamicMemory on $VM is already 'false'." -ForegroundColor Green 64 | } 65 | 66 | if ($VMNIC -eq "Off") { 67 | Write-Host "Setting MacAddressSpoofing on $VM to 'On'." -ForegroundColor Yellow 68 | Set-VMNetworkAdapter -VMName $VM -MacAddressSpoofing "On" 69 | } 70 | else { 71 | Write-Host "Setting MacAddressSpoofing on $VM is already 'On'." -ForegroundColor Green 72 | } 73 | 74 | } 75 | catch { 76 | Write-Warning $_.Exception.Message 77 | throw "That's all folks, something went terribly wrong. Cannot configure $VM." 78 | } 79 | } 80 | 81 | foreach ($VM in $VMName){ 82 | "" 83 | Write-Host "Starting $VM..." -ForegroundColor Yellow 84 | Start-VM -Name $VM 85 | 86 | do { 87 | Write-Host "[+]$VM not ready. Waiting..." -ForegroundColor Yellow 88 | $Heartbeat = (Get-VM -Name $VM).HeartBeat 89 | Start-Sleep -Seconds 3 90 | } 91 | 92 | until ($Heartbeat -eq 'OkApplicationsUnknown'-or $Heartbeat -eq 'OkApplicationsHealthy') 93 | "" 94 | Write-Host "[+]$VM is ready." -ForegroundColor Green 95 | Write-Host "VM status is $Heartbeat." -ForegroundColor Green 96 | } 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 @JaekelEDV 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /New-LabADSite.ps1: -------------------------------------------------------------------------------- 1 | function New-LabADSiteSubnet { 2 | <# 3 | .SYNOPSIS 4 | Creates csv-imported Active Directory sites and subnets in a virtual lab-environment. 5 | THIS CODE IS NOT FOR PRODUCTION USE! 6 | 7 | .DESCRIPTION 8 | New-LabADSiteSubnet relies on an csv-file containing columns for sites and subnets. 9 | It imports the sites and subnets. Nothing fancy. 10 | 11 | .PARAMETER CSVPath 12 | The Path to the csv-file you want to import. 13 | 14 | .EXAMPLE 15 | Dot source it or load it another way. 16 | New-LabADSiteSubnet -CSVPath "c:\ad_sites_subnets.csv" 17 | 18 | .LINK 19 | Online version: https://github.com/JaekelEDV/PSLabHelpers/blob/master/New-LabADSite.ps1 20 | 21 | .NOTES 22 | New-LabADSiteSubnet can also put all the sites in the DEFAULTIPSITELINK. 23 | If this is not wanted in your lab - just comment it out. 24 | 25 | FileName: New-LabADSiteSubnet.ps1 26 | Author: Oliver Jaekel 27 | EMail: oj@jaekel-edv.de 28 | www: github.com/JaekelEDV 29 | Twitter: @JaekelEDV 30 | #> 31 | [CmdletBinding()] 32 | param ( 33 | [Parameter(Mandatory = $true)] 34 | [ValidateScript( {Test-Path -Path $_ })] 35 | [string] $CSVPath 36 | ) 37 | 38 | #Requires –Modules ActiveDirectory 39 | 40 | $InformationPreference = 'Continue' 41 | $CSV = Import-Csv -Path $CSVPath 42 | 43 | foreach ($site in $CSV){ 44 | if (Get-ADReplicationSite -Filter * -Properties Name | 45 | Where-Object {$_.name -eq $site.site}) { 46 | Write-Information "Site $($site.site) already exists." 47 | } 48 | 49 | else { 50 | New-ADReplicationSite -Name $site.site 51 | Write-Information "Site $($site.site) is created." 52 | } 53 | } 54 | 55 | foreach ($subnet in $CSV) { 56 | if (Get-ADReplicationSubnet -Filter * -Properties Name | 57 | Where-Object {$_.name -eq $subnet.subnet}) { 58 | Write-Information "Subnet $($subnet.subnet) already exists." 59 | } 60 | 61 | else { 62 | New-ADReplicationSubnet -Name $subnet.subnet -Site $subnet.site 63 | Write-Information "Subnet $($subnet.subnet) is created." 64 | } 65 | } 66 | 67 | #Optional: putting all sites in DEFAULTIPSITELINK - don't like it, drop it. 68 | $AllSites = (Get-ADReplicationSite -Filter *).name 69 | Set-ADReplicationSiteLink -Identity DEFAULTIPSITELINK -SitesIncluded @{add = $AllSites} 70 | } 71 | -------------------------------------------------------------------------------- /New-LabCert.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Function New-LabCert creates a selfsigned computercertificate for lab environments 4 | .DESCRIPTION 5 | This script creates a selfsigned computercertificate for lab environments. It is stored in cert:\localmachine\my 6 | and from there it is exported as a pfx-file to c:\. This file then gets imported in 'Trusted Roots' to make it trustworthy. 7 | Finally some cleanup is performed, e.g. the pfx-file will be deleted. 8 | Please consider to run it with the -verbose parameter to receive some informative output. 9 | .PARAMETER DNSName 10 | This is the only but mandatory parameter. Please enter the DNSHostname of the machine you want this certificate for. 11 | This will become the CN of the certficate 12 | .EXAMPLE 13 | Execute New-LabCert.ps1 directly from shell with dot sourcing 14 | . .\New-LabCert.ps1 15 | New-LabCert -DNSName Value 16 | .NOTES 17 | Author: Oliver Jäkel | oj@jaekel-edv.de | @JaekelEDV 18 | #> 19 | 20 | #requires -Version 3.0 -Modules PKI 21 | 22 | #region Parameter Section 23 | Function New-LabCert { 24 | [CmdletBinding()] 25 | param ( 26 | [Parameter(Mandatory = $true, HelpMessage = 'Enter DNSName of the Host')] 27 | [string] $DNSName 28 | ) 29 | 30 | [string] $certstorelocation = 'Cert:\LocalMachine\' 31 | #endregion 32 | 33 | #region Create the selfsigned Certificate 34 | New-SelfSignedCertificate -CertStoreLocation $certstorelocation\My -DnsName $DNSName 35 | Write-Verbose -Message "Creating Selfsigned Computer Certificate for $DNSName" 36 | #endregion 37 | 38 | #region Export the certificate to filesystem 39 | 40 | Set-Location -Path Cert:\LocalMachine\My 41 | $cert = Get-ChildItem -Path .\ | 42 | Where-Object -EQ -Property Subject -Value "cn=$DNSName" 43 | $thumbprint = ($cert).Thumbprint 44 | 45 | $pass = ConvertTo-SecureString -String "Pa$$w0rd" -Force -AsPlainText 46 | 47 | $exportPfxCertificateSplat = @{ 48 | Cert = "$certstorelocation\My\$thumbprint" 49 | Password = $pass 50 | FilePath = "$env:HOMEDRIVE\$DNSName.pfx" 51 | } 52 | 53 | Export-PfxCertificate @exportPfxCertificateSplat 54 | Write-Verbose -Message "Export the Certificate to $env:HOMEDRIVE" 55 | #endregion 56 | 57 | #region Import the certificate to Trusted Root 58 | 59 | $importPfxCertificateSplat = @{ 60 | FilePath = "$env:HOMEDRIVE\$DNSName.pfx" 61 | Password = $pass 62 | CertStoreLocation = "$certstorelocation\Root" 63 | } 64 | 65 | Import-PfxCertificate @importPfxCertificateSplat 66 | Write-Verbose -Message 'Import the Certificate to Trusted Root' 67 | #endregion 68 | 69 | #region Cleanup 70 | 71 | Set-Location -Path $env:HOMEDRIVE 72 | Remove-Item -Path $env:HOMEDRIVE\$DNSName.pfx 73 | #endregion 74 | } 75 | -------------------------------------------------------------------------------- /New-LabData.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script creates a folder and some subfolders with randomly named example txt-files for Lab environments. 4 | .DESCRIPTION 5 | This is a quick and dirty solution - same timestamps, same size when content within files. 6 | You'll find plenty of better scripts, but this is exactly what I needed for a small project. 7 | The foldernames are hardcoded, you can adjust them. 8 | The filenames are generated randomly with the .NET class System.IO.File, the file-extensions are then 9 | all changed to *.txt. Originally they are created with different filesizes, but this changes when you'll 10 | make use of the Set-Content cmdlet (which you're free to comment out). 11 | The script uses a REST-API which downloads some Lorem Ipsum content (which obviously isn't needed, too). 12 | As said, when using it this way, you'll lose the different file-sizes which might be of interest. 13 | .EXAMPLE 14 | Just run it: PS C:\> .\New-LabData.ps1. There are no parameters. 15 | .NOTES 16 | File Name: New-LabData.ps1 17 | Author: Oliver Jaekel | oj@jaekel-edv.de | @JaekelEDV | https://github.com/JaekelEDV 18 | #> 19 | [CmdletBinding()] 20 | param () 21 | 22 | #region Create Rootfolder for LabData. 23 | $RootFolder = 'C:\LabData' 24 | if (-not (Test-Path -LiteralPath $RootFolder)) { 25 | 26 | try { 27 | New-Item -Path $RootFolder -ItemType 'Directory' -ErrorAction 'Stop' | Out-Null 28 | Write-Verbose "Created directory $RootFolder." 29 | } 30 | catch { 31 | Write-Error "Unable to create directory '$RootFolder'. Error: $_" -ErrorAction 'Stop' 32 | } 33 | } 34 | 35 | else { 36 | Write-Verbose "Directory $RootFolder already exists." 37 | } 38 | #endregion 39 | 40 | #region Create subfolders under RootFolder 41 | #Feel free to alter this section and write your preferred names to the variable. 42 | $Subfolder = @('logs','data1','data2','data3','misc','secret') 43 | 44 | foreach($Folder in $Subfolder) { 45 | New-Item -Path $RootFolder -Name $Folder -ItemType 'Directory' | Out-Null 46 | } 47 | $Subfolders = $Subfolder -join ', ' 48 | Write-Verbose "Created subfolders $Subfolders under $RootFolder" 49 | #endregion 50 | 51 | #region Download LoremIpsum and create content-file 52 | #If you're ok with empty files, you don't need this region. 53 | Invoke-WebRequest -Uri "https://loripsum.net/api/5/long/plaintext" | 54 | New-Item -Path $RootFolder -Name 'LoremIpsum.txt' -ItemType 'File' | Out-Null 55 | $Content = Get-Content -Path "$RootFolder\LoremIpsum.txt" 56 | Write-Verbose "Downloaded Lorem Ipsum and created content-txt-file." 57 | #endregion 58 | 59 | #region Create random files 60 | #function New-EmptyFile by @ShayLevy: https://www.powershellmagazine.com/2012/11/22/pstip-create-a-file-of-the-specified-size/ 61 | function New-EmptyFile { 62 | param([string]$FilePath,[double]$Size) 63 | $file = [System.IO.File]::Create($FilePath) 64 | $file.SetLength($Size) 65 | $file.Close() 66 | } 67 | 68 | do { 69 | $filename = [System.IO.Path]::GetRandomFileName() 70 | $Subfolder = (Get-ChildItem -Directory -Path $RootFolder\*).Name 71 | 72 | foreach($Folder in $Subfolder) { 73 | New-EmptyFile -FilePath "$RootFolder\$Folder\$filename" -Size (Get-Random -Minimum 1024 -Maximum 819200) 74 | } 75 | } 76 | #Please change the value (here: 50) if you'll need more files. 77 | until ((Get-ChildItem -File -Recurse -Path $RootFolder).count -ge 50) 78 | Write-Verbose "Created random files under $RootFolder\$Folder." 79 | #endregion 80 | 81 | #region Rename random file-extension to *.txt 82 | Get-ChildItem -File -Recurse -Path $RootFolder | 83 | Rename-Item -NewName { [io.path]::ChangeExtension($_.name, "txt") } 84 | Write-Verbose "Renamed random file-extension to *.txt" 85 | #endregion 86 | 87 | #region Set-Content Lorem Ipsum to all txt-files 88 | #Attention: this will result in same filesize for all files which perhaps is not desired. 89 | Set-Content -Path $RootFolder\*\* -Filter *.txt -Value "$Content" 90 | Write-Verbose "Set content Lorem Ipsum to all txt-files" 91 | #endregion 92 | $CountFiles = (Get-ChildItem -File -Recurse -Path $RootFolder).count 93 | Write-Host "READY! Created $CountFiles test-files under $RootFolder." -ForegroundColor Cyan 94 | -------------------------------------------------------------------------------- /New-LabForest.ps1: -------------------------------------------------------------------------------- 1 | function New-LabForest { 2 | <# 3 | .SYNOPSIS 4 | New-LabForest quickly sets up the first DomainController 5 | and an Active Directory Forest in a virtual lab-environment. 6 | THIS CODE IS NOT FOR PRODUCTION USE! 7 | 8 | .DESCRIPTION 9 | New-LabForest installs a new Active Directory Forest 10 | on a virtual DomainController in a lab-environment. 11 | New-LabForest will do the following: 12 | - Configures TCP/IP-Settings (see parameters) and renames GuestOS 13 | - Install-WindowsFeature 'AD-Domain-Services' 14 | - Install-ADDSForest 15 | - Checks status of some some key AD components 16 | Requires elevation; must be running in a PowerShell session as an administrator. 17 | 18 | .PARAMETER VM 19 | The name of the VM you want to turn into a DomainController. 20 | You can tab to autocomplete VMs installed on the hypervisor. 21 | 22 | .PARAMETER ForestName 23 | The DNSName of the forest you want to build, e.g. test.local. 24 | 25 | .PARAMETER DCName 26 | The Name of the DomainController. 27 | This is build via a ValidateSet with given names. You may change this in the code. 28 | 29 | .PARAMETER IPv4Address 30 | The IPv4Address of the DomainController. 31 | Has a default value if you omit the param. 32 | 33 | .PARAMETER PrefixLength 34 | The subnet mask of the DomainController. 35 | Has a default value if you omit the param. 36 | 37 | .PARAMETER StandardGateway 38 | The Standardgateway of the DomainController. 39 | Has a default value if you omit the param. 40 | 41 | .PARAMETER DNSServer 42 | The preferred DNS-Server of the DomainController. 43 | Has a default value set to itself if you omit the param. 44 | Will overwrite the localhost entry after dcpromo. 45 | 46 | .EXAMPLE 47 | Dot source it or load it another way. 48 | New-LabForest -VM 'vMACHINE' -ForestName 'test.local' -DCName 'DC01' 49 | 50 | .LINK 51 | Online version: https://github.com/JaekelEDV/PSLabHelpers/blob/master/New-LabForest.ps1 52 | 53 | .NOTES 54 | The idea of New-LabForest is to be run against a VM with an installed Server-OS. 55 | You might execute it directly from the Virtualization-Host. 56 | You don't need network-access to the VM at all: it relies on PowerShellDirect. 57 | 58 | File Name: New-LabForest.ps1 59 | Author: Oliver Jaekel | oj@jaekel-edv.de | @JaekelEDV | https://github.com/JaekelEDV 60 | #> 61 | [CmdletBinding(SupportsShouldProcess)] 62 | param ( 63 | [Parameter(Mandatory = $True)] 64 | [string]$VM, 65 | 66 | [Parameter(Mandatory = $True, HelpMessage="Enter a DomainName like 'domain.tld'.")] 67 | [ValidatePattern( 68 | '(?# ForestName seems to be NO VALID DOMAINNAME, e.g. domain.tld)^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$')] 69 | [string]$ForestName, 70 | 71 | [Parameter(Mandatory = $True)] 72 | [ValidateSet('DC1', 'DC01', 'RootDC')] 73 | [string]$DCName, 74 | 75 | [Parameter()] 76 | [IPAddress]$IPv4Address = '10.0.0.1', 77 | 78 | [Parameter()] 79 | [ValidateRange(1, 32)] 80 | [int]$PrefixLength = '24', 81 | 82 | [Parameter()] 83 | [IPAddress]$StandardGateway = '10.0.0.254', 84 | 85 | [Parameter()] 86 | [IPAddress]$DNSServer = '10.0.0.1' 87 | ) 88 | 89 | #region Defining some vars and getting the creds for local-admin, domain-admin and DSRM-Mode. 90 | $InformationPreference = 'Continue' 91 | $NetBIOSName = $ForestName.Split('.')[0] 92 | $VMName = (Get-VM -Name $VM).Name 93 | $LocalCreds = Get-Credential -UserName 'administrator' -Message 'Please enter Local-VM-Admin-Credentials!' 94 | $DomainCreds = Get-Credential -UserName "$NetBIOSName\administrator" -Message 'Please enter Domain-Admin-Credentials!' 95 | #Getting DSRM-Creds, extracting password, converting password to securestring 96 | $DSRMCreds = Get-Credential -UserName 'administrator' -Message 'Please enter DSRM-Credentials!' 97 | $SecureDSRMPassword = $DSRMCreds.GetNetworkCredential().Password | 98 | ConvertTo-SecureString -AsPlainText -Force 99 | #endregion 100 | 101 | #region Function to check if VM is already a DC (ProductType=2). If true, script will stop. 102 | function Test-IsDomainController { 103 | <# 104 | .SYNOPSIS 105 | Test if the target is a DomainController. 106 | .DESCRIPTION 107 | This function queries the CimInstance Win32_OperatingSystem.ProductType. 108 | Work Station (1), Domain Controller (2), Server (3). 109 | .EXAMPLE 110 | Test-IsDomainController 111 | .INPUTS 112 | 113 | .OUTPUTS 114 | 115 | .NOTES 116 | #> 117 | [CmdletBinding()] 118 | param ( 119 | ) 120 | $script:isDC = (Invoke-Command -VMName $VMName -Credential $DomainCreds -ScriptBlock { 121 | (Get-CimInstance -ClassName Win32_OperatingSystem).ProductType 122 | }) 123 | if ($script:isDC -eq '2') { 124 | throw "The VM already is a DomainController. Better stop here..." 125 | } 126 | } 127 | Test-IsDomainController 128 | Write-Verbose "GuestOS is checked if DomainController. Result: $isDC (see help)." 129 | #endregion 130 | 131 | #region Setting TCP/IP of the DC 132 | ""; "" 133 | Write-Information -MessageData "[+]Setting TCP/IP on $DCName..." 134 | "" 135 | Invoke-Command -VMName $VMName -Credential $LocalCreds -ScriptBlock { 136 | 137 | $NetAdapter = @{ 138 | InterfaceDescription = 'Microsoft Hyper-V Network Adapter' 139 | NewName = 'LAN' 140 | } 141 | 142 | Rename-NetAdapter @NetAdapter 143 | $NetAdapterName = (Get-NetAdapter).name 144 | Write-Verbose "The Netadapter is renamed to $NetAdapterName" 145 | 146 | #In case there is already a static config, it's removed now. This avoids some annoyances. 147 | #netsh is preferred: easy, reliable, done. 148 | function Remove-NetIPConfig { 149 | <# 150 | .SYNOPSIS 151 | Removes the TCP/IP and DNS settings of a machine. 152 | 153 | .DESCRIPTION 154 | The function resets the TCP/IP and DNS settings via netsh. 155 | Main goal is to have it clean before configuring it with desired values. 156 | 157 | .EXAMPLE 158 | Remove-NetIPConfig 159 | #> 160 | param ( 161 | ) 162 | #$NetAdapterName = (Get-NetAdapter).name 163 | $NetshDHCP = 'interface ipv4 set address name="LAN" source=dhcp' 164 | $NetshDNS = 'interface ipv4 delete dnsservers name="LAN" address=all' 165 | Invoke-Expression "netsh $NetshDHCP" 166 | Invoke-Expression "netsh $NetshDNS" 167 | } 168 | Remove-NetIPConfig | Out-Null 169 | 170 | $NICConfig = @{ 171 | #InterfaceAlias = 'LAN' 172 | InterfaceAlias = $NetAdapterName 173 | IPAddress = $using:IPv4Address 174 | AddressFamily = 'IPV4' 175 | DefaultGateway = $using:StandardGateway 176 | PrefixLength = $using:PrefixLength 177 | } 178 | New-NetIPAddress @NICConfig | Out-Null 179 | 180 | $DNS = @{ 181 | InterfaceAlias = 'LAN' 182 | ServerAddresses = $using:DNSServer 183 | } 184 | Set-DnsClientServerAddress @DNS 185 | #endregion 186 | } 187 | #region Rename Guest OS and reboot it. 188 | Invoke-Command -VMName $VMName -Credential $LocalCreds -ScriptBlock { 189 | if ($using:DCName -eq $env:COMPUTERNAME) { 190 | Write-Information -MessageData "[+]Hostname already is $DCName - skipping rename." 191 | } 192 | else { 193 | Write-Information -MessageData "[+]Renaming GuestOS to $using:DCName..." 194 | $WarningPreference = 'SilentlyContinue' 195 | Rename-Computer -NewName $using:DCName -Force 196 | "" 197 | } 198 | } 199 | Write-Verbose "Network Adapter is renamed to 'LAN'." 200 | Write-Verbose "Pre-Existing TCP/IP config is deleted." 201 | Write-Verbose "TCP/IP config is done according to parameters." 202 | Write-Verbose "GuestOS is renamed to $DCName" 203 | 204 | Stop-VM -Name $VMName 205 | Start-VM -Name $VMName 206 | 207 | do { 208 | Write-Information -MessageData "[+]$VMName not ready. Waiting..." 209 | $Heartbeat = (Get-VM -Name $VMName).HeartBeat 210 | Start-Sleep -Seconds 3 211 | } 212 | 213 | until ($Heartbeat -eq 'OkApplicationsUnknown'-or $Heartbeat -eq 'OkApplicationsHealthy') 214 | [void]$Heartbeat 215 | "" 216 | Write-Information -MessageData "[+]$VMName is ready. Continuing..." 217 | Write-Verbose "VM status is $Heartbeat." 218 | #endregion 219 | 220 | #region Install the role for ADDS 221 | Invoke-Command -VMName $VMName -Credential $LocalCreds -ScriptBlock { 222 | 223 | $AD = Get-WindowsFeature -Name 'AD-Domain-Services' | 224 | Where-Object { $_.Installed -eq $true } 225 | if ($AD -ne $null) { 226 | Write-Verbose "AD-Domain-Services already installed." 227 | } 228 | 229 | else { 230 | "" 231 | Write-Information -MessageData '[+]Installing AD-Domain-Services...' 232 | Install-WindowsFeature -Name 'AD-Domain-Services' -IncludeManagementTools 233 | } 234 | } 235 | #endregion 236 | 237 | #region Import the needed modules. 238 | #Attention: ADDSDeployment is only available after installing the ADDS-Feature 239 | Invoke-Command -VMName $VMName -Credential $LocalCreds -ScriptBlock { 240 | $LoadedModules = { 241 | Get-Module -ListAvailable -Name ServerManager, ADDSDeployment 242 | 243 | if ($LoadedModules -notcontains 'ServerManager') { 244 | try{ 245 | Import-Module -Name ServerManager -ErrorAction 'Stop' 246 | } 247 | catch { 248 | Write-Warning $_.Exception.Message 249 | throw "That's all folks, cannot continue without this..." 250 | } 251 | } 252 | 253 | if ($LoadedModules -notcontains 'ADDSDeployment'){ 254 | try { 255 | Import-Module -Name 'ADDSDeployment' -ErrorAction 'Stop' 256 | } 257 | catch { 258 | Write-Warning $_.Exception.Message 259 | throw "That's all folks, cannot continue without this..." 260 | } 261 | } 262 | } 263 | } 264 | Write-Verbose "Needed modules ServerManager and ADDSDeployment are imported." 265 | #endregion 266 | 267 | #region Install DC, first in new Forest, FunctionalLevel Server 2012 R2: 6, Server 2016: 7 268 | Invoke-Command -VMName $VMName -Credential $LocalCreds -ScriptBlock { 269 | 270 | $Params = @{ 271 | DatabasePath = 'C:\Windows\NTDS' 272 | LogPath = 'C:\Windows\NTDS' 273 | SysvolPath = 'C:\Windows\SYSVOL' 274 | DomainName = $using:ForestName 275 | DomainNetbiosName = $using:NetBIOSName 276 | SafeModeAdministratorPassword = $using:SecureDSRMPassword 277 | ForestMode = 7 278 | DomainMode = 7 279 | InstallDns = $true 280 | NoDnsOnNetwork = $true 281 | CreateDnsDelegation = $false 282 | NoRebootOnCompletion = $true 283 | Confirm = $false 284 | Force = $true 285 | WarningAction = 'SilentlyContinue' #Suppresses "Standard"-Warnings, i.e. DNS-Deleg., Crypto-Algo. etc. 286 | } 287 | 288 | Install-ADDSForest @Params 289 | 290 | Write-Verbose "Forest Install successfully completed." 291 | 292 | #In case you don't like the localhost-addresses for DNS - these lines 293 | #will revert the IPv4 to the own server-address and IPv6 switched to automatic (eg. fe80). 294 | Set-DnsClientServerAddress -InterfaceAlias 'LAN' -ResetServerAddresses 295 | Set-DnsClientServerAddress -InterfaceAlias 'LAN' -ServerAddresses $using:DNSServer 296 | #endregion 297 | } 298 | #region Rebooting VM and pausing the script to give the system some time. 299 | Stop-VM -Name $VMName 300 | Start-VM -Name $VMName 301 | 302 | do { 303 | Write-Information -MessageData "[+]$VMName not ready. Waiting..." 304 | $Heartbeat = (Get-VM -Name $VMName).HeartBeat 305 | Start-Sleep -Seconds 3 306 | } 307 | 308 | until ($Heartbeat -eq 'OkApplicationsUnknown'-or $Heartbeat -eq 'OkApplicationsHealthy') 309 | [void]$Heartbeat 310 | "" 311 | Write-Information -MessageData "[+]$VMName is ready. Giving the system some time..." 312 | Write-Verbose "VM status is $Heartbeat." 313 | "" 314 | $sec = 60 315 | 1..$sec | 316 | ForEach-Object { 317 | Write-Progress -Activity "Dreaming of electric sheep..." -Status "$($sec - $_) seconds remaining..." 318 | Start-Sleep -Seconds 1 319 | } 320 | #endregion 321 | 322 | #region Checking status of some key AD components 323 | Write-Information -MessageData "[+]Checking status of Active Directory..." 324 | "" 325 | do { 326 | Write-Information -MessageData "[+]Waiting for $DCName listening on Port 389..." 327 | "" 328 | Start-Sleep -Seconds 1 329 | } 330 | 331 | until (Invoke-Command -VMName $VMName -Credential $DomainCreds -ScriptBlock { 332 | (Test-NetConnection -ComputerName $using:DCName -Port 389 | 333 | Where-Object { $_.TcpTestSucceeded } ) 334 | }) 335 | 336 | Write-Information -MessageData "[+]$DCName is listening on Port 389..." 337 | "" 338 | 339 | do { 340 | Write-Information -MessageData "[+]Waiting for AD WebServices..." 341 | "" 342 | Start-Sleep -Seconds 1 343 | } 344 | 345 | until (Invoke-Command -VMName $VMName -Credential $DomainCreds -ScriptBlock { 346 | (Get-Service -Name 'adws' | Where-Object { $_.status -eq 'running' }) 347 | }) 348 | 349 | Write-Information -MessageData "[+]AD WebServices are ready..." 350 | "" 351 | 352 | Write-Information -MessageData "[+]Checking DNS-Server..." 353 | "" 354 | Invoke-Command -VMName $VMName -Credential $DomainCreds -ScriptBlock { 355 | 356 | if (Test-DnsServer -IPAddress $using:IPv4Address -WarningAction 'SilentlyContinue') { 357 | Write-Information -MessageData "[+]DNS-Server is functional." 358 | "" 359 | } 360 | 361 | (Get-Service adws, kdc, netlogon, dns -WarningAction 'SilentlyContinue' | 362 | Out-String).Trim() 363 | "" 364 | #The following is done because sometimes the machine refuses to set 365 | #the right firewall profile (domain) for the vNIC. Sometimes this helps... 366 | #You might play with these lines or simply omit them. 367 | Restart-Service -Name nlasvc -Force 368 | 369 | $NetAdapterName = (Get-NetAdapter).Name 370 | Disable-NetAdapter -Name $NetAdapterName -Confirm:$false 371 | Start-Sleep -Seconds 2 372 | Enable-NetAdapter -Name $NetAdapterName -Confirm:$false 373 | } 374 | 375 | Write-Information -MessageData "[+]Active Directory is ready." 376 | "" 377 | Write-Information -MessageData "[+]Forest $ForestName is hosted on $DCName" 378 | "" 379 | Write-Information -MessageData "[+]You'll still have to wait a moment for logon." 380 | #endregion 381 | 382 | } 383 | 384 | #region Register Argumentcompleter let's you tab out all your VMs. 385 | $GetVM = { Get-VM | Select-Object -ExpandProperty Name } 386 | 387 | $AutoCompleteVM = @{ 388 | CommandName = 'New-LabForest' 389 | ParameterName = 'VM' 390 | ScriptBlock = $GetVM 391 | } 392 | Register-ArgumentCompleter @AutoCompleteVM 393 | #endregion 394 | -------------------------------------------------------------------------------- /New-LabIIS.ps1: -------------------------------------------------------------------------------- 1 | function New-LabIIS { 2 | <# 3 | .SYNOPSIS 4 | New-LabIIS quickly sets up an IIS-Webserver in a virtual lab-environment. 5 | THIS CODE IS NOT FOR PRODUCTION USE! 6 | 7 | .DESCRIPTION 8 | New-LabIIS sets up an IIS-Webserver in a virtual lab-environment. 9 | New-LabIIS will do the following: 10 | - Install the IIS-Role 11 | - Install a set of Role-Services (hardcoded, without dev-components) 12 | Requires elevation; must be running in a PowerShell session as an administrator. 13 | 14 | .PARAMETER VM 15 | The name of the VM you want to install the IIS on. 16 | You can tab to autocomplete VMs installed on the hypervisor. 17 | 18 | .EXAMPLE 19 | Dot source it or load it another way. 20 | New-LabIIS -VM 'vMACHINE' 21 | 22 | .LINK 23 | Online version: https://github.com/JaekelEDV/ 24 | 25 | .NOTES 26 | The idea of New-LabIIS is to be run against a VM with an installed Server-OS. 27 | You might execute it directly from the Virtualization-Host. 28 | You don't need network-access to the VM at all: it relies on PowerShellDirect. 29 | 30 | If you should miss some role-services, the following are not installed. 31 | Simply add them to the $IISFeatures variable in the scriptblock. 32 | 33 | RDS-Web-Access 34 | Web-Application-Proxy 35 | Web-DAV-Publishing 36 | Web-Net-Ext 37 | Web-AppInit 38 | Web-Asp-Net 39 | Web-CGI 40 | Web-Includes 41 | Web-WebSockets 42 | Web-Ftp-Server 43 | Web-Ftp-Service 44 | Web-Ftp-Ext 45 | Web-Lgcy-Scripting 46 | Web-WMI 47 | Web-WHC 48 | WebDAV-Redirector 49 | WindowsPowerShellWeb 50 | 51 | File Name: New-LabIIS.ps1 52 | Author: Oliver Jaekel | oj@jaekel-edv.de | @JaekelEDV | https://github.com/JaekelEDV 53 | #> 54 | [CmdletBinding()] 55 | param ( 56 | [Parameter(Mandatory = $True, HelpMessage="Enter the Name of the VM you want to install IIS to.")] 57 | [string]$VM 58 | ) 59 | #Requires -RunAsAdministrator 60 | 61 | $DomainCreds = Get-Credential -UserName 'test\administrator' -Message 'Please enter Domain-Admin-Credentials!' 62 | 63 | Invoke-Command -VMName $VM -Credential $DomainCreds -ScriptBlock { 64 | 65 | $IISFeatures = "Web-Server", "Web-WebServer", "Web-Common-Http", "Web-Http-Errors", "Web-Default-Doc", "Web-Static-Content", "Web-Dir-Browsing", "Web-Http-Redirect", "Web-Performance", "Web-Stat-Compression", "Web-Dyn-Compression", "Web-Security", "Web-Filtering", "Web-Client-Auth", "Web-Cert-Auth", "Web-Digest-Auth", "Web-IP-Security", "Web-Basic-Auth", "Web-CertProvider", "Web-Url-Auth", "Web-Windows-Auth", "Web-Health", "Web-Http-Logging", "Web-Http-Tracing", "Web-Request-Monitor", "Web-Custom-Logging", "Web-ODBC-Logging", "Web-Log-Libraries", "Web-Mgmt-Tools", "Web-Mgmt-Console", "Web-Scripting-Tools", "Web-Mgmt-Service" 66 | try { 67 | Install-WindowsFeature -Name $IISFeatures -IncludeManagementTools 68 | } 69 | 70 | catch { 71 | Write-Warning $_.Exception.Message 72 | } 73 | } 74 | } 75 | 76 | #region Register Argumentcompleter let's you tab out all your VMs. 77 | $GetVM = { Get-VM | Select-Object -ExpandProperty Name } 78 | 79 | $AutoCompleteVM = @{ 80 | CommandName = 'New-LabIIS' 81 | ParameterName = 'VM' 82 | ScriptBlock = $GetVM 83 | } 84 | Register-ArgumentCompleter @AutoCompleteVM 85 | #endregion 86 | -------------------------------------------------------------------------------- /New-LabNCSI.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Function New-LabNCSI configures a server as IIS and DNS to support 4 | Network Connectivity Status Indicator (NCSI) scenarios for lab environments. 5 | .DESCRIPTION 6 | In some lab environments some machines really need to think they're on the internet, 7 | e.g. a Direct Access scenario. Most of my labs won't have internet access b/c I like to keep the VMs "sandboxed". 8 | This function helps you to turn a machine into a IIS- and DNS- 9 | Server simulating Microsofts Network Connectivity Status Indicator technique. 10 | To avoid touching the registry of the clients that will use this machine, the default MS 11 | domains and files have not been altered. 12 | Since Client OS might either use msftncsi.com (Win8.1 and earlier) or msftconnecttest.com (Win10), both is configured. 13 | Find more on NCSI under the link section of this help. 14 | This function is meant to be run on a Windows Server OS (tested with 2016) and does the following: 15 | Installs IIS with default settings and creates ncsi.txt and connecttest.txt in inetpub\wwwroot. 16 | Installs DNS and creates the two zones msftncsi.txt and msftconnecttest.com. 17 | Creates the necessary Host(A) records (www and dns). 18 | Sets the preferred DNS-Server to itself - be aware of this one!!! 19 | Sets a new default route to localhost if there is no default gateway - otherwise ncsi won't work at all. 20 | As a result you get a machine thinking it has real internet connection. All Clients pointing to 21 | this machine will change their network connectivity status to "internet connected" as well. 22 | .EXAMPLE 23 | Execute New-LabNCSI.ps1 directly from shell with dot sourcing 24 | . .\New-LabNCSI.ps1 25 | .LINK 26 | https://blogs.technet.microsoft.com/networking/2012/12/20/the-network-connection-status-icon/ 27 | .LINK 28 | https://blogs.technet.microsoft.com/netgeeks/2018/02/20/why-do-i-get-an-internet-explorer-or-edge-popup-open-when-i-get-connected-to-my-corpnet-or-a-public-network/ 29 | .NOTES 30 | Author: Oliver Jäkel | oj@jaekel-edv.de | @JaekelEDV | https://github.com/JaekelEDV 31 | #> 32 | 33 | Function New-LabNCSI 34 | { 35 | [CmdletBinding()] 36 | param ( 37 | $InformationPreference = 'Continue', 38 | $WarningPreference = 'Stop' 39 | ) 40 | 41 | BEGIN 42 | { 43 | #region Check if script is run as administrator. 44 | 45 | if 46 | (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) 47 | 48 | { 49 | Write-Verbose -Message 'Check: You are Admin and good to go.' 50 | } 51 | else 52 | { 53 | Write-Warning -Message 'Please run Powershell as Administrator - script will not continue.' 54 | } 55 | #endregion 56 | 57 | #region Check if IIS is installed - if not: Make it so! 58 | $IIS = Get-WindowsFeature -Name web-server | Where-Object {$_.Installed -eq $true} 59 | 60 | if ($IIS -ne $null) 61 | { 62 | Write-Verbose -Message 'IIS is already installed.' 63 | } 64 | else 65 | { 66 | Install-WindowsFeature -Name web-server -IncludeManagementTools | Out-Null 67 | Write-Information -MessageData 'Installed Webserver IIS.' 68 | } 69 | #endregion 70 | 71 | #region Check if DNS is installed - if not: Make it so! 72 | 73 | $DNS = Get-WindowsFeature -Name dns | Where-Object {$_.Installed -eq $true} 74 | 75 | if ($DNS -ne $null) 76 | { 77 | Write-Verbose -Message 'DNS is already installed.' 78 | } 79 | else 80 | { 81 | Install-WindowsFeature -Name dns -IncludeManagementTools | Out-Null 82 | Write-Information -MessageData 'Installed DNS-Server.' 83 | } 84 | #endregion 85 | 86 | #region Check if Zones are set up - if not: Make it so! 87 | 88 | $ZoneNCSI = 'msftncsi.com' 89 | $ZoneConnecttest = 'msftconnecttest.com' 90 | $Zones = Get-DnsServerZone | where-Object {$_.zonename -eq $ZoneNCSI -or $_.zonename -eq $ZoneConnecttest} 91 | 92 | if ($Zones.Count -eq 2) 93 | { 94 | Write-Verbose -Message "Zones $ZoneNCSI and $ZoneConnecttest already exist." 95 | } 96 | else 97 | { 98 | $zones = @($ZoneNCSI, $ZoneConnecttest) 99 | 100 | foreach ($zone in $zones) 101 | { 102 | Add-DnsServerPrimaryZone -Name $Zone -DynamicUpdate None -ZoneFile "$Zone.dns" | Out-Null 103 | Write-Information -MessageData "Created DNSZone $Zone." 104 | } 105 | 106 | } 107 | #endregion 108 | 109 | #region Setting up some Variables for the Process Block 110 | $wwwroot = "$env:HOMEDRIVE\inetpub\wwwroot" 111 | $Checkwwwroot = Test-Path -Path "$wwwroot" 112 | $NCSITXT = 'ncsi.txt' 113 | $CheckNCSITXT = Test-Path -Path "$wwwroot\$NCSITXT" 114 | $connecttestTXT = 'connecttest.txt' 115 | $CheckConnecttestTXT = Test-Path -Path "$wwwroot\$connecttestTXT" 116 | $CheckwwwNCSI = Get-DnsServerResourceRecord -Name www -ZoneName "$ZoneNCSI" -ErrorAction 'SilentlyContinue' 117 | $CheckdnsNCSI = Get-DnsServerResourceRecord -Name dns -ZoneName "$ZoneNCSI" -ErrorAction 'SilentlyContinue' 118 | $CheckwwwConnecttest = Get-DnsServerResourceRecord -Name www -ZoneName "$ZoneConnecttest" -ErrorAction 'SilentlyContinue' 119 | $CheckdnsConnecttest = Get-DnsServerResourceRecord -Name dns -ZoneName "$ZoneConnecttest" -ErrorAction 'SilentlyContinue' 120 | $HostIP = (Get-NetIPConfiguration | Where-Object {$_.IPv4DefaultGateway -ne "$null" -and $_.NetAdapter.Status -ne "Disconnected"}).IPv4Address.IPAddress 121 | $splitIP = $HostIP.Split('.') 122 | $splitIP[2] = 255 123 | $splitIP[3] = 255 124 | $dnsIP = ($splitIP -join '.') 125 | $Interface = (Get-NetIPAddress -IPAddress $HostIP).InterfaceAlias 126 | $CheckPrefDNS = (Get-DnsClientServerAddress -AddressFamily IPv4 -InterfaceAlias $Interface).ServerAddresses 127 | $CheckDefGateway = (Get-NetIPConfiguration).IPv4DefaultGateway.NextHop 128 | #endregion 129 | } 130 | 131 | PROCESS 132 | { 133 | #region Check if inetpub\wwwroot is present - if not: Make it so! 134 | 135 | if ($Checkwwwroot -eq $true) 136 | { 137 | Write-Verbose -Message "Folder $wwwroot already exists." 138 | } 139 | else 140 | { 141 | New-Item -Path "$wwwroot" -ItemType Directory | Out-Null 142 | Write-Information -MessageData "Created $wwwroot." 143 | } 144 | #endregion 145 | 146 | #region Creating the ncsi-files in wwwroot 147 | 148 | if ($CheckNCSITXT -eq $true) 149 | { 150 | Write-Verbose -Message "File $NCSITXT already exists." 151 | } 152 | else 153 | { 154 | New-Item -Path "$wwwroot" -ItemType File -Name $ncsiTXT -Value 'Microsoft NCSI' | Out-Null 155 | Write-Information -MessageData "Created file $wwwroot\$NCSITXT." 156 | } 157 | 158 | if ($CheckConnecttestTXT -eq $true) 159 | { 160 | Write-Verbose -Message "File $connecttestTXT already exists." 161 | } 162 | else 163 | { 164 | New-Item -Path "$wwwroot" -ItemType File -Name $connecttestTXT -Value 'Microsoft Connect Test' | Out-Null 165 | Write-Information -MessageData "Created file $wwwroot\$connecttestTXT." 166 | } 167 | #endregion 168 | 169 | 170 | #region Create ResourceRecords, split HostIP to modify the last two octets for dns-record 171 | 172 | if ($CheckwwwNCSI -ne $null) 173 | { 174 | Write-Verbose -Message "Host(A) www already exists in $ZoneNCSI." 175 | } 176 | else 177 | { 178 | Add-DnsServerResourceRecordA -ZoneName "$ZoneNCSI" -Name www -IPv4Address $HostIP 179 | Write-Information -MessageData "Created Host(A) www in $ZoneNCSI with IP $HostIP." 180 | } 181 | 182 | if ($CheckdnsNCSI -ne $null) 183 | { 184 | Write-Verbose -Message "Host(A) dns already exists in $ZoneNCSI." 185 | } 186 | else 187 | { 188 | Add-DnsServerResourceRecordA -ZoneName "$ZoneNCSI" -Name dns -IPv4Address $dnsIP 189 | Write-Information -MessageData "Created Host(A) dns in $ZoneNCSI with IP $dnsIP." 190 | } 191 | 192 | if ($CheckwwwConnecttest -ne $null) 193 | { 194 | Write-Verbose -Message "Host(A) www already exists in $ZoneConnecttest." 195 | } 196 | else 197 | { 198 | Add-DnsServerResourceRecordA -ZoneName "$ZoneConnecttest" -Name www -IPv4Address $HostIP 199 | Write-Information -MessageData "Created Host(A) www in $ZoneConnecttest with IP $HostIP." 200 | } 201 | 202 | if ($CheckdnsConnecttest -ne $null) 203 | { 204 | Write-Verbose -Message "Host(A) dns already exists in $ZoneConnecttest." 205 | } 206 | else 207 | { 208 | Add-DnsServerResourceRecordA -ZoneName "$ZoneConnecttest" -Name dns -IPv4Address $dnsIP 209 | Write-Information -MessageData "Created Host(A) dns in $ZoneConnecttest with IP $dnsIP." 210 | } 211 | #endregion 212 | } 213 | 214 | END 215 | { 216 | #region Set HostIP as prefered DNS-Server 217 | 218 | if ($CheckPrefDNS -eq $HostIP) 219 | { 220 | Write-Verbose -Message "Preferred DNS-Server is $HostIP - localhost." 221 | } 222 | else 223 | { 224 | Set-DnsClientServerAddress -InterfaceAlias $Interface -ServerAddresses $HostIP | Out-Null 225 | Write-Information -MessageData "Preferred DNS-Server has been changed to $HostIP - localhost." 226 | } 227 | 228 | if ($CheckDefGateway -eq $null) 229 | { 230 | New-NetRoute -InterfaceAlias $Interface -DestinationPrefix '0.0.0.0/0' -NextHop $HostIP -Confirm:$false | Out-Null 231 | Write-Information -MessageData "Set default route to $HostIP - localhost." 232 | 233 | } 234 | 235 | # region Disable and enable NetworkAdapter (ugly, but less risky than Restart-Service NLASvc b/c of its dependencies...) 236 | Disable-NetAdapter -Name $Interface -Confirm:$false 237 | Enable-NetAdapter -Name $Interface -Confirm:$false 238 | Write-Information -MessageData 'THE MACHINE NOW THINKS IT HAS A REAL INTERNET CONNECTION.' 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /New-LabUser.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | New-LabUser.ps1 creates User-Accounts and Groups for Lab Environments based on a csv-file. 4 | .DESCRIPTION 5 | This Script creates User-Accounts for a Lab based on a csv-file. 6 | Right now the script will look for the headers Name,SamAccountName,UPN,GivenName,Surname,DisplayName,EmailAddress and GroupName. 7 | Of course you might add others as well. Adjust the csv and the hashtable for New-ADUser accordingly. 8 | The users will get a Password which you might set in the parameter section below. 9 | The Script has two mandatory Parameters (see the parameters help section): 10 | You must point to your csv-file and you must specify a OU in which the users will be created. If this OU doesn't exist, the script will create it for you. 11 | You can simply pass a name, no need to type a distinguished name. 12 | 13 | .PARAMETER CSVPath 14 | Please enter the Path where your csv-file lives. 15 | 16 | .PARAMETER OU 17 | Please enter the Name of the OU where your new users shall live. There is no need of using the DistinguishedName - just write a name. 18 | 19 | .EXAMPLE 20 | New-LabUser -CSVPath .\lab_users.csv -OU Foo 21 | 22 | .NOTES 23 | Author: Oliver Jäkel | oj@jaekel-edv.de | @JaekelEDV 24 | #> 25 | 26 | [CmdletBinding()] 27 | param ( 28 | [Parameter(Mandatory = $true, 29 | HelpMessage="Enter just the name of the OU!")] 30 | [string] $CSVPath, 31 | [Parameter(Mandatory = $true, 32 | HelpMessage="Enter the path to the csv-file!!")] 33 | [string] $OU 34 | ) 35 | 36 | #region Setting some variables and starting a transcript 37 | $OU='Benutzer' 38 | $csvpath = 'C:\lab_user.csv' 39 | 40 | Start-Transcript -Path $env:userprofile\Desktop\LOG-NewLabUser.txt -IncludeInvocationHeader 41 | 42 | $CSVUser = Import-Csv -LiteralPath $CSVPath 43 | $Password = (ConvertTo-SecureString -String 'Pa$$w0rd' -AsPlainText -Force) #Change the Password here if you like. 44 | $Domain = (Get-ADDomain).DistinguishedName 45 | #endregion 46 | 47 | #region Creating the OU from parameter $OU 48 | Try { 49 | New-ADOrganizationalUnit -Name $OU -ProtectedFromAccidentalDeletion $false 50 | } 51 | Catch { 52 | $error[0] 53 | } 54 | 55 | $DestOU = (Get-ADOrganizationalUnit -Identity "ou=$OU,$Domain").DistinguishedName 56 | 57 | Write-Host "Creating OU $DestOU..." -ForegroundColor Yellow 58 | Write-Host "" 59 | Write-Host "Creating Users..." -ForegroundColor Yellow 60 | Write-Host "" 61 | #endregion 62 | 63 | #region Creating the users from csv 64 | try{ 65 | foreach ($user in $CSVUser) { 66 | $params = @{ 67 | Name = $user.Name 68 | Displayname = "$($user.GivenName) $($user.Surname)" 69 | Path = $DestOU 70 | Samaccountname = $user.SamAccountName 71 | UserPrincipalName = $user.UPN 72 | Surname = $user.Surname 73 | GivenName = $user.GivenName 74 | EmailAddress = $user.EmailAddress 75 | Department = $user.Department 76 | AccountPassword = $Password 77 | Enabled = $True 78 | } 79 | New-ADUser @params -PassThru 80 | } 81 | } 82 | 83 | Catch { 84 | $error[0] 85 | } 86 | 87 | Write-Host "Creating Groups..." -ForegroundColor Yellow 88 | Write-Host "" 89 | #endregion 90 | 91 | #region Creating the groups from csv 92 | try{ 93 | $CSVGroups = Import-Csv -LiteralPath $CSVPath | Select-Object 'GroupName' -Unique 94 | 95 | foreach ($group in $CSVGroups) { 96 | 97 | $params = @{ 98 | Name = $group.groupName 99 | Path = $DestOU 100 | GroupScope = 'Global' 101 | SamAccountName = $group.groupName 102 | GroupCategory = 'Security' 103 | DisplayName = $group.groupName 104 | } 105 | 106 | New-ADGroup @params 107 | Write-Host "Creating Group "$group.groupName"..." -ForegroundColor Green 108 | Write-Host "" 109 | } 110 | } 111 | 112 | Catch { 113 | $error[0] 114 | } 115 | 116 | Write-Host "Adding Users to Groups..." -ForegroundColor Yellow 117 | Write-Host "" 118 | #endregion 119 | 120 | #region Adding the users to the corresponding groups 121 | try{ 122 | $User2Group = Import-Csv -Path $CSVPath | Select-Object GivenName, GroupName 123 | 124 | foreach ($user in $User2Group) { 125 | 126 | Add-ADGroupMember -Identity $user.groupname -Members $user.GivenName 127 | Write-Host "Adding User "$user.GivenName" to Group "$user.groupname"..." -ForegroundColor Green 128 | } 129 | } 130 | 131 | Catch { 132 | $error[0] 133 | } 134 | #endregion 135 | 136 | #region Writing some logs and stopping transcript 137 | $log = "$env:userprofile\Desktop\UsersSIDGroups.txt" 138 | 139 | (Get-ADUser -Filter * -SearchBase $DestOU -Properties * | 140 | Select-Object Name, MemberOf, SID) | 141 | Out-File -FilePath $log 142 | 143 | (Get-ADGroup -Filter * -SearchBase $DestOU | 144 | Select-Object Name, SID) | 145 | Out-File -FilePath $log -Append 146 | 147 | Stop-Transcript 148 | #endregion 149 | -------------------------------------------------------------------------------- /New-LabVM.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | New-LabVM quickly creates VM on Hyper-V for Lab Environments 4 | .DESCRIPTION 5 | This Script creates a Windows Server 2016 or Windows Server 2022 Generation 2 VM 6 | with differencing disk based on *existing* Master-VHDx. 7 | It connects to an existing external vSwitch to activate the License. 8 | Automatic checkpoints are disabled. 9 | The VM starts automatically. 10 | Be sure to adjust paths in the variables according to your environment - 11 | this script relies heavily on personal surroundings. 12 | .PARAMETER OSType 13 | Choice between Server 2016 or Server 2022. 14 | All depending on existing Master-VHDx. 15 | .PARAMETER VMName 16 | Sets the Name of the VM to create. 17 | .EXAMPLE 18 | New-LabVM -VMName [Value] -OSType [Value] 19 | .NOTES 20 | Author: Oliver Jäkel | oj@jaekel-edv.de | @JaekelEDV | https://github.com/jaekeledv 21 | #> 22 | 23 | [CmdletBinding()] 24 | param ( 25 | [Parameter( 26 | Mandatory = $true, 27 | Position=0 28 | )] 29 | [string]$VMName, 30 | 31 | [Parameter( 32 | Mandatory = $true, 33 | Position=1 34 | )] 35 | [ValidateSet('Server2016', 'Server2022')] 36 | [string]$OSType 37 | ) 38 | 39 | $VMPath = "D:\$VMName" 40 | $VHDXPath = "D:\$VMName\$VMName.vhdx" 41 | $VHDXSize = 127GB 42 | $MasterVHDXServer2016 = "D:\MASTER\master_2016.vhdx" 43 | $MasterVHDXServer2022 = "D:\MASTER\master_2022.vhdx" 44 | 45 | $WarningPreference = 'Stop' 46 | 47 | #region Check if chosen Master.vhdx exists 48 | 49 | if ($OSType -eq 'Server2016') { 50 | if (Test-Path -Path $MasterVHDXServer2016) { 51 | Write-Verbose -Message 'Master_2016 found.' 52 | } 53 | else { 54 | Write-Warning -Message 'Cannot find master_2016.vhdx. Check filename or create it.' 55 | } 56 | } 57 | 58 | if ($OSType -eq 'Server2022') { 59 | if (Test-Path -Path $MasterVHDXServer2022) { 60 | Write-Verbose -Message 'Master_2022 found.' 61 | } 62 | else { 63 | Write-Warning -Message 'Cannot find master_2022.vhdx. Check filename or create it.' 64 | } 65 | } 66 | #endregion 67 | 68 | #region Create VM 69 | $VMParams = @{ 70 | Name = $VMName 71 | MemoryStartupBytes = 1024MB 72 | Generation = 2 73 | NoVHD = $true 74 | Path = $VMPath 75 | } 76 | 77 | New-VM @VMParams 78 | Write-Verbose -Message "Created VM $VMName in $VMPath." 79 | #endregion 80 | 81 | #region Set VM 82 | $VMConfig = @{ 83 | Name = $VMName 84 | DynamicMemory = $true 85 | ProcessorCount = 4 86 | MemoryMaximumBytes = 4GB 87 | CheckpointType = "Disabled" 88 | } 89 | 90 | Set-VM @VMConfig 91 | Write-Verbose -Message "Setting VM $VMName as desired." 92 | #endregion 93 | 94 | #region Create differencing Disks according to OSType-Selection 95 | switch ($OSType) { 96 | 'Server2016' { 97 | $2016VM = @{ 98 | Differencing = $true 99 | ParentPath = $MasterVHDXServer2016 100 | Path = $VHDXPath 101 | SizeBytes = $VHDXSize 102 | } 103 | New-VHD @2016VM 104 | Write-Verbose -Message "Created differencing disk for $VMName." 105 | } 106 | 'Server2022' { 107 | $2022VM = @{ 108 | Differencing = $true 109 | ParentPath = $MasterVHDXServer2022 110 | Path = $VHDXPath 111 | SizeBytes = $VHDXSize 112 | } 113 | New-VHD @2022VM 114 | Write-Verbose -Message "Created differencing disk for $VMName." 115 | } 116 | } 117 | #endregion 118 | 119 | #region Attach Disk to VM 120 | $VMHDD = @{ 121 | VMName = $VMName 122 | ControllerType = "SCSI" 123 | ControllerNumber = "0" 124 | Path = $VHDXPath 125 | } 126 | 127 | Add-VMHardDiskDrive @VMHDD 128 | #endregion 129 | 130 | #region Set Bootorder 131 | $VMDisk = Get-VMHardDiskDrive -VMName $VMName 132 | Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDisk 133 | #endregion 134 | 135 | #region Determine external vSwitch and connect vNIC to it 136 | $VMSwitch = (Get-VMSwitch -SwitchType External).Name 137 | Get-VMNetworkAdapter -VMName $VMName | 138 | Connect-VMNetworkAdapter -SwitchName $VMSwitch 139 | #endregion 140 | 141 | #region Start-VM 142 | Start-VM -Name $VMName 143 | #endregion 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSLabHelpers 2 | A collection of some Powershell scripts and functions for building lab-environments 3 | -------------------------------------------------------------------------------- /Test-IntSvcGuest.ps1: -------------------------------------------------------------------------------- 1 | function Test-IntSvcGuest { 2 | <# 3 | .SYNOPSIS 4 | Test-IntSvcGuest checks if the Hyper-V Guest Integration Service is enabled. 5 | 6 | .DESCRIPTION 7 | Test-IntSvcGuest checks if the Hyper-V Guest Integration Service is enabled. 8 | The Guest Integration Services are a prequisite if you'd like to 9 | copy files from the Hyper-V-Host to a VM without using a network stack, e.g. 10 | with Copy-VMFile. 11 | https://docs.microsoft.com/en-us/powershell/module/hyper-v/copy-vmfile?view=win10-ps 12 | Requires elevation; must be run in a PowerShell session as an administrator. 13 | 14 | .PARAMETER VM 15 | The name of the VM(s) you want to check. 16 | 17 | .EXAMPLE 18 | Dot source it or load it another way. 19 | C:\PS>. .\Test-IntSvcGuest 20 | C:\PS>Test-IntSvcGuest -VM 'vMACHINE' 21 | This will check one VM. 22 | 23 | .EXAMPLE 24 | Dot source it or load it another way. 25 | C:\PS>. .\Test-IntSvcGuest 26 | C:\PS>Test-IntSvcGuest -VM 'vMACHINE1','vMACHINE2' 27 | This will check multiple VMs separated by comma. 28 | 29 | .EXAMPLE 30 | Dot source it or load it another way. 31 | C:\PS>. .\Test-IntSvcGuest 32 | C:\PS>$Computers = Get-Content -Path computer.txt 33 | C:\PS>$Computers | Test-IntSvcGuest 34 | This will allow you to pipe a list of VMNames from a .txt-file. 35 | 36 | .INPUTS 37 | Test-IntSvcGuest accepts input from the pipe, e.g. from a txt.-file. 38 | 39 | .OUTPUTS 40 | Test-IntSvcGuest 41 | System.String. Test-IntSvcGuest returns a string from Write-Host. 42 | 43 | .LINK 44 | Online version: https://github.com/JaekelEDV/ 45 | 46 | .NOTES 47 | File Name: Test-IntSvcGuest.ps1 48 | Author: Oliver Jaekel | oj@jaekel-edv.de | @JaekelEDV | https://github.com/JaekelEDV 49 | #> 50 | [CmdletBinding()] 51 | param ( 52 | [Parameter(ValueFromPipeline=$true)] 53 | [string[]]$VMName 54 | ) 55 | #Requires -RunAsAdministrator 56 | begin {#not needed 57 | } 58 | process { 59 | ForEach ($VM in $VMName) { 60 | $IntSvc = Get-VMIntegrationService -VMName $VM | 61 | Where-Object { 62 | $_.Name -eq 'Gastdienstschnittstelle' -or 63 | $_.Name -eq 'Guest Service Interface' 64 | } 65 | 66 | if ($IntSvc.Enabled -eq $false) { 67 | Enable-VMIntegrationService -VMName $VM -Name $IntSvc.Name -ErrorAction 'Stop' 68 | Write-Host "Guest Integration Services now enabled on $VM." -ForegroundColor Yellow 69 | } 70 | else { 71 | Write-Host "Guest Integration Services already enabled on $VM." -ForegroundColor Green 72 | } 73 | } 74 | }#process end 75 | end {#not needed 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ad_sites_subnets.csv: -------------------------------------------------------------------------------- 1 | Subnet,Site,SiteLink 2 | 10.0.0.0/24,BERLIN,DEFAULTIPSITELINK 3 | 172.16.0.0/24,HAMBURG,DEFAULTIPSITELINK 4 | 192.168.0.0/24,MUENCHEN,DEFAULTIPSITELINK 5 | -------------------------------------------------------------------------------- /lab_user.csv: -------------------------------------------------------------------------------- 1 | Name,SamAccountName,UPN,GivenName,Surname,DisplayName,EmailAddress,GroupName 2 | Jasper Beardley,Jasper,jasper@test.local,Jasper,Beardley,Jasper Beardley,jasper@test.local,Retired 3 | Patty Bouvier,Patty,patty@test.local,Patty,Bouvier,Patty Bouvier,patty@test.local,Bouvier 4 | Selma Bouvier,Selma,selma@test.local,Selma,Bouvier,Selma Bouvier,selma@test.local,Bouvier 5 | Kent Brockman,Kent,kent@test.local,Kent,Brockman,Kent Brockman,kent@test.local,Resident 6 | Charles Montgomery Burns,Charles Montgomery,monty@test.local,Charles Montgomery,Burns,Charles Montgomery Burns,monty@test.local,Powerplant 7 | Carl Carlson,Carl,carl@test.local,Carl,Carlson,Carl Carlson,carl@test.local,Powerplant 8 | Maude Flanders,Maude,maude@test.local,Maude,Flanders,Maude Flanders,maude@test.local,Flanders 9 | Ned Flanders,Ned,ned@test.local,Ned,Flanders,Ned Flanders,ned@test.local,Flanders 10 | Rod Flanders,Rod,rod@test.local,Rod,Flanders,Rod Flanders,rod@test.local,Flanders 11 | Todd Flanders,Todd,todd@test.local,Todd,Flanders,Todd Flanders,todd@test.local,Flanders 12 | Matt Groening,Matt,matt@test.local,Matt,Groening,Matt Groening,matt@test.local,Resident 13 | Barney Gumble,Barney,barney@test.local,Barney,Gumble,Barney Gumble,barney@test.local,Resident 14 | Julius Hibbert,Julius,julius@test.local,Julius,Hibbert,Julius Hibbert,julius@test.local,Resident 15 | Bernice Hibbert,Bernice,bernice@test.local,Bernice,Hibbert,Bernice Hibbert,bernice@test.local,Resident 16 | Jimbo Jones,Jimbo,jimbo@test.local,Jimbo,Jones,Jimbo Jones,jimbo@test.local,Resident 17 | Edna Krabappel,Edna,edna@test.local,Edna,Krabappel,Edna Krabappel,edna@test.local,School 18 | Lenny Leonard,Lenny,lenny@test.local,Lenny,Leonard,Lenny Leonard,lenny@test.local,Powerplant 19 | Helen Lovejoy,Helen,helen@test.local,Helen,Lovejoy,Helen Lovejoy,helen@test.local,Resident 20 | ResidentTimothy Lovejoy,Timothy,timothy@test.local,Timothy,Lovejoy,Timothy Lovejoy,timothy@test.local,Resident 21 | Otto Mann,Otto,otto@test.local,Otto,Mann,Otto Mann,otto@test.local,School 22 | Nelson Muntz,Nelson,nelson@test.local,Nelson,Muntz,Nelson Muntz,nelson@test.local,Resident 23 | Abraham Simpson,Abraham,abe@test.local,Abraham,Simpson,Abraham Simpson,abe@test.local,Retired 24 | Bart Simpson,Bart,bart@test.local,Bart,Simpson,Bart Simpson,bart@test.local,Simpsons 25 | Homer Simpson,Homer,homer@test.local,Homer,Simpson,Homer Simpson,homer@test.local,Simpsons 26 | Lisa Simpson,Lisa,lisa@test.local,Lisa,Simpson,Lisa Simpson,lisa@test.local,Simpsons 27 | Maggie Simpson,Maggie,maggie@test.local,Maggie,Simpson,Maggie Simpson,maggie@test.local,Simpsons 28 | Marge Simpson,Marge,marge@test.local,Marge,Simpson,Marge Simpson,marge@test.local,Simpsons 29 | Agnes Skinner,Agnes,agnes@test.local,Agnes,Skinner,Agnes Skinner,agnes@test.local,School 30 | Seymour Skinner,Seymour,seymour@test.local,Seymour,Skinner,Seymour Skinner,seymour@test.local,School 31 | Waylon Smithers,Waylon,waylon@test.local,Waylon,Smithers,Waylon Smithers,waylon@test.local,Powerplant 32 | Moe Szyslak,Moe,moe@test.local,Moe,Szyslak,Moe Szyslak,moe@test.local,Resident 33 | Kirk van Houten,Kirk,kirk@test.local,Kirk,van Houten,Kirk van Houten,kirk@test.local,van Houten 34 | Luann van Houten,Luann,luann@test.local,Luann,van Houten,Luann van Houten,luann@test.local,van Houten 35 | Milhouse van Houten,Milhouse,milhouse@test.local,Milhouse,van Houten,Milhouse van Houten,milhouse@test.local,van Houten 36 | Clancy Wiggum,Clancy,clancy@test.local,Clancy,Wiggum,Clancy Wiggum,clancy@test.local,Wiggum 37 | Ralph Wiggum,Ralph,ralph@test.local,Ralph,Wiggum,Ralph Wiggum,ralph@test.local,Wiggum 38 | Sarah Wiggum,Sarah,sarah@test.local,Sarah,Wiggum,Sarah Wiggum,sarah@test.local,Wiggum 39 | --------------------------------------------------------------------------------