Hyper-V PowerShell Windows Server 2016 Windows Server 2019

PowerShell: Set customizations for a Windows Server 2016 or 2019 base image


Because I do a lot of research and testing, and therefore use a lot of different virtual machines (VM), which get build and rebuild, I mostly deploy them from a pre-configured template (base image or golden image) First of all to save time, but also to reduce errors and to ensure consistency by eliminating the need for repetitive configuration changes and performance tweaks.

To easy up the template creation process, I created the below PowerShell script which allows me to set some customizations for a Windows Server 2016 or 2019 VM base image.

This PowerShell script will do all of the following:

  • Enable Remote Desktop and add Windows Firewall exception
  • Allow ICMP (ping) through Windows Firewall IPv4 and IPv6
  • Disable guest account
  • Disable RDP printer mapping
  • Disable WAC pop-up in Server Manager on Windows Server 2019
  • Remove the description of the Local Administrator Account
  • Set Local Administrator password
  • Rename Local Administrator Account
  • Set volume label of C: to OS
  • Change CD-ROM drive letter
  • Create the C:\Temp folder if not exists
  • Set Windows Diagnostic level (Telemetry) to Security
  • Set Time Zone
  • Set Power Management to High Performance if it is not currently the active plan
  • Disable IE security for Administrators
  • Set the Interactive Login to “Do not display the last user name”
  • Enable User Account Control (UAC) 
  • Set Windows Server 2016 or 2019 Automatic Virtual Machine Activation (AVMA) key
  • Rename server
  • Restart server to apply all changes


Prerequisites

  • Clean Windows Server 2016 or 2019 VM
  • VM can be running on Hyper-V (2016 or 2019)
  • Run the script with PowerShell administrator privileges


PowerShell Script


<#
.Synopsis

A script used to customize a template for Windows Server 2016 or 2019.

.Description

A script used to customize a Windows Server 2016 or 2019 virtual machine (VM) template (base image).
When all customizations are set, you will be asked to reboot the server to apply all changes.

.Notes

File Name:      Template_Customization_Windows_Server_2016_2019.ps1
Created:        09/09/2019
Last modified:  10/05/2020
Author:         Wim Matthyssen
PowerShell:     5.1 or above 
Requires:       -RunAsAdministrator
OS:             Windows Server 2016 and Windows Server 2019
Version:        2.0
Action:         Change variables were needed to fit your needs
Disclaimer:     This script is provided "As Is" with no warranties.

.Example

.\Template_Customization_Windows_Server_2016_2019.ps1

.LINK

https://tinyurl.com/y8l99kaj
#>

## Variables

$serverName = "vm-tmpl-w2k19"
$driveLabel = "OS"
$tempFolder = "C:\Temp"
$timezone = "Romance Standard Time"
$powerManagement = "High performance"
$cdromDriveletter = "z:"
$adminIEsecurityregpath = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}"
$adminIEsecuritykey = "IsInstalled"
$windowsBuildNumber = (Get-WmiObject Win32_OperatingSystem).BuildNumber
$interActiveLogonregpath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$interActiveLogonkey = "DontDisplayLastUsername"
$regkeyPathUAC = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$regkeyRDPPrinterMapping = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
$regkeyServerManager = "HKLM:\SOFTWARE\Microsoft\ServerManager"
$regkeyWindowsDiagnosticLevel = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection"
$oldLocalAdministratorName = "Administrator"
$newLocalAdministratorName = "locadmin23"
$password = "Sup3rS3cr3tP@ssword" | ConvertTo-SecureString -AsPlainText -Force
$windowsServer2016 = "14393"
$windowsServer2019 = "17763"
$writeEmptyLine = "`n"
$writeSeperator = " - "
$time = Get-Date -UFormat "%A %m/%d/%Y %R"
$foregroundColor1 = "Yellow"
$foregroundColor2 = "Red"

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Write Download started

Write-Host ($writeEmptyLine + "# Template custimization started" + $writeSeperator + $time)`
-foregroundcolor $foregroundColor2 $writeEmptyLine

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Enable Remote Desktop and add Windows Firewall exception

Import-Module NetSecurity
(Get-WmiObject Win32_TerminalServiceSetting -Namespace root\cimv2\TerminalServices).SetAllowTsConnections(1,1) | Out-Null
(Get-WmiObject -Class "Win32_TSGeneralSetting" -Namespace root\cimv2\TerminalServices -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null
Get-NetFirewallRule -DisplayName "Remote Desktop*" | Set-NetFirewallRule -enabled true
Write-Host ($writeEmptyLine + "# Remote Deskopt enabled" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 
 
##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Enable secure RDP authentication Network Level Authentication (NLA)

Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -name "UserAuthentication" -Value 1
Write-Host ($writeEmptyLine + "# RDP NLA enabled" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Allow ICMP (ping) through Windows Firewall IPv4 and IPv6

New-NetFirewallRule -Name Allow_Ping_ICMPv4 -DisplayName "Allow Ping ICMPv4" -Description "Packet Internet Groper ICMPv4" -Protocol ICMPv4 -IcmpType 8 -Enabled True -Profile Any -Action Allow
New-NetFirewallRule -Name Allow_Ping_ICMPv6 -DisplayName "Allow Ping ICMPv6" -Description "Packet Internet Groper ICMPv6" -Protocol ICMPv6 -IcmpType 8 -Enabled True -Profile Any -Action Allow
Write-Host ($writeEmptyLine + "# Allowed ping trough firewall" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine  

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Disable guest account

net user guest /active:no
Disable-LocalUser -Name "guest"
Write-Host ($writeEmptyLine + "# Guest account disabled" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Disable RDP printer mapping

Set-ItemProperty -Path $regkeyRDPPrinterMapping  -Name fDisableCpm -Value 1
Write-Host ($writeEmptyLine + "# RDP printer mapping disabled" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Disable WAC pop-up in Server Manager on Windows Server 2019

## 

If ($windowsBuildNumber -eq $windowsServer2019)
	{
		New-ItemProperty -Path $regkeyServerManager -Name 'DoNotPopWACConsoleAtSMLaunch' -PropertyType 'DWord' -Value '1' -Force | Out-Null
		Write-Host ($writeEmptyLine + "# WAC pop-up disabled in Server Manager" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 
	}

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Remove description of the Local Administrator Account

Set-LocalUser -Name $oldLocalAdministratorName -Description ""
Write-Host ($writeEmptyLine + "# Description removed from Local Administrator Account" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set Local Administrator password

$userAccount = Get-LocalUser -Name $oldLocalAdministratorName
$userAccount | Set-LocalUser -Password $password
Write-Host ($writeEmptyLine + "# Local Administrator password set" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Rename Local Administrator Account

Rename-LocalUser -Name $oldLocalAdministratorName -NewName $newLocalAdministratorName
Write-Host ($writeEmptyLine + "# Local Administrator renamed" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set volume label of C: to OS

$drive = Get-WmiObject win32_volume -Filter "DriveLetter = 'C:'"
$drive.Label = $driveLabel
$drive.put()
Write-Host ($writeEmptyLine + "# Volumelabel of C: set to $driveLabel" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Change CD-ROM drive letter

(Get-WmiObject Win32_cdromdrive).drive | ForEach-Object{$a = mountvol $_ /l;mountvol $_ /d;$a = $a.Trim();mountvol $cdromDriveletter $a}
Write-Host ($writeEmptyLine + "# CD-ROM driveletter set to $$cdromDriveletter" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Create the C:\Temp folder if not exists

If(!(test-path $tempFolder))
{
New-Item -ItemType Directory -Force -Path $tempFolder
}
Write-Host ($writeEmptyLine + "# $tempFolder created" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set Windows Diagnostic level (Telemetry) to Security

New-ItemProperty -Path $regkeyWindowsDiagnosticLevel -Name 'AllowTelemetry' -PropertyType 'DWord' -Value '0' -Force | Out-Null
Write-Host ($writeEmptyLine + "# Windows Diagnostic level (Telemetry) set to Security" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set Time Zone

Set-TimeZone -Name $timezone
Write-Host ($writeEmptyLine + "# Timezone set to $timezone" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set Power Management to High Performance if it is not currently the active plan

Try {
        $highPerf = powercfg -l | ForEach-Object{if($_.contains($powerManagement)) {$_.split()[3]}}
        $currPlan = $(powercfg -getactivescheme).split()[3]
        if ($currPlan -ne $highPerf) {powercfg -setactive $highPerf}
    } Catch {
        Write-Warning -Message "Unable to set power plan to $powerManagement" -foregroundcolor $foregroundColor2
    }
Write-Host ($writeEmptyLine + "# Power Management set to $powerManagement" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Disable IE security for Administrators

Set-ItemProperty -Path $adminIEsecurityregpath -Name $adminIEsecuritykey -Value 0
Stop-Process -Name Explorer
Write-Host ($writeEmptyLine + "# Done Disabling IE Enhanced Security Configuration for the Administrator" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set the Interactive Login to "Do not display the last user name"

Set-ItemProperty -Path $interActiveLogonregpath -Name $interActiveLogonkey -Value 1
Write-Host ($writeEmptyLine + "# Interactive Login set to - Do not display last user name" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Enable User Account Control (UAC) 

 Set-ItemProperty -Path $regkeyPathUAC -Name "EnableLUA" -Value 1
 Write-Host ($writeEmptyLine + "# User Access Control (UAC) enalbed" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Set Windows Server 2016 or 2019 Automatic Virtual Machine Activation (AVMA) key

If ($windowsBuildNumber -eq $windowsServer2016)
	{
		slmgr /ipk C3RCX-M6NRP-6CXC9-TW2F2-4RHYD
		Write-Host ($writeEmptyLine + "# Windows Server 2016 Standard AVMA key set" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 
	}
Else 
	{
		slmgr /ipk TNK62-RXVTB-4P47B-2D623-4GF74
		Write-Host ($writeEmptyLine + "# Windows Server 2019 Standard AVMA key set" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 
	}

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Rename server

Rename-Computer –NewName $serverName
Write-Host ($writeEmptyLine + "# Server renamed to $serverName" + $writeSeperator + $time) -foregroundcolor $foregroundColor1 $writeEmptyLine 

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Restart server to apply all changes

Write-Host ($writeEmptyLine + "# This server will restart to apply all changes" + $writeSeperator + $time) -foregroundcolor $foregroundColor2 $writeEmptyLine 
Restart-Computer -ComputerName localhost

##------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


To use the script copy and save the above as Template_Customization_Windows_Server_2016_2019.ps1 or download it from the GitHub. Afterwards run the script with Administrator privileges from the server you wish to use for your VM template.












In a previous blog post I already shared the BgInfo Automation script for Windows Server 2016 & 2019 which I also ran during the creation of this Windows Sever 2016 or 2019 VM base image.

Hope this PowerShell script comes in handy for you. If you have any questions or recommendations about it, feel free to contact me through my Twitter handle.

%d bloggers like this: