Pages

lørdag 15. juni 2013

Windows Server 2012 Core: Install SQL Server 2012 w/SP1

Assuming you have installed the Operating System and are ready with the command line... How to join computer to the domain and setup SQL server:

You should edit all text in red to match your environment!

Start PowerShell by typing:
powershell

# Set IP address:
$NetIPIF = Get-NetIPInterface -AddressFamily IPv4 -ConnectionState Connected | ? InterfaceAlias -ne 'Loopback Pseudo-Interface 1'
If ($NetIPIF) {
  Set-NetIPInterface -InterfaceAlias $NetIPIF.InterfaceAlias -Dhcp Disabled
  $NetIPIF | Set-NetIPInterface -Dhcp Disabled
  New-NetIPAddress -InterfaceAlias $NetIPIF.InterfaceAlias -IPAddress 192.168.160.30 -DefaultGateway 192.168.160.5 -AddressFamily IPv4 -PrefixLength 24
  Set-DnsClientServerAddress -InterfaceAlias $NetIPIF.InterfaceAlias -ServerAddresses ('192.168.160.10')
}

# Join Computer to the Domain
$Cred = Get-Credential
If ($Cred) {Add-Computer -DomainName 'lab.domain' -Credential $Cred -OUPath "OU=SQL,OU=Servers,OU=HQ,DC=lab,DC=domain" -Force}
# After verifying success, restart computer
Restart-Computer

After restart, remember to log in with your domain administrator account.

# Sync time with the PDC emulator
powershell
# If using Hyper-V, Disable aspects of time synchronization from Hyper-V with:
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider' -Name 'Enabled' -Value 0

# PS! Never disable Hyper-V Time Synchronization, it's important when the virtual machine boots and resumes saved states. Hyper-V Integration Services synchronizes the time of virtual machines with the physical host because virtual machines tend to experience time drift over time.

& C:\Windows\System32\w32tm.exe /config /syncfromflags:DOMHIER /update
Restart-Service w32time
& C:\Windows\System32\w32tm.exe /resync /rediscover /force

# If you have added extra disks for user databases tempdb and log, you must configure them
# Change Drive Letter on DVD Drive to X
gwmi Win32_Volume -Filter "DriveType = '5'" | swmi -Arguments @{DriveLetter = "X:"}

# Make all offline disks online:
Get-Disk | ? IsOffline –eq $true | Set-Disk –IsOffline $false -IsReadOnly $false

# Initialize all disks with RAW partition
Get-Disk | Where-Object PartitionStyle –eq 'RAW' | Initialize-Disk –PartitionStyle MBR

# List all disks without any partitions
Get-Disk | Where-Object NumberOfPartitions -eq 0

# Create partition on the disks, change DiskNumber and other paramters to match your setup
New-Partition –DiskNumber 2 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'mssqlserver' -FileSystem NTFS -Confirm:$false
New-Partition –DiskNumber 3 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'tempdbpart1' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false
New-Partition –DiskNumber 4 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'tempdbpart2' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false
New-Partition –DiskNumber 5 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'tempdbpart3' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false
New-Partition –DiskNumber 6 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'tempdbpart4' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false
New-Partition –DiskNumber 7 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'log' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false
New-Partition –DiskNumber 8 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'opsmgrdb' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false
New-Partition –DiskNumber 9 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'opsmgrdw' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false

# Set filesystem label on C disk
Set-Volume -NewFileSystemLabel 'os' -DriveLetter C

# Verify volumes
Get-Volume

# Disable Indexing on all drives
gwmi Win32_Volume -Filter "IndexingEnabled=$true" | swmi -Arguments @{IndexingEnabled=$false}

# Open firewall ports
# Open up firewall for SQL server DB Engine access
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 1433 -DisplayName "SQL Server (TCP 1433)"
# Used when a default instance is used, we use that so enable this
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 1434 -DisplayName "SQL Admin Connection (TCP 1434)"
# Used By Service Broker, we do not install that so we do not enable it
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 4022 -DisplayName "SQL Service Broker (TCP 4022)" -Enabled False
# Used when a named instance is used, we used default instance so we do not enable it
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol UDP -LocalPort 1434 -DisplayName "SQL Browser (UDP 1434)" -Enabled False
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 2382 -DisplayName "SQL Browser (TCP 2382)" -Enabled False
# Used By Analysis Services when a named instance is used, we do not install that so we do not enable it
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 2385 -DisplayName "Analysis Services SQL Browser (TCP 2385)" -Enabled False
# Used By Analysis Services when default instance is used, we do not install that so we do not enable it
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 2383 -DisplayName "Analysis Services (TCP 2383)" -Enabled False
# Used By Reporting Services, we do not install that so we do not enable it
New-NetFirewallRule -Group "SQL Server" -Direction Inbound -Action Allow -RemoteAddress LocalSubnet -Profile Domain -Protocol TCP -LocalPort 80 -DisplayName "Reporting Services Web (TCP 80)" -Enabled False
# Allow WMI and SMB etc
Set-NetFirewallRule -Name FPS-NB_Name-In-UDP -Enabled True
Set-NetFirewallRule -Name FPS-NB_Datagram-In-UDP -Enabled True
Set-NetFirewallRule -Name FPS-NB_Session-In-TCP -Enabled True
Set-NetFirewallRule -Name FPS-SMB-In-TCP -Enabled True
Set-NetFirewallRule -DisplayGroup "Windows Management Instrumentation (WMI)" -Enabled True

# Install Required Windows Features - Insert Windows Server 2012 installation media in the DVD drive
dism /online /enable-feature /featurename:netfx3 /all /source:X:\sources\sxs
Install-WindowsFeature -Name Net-Framework-Core -Source:wim:X:\Sources\install.wim:1

To setup SQL server you should create some service accounts, groups and users. To do this head over to your domain controller and start PowerShell. Example:

# Create User and Service Accounts for the SQL server
New-ADUser svc-SQL1-DBEngine -AccountPassword (Read-Host -AsSecureString -Prompt "Enter Password") -ChangePasswordAtLogon $False -PasswordNeverExpires $True -Path 'OU=ServiceAccounts,OU=HQ,DC=lab,DC=domain' -Description 'SQL Server service account' -Enabled $True
New-ADUser svc-SQL1-Agent -AccountPassword (Read-Host -AsSecureString -Prompt "Enter Password") -ChangePasswordAtLogon $False -PasswordNeverExpires $True -Path 'OU=ServiceAccounts,OU=HQ,DC=lab,DC=domain' -Description 'Executes jobs, monitors SQL Server, fires alerts, and enables automation of some administrative tasks' -Enabled $True
New-ADUser adm-SQL1-sysadmin -AccountPassword (Read-Host -AsSecureString -Prompt "Enter Password") -ChangePasswordAtLogon $False -PasswordNeverExpires $True -Path 'OU=Users,OU=Administrators,OU=HQ,DC=lab,DC=domain' -Description 'SQL Server sysadmin account' -Enabled $True

# Create a group for defining who will have the DB role sysadmin right on SQL1 server
New-ADGroup -Name acl-SQL1-sysadmin -GroupCategory Security -GroupScope Global -Path 'OU=ServerAccess,OU=Servers,OU=HQ,DC=lab,DC=domain' -Description 'Access group for server SQL1, Database role sysadmin right'

# Create a group for defining local administrator access to the SQL1 server
New-ADGroup -Name acl-SQL1-localadmin -GroupCategory Security -GroupScope Global -Path 'OU=ServerAccess,OU=Servers,OU=HQ,DC=lab,DC=domain' -Description 'Access group for server SQL1, Local Administrators'

# Create a group for defining remote desktop access to the SQL1 server
New-ADGroup -Name acl-SQL1-remoteuser -GroupCategory Security -GroupScope Global -Path 'OU=ServerAccess,OU=Servers,OU=HQ,DC=lab,DC=domain' -Description 'Access group for server SQL1, Remote Desktop Users'

# Add user adm-SQL1-sysadmin and Administrator to the group acl-SQL1-sysadmin
Add-ADGroupMember -Identity acl-SQL1-sysadmin -Members adm-SQL1-sysadmin, Administrator

# Add user adm-SQL1-sysadmin to the group acl-SQL1-localadmin
Add-ADGroupMember -Identity acl-SQL1-localadmin -Members adm-SQL1-sysadmin

# Add user adm-SQL1-sysadmin to the group acl-SQL1-remoteuser
Add-ADGroupMember -Identity acl-SQL1-remoteuser -Members adm-SQL1-sysadmin

You should trust the service account for delegation if you use linked servers:
http://msdn.microsoft.com/en-us/library/ms189580(v=sql.105).aspx
# Trust the svc-SQL1-DBEngine account for delegation
Set-ADUser -Identity svc-SQL1-DBEngine -TrustedForDelegation $true

Go back to the SQL server and PowerShell and continue preparing for SQL server installation:
# Add domain group acl-SQL1-localadmin to the local group Administrators
([ADSI]"WinNT://./Administrators,group").Add("WinNT://lab.domain/acl-SQL1-localadmin")
# Add domain group acl-SQL1-remoteuser to the local group Remote Desktop Users
([ADSI]"WinNT://./Remote Desktop Users,group").Add("WinNT://lab.domain/acl-SQL1-remoteuser")

To avoid error 1032 messages in the Application log in Windows Server 2012 we should grant permission to dbengine service account to LogFiles\Sum folder. (http://support.microsoft.com/kb/2811566)
# Give modify permission on C:\Windows\System32\LogFiles\Sum to user svc-SQL1-DBEngine.
& icacls 'C:\Windows\System32\LogFiles\Sum' /grant 'LAB\svc-SQL1-DBEngine:W' /T /Q

# Register SPN for the DBEngine account
& setspn -A MSSQLSvc/SQL1:1433 LAB\svc-SQL1-DBEngine
& setspn -A MSSQLSvc/SQL1.lab.domain:1433 LAB\svc-SQL1-DBEngine
# Verify that SPN records was created
& setspn -L LAB\svc-SQL1-DBEngine

Now we should be ready to install SQL server. PS! You must have internet access on the server. For detailed information on installing SQL server look at:
http://msdn.microsoft.com/en-us/library/hh231669.aspx
http://msdn.microsoft.com/en-us/library/ms144259.aspx
# Install SQL Server with DBEngine and fulltext
& X:\Setup.exe /qs /ACTION=Install /FEATURES=SQLEngine,FULLTEXT /INSTANCENAME=MSSQLSERVER /INSTALLSQLDATADIR='E:\MSSQLSERVER\' /INSTANCEDIR='E:\MSSQLSERVER' /SQLTEMPDBDIR='F:\MSSQLSERVER\MSSQL\TempDB\Data' /SQLTEMPDBLOGDIR='F:\MSSQLSERVER\MSSQL\TempDB\Log' /SQLUSERDBLOGDIR='J:\MSSQLSERVER\MSSQL\Log' /SQLCOLLATION='SQL_Latin1_General_CP1_CI_AS' /SQMREPORTING=0 /SQLSVCACCOUNT='LAB\svc-SQL1-DBEngine' /SQLSVCPASSWORD='UserPassword1' /SQLSYSADMINACCOUNTS='LAB\acl-SQL1-sysadmin' /SQLSVCSTARTUPTYPE=Automatic /TCPENABLED=1 /AGTSVCACCOUNT='LAB\svc-SQL1-Agent' /AGTSVCPASSWORD='UserPassword1' /AGTSVCSTARTUPTYPE=Automatic /IACCEPTSQLSERVERLICENSETERMS

# After installation you should verify that the SQL Server instance was installed by looking at the Microsoft SQL Server
DIR E:\MSSQLSERVER

# And also check that the services are running
Get-Service | ? Name -Like '*SQL*'

# Configure tempdb with size and more files, set remote access and sql min max memory size
http://msdn.microsoft.com/library/ms175527.aspx
# Create folders for tempdb files
If (-Not (Test-Path 'G:\MSSQLSERVER\MSSQL\TempDB\Data\' -PathType Container)) { New-Item -Path 'G:\MSSQLSERVER\MSSQL\TempDB\Data\' -ItemType 'Directory'}
If (-Not (Test-Path 'H:\MSSQLSERVER\MSSQL\TempDB\Data\' -PathType Container)) { New-Item -Path 'H:\MSSQLSERVER\MSSQL\TempDB\Data\' -ItemType 'Directory'}
If (-Not (Test-Path 'I:\MSSQLSERVER\MSSQL\TempDB\Data\' -PathType Container)) { New-Item -Path 'I:\MSSQLSERVER\MSSQL\TempDB\Data\' -ItemType 'Directory'}

# View current tempdb files
Invoke-Sqlcmd -Query "SELECT physical_name AS FileName, size*1.0/128 AS FileSizeinMB, CASE max_size WHEN 0 THEN 'Autogrowth is off.' WHEN -1 THEN 'Autogrowth is on.' ELSE 'Log file will grow to a maximum size of 2 TB.' END, growth AS 'GrowthValue', 'GrowthIncrement' = CASE WHEN growth = 0 THEN 'Size is fixed and will not grow.' WHEN growth > 0 AND is_percent_growth = 0 THEN 'Growth value is in 8-KB pages.' ELSE 'Growth value is a percentage.' END FROM tempdb.sys.database_files;"

# Verify space before increasing tempdb size
Get-Volume

# If increasing disksize in hyper-v/vmware, resize partition
$size = (Get-PartitionSupportedSize -DriveLetter F)
Resize-Partition -DriveLetter F -Size $size.SizeMax

# Alter tempdb database
Invoke-Sqlcmd -Query "ALTER DATABASE tempdb MODIFY FILE (NAME='tempdev', SIZE=2GB, FILEGROWTH=10%)"
Invoke-Sqlcmd -Query "ALTER DATABASE tempdb MODIFY FILE (NAME='templog', SIZE=2GB, FILEGROWTH=10%)"
Invoke-Sqlcmd -Query "ALTER DATABASE tempdb ADD FILE (NAME='tempdev2', FILENAME='G:\MSSQLSERVER\MSSQL\TempDB\Data\tempdb2.ndf', SIZE=2GB, FILEGROWTH=10%)"
Invoke-Sqlcmd -Query "ALTER DATABASE tempdb ADD FILE (NAME='tempdev3', FILENAME='H:\MSSQLSERVER\MSSQL\TempDB\Data\tempdb3.ndf', SIZE=2GB, FILEGROWTH=10%)"
Invoke-Sqlcmd -Query "ALTER DATABASE tempdb ADD FILE (NAME='tempdev4', FILENAME='I:\MSSQLSERVER\MSSQL\TempDB\Data\tempdb4.ndf', SIZE=2GB, FILEGROWTH=10%)"

# Verify tempdb files
Invoke-Sqlcmd -Query "SELECT physical_name AS FileName, size*1.0/128 AS FileSizeinMB, CASE max_size WHEN 0 THEN 'Autogrowth is off.' WHEN -1 THEN 'Autogrowth is on.' ELSE 'Log file will grow to a maximum size of 2 TB.' END, growth AS 'GrowthValue', 'GrowthIncrement' = CASE WHEN growth = 0 THEN 'Size is fixed and will not grow.' WHEN growth > 0 AND is_percent_growth = 0 THEN 'Growth value is in 8-KB pages.' ELSE 'Growth value is a percentage.' END FROM tempdb.sys.database_files;"

# Configure SQL Remote Access - in my case this was not needed - it was already configured
Invoke-Sqlcmd -Query "EXEC sys.sp_configure 'remote access', '1'; RECONFIGURE"
Invoke-Sqlcmd -Query "RECONFIGURE"

# Set min and max memory to 4096 MB
Invoke-Sqlcmd -Query "exec sp_configure 'show advanced options', 1; RECONFIGURE"
Invoke-Sqlcmd -Query "exec sp_configure 'min server memory', 4096"
Invoke-Sqlcmd -Query "exec sp_configure 'max server memory', 4096"
Invoke-Sqlcmd -Query "exec sp_configure 'show advanced options', 0; RECONFIGURE"

# To log off you can simply type:
LOGOFF

fredag 14. juni 2013

Windows Server 2012 Core: Install first Domain Controller

Assuming you have installed the Operating System and are ready with the command line... this is example on how to setup Active Directory with DNS and DHCP on Windows Server 2012 Core... All text in red should be modified to your environment.

Start PowerShell by typing:
powershell

# Set IP address:
$NetIPIF = Get-NetIPInterface -AddressFamily IPv4 -ConnectionState Connected | ? InterfaceAlias -ne 'Loopback Pseudo-Interface 1'
If ($NetIPIF) {
  $NetIPIF | Set-NetIPInterface -Dhcp Disabled
  New-NetIPAddress -InterfaceAlias $NetIPIF.InterfaceAlias -IPAddress 192.168.160.10 -DefaultGateway 192.168.160.5 -AddressFamily IPv4 -PrefixLength 24
  Set-DnsClientServerAddress -InterfaceAlias $NetIPIF.InterfaceAlias -ServerAddresses ('192.168.160.10','127.0.0.1')
}

# Install Windows Features
Install-WindowsFeature -name AD-Domain-Services,DNS,DHCP

# If Windows Features have been removed, insert installation media and specify source
Install-WindowsFeature -name AD-Domain-Services,DNS,DHCP -Source:wim:E:\Sources\install.wim:1

# When all features have been successfully installed, create ADDS Forest
Install-ADDSForest -CreateDNSDelegation:$false -DatabasePath 'C:\Windows\NTDS' -DomainMode Win2012 -DomainName 'lab.domain' -DomainNetBIOSName 'LAB' -ForestMode Win2012 -InstallDNS:$true -LogPath "C:\Windows\NTDS" -NoRebootOnCompletion:$false -SYSVOLPath "C:\Windows\SYSVOL" -Force:$true -SafeModeAdministratorPassword (Read-Host -AsSecureString -Prompt "Enter Password")

Read all warnings (can mostly be ignored) and errors (if any). Computer will reboot after creating ADDS Forest.

After reboot, change user to domain administrator (e.g. LAB\Administrator) and start PowerShell:
powershell

# Now you can create some structure and configure your AD. Example:
$DomainFQDN = 'lab.domain'
$DomainNETBIOS = 'LAB'
$ADPath = 'DC=lab,DC=domain'
# Create some OU structure in AD
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name HQ -Path "$ADPath" -Description 'Only place other OUs in here'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Users -Path "OU=HQ,$ADPath" -Description 'Users in HQ'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Resources -Path "OU=HQ,$ADPath" -Description 'Users that are Resources in HQ, e.g meeting rooms, equipment'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Contacts -Path "OU=HQ,$ADPath" -Description 'Contacts in HQ'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name DistributionLists -Path "OU=HQ,$ADPath" -Description 'Groups that are Distribution Lists in HQ'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Computers -Path "OU=HQ,$ADPath" -Description 'Only place other OUs in here, new Computers may be placed here but they should be moved as soon as possible to a sub OU'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Desktops -Path "OU=Computers,OU=HQ,$ADPath" -Description 'Desktop Computers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Laptops -Path "OU=Computers,OU=HQ,$ADPath" -Description 'Laptop Computers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name ThinClients -Path "OU=Computers,OU=HQ,$ADPath" -Description 'Thin Client Computers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name ComputerAccess -Path "OU=Computers,OU=HQ,$ADPath" -Description 'Groups that define access to Computers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Servers -Path "OU=HQ,$ADPath" -Description 'Only place other OUs in here'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name SQL -Path "OU=Servers,OU=HQ,$ADPath" -Description 'SQL Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Exchange -Path "OU=Servers,OU=HQ,$ADPath" -Description 'Exchange Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Print -Path "OU=Servers,OU=HQ,$ADPath" -Description 'Print Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name File -Path "OU=Servers,OU=HQ,$ADPath" -Description 'File Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name RD -Path "OU=Servers,OU=HQ,$ADPath" -Description 'Remote Desktop Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name SC -Path "OU=Servers,OU=HQ,$ADPath" -Description 'System Center Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name ServerAccess -Path "OU=Servers,OU=HQ,$ADPath" -Description 'Groups that define access to Servers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Groups -Path "OU=HQ,$ADPath" -Description 'Only place other OUs in here'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Roles -Path "OU=Groups,OU=HQ,$ADPath" -Description 'Groups that define a Role, e.g Staff'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Resources -Path "OU=Groups,OU=HQ,$ADPath" -Description 'Groups that define a Resource, e.g Printers'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Administrators -Path "OU=HQ,$ADPath" -Description 'Only place other OUs in here'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Users -Path "OU=Administrators,OU=HQ,$ADPath" -Description 'Administrators in HQ'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name Groups -Path "OU=Administrators,OU=HQ,$ADPath" -Description 'Groups that define Administrative Roles, e.g. DesktopSupport'
New-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $True -Name ServiceAccounts -Path "OU=HQ,$ADPath" -Description 'Accounts used by services and applications'

# Enable AD Recycle BIN:
Enable-ADOptionalFeature -Identity 'Recycle Bin Feature' -Scope ForestOrConfigurationSet -Target $DomainFQDN -Confirm:$False

# Redirect Users to be placed in correct Organizational Unit for users when created
Set-ADObject -Identity "$ADPath" -Add @{wellKnownObjects="B:32:A9D1CA15768811D1ADED00C04FD8D5CD:OU=Users,OU=HQ,$ADPath"} -Remove @{wellKnownObjects="B:32:A9D1CA15768811D1ADED00C04FD8D5CD:CN=Users,$ADPath"}

# Redirect Computers to be placed in correct Organizational Unit for computers when created
Set-ADObject -Identity "$ADPath" -Add @{wellKnownObjects="B:32:AA312825768811D1ADED00C04FD8D5CD:OU=Computers,OU=HQ,$ADPath"} -Remove @{wellKnownObjects="B:32:AA312825768811D1ADED00C04FD8D5CD:CN=Computers,$ADPath"}

# Make all AD OUs protected from accidental deletion
Get-ADOrganizationalUnit -filter * -Properties ProtectedFromAccidentalDeletion | where {$_.ProtectedFromAccidentalDeletion -eq $false} | Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $true

# Set Password never expires on Administrator
Set-ADUser -Identity Administrator -PasswordNeverExpires $True

Configure DNS Server:
# DNS configuration: Add a reverse zone for internal network IPv4
Add-DnsServerPrimaryZone 160.168.192.in-addr.arpa -DynamicUpdate Secure -ReplicationScope Domain -DirectoryPartitionName DomainDnsZones.$DomainFQDN
# DNS configuration: Turn on scavenging on all Zones
Set-DnsServerScavenging -ApplyOnAllZones -ScavengingState $True -ScavengingInterval 7.00:00:00
# DNS configuration: Forward external requests to OpenDNS servers (to avoid bothering root servers)
Set-DnsServerForwarder 208.67.222.222, 208.67.220.220

Configure time sync:
# If using Hyper-V, Disable aspects of time synchronization from Hyper-V with:
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider' -Name 'Enabled' -Value 0

# PS! Never disable Hyper-V Time Synchronization, it's important when the virtual machine boots and resumes saved states. Hyper-V Integration Services synchronizes the time of virtual machines with the physical host because virtual machines tend to experience time drift over time.

# Sync this PDC emulator with a remote time source
& C:\Windows\System32\w32tm.exe /config /manualpeerlist:"0.no.pool.ntp.org,0×1 1.no.pool.ntp.org,0×1 3.no.pool.ntp.org,0×1 4.no.pool.ntp.org,0×1" /syncfromflags:manual /reliable:YES /update
Restart-Service w32time
& C:\Windows\System32\w32tm.exe /resync /rediscover

Configure DHCP for IPv4:
# DHCP configuration: Add DHCP securitygroups to AD
& C:\Windows\System32\netsh.exe dhcp add securitygroups
# DHCP configuration: Set the path to DHCP Audit Log and limit its size to 100 MB
Set-DhcpServerAuditLog -ComputerName DC1.$DomainFQDN -Enable $True -Path C:\Windows\system32\dhcp -MaxMBFileSize 100
# DHCP configuration: Set the path to DHCP database and backup, change intervalls from 1 hour to 2 hours
Set-DhcpServerDatabase -ComputerName DC1.$DomainFQDN -FileName C:\Windows\system32\dhcp\dhcp.mdb -BackupPath C:\Windows\system32\dhcp\backup -BackupInterval 120 -CleanupInterval 120
# DHCP configuration: Restart the service to make it take effect
Restart-Service DHCPServer
# DHCP configuration: Bind Dhcpv4 to interface
Set-DhcpServerv4Binding -BindingState $true -InterfaceAlias Ethernet
# DHCP configuration: Authorize this server as DHCP server in this AD
Add-DhcpServerInDC -DnsName "DC1.$DomainFQDN" -IPAddress 192.168.160.10
# DHCP configuration: Add a Dhcpv4 Scope for the clients
Add-DhcpServerv4Scope -Name "IPv4 CORP Network" -StartRange 192.168.160.1 -EndRange 192.168.160.254 -SubnetMask 255.255.255.0
# DHCP configuration: Set Exclusions for the Dhcpv4 scope
Add-Dhcpserverv4ExclusionRange -ScopeId 192.168.160.0 -StartRange 192.168.160.1 -EndRange 192.168.160.99
Add-Dhcpserverv4ExclusionRange -ScopeId 192.168.160.0 -StartRange 192.168.160.200 -EndRange 192.168.160.254
# DHCP configuration: Set Dhcpv4 Scope Option for Gateway (to satisfy BPA for DHCP)
Set-DhcpServerv4OptionValue -OptionId 3 -Value 192.168.160.5 -ScopeId 192.168.160.0
# DHCP configuration: Set Dhcpv4 Option for DNS
Set-DhcpServerv4OptionValue -OptionId 6 -Value 192.168.160.10
# DHCP configuration: Set Dhcpv4 Option for DNS Server prefix
Set-DhcpServerv4OptionValue -OptionId 15 -Value $DomainFQDN
# DHCP configuration: Create DHCP application account for DNS dynamic update registration credentials
New-ADUser app-DC1-DNSupd -AccountPassword (Read-Host -AsSecureString -Prompt "Enter Password") -ChangePasswordAtLogon $False -PasswordNeverExpires $True -Path "OU=ServiceAccounts,OU=HQ,$ADPath" -Description "DNS dynamic update registration credentials" -Enabled $True
# DHCP configuration: Add this account to DnsUpdateProxy group so it will be allowed to register records in DNS
Add-ADGroupMember DnsUpdateProxy app-DC1-DNSupd
# DHCP configuration: Register this account with the DHCP server
& C:\Windows\System32\netsh.exe dhcp server \\DC1.$DomainFQDN set dnscredentials app-DC1-DNSupd $DomainFQDN (Read-Host -AsSecureString -Prompt 'Enter Password for app-DC1-DNSupd')
# DHCP configuration: Because the DHCP Post-Deployment Configuration wizard will complain that it has not been run, we must update the registry
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ServerManager\Roles\12" -Name "ConfigurationState" -Value 2
# DHCP configuration: The groups for DHCP has been placed in the Users group under HQ, we want only users there so we move to Users under domain root
Move-ADObject -Identity "CN=DHCP Administrators,OU=Users,OU=HQ,$ADPath" -TargetPath "CN=Users,$ADPath"
Move-ADObject -Identity "CN=DHCP Users,OU=Users,OU=HQ,$ADPath" -TargetPath "CN=Users,$ADPath"

# Configure extra disks, if any
# Change Drive Letter on DVD Drive to X
gwmi Win32_Volume -Filter "DriveType = '5'" | swmi -Arguments @{DriveLetter = 'X:'}

# Make all offline disks online:
Get-Disk | ? IsOffline –eq $true | Set-Disk –IsOffline $false -IsReadOnly $false

# Initialize all disks with RAW partition
Get-Disk | Where-Object PartitionStyle –eq 'RAW' | Initialize-Disk –PartitionStyle MBR

# List all disks without any partitions
Get-Disk | Where-Object NumberOfPartitions -eq 0

# Create partition on the disks, change DiskNumber and other paramters, examples
New-Partition –DiskNumber 2 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'AppDisk' -FileSystem NTFS -Confirm:$false
New-Partition –DiskNumber 3 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'sqldb' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false

# Set filesystem label on C disk
Set-Volume -NewFileSystemLabel 'os' -DriveLetter C

# Verify volumes
Get-Volume

# To log off you can simply type:
LOGOFF

Windows Server 2012: VM template tuning using PowerShell

This is a list of commands that I use when setting up a Windows Server 2012 Core template (can also be used for GUI template). I run all of these commands in PowerShell, either as a script or manually. In server core you start PowerShell by typing powershell.

I hope others will find this usefull. Please give me feedback if you have suggestions or improvements...

# Turn off 8dot3name
fsutil.exe 8dot3name set C: 1
fsutil.exe 8dot3name set 1

# Set Power plan to High performance
powercfg.exe /SETACTIVE 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c

# Disable the hibernate feature
powercfg.exe /HIBERNATE off

# Set Password Never Expires on the local Administrator account
gwmi Win32_UserAccount -Filter "name = 'Administrator'" | swmi -Arguments @{PasswordExpires = 0}

# Change Drive Letter on DVD Drive to X
gwmi Win32_Volume -Filter "DriveType = '5'" | swmi -Arguments @{DriveLetter = 'X:'}

# Initialize RAW disks
Get-Disk | Where-Object PartitionStyle –eq 'RAW' | Initialize-Disk –PartitionStyle MBR

# Format disk 0 for pagefile
# Verify correct disknumber before use
New-Partition –DiskNumber 0 -UseMaximumSize -AssignDriveLetter | Format-Volume -NewFileSystemLabel 'Pagefile' -FileSystem NTFS -AllocationUnitSize 65536 -Confirm:$false

# Disable Indexing on all drives
gwmi Win32_Volume -Filter "IndexingEnabled=$true" | swmi -Arguments @{IndexingEnabled=$false}

# Set location for dedicated dump file at system failure
# Verify correct path before use
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl' -Name 'DedicatedDumpFile' -Value 'D:\MEMORY.DMP'
gwmi Win32_OSRecoveryConfiguration -EnableAllPrivileges | swmi -Arguments @{DebugFilePath='D:\MEMORY.DMP'}

# Use small memory dump at system failure
# 0=None, 1=Complete, 2=Kernel, 3=Small, 7=Automatic
gwmi Win32_OSRecoveryConfiguration -EnableAllPrivileges | swmi -Arguments @{DebugInfoType=3}

# Change setting to: Do not automatically restart at system failure
gwmi Win32_OSRecoveryConfiguration -EnableAllPrivileges | swmi -Arguments @{AutoReboot=$false}

# Turn off automatically manage paging file size for all drives
gwmi Win32_ComputerSystem -EnableAllPrivileges | swmi -Arguments @{AutomaticManagedPagefile=$false}

# Change paging file to the pagefile drive
# Verify correct location/name and size
# Get current paging file on drive C
$CurrentPageFile = gwmi -Query "select * from Win32_PageFileSetting where name='c:\\pagefile.sys'" -EnableAllPrivileges
# Delete current paging file on drive C
If($CurrentPageFile){$CurrentPageFile.Delete()}
# Create paging file on paging drive
swmi Win32_PageFileSetting -Arguments @{Name='D:\pagefile.sys'; InitialSize=2048; MaximumSize=2048}

# Allow all MMC snap-ins to connect from remote
Enable-NetFirewallRule -DisplayGroup 'Windows Remote Management'

# Enable Remote Desktop for Administration mode to accept connections
cscript C:\Windows\System32\Scregedit.wsf /ar 0

# Allow previous versions (XP/Win2003) of windows to connect
cscript C:\Windows\System32\Scregedit.wsf /cs 0

# Allow server response to Ping
netsh firewall set icmpsetting 8

# Verify time, date and timezone
control timedate.cpl

# Disable NetBIOS over TCP/IP - 2=disable, 1=enable, 0=DHCP default
# And WINS LMHOSTS lookup
# Verify correct ServiceName, for vmware use -like 'vmxnet*'
$nics = gwmi Win32_NetworkAdapterConfiguration | Where-Object {$_.ServiceName -eq 'netvsc'}
foreach ($nic in $nics) {
  If ($nic.TcpipNetbiosOptions -ne 2) {
    $nic.SetTcpipNetbios(2)
    # Turn off LMHOSTS lookup
    $nic.EnableWINS($false,$false)
  }
}

# Turn off DHCP on Ethernet Interface
# Verify InterfaceAlias
Set-NetIPInterface -InterfaceAlias Ethernet -Dhcp Disabled

# Set static IP on Ethernet Interface
# Verify IP address and Default Gateway
New-NetIPAddress -InterfaceAlias Ethernet -IPAddress 192.168.160.9 -DefaultGateway 192.168.160.5 -AddressFamily IPv4 -PrefixLength 24

# Set DNS server for Ethernet interface to Google and OpenDNS
Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses ('8.8.8.8', '208.67.222.222')

# Install Windows Updates, reboot and repeat until no updates exist - alternative is to use sconfig
cscript c:\Windows\System32\en-US\WUA_SearchDownloadInstall.vbs

# Install WMI hotfix for WS2012 KB2790831
# Verify correct location for downloading the hotfix - using dropbox in this example (link is modified)
$WebClient = New-Object Net.WebClient
$WebClient.DownloadFile("https://dl.dropboxusercontent.com/u/123456789/Windows8-RT-KB2790831-x64.msu","C:\Users\Administrator\Downloads\Windows8-RT-KB2790831-x64.msu")
& C:\Users\Administrator\Downloads\Windows8-RT-KB2790831-x64.msu
del C:\Users\Administrator\Downloads\Windows8-RT-KB2790831-x64.msu

# For Hyper-V VM update integration services (insert Integration Services Setup Disk in Hyper-V Manager)
D:\support\amd64\setup.exe

# Allow blank password on Administrator account (change to blank password - only for the template - using Ctrl-Alt-Del screen)
$Signature='"$CHICAGO$"'
Write-Output "[System Access]`r`nPasswordComplexity = 0`r`n[Version]`r`nsignature=$Signature`r`nRevision=1" | Out-File -FilePath "C:\Users\Administrator\local.cfg" -Encoding unicode
secedit /configure /db C:\Windows\security\local.sdb /cfg C:\Users\Administrator\local.cfg /areas SECURITYPOLICY
del C:\Users\Administrator\local.cfg

# Remove all windows features not used:
# To install a removed feature later, insert windows media and use (example for Web-Server):
# Install-WindowsFeature Web-Server -Source:wim:X:\Sources\install.wim:1
Get-WindowsFeature | Where-Object {$_.InstallState -eq 'Available'} | Uninstall-WindowsFeature -Remove
# Or you can remove only selected features:
Uninstall-WindowsFeature -Remove -Name WINS,Migration,RSAT,MSMQ,Server-Media-Foundation,GPMC,BitLocker,Hyper-V,Subsystem-UNIX-Apps,qWave

# Start Disk Optimization and Defrag all disks
Get-Volume | Where-Object {$_.DriveType -eq 'Fixed'} | Optimize-Volume -Defrag

# Zero out free space with SDelete (download it and copy to C:\Windows\System32)
sdelete -z c: -accepteula

# Remove static IP before creating a template from this VM
$Adapter = Get-NetAdapter -Name Ethernet
$Adapter | Set-NetIPInterface -Dhcp Enabled
Remove-NetRoute -InterfaceAlias $Adapter.Name -Confirm:$false
ipconfig /release

For SCVMM:
As a last step shutdown VM and use compact disk before clone and create template.
When I create a template I often use a custom unattend.xml - example for Core template:
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="generalize">
    <component language="neutral" name="Microsoft-Windows-PnpSysprep" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <persistalldeviceinstalls>true</persistalldeviceinstalls>
    </component>
  </settings>
  <settings pass="oobeSystem">
    <component language="neutral" name="Microsoft-Windows-Shell-Setup" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <oobe>
        <hideeulapage>true</hideeulapage>
        <protectyourpc>3</protectyourpc>
        <networklocation>Work</networklocation>
        <hidewirelesssetupinoobe>true</hidewirelesssetupinoobe>
        <hidelocalaccountscreen>true</hidelocalaccountscreen>
        <hideonlineaccountscreens>true</hideonlineaccountscreens>
        <hideoemregistrationscreen>true</hideoemregistrationscreen>
      </oobe>
      <registeredorganization>Contoso</registeredorganization>
      <registeredowner>Admin User</registeredowner>
      <timezone>W. Europe Standard Time</timezone>
    </component>
    <component language="neutral" name="Microsoft-Windows-International-Core" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <inputlocale>0409:00000414</inputlocale>
      <systemlocale>en-US</systemlocale>
      <uilanguage>en-US</uilanguage>
      <userlocale>en-US</userlocale>
      <uilanguagefallback>en-US</uilanguagefallback>
    </component>
  </settings>
  <settings pass="specialize">
    <component language="neutral" name="Microsoft-Windows-ErrorReportingCore" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <disablewer>1</disablewer>
    </component>
    <component language="neutral" name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <fdenytsconnections>false</fdenytsconnections>
    </component>
    <component language="neutral" name="Networking-MPSSVC-Svc" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <firewallgroups>
        <firewallgroup wcm:action="add" wcm:keyvalue="RemoteDesktop">
          <active>true</active>
          <group>Remote Desktop</group>
          <profile>all</profile>
        </firewallgroup>
      </firewallgroups>
    </component>
    <component language="neutral" name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <userauthentication>0</userauthentication>
    </component>
    <component language="neutral" name="Microsoft-Windows-Shell-Setup" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <timezone>W. Europe Standard Time</timezone>
      <registeredorganization>Contoso</registeredorganization>
      <registeredowner>Admin User</registeredowner>
    </component>
    <component language="neutral" name="Microsoft-Windows-Deployment" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <runsynchronous>
        <runsynchronouscommand wcm:action="add">
          <order>1</order>
          <path>cmd /c reg add "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /v ExecutionPolicy /t REG_SZ /d RemoteSigned /f</path>
          <description>Configure Powershell security settings</description>
        </runsynchronouscommand>
      </runsynchronous>
    </component>
    <component language="neutral" name="Microsoft-Windows-SQMApi" processorarchitecture="amd64" publickeytoken="31bf3856ad364e35" versionscope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <ceipenabled>0</ceipenabled>
    </component>
  </settings>
</unattend>

And another example for a GUI:
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="generalize">
    <component name="Microsoft-Windows-PnpSysprep" 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">
      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
    </component>
  </settings>
  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <VisualEffects>
        <FontSmoothing>ClearType</FontSmoothing>
        <SystemDefaultBackgroundColor>24</SystemDefaultBackgroundColor>
      </VisualEffects>
      <OOBE>
        <HideEULAPage>true</HideEULAPage>
        <ProtectYourPC>3</ProtectYourPC>
        <NetworkLocation>Work</NetworkLocation>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <HideLocalAccountScreen>true</HideLocalAccountScreen>
        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
      </OOBE>
      <WindowsFeatures>
        <ShowMediaCenter>false</ShowMediaCenter>
        <ShowWindowsMediaPlayer>false</ShowWindowsMediaPlayer>
      </WindowsFeatures>
      <RegisteredOrganization>Contoso</RegisteredOrganization>
      <RegisteredOwner>Admin User</RegisteredOwner>
      <TimeZone>W. Europe Standard Time</TimeZone>
    </component>
    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <InputLocale>0409:00000414</InputLocale>
      <SystemLocale>en-US</SystemLocale>
      <UILanguage>en-US</UILanguage>
      <UserLocale>en-US</UserLocale>
      <UILanguageFallback>en-US</UILanguageFallback>
    </component>
  </settings>
  <settings pass="specialize">
    <component name="Microsoft-Windows-ErrorReportingCore" 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">
      <DisableWER>1</DisableWER>
    </component>
    <component name="Microsoft-Windows-IE-ESC" 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">
      <IEHardenAdmin>false</IEHardenAdmin>
      <IEHardenUser>false</IEHardenUser>
    </component>
    <component name="Microsoft-Windows-IE-InternetExplorer" 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">
      <DisableOOBAccelerators>true</DisableOOBAccelerators>
      <DisableFirstRunWizard>true</DisableFirstRunWizard>
      <DisableAccelerators>true</DisableAccelerators>
      <Home_Page>about:blank</Home_Page>
      <DisableDevTools>true</DisableDevTools>
      <DisableDataExecutionPrevention>false</DisableDataExecutionPrevention>
      <BlockPopups>no</BlockPopups>
      <SuggestedSitesEnabled>false</SuggestedSitesEnabled>
    </component>
    <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" 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">
      <fDenyTSConnections>false</fDenyTSConnections>
    </component>
    <component name="Networking-MPSSVC-Svc" 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">
      <FirewallGroups>
        <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
          <Active>true</Active>
          <Group>Remote Desktop</Group>
          <Profile>all</Profile>
        </FirewallGroup>
      </FirewallGroups>
    </component>
    <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" 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">
      <UserAuthentication>0</UserAuthentication>
    </component>
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <TimeZone>W. Europe Standard Time</TimeZone>
      <RegisteredOrganization>Contoso</RegisteredOrganization>
      <RegisteredOwner>Admin User</RegisteredOwner>
      <Themes>
        <WindowColor>Color 1</WindowColor>
      </Themes>
    </component>
    <component name="Microsoft-Windows-Deployment" 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">
      <RunSynchronous>
        <RunSynchronousCommand wcm:action="add">
          <Order>1</Order>
          <Path>cmd /c reg add &quot;HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell&quot; /v ExecutionPolicy /t REG_SZ /d RemoteSigned /f</Path>
          <Description>Configure Powershell security settings</Description>
        </RunSynchronousCommand>
      </RunSynchronous>
    </component>
    <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <CEIPEnabled>0</CEIPEnabled>
    </component>
    <component name="Microsoft-Windows-ServerManager-SvrMgrNc" 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">
      <DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon>
    </component>
  </settings>
</unattend>

tirsdag 4. juni 2013

OpsMgr: Use Command Channel to reset monitor health

Lets assume we have created a two state script monitor (see my example) that generate critical alerts. The monitor turns from healthy to critical. Now it will not generate another alert unless it becomes healthy again and then turns critical a second time. So, what if you want the script to generate alert each time it runs?

Of course you could do this in many ways and one way would be to use a rule instead of a monitor. The problem with this is that Operations Manager Console do not include a wizard to do that. We can create Alert Generating Rules, but none of those will run a script. We can create a Timed Commands Rule, but it will not generate an alert. So if we want to do this with a rule, we must use a tool like Visual Studio Authoring Extensions or Visio MP Designer (or XML editor for those geeky enough to try).

However, if we keep the two state script monitor, we can send the alert to a Notification Command Channel and use PowerShell to reset the monitor health. This is how:

Before you create a command channel that will interact with Operations Manager, you need a user with proper permissions for this purpose. The user that will be used is the one assigned to the Run As profile called Notification Account. To set this open Operations Manager Console and then Administration > Run As Configuration > Profiles. Open Notification Account and add the domain user to Run As Accounts. The account you add here must also be added to Administration > Security > User Roles > Operations Manager Operators.

Create a new Command ChannelOperations Console > Administration > Notifications > Channels > New channel > Command...:

Give it a name, e.g: Reset monitor health Command, then configure settings as follows:



Full path of the command line:
c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe
Command line parameters:
-noexit D:\Scripts\resetmonitorhealth.ps1 '$Data/Context/DataItem/AlertId$'
Startup folder for the command line:
c:\windows\system32\windowspowershell\v1.0\

Create the script resetmonitorhealth.ps1 with the following content and save it to D:\Scripts (or the same path used in the Command line parameters) on each Management Server that is member of the Notifications Resource Pool:
Param([string]$AlertID)

# Import PowerShell Modules
Import-Module OperationsManager

# Get the alert
$SCOMAlert = Get-SCOMAlert -Id ($AlertID) -ErrorAction SilentlyContinue

If ($SCOMAlert) {
  # Get the Monitoring Object (e.g. Windows Computer Object)
  $MonObj = Get-SCOMMonitoringobject -Id $SCOMAlert.MonitoringObjectId

  # If the monitor exist and is unhealthy
  If (($MonObj) -and ($MonObj.HealthState -ne "Success")) {
    # Get the monitor for the alert
    $Mon = Get-SCOMMonitor -Id $SCOMAlert.MonitoringRuleId
    If ($Mon) {
      $MonObj.ResetMonitoringState($Mon)
      # If your monitor do not auto-close the alert, uncomment this if you would like to do so
      #$SCOMAlert | Set-SCOMAlert -Comment "Command Channel: Reset Monitor Health" -ResolutionState 255
      #Write-EventLog -LogName Application -Source "PowerShellScript" -EntryType Information -EventID 101 -Message "Done resetting Monitor $($Mon.DisplayName)"
    }
    #Else {
    #  Write-EventLog -LogName Application -Source "PowerShellScript" -EntryType Warning -EventID 201 -Message "Unable to find monitor for Alert with ID $AlertID"
    #}
  }
  #ElseIf (!$MonObj) {
  #  Write-EventLog -LogName Application -Source "PowerShellScript" -EntryType Warning -EventID 202 -Message "Unable to find monitoring object for Alert with ID $AlertID"
  #}

}
# If you need to debug you can write to the event log like this:
# First, on each management server, open PowerShell and run:
#   New-EventLog -LogName Application -Source "PowerShellScript"
# This will create a new source that you can filter on in Event Viewer
# Then, in this script uncomment all the lines with Write-EventLog in this script, including the Else sections
#Else {
#  Write-EventLog -LogName Application -Source "PowerShellScript" -EntryType Warning -EventID 203 -Message "Unable to find Alert with ID $AlertID"
#}

Then create a Notification Subscription, specify Criteria with the options Created by specific rules or monitors, select the montor you have created, and also With a specific resolution state of New (0):

To send the alert to the Command Channel you must also create a Subscriber:
Subscriber Name: Reset monitor health Subscriber
Addresses: Create a new Address: Reset monitor health Subscriber Address, Choose the Channel Type Command and select the Command Channel you created earlier.

Also add any other subscribers you would like to send notification to, e.g. e-mail or sms.

In the Channels section add the Command Channel you created earlier.

When done with the Notification Subsribtion Wizard the Summary should look something like this:
Name
DC1 Backup Folder State

Description
Reset the monitor state for DC1 Backup Folder State.

Criteria
Notify on all alerts where
created by DC1 Backup Folder State rules or monitors (e.g., sources)
and with New (0) resolution state

Subscribers
Reset monitor health Subscriber

Channels
Reset monitor health Command


You may run in to a problem with Maximum Number of Asynchronous Responses (5) has Been Reached showing up in the event log. Read more in Jim Moldenhauer's Blog.

To resolve this try to modify registry by executing the following commands on each management server in the Notifications Resource Pool:
REG ADD "HKLM\Software\Microsoft\Microsoft Operations Manager\3.0\Modules\Global\Command Executer" /v AsyncProcessLimit /t REG_DWORD /d 20 /f
net stop healthservice
net start healthservice
Set the decimal value between 1 and 100. We start with 20. Move up from there if needed. Also we restart the Health Service to allow the new settings to take effect.