2021-01-23 15:04:42 +00:00
|
|
|
#Requires -Modules 'ADDSDeployment'
|
|
|
|
[CmdletBinding()]
|
|
|
|
Param(
|
|
|
|
# No parameters
|
|
|
|
)
|
|
|
|
|
2021-01-27 14:47:29 +00:00
|
|
|
Function New-ToastNotification {
|
|
|
|
Param(
|
2021-01-27 15:06:20 +00:00
|
|
|
[Parameter()]
|
2021-01-27 14:47:29 +00:00
|
|
|
[string]$Stream = 'OVF Properties',
|
|
|
|
[Parameter(Mandatory)]
|
|
|
|
[string]$Title,
|
|
|
|
[Parameter(Mandatory)]
|
|
|
|
[string]$Text
|
|
|
|
)
|
|
|
|
|
2021-01-27 15:06:20 +00:00
|
|
|
[void][Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
|
2021-01-27 14:47:29 +00:00
|
|
|
$Template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
|
|
|
|
|
|
|
|
$XML = [xml]$Template.GetXml()
|
|
|
|
$XML.SelectSingleNode("toast/visual/binding/text[@id='1']").InnerText = $Title
|
|
|
|
$XML.SelectSingleNode("toast/visual/binding/text[@id='2']").InnerText = $Text
|
|
|
|
|
|
|
|
$SerializedXml = New-Object Windows.Data.Xml.Dom.XmlDocument
|
|
|
|
$SerializedXml.LoadXml($XML.OuterXml)
|
|
|
|
|
|
|
|
$Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($Stream)
|
|
|
|
$Notifier.Show([Windows.UI.Notifications.ToastNotification]::new($SerializedXml))
|
|
|
|
}
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$NewEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
ErrorAction = 'SilentlyContinue'
|
|
|
|
}
|
|
|
|
New-EventLog @NewEventLogSplat
|
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Information'
|
|
|
|
EventID = 1
|
|
|
|
Message = 'OVF-Properties sequence initiated'
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
|
|
|
|
$VMwareToolsExecutable = "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"
|
|
|
|
|
|
|
|
[xml]$ovfEnv = & $VMwareToolsExecutable --cmd "info-get guestinfo.ovfEnv" | Out-String
|
|
|
|
$ovfProperties = $ovfEnv.ChildNodes.NextSibling.PropertySection.Property
|
|
|
|
|
|
|
|
$ovfPropertyValues = @{}
|
|
|
|
foreach ($ovfProperty in $ovfProperties) {
|
|
|
|
$ovfPropertyValues[$ovfProperty.key] = $ovfProperty.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
# Check for mandatory values
|
2021-01-27 11:12:44 +00:00
|
|
|
Switch ($ovfPropertyValues['deployment.type']) {
|
|
|
|
'primary' {
|
|
|
|
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.gateway', 'addsconfig.domainname', 'addsconfig.netbiosname', 'addsconfig.administratorpw', 'addsconfig.safemodepw', 'addsconfig.ntpserver'), @()
|
|
|
|
}
|
|
|
|
'secondary' {
|
|
|
|
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.dnsserver', 'guestinfo.gateway', 'addsconfig.domainname', 'addsconfig.netbiosname', 'addsconfig.administratorpw', 'addsconfig.safemodepw', 'dhcpconfig.startip', 'dhcpconfig.endip', 'dhcpconfig.subnetmask', 'dhcpconfig.gateway', 'dhcpconfig.leaseduration'), @()
|
|
|
|
}
|
|
|
|
'standalone' {
|
|
|
|
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.gateway', 'addsconfig.domainname', 'addsconfig.netbiosname', 'addsconfig.administratorpw', 'addsconfig.safemodepw', 'addsconfig.ntpserver', 'dhcpconfig.startip', 'dhcpconfig.endip', 'dhcpconfig.subnetmask', 'dhcpconfig.gateway', 'dhcpconfig.leaseduration'), @()
|
|
|
|
}
|
|
|
|
default {
|
2021-01-23 15:04:42 +00:00
|
|
|
# Mandatory values missing, cannot provision.
|
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Error'
|
|
|
|
EventID = 66
|
2021-01-27 11:12:44 +00:00
|
|
|
Message = "Unexpected or no value set for property 'deployment.type', cannot provision."
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
& schtasks.exe /Change /TN 'OVF-Properties' /DISABLE
|
|
|
|
Stop-Computer -Force
|
|
|
|
Exit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ForEach ($Property in $MandatoryProperties) {
|
|
|
|
If (!$ovfPropertyValues[$Property]) {
|
|
|
|
$MissingProperties += $Property
|
|
|
|
}
|
|
|
|
}
|
|
|
|
If ($MissingProperties.Length -gt 0) {
|
|
|
|
# Mandatory values missing, cannot provision.
|
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Error'
|
|
|
|
EventID = 66
|
|
|
|
Message = "Missing values for mandatory properties $(($MissingProperties | ForEach-Object {'{0}' -f $_}) -join ', '), cannot provision."
|
2021-01-23 15:04:42 +00:00
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
& schtasks.exe /Change /TN 'OVF-Properties' /DISABLE
|
|
|
|
Stop-Computer -Force
|
|
|
|
Exit
|
|
|
|
}
|
|
|
|
|
|
|
|
# Set hostname and description
|
|
|
|
If ($Env:ComputerName -ne $ovfPropertyValues['guestinfo.hostname']) {
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = 'Configuring hostname and description...'
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$RenameComputerSplat = @{
|
|
|
|
NewName = $ovfPropertyValues['guestinfo.hostname']
|
|
|
|
Force = $True
|
|
|
|
Confirm = $False
|
|
|
|
}
|
|
|
|
Rename-Computer @RenameComputerSplat
|
|
|
|
$SetCimInstanceSplat = @{
|
|
|
|
InputObject = (Get-CimInstance -ClassName 'Win32_OperatingSystem')
|
|
|
|
Property = @{
|
|
|
|
Description = $ovfPropertyValues['guestinfo.hostname']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Set-CimInstance @SetCimInstanceSplat
|
|
|
|
|
|
|
|
# Restart the computer to apply changes
|
|
|
|
Restart-Computer -Force
|
|
|
|
Exit
|
|
|
|
}
|
|
|
|
|
|
|
|
# Configure network interface
|
|
|
|
If ((Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration').IPAddress -NotContains $ovfPropertyValues['guestinfo.ipaddress']) {
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = 'Configuring network...'
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$NewNetIPAddressSplat = @{
|
|
|
|
InterfaceAlias = (Get-NetAdapter).Name
|
|
|
|
AddressFamily = 'IPv4'
|
|
|
|
IPAddress = $ovfPropertyValues['guestinfo.ipaddress']
|
|
|
|
PrefixLength = $ovfPropertyValues['guestinfo.prefixlength']
|
|
|
|
DefaultGateway = $ovfPropertyValues['guestinfo.gateway']
|
|
|
|
}
|
|
|
|
New-NetIPAddress @NewNetIPAddressSplat
|
|
|
|
|
|
|
|
# Wait for network connection to become available
|
|
|
|
$Timestamp, $TimeoutMinutes = (Get-Date), 5
|
|
|
|
Do {
|
|
|
|
If ($Timestamp.AddMinutes($TimeoutMinutes) -lt (Get-Date)) {
|
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Warning'
|
|
|
|
EventID = 13
|
|
|
|
Message = "Timeout after $($TimeoutMinutes) minutes waiting for network connection to become available."
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
Break
|
|
|
|
}
|
|
|
|
|
|
|
|
Start-Sleep -Milliseconds 250
|
|
|
|
|
|
|
|
$GetNetIPAddressSplat = @{
|
|
|
|
InterfaceAlias = (Get-NetAdapter).Name
|
|
|
|
AddressFamily = 'IPv4'
|
|
|
|
ErrorAction = 'SilentlyContinue'
|
|
|
|
}
|
|
|
|
} Until ((Get-NetIPAddress @GetNetIPAddressSplat).AddressState -eq 'Preferred')
|
|
|
|
|
|
|
|
$OldErrorActionPreference, $ErrorActionPreference = $ErrorActionPreference, 'SilentlyContinue'
|
|
|
|
$TestNetConnectionSplat = @{
|
|
|
|
ComputerName = ([IPAddress]$ovfPropertyValues['guestinfo.dnsserver']).IPAddressToString
|
|
|
|
InformationLevel = 'Quiet'
|
|
|
|
}
|
|
|
|
$SetDnsClientServerAddressSplat = @{
|
|
|
|
InterfaceAlias = (Get-NetAdapter).Name
|
|
|
|
ServerAddresses = If (
|
|
|
|
[boolean]($ovfPropertyValues['guestinfo.dnsserver'] -as [IPaddress]) -and (Test-NetConnection @TestNetConnectionSplat)) {
|
|
|
|
($ovfPropertyValues['guestinfo.dnsserver'])
|
|
|
|
} else {
|
|
|
|
('127.0.0.1')
|
|
|
|
}
|
|
|
|
Validate = $False
|
|
|
|
}
|
|
|
|
Set-DnsClientServerAddress @SetDnsClientServerAddressSplat
|
|
|
|
$ErrorActionPreference, $OldErrorActionPreference = $OldErrorActionPreference, $NULL
|
|
|
|
}
|
|
|
|
|
|
|
|
# Promote to Domain Controller
|
|
|
|
If ((4,5) -NotContains (Get-WmiObject -Class 'Win32_ComputerSystem').DomainRole) {
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = 'Configuring local administrator password...'
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
# Change password of built-in Administrator
|
|
|
|
$BuiltinAdministrator = (Get-LocalUser | Where-Object {$_.SID -match '-500'})
|
|
|
|
$ConvertToSecureStringSplat = @{
|
|
|
|
String = $ovfPropertyValues['addsconfig.administratorpw']
|
|
|
|
AsPlainText = $True
|
|
|
|
Force = $True
|
|
|
|
}
|
|
|
|
$SetLocalUserSplat = @{
|
|
|
|
InputObject = $BuiltinAdministrator
|
|
|
|
Password = ConvertTo-SecureString @ConvertToSecureStringSplat
|
|
|
|
PasswordNeverExpires = $True
|
|
|
|
AccountNeverExpires = $True
|
|
|
|
### This setting is not allowed on the last administrator
|
|
|
|
#UserMayChangePassword = $False
|
|
|
|
Confirm = $False
|
|
|
|
}
|
|
|
|
Set-LocalUser @SetLocalUserSplat
|
|
|
|
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = 'Promoting to Domain Controller...'
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$ResolveDNSNameSplat = @{
|
|
|
|
Name = "_ldap._tcp.dc._msdcs.$($ovfPropertyValues['addsconfig.domainname'])"
|
|
|
|
ErrorAction = 'SilentlyContinue'
|
|
|
|
}
|
|
|
|
$DNSRecord = Resolve-DnsName @ResolveDNSNameSplat
|
|
|
|
If ([boolean]$DNSRecord.PrimaryServer -eq $False) {
|
|
|
|
# No Primary Domain Controller found, installing as primary
|
|
|
|
$InstallADDSForestSplat = @{
|
|
|
|
DomainName = $ovfPropertyValues['addsconfig.domainname']
|
|
|
|
DomainNetbiosName = $ovfPropertyValues['addsconfig.netbiosname']
|
|
|
|
SafeModeAdministratorPassword = ConvertTo-SecureString $ovfPropertyValues['addsconfig.safemodepw'] -AsPlainText -Force
|
|
|
|
InstallDns = $True
|
|
|
|
DomainMode = 'WinThreshold'
|
|
|
|
ForestMode = 'WinThreshold'
|
|
|
|
Confirm = $False
|
|
|
|
Force = $True
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
}
|
|
|
|
Try {
|
|
|
|
Install-ADDSForest @InstallADDSForestSplat
|
|
|
|
|
|
|
|
# Previous cmdlet performs a reboot on completion; so these are commented out
|
|
|
|
# Restart-Computer -Force
|
|
|
|
# Exit
|
|
|
|
}
|
|
|
|
Catch {
|
|
|
|
& schtasks.exe /Change /TN 'OVF-Properties' /DISABLE
|
|
|
|
Stop-Computer -Force
|
|
|
|
Exit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Else {
|
|
|
|
# Primary Domain Controller is present, installing as secondary
|
|
|
|
$InstallADDSDomainControllerSplat = @{
|
|
|
|
DomainName = $ovfPropertyValues['addsconfig.domainname']
|
|
|
|
Credential = New-Object System.Management.Automation.PSCredential("$($ovfPropertyValues['addsconfig.netbiosname'])\$($BuiltinAdministrator.Name)", (ConvertTo-SecureString @ConvertToSecureStringSplat))
|
|
|
|
SafeModeAdministratorPassword = ConvertTo-SecureString $ovfPropertyValues['addsconfig.safemodepw'] -AsPlainText -Force
|
|
|
|
InstallDns = $True
|
|
|
|
Confirm = $False
|
|
|
|
Force = $True
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
}
|
|
|
|
Try {
|
|
|
|
Install-ADDSDomainController @InstallADDSDomainControllerSplat
|
|
|
|
|
|
|
|
# Previous cmdlet performs a reboot on completion; so these are commented out
|
|
|
|
# Restart-Computer -Force
|
|
|
|
# Exit
|
|
|
|
}
|
|
|
|
Catch {
|
|
|
|
& schtasks.exe /Change /TN 'OVF-Properties' /DISABLE
|
|
|
|
Stop-Computer -Force
|
|
|
|
Exit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Wait for Active Directory to become available
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = 'Waiting for Active Directory services...'
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$Timestamp, $TimeoutMinutes = (Get-Date), 15
|
|
|
|
Do {
|
|
|
|
If ($Timestamp.AddMinutes($TimeoutMinutes) -lt (Get-Date)) {
|
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Warning'
|
|
|
|
EventID = 13
|
|
|
|
Message = "Timeout after $($TimeoutMinutes) minutes waiting for Active Directory to become available."
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
Break
|
|
|
|
}
|
|
|
|
|
|
|
|
Start-Sleep -Seconds 30
|
|
|
|
|
|
|
|
$GetADComputerSplat = @{
|
|
|
|
Identity = $Env:ComputerName
|
|
|
|
ErrorAction = 'SilentlyContinue'
|
|
|
|
}
|
|
|
|
Get-ADComputer @GetADComputerSplat | Out-Null
|
|
|
|
} Until ($?)
|
|
|
|
|
|
|
|
# Iterate through and invoke all payload scripts
|
|
|
|
#! TODO: add registry values to determine which scripts have already been invoked (in case of intermediate reboots)
|
|
|
|
$GetItemSplat = @{
|
|
|
|
Path = "$($PSScriptRoot)\Scripts\*.ps1"
|
|
|
|
}
|
|
|
|
Get-Item @GetItemSplat | ForEach-Object {
|
|
|
|
Try {
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = "Running script: '$($_.FullName)'"
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Information'
|
|
|
|
EventID = 4
|
|
|
|
Message = "Running script: '$($_.FullName)'"
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
& $_.FullName -Parameter $ovfPropertyValues
|
|
|
|
}
|
|
|
|
Catch {
|
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Error'
|
|
|
|
EventID = 66
|
|
|
|
Message = $_.Exception.Message
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 12:56:13 +00:00
|
|
|
$NewToastSplat = @{
|
|
|
|
Title = 'OVF Properties'
|
|
|
|
Text = "Sequence finished; ready for use!"
|
|
|
|
}
|
|
|
|
New-ToastNotification @NewToastSplat
|
|
|
|
|
2021-01-23 15:04:42 +00:00
|
|
|
$WriteEventLogSplat = @{
|
|
|
|
LogName = 'Application'
|
|
|
|
Source = 'OVF-Properties'
|
|
|
|
EntryType = 'Information'
|
|
|
|
EventID = 42
|
|
|
|
Message = 'OVF-Properties sequence applied and finished'
|
|
|
|
}
|
|
|
|
Write-EventLog @WriteEventLogSplat
|
|
|
|
& schtasks.exe /Change /TN 'OVF-Properties' /DISABLE
|