Archived
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4f62b465d | ||
|
|
060ac37dc0 | ||
|
|
b75694479a | ||
|
|
b304a17ee1 | ||
|
|
d155db26e7 | ||
|
|
b2f607e2b5 | ||
|
|
465088b455 | ||
|
|
3db5e7dbf7 | ||
|
|
f84e065e15 | ||
|
|
00557e50f9 | ||
|
|
e455419986 | ||
|
|
08b63f30d6 |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 249 KiB |
+45
-6
@@ -2,6 +2,14 @@ kind: pipeline
|
||||
type: kubernetes
|
||||
name: 'Packer Build'
|
||||
|
||||
volumes:
|
||||
- name: output
|
||||
claim:
|
||||
name: flexvolsmb-drone-output
|
||||
- name: scratch
|
||||
claim:
|
||||
name: flexvolsmb-drone-scratch
|
||||
|
||||
steps:
|
||||
- name: Debugging information
|
||||
image: bv11-cr01.bessems.eu/library/packer-extended
|
||||
@@ -10,10 +18,15 @@ steps:
|
||||
- packer --version
|
||||
- pwsh --version
|
||||
- ovftool --version
|
||||
- name: Active Directory Domain Services
|
||||
- name: Windows 10
|
||||
image: bv11-cr01.bessems.eu/library/packer-extended
|
||||
pull: always
|
||||
commands:
|
||||
- sed -i -e "s/<<img-productkey>>/$${PRODUCTKEY}/" packer/preseed/Windows10/Autounattend.xml
|
||||
- |
|
||||
sed -i -e "s/<<img-password>>/$${WINRM_PASSWORD}/g" \
|
||||
packer/preseed/Windows10/Autounattend.xml \
|
||||
packer/preseed/Windows10/Sysprep_Unattend.xml
|
||||
- |
|
||||
yamllint -d "{extends: relaxed, rules: {line-length: disable}}" scripts
|
||||
- |
|
||||
@@ -22,13 +35,19 @@ steps:
|
||||
- |
|
||||
packer validate \
|
||||
-var vm_name=$DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \
|
||||
-var vm_guestos=win10 \
|
||||
-var repo_username=$${REPO_USERNAME} \
|
||||
-var repo_password=$${REPO_PASSWORD} \
|
||||
-var vsphere_password=$${VSPHERE_PASSWORD} \
|
||||
-var winrm_password=$${WINRM_PASSWORD} \
|
||||
./packer
|
||||
- |
|
||||
packer build \
|
||||
-on-error=cleanup -timestamp-ui \
|
||||
-on-error=cleanup \
|
||||
-var vm_name=$DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \
|
||||
-var vm_guestos=win10 \
|
||||
-var repo_username=$${REPO_USERNAME} \
|
||||
-var repo_password=$${REPO_PASSWORD} \
|
||||
-var vsphere_password=$${VSPHERE_PASSWORD} \
|
||||
-var winrm_password=$${WINRM_PASSWORD} \
|
||||
./packer
|
||||
@@ -37,12 +56,32 @@ steps:
|
||||
from_secret: vsphere_password
|
||||
WINRM_PASSWORD:
|
||||
from_secret: winrm_password
|
||||
REPO_USERNAME:
|
||||
from_secret: repo_username
|
||||
REPO_PASSWORD:
|
||||
from_secret: repo_password
|
||||
PRODUCTKEY:
|
||||
from_secret: prodkey_win10
|
||||
# PACKER_LOG: 1
|
||||
volumes:
|
||||
- name: output
|
||||
path: /output
|
||||
|
||||
- name: Remove temporary resources
|
||||
image: bv11-cr01.bessems.eu/library/packer-extended
|
||||
commands:
|
||||
- |
|
||||
pwsh -file scripts/Remove-Resources.ps1 \
|
||||
-VMName $DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \
|
||||
-VSphereFQDN 'bv11-vc.bessems.lan' \
|
||||
-VSphereUsername 'administrator@vsphere.local' \
|
||||
-VSpherePassword $${VSPHERE_PASSWORD}
|
||||
environment:
|
||||
VSPHERE_PASSWORD:
|
||||
from_secret: vsphere_password
|
||||
volumes:
|
||||
- name: output
|
||||
claim:
|
||||
name: flexvolsmb-drone-output
|
||||
- name: scratch
|
||||
path: /scratch
|
||||
when:
|
||||
status:
|
||||
- success
|
||||
- failure
|
||||
|
||||
@@ -1,108 +1 @@
|
||||
# Packer.Images [](https://ci.spamasaurus.com/djpbessems/Packer.Images)
|
||||
|
||||
This OVA appliance allows deploying an Active Directory Domain Controller fully automated:
|
||||
|
||||
The included `.ovf` file has the following XML contents (simplified for clarity) to facilitate the different `DeploymentOption`s:
|
||||
```xml
|
||||
<Envelope [...]>
|
||||
[...]
|
||||
<DeploymentOptionSection>
|
||||
<Info>Deployment Type</Info>
|
||||
<Configuration ovf:id="primary">
|
||||
<Label>Primary (redundant deployment)</Label>
|
||||
<Description>Initial Domain Controller with 'PDC Emulator'-role</Description>
|
||||
</Configuration>
|
||||
<Configuration ovf:id="secondary">
|
||||
<Label>Secondary (redundant deployment)</Label>
|
||||
<Description>Additional Domain Controller</Description>
|
||||
</Configuration>
|
||||
<Configuration ovf:id="standalone">
|
||||
<Label>Stand-alone (non-redundant deployment)</Label>
|
||||
<Description>Single Domain Controller</Description>
|
||||
</Configuration>
|
||||
</DeploymentOptionSection>
|
||||
<VirtualSystem ovf:id="[...]">
|
||||
[...]
|
||||
<ProductSection>
|
||||
[...]
|
||||
<Category>1) Operating System</Category>
|
||||
<Property ovf:configuration="primary secondary standalone" ovf:key="guestinfo.hostname" [...]>
|
||||
<Label>Hostname*</Label>
|
||||
</Property>
|
||||
[...]
|
||||
<Category>2) Networking</Category>
|
||||
<Property ovf:configuration="secondary" ovf:key="guestinfo.dnsserver" [...]>
|
||||
<Label>DNS server*</Label>
|
||||
</Property>
|
||||
[...]
|
||||
<Category>3) Active Directory Domain Services</Category>
|
||||
<Property ovf:configuration="primary standalone" ovf:key="addsconfig.ntpserver" [...]>
|
||||
<Label>NTP Server*</Label>
|
||||
[...]
|
||||
</Property>
|
||||
</ProductSection>
|
||||
</VirtualSystem>
|
||||
</Envelope>
|
||||
```
|
||||
|
||||
When **provisioning** the appliance through the vCenter 'Deploy OVF template...' wizard, or through vApp-compatible *Infrastructure as code* tooling (e.g. HashiCorp Terraform), it is possible to provide all relevant configuration through vApp properties.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><em>vSphere 'Deploy OVF template...' wizard</em></td> <td> <a href="https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs/resources/virtual_machine#deploying-vm-from-an-ovfova-template">HashiCorp Terraform vSphere provider</a> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src=".assets/vAppConfigurations-ADDS-example.png" alt="vApp properties" width="400" /><br/><img src=".assets/vAppProperties-ADDS-example.png" alt="vApp properties" width="400" /></td>
|
||||
<td>
|
||||
|
||||
```hcl
|
||||
vapp {
|
||||
properties = {
|
||||
# "deployment.type = "primary"
|
||||
|
||||
"guestinfo.hostname" = "DC01"
|
||||
"guestinfo.ipaddress" = "10.0.0.21"
|
||||
"guestinfo.prefixlength" = "24"
|
||||
# "guestinfo.dnsserver" = "0.0.0.0"
|
||||
"guestinfo.gateway" = "10.0.0.1"
|
||||
|
||||
"addsconfig.domainname" = "contoso.com"
|
||||
"addsconfig.netbiosname" = "CONTOSO"
|
||||
"addsconfig.administratorpw" = var.adds_adminpassword
|
||||
"addsconfig.safemodepw" = var.adds_safemodepassword
|
||||
# "addsconfig.ntpserver" = "0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org"
|
||||
|
||||
"vault.api" = "https://vault.example.org/v1"
|
||||
"vault.token" = var.vault_token
|
||||
"vault.pwpolicy" = "complex"
|
||||
"vault.secret" = "contoso-project42"
|
||||
|
||||
# "dhcpconfig.startip" = "10.0.0.50"
|
||||
# "dhcpconfig.endip" = "10.0.0.250"
|
||||
# "dhcpconfig.subnetmask" = "255.255.255.0"
|
||||
# "dhcpconfig.gateway" = "10.0.0.1"
|
||||
# "dhcpconfig.leaseduration" = "01:00:00.00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
On first boot, the appliance will start **configuring** itself without any further user-input, by performing the following steps:
|
||||
- Change hostname
|
||||
- Configure network
|
||||
- Set password for local administrator
|
||||
- Promote to Domain Controller
|
||||
- Iterate through all payload scripts:
|
||||
- Create Active Directory Organizational Units
|
||||
- Create Active Directory security groups
|
||||
- Create Active Directory user accounts
|
||||
- Set up Delegation of Control
|
||||
- Configure Active Directory Group Policy Objects with Windows Firewall settings
|
||||
- Configure DHCP (scopes, options and Failover relationship)
|
||||
- Create DNS records
|
||||
- Define Active Directory Group Policy WMI Filters
|
||||
- Define and link Active Directory Group Policy Objects and Preferences
|
||||
- Set Active Directory Default domain Password policy
|
||||
# Packer.Images [](https://ci.spamasaurus.com/djpbessems/Packer.Images)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
script = <<-EOH
|
||||
$nic = get-netadapter
|
||||
|
||||
Get-NetAdapterBinding –InterfaceAlias $nic.name –ComponentID ms_tcpip6
|
||||
EOH
|
||||
|
||||
control "ipv6" do
|
||||
title 'Disabled network protocol IPv6'
|
||||
desc '
|
||||
This test assures that IPv6 is disabled
|
||||
'
|
||||
|
||||
describe powershell(script) do
|
||||
its('stdout') { should match 'False' }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
script = <<-EOH
|
||||
# Initialize variable to empty array
|
||||
$NonCompliantServices = @()
|
||||
|
||||
# Specify relevant services
|
||||
$Services = @(
|
||||
"wuauserv",
|
||||
"W3SVC",
|
||||
"XboxGipSvc",
|
||||
"XblGameSave"
|
||||
)
|
||||
|
||||
# Enumerate all services
|
||||
$NonCompliantServices += Get-Service $Services -ErrorAction 'SilentlyContinue' | Where-Object {$_.StartType -ne 'Disabled'}
|
||||
|
||||
# Output; 'True' or list of noncompliant services
|
||||
Write-Output ($True, $NonCompliantServices)[!($NonCompliantServices.Count -eq 0)]
|
||||
EOH
|
||||
|
||||
control "disabled_services" do
|
||||
title 'Disabled services'
|
||||
desc '
|
||||
This test assures that all unneeded services are set to "disabled".
|
||||
'
|
||||
|
||||
describe powershell(script) do
|
||||
its('stdout') { should match 'True' }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
script = <<-EOH
|
||||
# Initialize variable to empty array
|
||||
$LogicalDisks = @()
|
||||
|
||||
# Enumerate all logicaldisks
|
||||
# DriveType:
|
||||
# Unknown (0)
|
||||
# No Root Directory (1)
|
||||
# Removable Disk (2)
|
||||
# Local Disk (3)
|
||||
# Network Drive (4)
|
||||
# Compact Disc (5)
|
||||
# RAM Disk (6)
|
||||
$LogicalDisks += Get-WmiObject -Class 'win32_logicaldisk' -Filter 'DriveType=3'
|
||||
|
||||
# Filter/Quantify
|
||||
($LogicalDisks.Count -eq 1) -and (($LogicalDisks | Where-Object {$_.DeviceID -ne 'C:'}).Count -eq 0)
|
||||
EOH
|
||||
|
||||
control "single_disk" do
|
||||
title 'Single Disk'
|
||||
desc '
|
||||
This test assures that only a single disk (C:) is available
|
||||
'
|
||||
|
||||
describe powershell(script) do
|
||||
its('stdout') { should match 'True' }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
control "software_installed-7zip" do
|
||||
title 'Included Default Applications: 7-Zip'
|
||||
desc '
|
||||
This test assures that the software application "7-Zip" is installed.
|
||||
'
|
||||
|
||||
describe chocolatey_package('7zip.install') do
|
||||
it { should be_installed }
|
||||
end
|
||||
end
|
||||
|
||||
# control "software_installed-dotnetfx" do
|
||||
# title 'Included Default Applications: .NET'
|
||||
# desc '
|
||||
# This test assures that the software application ".NET" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('dotnetfx') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
|
||||
# control "software_installed-foxitreader" do
|
||||
# title 'Included Default Applications: Foxit Reader'
|
||||
# desc '
|
||||
# This test assures that the software application "Foxit Reader" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('foxitreader') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
|
||||
# control "software_installed-notepadplusplus" do
|
||||
# title 'Included Default Applications: Notepad++'
|
||||
# desc '
|
||||
# This test assures that the software application "Notepad++" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('notepadplusplus') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
|
||||
# control "software_installed-putty" do
|
||||
# title 'Included Default Applications: Putty'
|
||||
# desc '
|
||||
# This test assures that the software application "PuTTy" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('putty') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Windows 10 IoT Enterprise
|
||||
title: Windows 10 IoT Enterprise InSpec Tests
|
||||
summary: Unit test for Windows 10 IoT Enterprise
|
||||
version: 1.0.0
|
||||
maintainer: https://code.spamasaurus.com/djpbessems
|
||||
copyright: https://code.spamasaurus.com/djpbessems
|
||||
license: Proprietary
|
||||
supports:
|
||||
- platform-family: windows
|
||||
@@ -1,90 +0,0 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
windows-update = {
|
||||
version = ">= 0.12.0"
|
||||
source = "github.com/rgl/windows-update"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source "vsphere-clone" "adds" {
|
||||
vcenter_server = var.vcenter_server
|
||||
username = var.vsphere_username
|
||||
password = var.vsphere_password
|
||||
insecure_connection = "true"
|
||||
|
||||
vm_name = "adds-${var.vm_name}"
|
||||
datacenter = var.vsphere_datacenter
|
||||
host = var.vsphere_host
|
||||
folder = var.vsphere_folder
|
||||
datastore = var.vsphere_datastore
|
||||
|
||||
template = "Windows-Server-2019-LTSC"
|
||||
|
||||
boot_order = "disk,cdrom"
|
||||
boot_command = [""]
|
||||
boot_wait = "2m30s"
|
||||
|
||||
communicator = "winrm"
|
||||
winrm_password = var.winrm_password
|
||||
winrm_timeout = "10m"
|
||||
winrm_username = "administrator"
|
||||
|
||||
RAM = 8192
|
||||
CPUs = 2
|
||||
|
||||
floppy_files = [
|
||||
"packer/preseed/ADDS/Sysprep_Unattend.xml"
|
||||
]
|
||||
|
||||
shutdown_command = "C:\\Windows\\System32\\Sysprep\\sysprep.exe /generalize /oobe /unattend:A:\\Sysprep_Unattend.xml"
|
||||
shutdown_timeout = "1h"
|
||||
|
||||
export {
|
||||
images = false
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.vsphere-clone.adds"]
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"New-Item -Path 'C:\\Payload\\Scripts' -ItemType 'Directory' -Force:$True -Confirm:$False"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "C:\\Payload\\"
|
||||
source = "scripts/ADDS/payload/"
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
scripts = [
|
||||
"scripts/ADDS/Install-Prerequisites.ps1",
|
||||
"scripts/ADDS/Register-ScheduledTask.ps1"
|
||||
]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = [
|
||||
"pwsh -command \"& scripts/Update-OvfConfiguration.ps1 \\",
|
||||
" -OVFFile './output-adds/adds-${var.vm_name}.ovf' \\",
|
||||
" -Parameter @{'appliance.name'='ADDS';'appliance.version'='${var.vm_name}'}\"",
|
||||
"pwsh -file scripts/Update-Manifest.ps1 \\",
|
||||
" -ManifestFileName './output-adds/adds-${var.vm_name}.mf'",
|
||||
"ovftool --acceptAllEulas --allowExtraConfig --overwrite \\",
|
||||
" './output-adds/adds-${var.vm_name}.ovf' \\",
|
||||
" /output/ADDS-appliance.ova"
|
||||
]
|
||||
}
|
||||
post-processor "shell-local" {
|
||||
inline = [
|
||||
"pwsh -file scripts/Remove-Resources.ps1 \\",
|
||||
" -VMName 'adds-${var.vm_name}' \\",
|
||||
" -VSphereFQDN '${var.vcenter_server}' \\",
|
||||
" -VSphereUsername '${var.vsphere_username}' \\",
|
||||
" -VSpherePassword '${var.vsphere_password}'"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<unattend xmlns="urn:schemas-microsoft-com:unattend">
|
||||
<servicing/>
|
||||
<settings pass="windowsPE">
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<DiskConfiguration>
|
||||
<Disk wcm:action="add">
|
||||
<CreatePartitions>
|
||||
<CreatePartition wcm:action="add">
|
||||
<Order>1</Order>
|
||||
<Type>Primary</Type>
|
||||
<Extend>true</Extend>
|
||||
</CreatePartition>
|
||||
</CreatePartitions>
|
||||
<ModifyPartitions>
|
||||
<ModifyPartition wcm:action="add">
|
||||
<Extend>false</Extend>
|
||||
<Format>NTFS</Format>
|
||||
<Letter>C</Letter>
|
||||
<Order>1</Order>
|
||||
<PartitionID>1</PartitionID>
|
||||
<Label>Windows 10</Label>
|
||||
</ModifyPartition>
|
||||
</ModifyPartitions>
|
||||
<DiskID>0</DiskID>
|
||||
<WillWipeDisk>true</WillWipeDisk>
|
||||
</Disk>
|
||||
<WillShowUI>OnError</WillShowUI>
|
||||
</DiskConfiguration>
|
||||
<UserData>
|
||||
<AcceptEula>true</AcceptEula>
|
||||
<!-- <FullName>Spamasaurus Rex</FullName>
|
||||
<Organization>Spamasaurus Rex</Organization> -->
|
||||
<ProductKey>
|
||||
<Key><<img-productkey>></Key>
|
||||
<WillShowUI>Never</WillShowUI>
|
||||
</ProductKey>
|
||||
</UserData>
|
||||
<ImageInstall>
|
||||
<OSImage>
|
||||
<InstallTo>
|
||||
<DiskID>0</DiskID>
|
||||
<PartitionID>1</PartitionID>
|
||||
</InstallTo>
|
||||
<WillShowUI>OnError</WillShowUI>
|
||||
<InstallToAvailablePartition>false</InstallToAvailablePartition>
|
||||
<InstallFrom>
|
||||
<MetaData wcm:action="add">
|
||||
<Key>/IMAGE/INDEX</Key>
|
||||
<Value>3</Value>
|
||||
</MetaData>
|
||||
</InstallFrom>
|
||||
</OSImage>
|
||||
</ImageInstall>
|
||||
</component>
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<SetupUILanguage>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
</SetupUILanguage>
|
||||
<InputLocale>en-US</InputLocale>
|
||||
<SystemLocale>en-US</SystemLocale>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
<UILanguageFallback>en-US</UILanguageFallback>
|
||||
<UserLocale>en-US</UserLocale>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="offlineServicing">
|
||||
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<EnableLUA>false</EnableLUA>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="oobeSystem">
|
||||
<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>en-US</InputLocale>
|
||||
<SystemLocale>en-US</SystemLocale>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
<UserLocale>en-US</UserLocale>
|
||||
</component>
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<UserAccounts>
|
||||
<AdministratorPassword>
|
||||
<Value><<img-password>></Value>
|
||||
<PlainText>true</PlainText>
|
||||
</AdministratorPassword>
|
||||
</UserAccounts>
|
||||
<OOBE>
|
||||
<HideEULAPage>true</HideEULAPage>
|
||||
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
|
||||
<NetworkLocation>Home</NetworkLocation>
|
||||
<ProtectYourPC>1</ProtectYourPC>
|
||||
</OOBE>
|
||||
<AutoLogon>
|
||||
<Password>
|
||||
<Value><<img-password>></Value>
|
||||
<PlainText>true</PlainText>
|
||||
</Password>
|
||||
<Username>administrator</Username>
|
||||
<Enabled>true</Enabled>
|
||||
</AutoLogon>
|
||||
<FirstLogonCommands>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
|
||||
<Description>Set execution policy 64bit</Description>
|
||||
<Order>1</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
|
||||
<Description>Set execution policy 32bit</Description>
|
||||
<Order>2</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c reg add "HKLM\System\CurrentControlSet\Control\Network\NewNetworkWindowOff"</CommandLine>
|
||||
<Description>Disable new network prompt</Description>
|
||||
<Order>3</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\Set-NetworkProfile.ps1</CommandLine>
|
||||
<Description>Set network profile to private</Description>
|
||||
<Order>4</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\Disable-WinRM.ps1</CommandLine>
|
||||
<Description>Disable WinRM</Description>
|
||||
<Order>5</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c a:\Install-VMwareTools.cmd</CommandLine>
|
||||
<Order>13</Order>
|
||||
<Description>Install VMware Tools</Description>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\Enable-WinRM.ps1</CommandLine>
|
||||
<Description>Enable WinRM</Description>
|
||||
<Order>99</Order>
|
||||
</SynchronousCommand>
|
||||
</FirstLogonCommands>
|
||||
<ShowWindowsLive>false</ShowWindowsLive>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="specialize">
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<OEMInformation>
|
||||
<HelpCustomized>false</HelpCustomized>
|
||||
</OEMInformation>
|
||||
<!-- Rename computer here. -->
|
||||
<ComputerName>packer-template</ComputerName>
|
||||
<TimeZone>W. Europe Standard Time</TimeZone>
|
||||
<RegisteredOwner/>
|
||||
</component>
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<SkipAutoActivation>true</SkipAutoActivation>
|
||||
</component>
|
||||
</settings>
|
||||
</unattend>
|
||||
+15
@@ -10,6 +10,12 @@
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="oobeSystem">
|
||||
<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>en-US</InputLocale>
|
||||
<SystemLocale>en-US</SystemLocale>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
<UserLocale>en-US</UserLocale>
|
||||
</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">
|
||||
<OOBE>
|
||||
<HideEULAPage>true</HideEULAPage>
|
||||
@@ -22,6 +28,15 @@
|
||||
<SkipMachineOOBE>true</SkipMachineOOBE>
|
||||
<SkipUserOOBE>true</SkipUserOOBE>
|
||||
</OOBE>
|
||||
<TimeZone>UTC</TimeZone>
|
||||
<UserAccounts>
|
||||
<AdministratorPassword>
|
||||
<Value><<img-password>></Value>
|
||||
<PlainText>true</PlainText>
|
||||
</AdministratorPassword>
|
||||
</UserAccounts>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="specialize">
|
||||
</settings>
|
||||
</unattend>
|
||||
@@ -11,4 +11,8 @@ variable "vsphere_datastore" {}
|
||||
variable "vsphere_network" {}
|
||||
|
||||
variable "vm_name" {}
|
||||
variable "vm_guestos" {}
|
||||
variable "winrm_password" {}
|
||||
|
||||
variable "repo_username" {}
|
||||
variable "repo_password" {}
|
||||
@@ -0,0 +1,133 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
windows-update = {
|
||||
version = ">= 0.14.0"
|
||||
source = "github.com/rgl/windows-update"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source "vsphere-iso" "win10" {
|
||||
vcenter_server = var.vcenter_server
|
||||
username = var.vsphere_username
|
||||
password = var.vsphere_password
|
||||
insecure_connection = "true"
|
||||
|
||||
vm_name = "${var.vm_guestos}-${var.vm_name}"
|
||||
datacenter = var.vsphere_datacenter
|
||||
host = var.vsphere_host
|
||||
folder = var.vsphere_folder
|
||||
datastore = var.vsphere_datastore
|
||||
|
||||
guest_os_type = "windows9_64Guest"
|
||||
|
||||
boot_order = "disk,cdrom"
|
||||
boot_command = [""]
|
||||
boot_wait = "5m"
|
||||
|
||||
communicator = "winrm"
|
||||
winrm_username = "administrator"
|
||||
winrm_password = var.winrm_password
|
||||
winrm_timeout = "10m"
|
||||
|
||||
CPUs = 2
|
||||
RAM = 8192
|
||||
|
||||
network_adapters {
|
||||
network = var.vsphere_network
|
||||
network_card = "vmxnet3"
|
||||
}
|
||||
storage {
|
||||
disk_size = 20480
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
disk_controller_type = ["lsilogic-sas"]
|
||||
usb_controller = ["xhci"]
|
||||
|
||||
floppy_files = [
|
||||
"packer/preseed/Windows10/Autounattend.xml",
|
||||
"packer/preseed/Windows10/Sysprep_Unattend.xml",
|
||||
"scripts/Set-NetworkProfile.ps1",
|
||||
"scripts/Disable-WinRM.ps1",
|
||||
"scripts/Enable-WinRM.ps1",
|
||||
"scripts/Install-VMwareTools.cmd"
|
||||
]
|
||||
iso_checksum = "sha256:8D1663B71280533824CF95C7AB48ADAF5A187C38FCFF5B16A569F903688916D0"
|
||||
iso_paths = [
|
||||
"ISO-files/VMware-tools-windows-11.3.5-18557794/VMware-tools-windows-11.3.5-18557794.iso"
|
||||
]
|
||||
iso_url = "https://${var.repo_username}:${var.repo_password}@sn.itch.fyi/Repository/iso/Microsoft/Windows%2010/20H2/en_windows_10_enterprise_20H2_x64.iso"
|
||||
|
||||
shutdown_command = "C:\\Windows\\System32\\Sysprep\\sysprep.exe /generalize /oobe /unattend:A:\\Sysprep_Unattend.xml"
|
||||
shutdown_timeout = "1h"
|
||||
|
||||
export {
|
||||
images = false
|
||||
output_directory = "/scratch/win10"
|
||||
}
|
||||
remove_cdrom = true
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.vsphere-iso.win10"]
|
||||
|
||||
provisioner "windows-update" {
|
||||
filters = [
|
||||
"exclude:$_.Title -like '*Preview*'",
|
||||
"include:$true"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12",
|
||||
"Invoke-Expression ((New-Object Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"choco config set --name=limit-output --value=LimitOutput",
|
||||
"choco install -y 7zip.install",
|
||||
"choco install -y sysinternals",
|
||||
"choco install -y firefox"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "windows-update" {
|
||||
filters = [
|
||||
"exclude:$_.Title -like '*Preview*'",
|
||||
"include:$true"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"New-Item -Path 'C:\\Payload\\Scripts' -ItemType 'Directory' -Force:$True -Confirm:$False"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "C:\\Payload\\"
|
||||
source = "scripts/Windows10/payload/"
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
scripts = [
|
||||
"scripts/Windows10/Register-ScheduledTask.ps1"
|
||||
]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = [
|
||||
"pwsh -command \"& scripts/Update-OvfConfiguration.ps1 \\",
|
||||
" -OVFFile '/scratch/win10/${var.vm_guestos}-${var.vm_name}.ovf' \\",
|
||||
" -Parameter @{'appliance.name'='${var.vm_guestos}';'appliance.version'='${var.vm_name}'}\"",
|
||||
"pwsh -file scripts/Update-Manifest.ps1 \\",
|
||||
" -ManifestFileName '/scratch/win10/${var.vm_guestos}-${var.vm_name}.mf'",
|
||||
"ovftool --acceptAllEulas --allowExtraConfig --overwrite \\",
|
||||
" '/scratch/win10/${var.vm_guestos}-${var.vm_name}.ovf' \\",
|
||||
" /output/Windows10.ova"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
# No parameters
|
||||
)
|
||||
|
||||
$InstallWindowsFeatureSplat = @{
|
||||
Name = 'AD-Domain-Services', 'DHCP', 'RSAT-DNS-Server'
|
||||
IncludeAllSubFeature = $True
|
||||
IncludeManagementTools = $True
|
||||
Restart = $False
|
||||
Confirm = $False
|
||||
}
|
||||
Install-WindowsFeature @InstallWindowsFeatureSplat
|
||||
|
||||
$InstallPackageProviderSplat = @{
|
||||
Name = 'NuGet'
|
||||
MinimumVersion = '2.8.5.201'
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Install-PackageProvider @InstallPackageProviderSplat
|
||||
$SetPSRepositorySplat = @{
|
||||
Name = 'PSGallery'
|
||||
InstallationPolicy = 'Trusted'
|
||||
}
|
||||
Set-PSRepository @SetPSRepositorySplat
|
||||
$InstallModuleSplat = @{
|
||||
Name = 'powershell-yaml','gpwmifilter'
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Install-Module @InstallModuleSplat
|
||||
$SetPSRepositorySplat = @{
|
||||
Name = 'PSGallery'
|
||||
InstallationPolicy = 'Untrusted'
|
||||
}
|
||||
Set-PSRepository @SetPSRepositorySplat
|
||||
|
||||
# Double check whether the required PowerShell modules are available
|
||||
$RequiredModules = @(
|
||||
'powershell-yaml', # Provides cmdlets 'ConvertTo-Yaml' and 'ConvertFrom-Yaml'
|
||||
'gpwmifilter', # Provides cmdlets '*-GPWmiFilter' and '*-GPWmiFilterAssignment'
|
||||
'psframework' # Dependency for GMWmiFilter
|
||||
)
|
||||
ForEach ($Module in $RequiredModules) {
|
||||
If ([boolean](Get-Module -Name $Module -ListAvailable) -ne $True) {
|
||||
Write-Error -Message "Missing PowerShell module '$($Module)'"
|
||||
Exit 1
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter()]
|
||||
[string]$VaultAPIAddress,
|
||||
[Parameter()]
|
||||
[string]$VaultToken,
|
||||
[Parameter()]
|
||||
[string]$VaultPwPolicy,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$VaultSecret,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Username
|
||||
)
|
||||
|
||||
# Generate new password
|
||||
$InvokeWebRequestSplat = @{
|
||||
Uri = "$($VaultAPIAddress)/sys/policies/password/$($VaultPwPolicy)/generate"
|
||||
Headers = @{'X-Vault-Token'="$VaultToken"}
|
||||
UseBasicParsing = $True
|
||||
}
|
||||
$NewPassword = (Invoke-WebRequest @InvokeWebRequestSplat | ConvertFrom-Json).data.password
|
||||
|
||||
# Check for existense of secret
|
||||
$Response, $ErrResponse = $Null, $Null
|
||||
Try {
|
||||
$InvokeWebRequestSplat = @{
|
||||
Uri = "$($VaultAPIAddress)/secret/metadata/$($VaultSecret)"
|
||||
Headers = @{'X-Vault-Token' = "$VaultToken"}
|
||||
UseBasicParsing = $True
|
||||
}
|
||||
$Response = Invoke-WebRequest @InvokeWebRequestSplat
|
||||
}
|
||||
Catch [System.Net.WebException] {
|
||||
$StreamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
|
||||
$StreamReader.BaseStream.Position = 0
|
||||
$ErrResponse = $StreamReader.ReadToEnd()
|
||||
$StreamReader.Close()
|
||||
}
|
||||
|
||||
If ([boolean]$Response) {
|
||||
# Secret already exists; retrieve existing key/value pairs
|
||||
$InvokeWebRequestSplat = @{
|
||||
Uri = "$($VaultAPIAddress)/secret/data/$($VaultSecret)"
|
||||
Headers = @{'X-Vault-Token' = "$VaultToken"}
|
||||
UseBasicParsing = $True
|
||||
}
|
||||
$Secret = (Invoke-WebRequest @InvokeWebRequestSplat | ConvertFrom-Json).data
|
||||
|
||||
# Merge new password into dictionary
|
||||
$AddMemberSplat = @{
|
||||
MemberType = 'NoteProperty'
|
||||
Name = "password.$($Username)"
|
||||
Value = $NewPassword
|
||||
Force = $True
|
||||
}
|
||||
$Secret.data | Add-Member @AddMemberSplat
|
||||
|
||||
# Store as new version
|
||||
$InvokeWebRequestSplat = @{
|
||||
Uri = "$($VaultAPIAddress)/secret/data/$($VaultSecret)"
|
||||
Method = 'POST'
|
||||
UseBasicParsing = $True
|
||||
Headers = @{'X-Vault-Token'="$VaultToken"}
|
||||
Body = @{
|
||||
data = $Secret.data
|
||||
} | ConvertTo-Json
|
||||
}
|
||||
Invoke-WebRequest @InvokeWebRequestSplat | Out-Null
|
||||
}
|
||||
ElseIf ([boolean]$ErrResponse) {
|
||||
# Secret did not exist yet, store as new secret
|
||||
$InvokeWebRequestSplat = @{
|
||||
Uri = "$($VaultAPIAddress)/secret/data/$($VaultSecret)"
|
||||
Method = 'POST'
|
||||
UseBasicParsing = $True
|
||||
Headers = @{'X-Vault-Token'="$VaultToken"}
|
||||
Body = @{
|
||||
data = @{
|
||||
"password.$($Username)" = $NewPassword
|
||||
}
|
||||
} | ConvertTo-Json
|
||||
}
|
||||
Invoke-WebRequest @InvokeWebRequestSplat | Out-Null
|
||||
}
|
||||
|
||||
Return $NewPassword
|
||||
@@ -1,52 +0,0 @@
|
||||
#Requires -Modules 'ActiveDirectory'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".yml")
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
$Entries = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$Entries = $YamlDocuments
|
||||
}
|
||||
|
||||
ForEach ($OU in $Entries.OrganizationalUnits) {
|
||||
$OUName, $OUPath = $OU.DistinguishedName -split ',', 2
|
||||
If ($OUPath.Length -ne 0) {
|
||||
$OUPath += ','
|
||||
}
|
||||
|
||||
$NewADOrganizationalUnitSplat = @{
|
||||
Name = $OUName.Substring(3)
|
||||
Path = $OUPath + (Get-ADRootDSE).rootDomainNamingContext
|
||||
Description = $OU.Description
|
||||
ProtectedFromAccidentalDeletion = $False
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
New-ADOrganizationalUnit @NewADOrganizationalUnitSplat
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
OrganizationalUnits:
|
||||
- DistinguishedName: OU=Computer accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Clients,OU=Computer accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Desktops,OU=Clients,OU=Computer accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Laptops,OU=Clients,OU=Computer accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Kiosks,OU=Clients,OU=Computer accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Servers,OU=Computer accounts
|
||||
Description: ''
|
||||
|
||||
- DistinguishedName: OU=Groups
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Resources,OU=Groups
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Roles,OU=Groups
|
||||
Description: ''
|
||||
|
||||
- DistinguishedName: OU=User accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Privileged,OU=User accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Administrators,OU=Privileged,OU=User accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Service accounts,OU=Privileged,OU=User accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Non-privileged,OU=User accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Employees,OU=Non-privileged,OU=User accounts
|
||||
Description: ''
|
||||
- DistinguishedName: OU=Contractors,OU=Non-privileged,OU=User accounts
|
||||
Description: ''
|
||||
@@ -1,60 +0,0 @@
|
||||
#Requires -Modules 'ActiveDirectory'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".yml")
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
$Entries = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$Entries = $YamlDocuments
|
||||
}
|
||||
|
||||
ForEach ($Group in $Entries.SecurityGroups) {
|
||||
$NewADGroupSplat = @{
|
||||
Name = ($Group.DistinguishedName -split ',', 2)[0].Substring(3)
|
||||
Path = ($Group.DistinguishedName -split ',', 2)[1] + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext)
|
||||
Description = $Group.Description
|
||||
GroupCategory = 'Security'
|
||||
GroupScope = $Group.Scope
|
||||
PassThru = $True
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$NewADGroup = New-ADGroup @NewADGroupSplat
|
||||
|
||||
If ([boolean]$Group.MemberOf) {
|
||||
ForEach ($ParentGroup in $Group.MemberOf) {
|
||||
$AddADGroupMemberSplat = @{
|
||||
Identity = $ParentGroup + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext)
|
||||
Members = $NewADGroup.DistinguishedName
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Add-ADGroupMember @AddADGroupMemberSplat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
SecurityGroups:
|
||||
# Resource groups
|
||||
- DistinguishedName: CN=RemoteDesktop - Management servers,OU=Resources,OU=Groups
|
||||
Description: ''
|
||||
Scope: 'DomainLocal'
|
||||
MemberOf: []
|
||||
- DistinguishedName: CN=ContentLibraryAdmin - vSphere servers,OU=Resources,OU=Groups
|
||||
Description: ''
|
||||
Scope: 'DomainLocal'
|
||||
MemberOf: []
|
||||
- DistinguishedName: CN=DatastoreAdmin - vSphere servers,OU=Resources,OU=Groups
|
||||
Description: ''
|
||||
Scope: 'DomainLocal'
|
||||
MemberOf: []
|
||||
|
||||
# Role groups
|
||||
- DistinguishedName: CN=Hypervisor administrators,OU=Roles,OU=Groups
|
||||
Description: ''
|
||||
Scope: 'Global'
|
||||
MemberOf:
|
||||
- CN=RemoteDesktop - Management servers,OU=Resources,OU=Groups
|
||||
- CN=DatastoreAdmin - vSphere servers,OU=Resources,OU=Groups
|
||||
- CN=ContentLibraryAdmin - vSphere servers,OU=Resources,OU=Groups
|
||||
- DistinguishedName: CN=Firewall administrators,OU=Roles,OU=Groups
|
||||
Description: ''
|
||||
Scope: 'Global'
|
||||
MemberOf:
|
||||
- CN=RemoteDesktop - Management servers,OU=Resources,OU=Groups
|
||||
@@ -1,69 +0,0 @@
|
||||
#Requires -Modules 'ActiveDirectory'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".yml")
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
$Entries = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$Entries = $YamlDocuments
|
||||
}
|
||||
|
||||
ForEach ($User in $Entries.Users) {
|
||||
$UserName = ($User.DistinguishedName -split ',', 2)[0].Substring(3)
|
||||
$SanitizedUPN = ($UserName -replace "[^a-zA-Z0-9'\.-_!#\^~]").Trim('.')
|
||||
|
||||
# Create new user
|
||||
$NewADUserSplat = @{
|
||||
Name = $UserName
|
||||
UserPrincipalName = "$($SanitizedUPN)@$((Get-ADDomain).DNSRoot)"
|
||||
Path = ($User.DistinguishedName -split ',', 2)[1] + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext)
|
||||
AccountPassword = ConvertTo-SecureString $User.Password -AsPlainText -Force
|
||||
PassThru = $True
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$NewADUser = New-ADUser @NewADUserSplat
|
||||
# Add user to group(s)
|
||||
If ([boolean]$User.MemberOf) {
|
||||
ForEach ($Group in $User.MemberOf) {
|
||||
$AddADGroupMemberSplat = @{
|
||||
Identity = $Group + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext)
|
||||
Members = $NewADUser.DistinguishedName
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Add-ADGroupMember @AddADGroupMemberSplat
|
||||
}
|
||||
}
|
||||
# Enable user
|
||||
$EnableADAccountSplat = @{
|
||||
Identity = $NewADUser.DistinguishedName
|
||||
ErrorAction = 'Continue'
|
||||
}
|
||||
Enable-ADAccount @EnableADAccountSplat
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
Users:
|
||||
- DistinguishedName: CN=Jane Doe,OU=Employees,OU=Non-privileged,OU=User accounts
|
||||
Password: "{{ password.janedoe }}"
|
||||
MemberOf: []
|
||||
- DistinguishedName: CN=John Doe,OU=Contractors,OU=Non-privileged,OU=User accounts
|
||||
Password: "{{ password.johndoe }}"
|
||||
MemberOf: []
|
||||
- DistinguishedName: CN=admJaneD,OU=Administrators,OU=Privileged,OU=User accounts
|
||||
Password: "{{ password.admjaned }}"
|
||||
MemberOf: []
|
||||
- DistinguishedName: CN=zzLDAP,OU=Service accounts,OU=Privileged,OU=User accounts
|
||||
Password: "{{ password.zzldap }}"
|
||||
MemberOf: []
|
||||
---
|
||||
Variables:
|
||||
- Name: password.janedoe
|
||||
Expression: |
|
||||
& ".\Provision-VaultPassword.ps1" -VaultSecret $Parameter['vault.secret'] -Username 'janedoe' -VaultAPIAddress $Parameter['vault.api'] -VaultToken $Parameter['vault.token'] -VaultPwPolicy $Parameter['vault.pwpolicy']
|
||||
- Name: password.johndoe
|
||||
Expression: |
|
||||
& ".\Provision-VaultPassword.ps1" -VaultSecret $Parameter['vault.secret'] -Username 'johndoe' -VaultAPIAddress $Parameter['vault.api'] -VaultToken $Parameter['vault.token'] -VaultPwPolicy $Parameter['vault.pwpolicy']
|
||||
- Name: password.admjaned
|
||||
Expression: |
|
||||
& ".\Provision-VaultPassword.ps1" -VaultSecret $Parameter['vault.secret'] -Username 'admjaned' -VaultAPIAddress $Parameter['vault.api'] -VaultToken $Parameter['vault.token'] -VaultPwPolicy $Parameter['vault.pwpolicy']
|
||||
- Name: password.zzldap
|
||||
Expression: |
|
||||
& ".\Provision-VaultPassword.ps1" -VaultSecret $Parameter['vault.secret'] -Username 'zzldap' -VaultAPIAddress $Parameter['vault.api'] -VaultToken $Parameter['vault.token'] -VaultPwPolicy $Parameter['vault.pwpolicy']
|
||||
@@ -1,133 +0,0 @@
|
||||
#Requires -Modules 'ActiveDirectory'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$PSDrive = Get-PSDrive -Name 'AD'
|
||||
If ([boolean]$PSDrive -eq $False) {
|
||||
$NewPSDriveSplat = @{
|
||||
Name = 'ADDS'
|
||||
Root = ''
|
||||
PSProvider = 'ActiveDirectory'
|
||||
}
|
||||
$PSDrive = New-PSDrive @NewPSDriveSplat
|
||||
}
|
||||
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".yml")
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
$Delegations = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$Delegations = $YamlDocuments
|
||||
}
|
||||
|
||||
# Store GUIDs for all known AD schema classes
|
||||
$GUIDMap, $GetADObjectSplat = @{}, @{
|
||||
SearchBase = (Get-ADRootDSE).SchemaNamingContext
|
||||
LDAPFilter = '(schemaidguid=*)'
|
||||
Properties = 'lDAPDisplayName','schemaIDGUID'
|
||||
}
|
||||
Get-ADObject @GetADObjectSplat | ForEach-Object {
|
||||
$GUIDMap[$_.lDAPDisplayName] = [GUID]$_.schemaIDGUID
|
||||
}
|
||||
# Store GUIDs for all extended rights
|
||||
$GetADObjectSplat = @{
|
||||
SearchBase = (Get-ADRootDSE).ConfigurationNamingContext
|
||||
LDAPFilter = '(&(objectclass=controlAccessRight)(rightsguid=*))'
|
||||
Properties = 'displayName','rightsGuid'
|
||||
}
|
||||
Get-ADObject @GetADObjectSplat | ForEach-Object {
|
||||
$GUIDMap[$_.displayName] = [GUID]$_.rightsGuid
|
||||
}
|
||||
$GUIDMap['null'] = [Guid]::Empty
|
||||
|
||||
ForEach ($Entry in $Delegations.DelegationEntries) {
|
||||
$GetADObjectSplat = @{
|
||||
Filter = "sAMAccountName -eq '$($Entry.Principal)'"
|
||||
Properties = 'objectSID'
|
||||
}
|
||||
$Principal = Get-ADObject @GetADObjectSplat
|
||||
|
||||
ForEach ($OU in $Entry.OrganizationalUnit) {
|
||||
$GetADObjectSplat = @{
|
||||
Identity = ($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
$OU = Get-ADObject @GetADObjectSplat
|
||||
If ([boolean]$OU) {
|
||||
$GetACLSPlat = @{
|
||||
Path = "$($PSDrive.Name):\$($OU.DistinguishedName)"
|
||||
}
|
||||
$ACL = Get-ACL @GetACLSPlat
|
||||
}
|
||||
Else {
|
||||
# Respective OU was not found in Active Directory; skipping permission assignment
|
||||
Continue
|
||||
}
|
||||
|
||||
ForEach ($Rule in $Entry.AccessRules) {
|
||||
If ($Rule.ObjectType -eq '') {
|
||||
$Rule.ObjectType = 'null'
|
||||
}
|
||||
If ($Rule.InheritedObjectType -eq '') {
|
||||
$Rule.InheritedObjectType = 'null'
|
||||
}
|
||||
|
||||
$NewACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
|
||||
# An IdentityReference object that identifies the trustee of the access rule.
|
||||
[System.Security.Principal.IdentityReference]$Principal.objectSID,
|
||||
# A combination of one or more of the ActiveDirectoryRights enumeration values that specifies the rights of the access rule.
|
||||
[System.DirectoryServices.ActiveDirectoryRights]$Rule.ActiveDirectoryRights,
|
||||
# One of the AccessControlType enumeration values that specifies the access rule type.
|
||||
[System.Security.AccessControl.AccessControlType]$Rule.AccessControlType,
|
||||
# The schema GUID of the object to which the access rule applies.
|
||||
[Guid]$GUIDMap[$Rule.ObjectType],
|
||||
# One of the ActiveDirectorySecurityInheritance enumeration values that specifies the inheritance type of the access rule.
|
||||
[System.DirectoryServices.ActiveDirectorySecurityInheritance]$Rule.ActiveDirectorySecurityInheritance,
|
||||
# The schema GUID of the child object type that can inherit this access rule.
|
||||
[Guid]$GUIDMap[$Rule.InheritedObjectType]
|
||||
)
|
||||
$ACL.AddAccessRule($NewACE)
|
||||
}
|
||||
|
||||
$SetAclSplat = @{
|
||||
Path = "$($PSDrive.Name):\$($OU.DistinguishedName)"
|
||||
AclObject = $ACL
|
||||
ErrorAction = 'Continue'
|
||||
}
|
||||
Set-Acl @SetAclSplat
|
||||
}
|
||||
}
|
||||
|
||||
If ([boolean]($PSDrive.Name -eq 'ADDS') -eq $True) {
|
||||
$RemovePSDriveSplat = @{
|
||||
Name = 'ADDS'
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Remove-PSDrive @RemovePSDriveSplat | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
DelegationEntries:
|
||||
- Principal: admJaneD # Entries will be concatenated with ',DC=<example>,DC=<org>' automatically
|
||||
OrganizationalUnit:
|
||||
- CN=Computers
|
||||
- OU=Kiosks,OU=Clients,OU=Computer accounts
|
||||
AccessRules:
|
||||
- ActiveDirectoryRights: Self # A combination of one or more of the ActiveDirectoryRights enumeration values that specifies the rights of the access rule.
|
||||
AccessControlType: Allow # One of the AccessControlType enumeration values that specifies the access rule type.
|
||||
ActiveDirectorySecurityInheritance: Descendents # One of the ActiveDirectorySecurityInheritance enumeration values that specifies the inheritance type of the access rule.
|
||||
ObjectType: Validated write to DNS host name # The object type to which the access rule applies.
|
||||
InheritedObjectType: Computer # The child object type that can inherit this access rule.
|
||||
- ActiveDirectoryRights: Self
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: Validated write to service principal name
|
||||
InheritedObjectType: Computer
|
||||
- ActiveDirectoryRights: WriteProperty, WriteDacl
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: ''
|
||||
InheritedObjectType: Computer
|
||||
- ActiveDirectoryRights: ExtendedRight
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: Reset Password
|
||||
InheritedObjectType: Computer
|
||||
- ActiveDirectoryRights: ExtendedRight
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: Change Password
|
||||
InheritedObjectType: Computer
|
||||
- ActiveDirectoryRights: ReadProperty
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: ''
|
||||
InheritedObjectType: Computer
|
||||
- ActiveDirectoryRights: WriteProperty
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: ''
|
||||
InheritedObjectType: Computer
|
||||
- ActiveDirectoryRights: CreateChild, DeleteChild
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: All
|
||||
ObjectType: Computer
|
||||
InheritedObjectType: ''
|
||||
- ActiveDirectoryRights: GenericAll
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: Computer
|
||||
InheritedObjectType: ''
|
||||
- Principal: admJaneD
|
||||
OrganizationalUnit:
|
||||
- OU=Clients,OU=Computer accounts
|
||||
AccessRules:
|
||||
- ActiveDirectoryRights: CreateChild, DeleteChild
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: All
|
||||
ObjectType: User
|
||||
InheritedObjectType: ''
|
||||
- ActiveDirectoryRights: GenericAll
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: ''
|
||||
InheritedObjectType: ''
|
||||
- ActiveDirectoryRights: WriteProperty, ReadProperty
|
||||
AccessControlType: Allow
|
||||
ActiveDirectorySecurityInheritance: Descendents
|
||||
ObjectType: Member
|
||||
InheritedObjectType: Group
|
||||
|
||||
# ---
|
||||
# Variables:
|
||||
# - Name: foo
|
||||
# Expression: |
|
||||
# Write-Host 'bar'
|
||||
@@ -1,65 +0,0 @@
|
||||
Name: 'COMP: Firewall (Clients)'
|
||||
LinkedOUs:
|
||||
- OU=Clients,OU=Computer accounts
|
||||
FirewallRules:
|
||||
- Description: Rule A
|
||||
Action: Block
|
||||
Direction: Inbound
|
||||
Program: ''
|
||||
Port: '21-22,25'
|
||||
Protocol: TCP
|
||||
- Description: Rule B
|
||||
Action: Allow
|
||||
Direction: Inbound
|
||||
Program: D:\MSSQL\sqlsvr.exe
|
||||
Port: ''
|
||||
Protocol: ''
|
||||
FirewallProfiles:
|
||||
- Name: Domain
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\domainfw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
- Name: Private
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\privatefw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
- Name: Public
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\publicfw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
|
||||
# ---
|
||||
# Variables:
|
||||
# - Name: foo
|
||||
# Expression: |
|
||||
# Write-Host 'bar'
|
||||
@@ -1,65 +0,0 @@
|
||||
Name: 'COMP: Firewall (DomainControllers)'
|
||||
LinkedOUs:
|
||||
- OU=Domain Controllers
|
||||
FirewallRules:
|
||||
- Description: Rule A
|
||||
Action: Block
|
||||
Direction: Inbound
|
||||
Program: ''
|
||||
Port: '21-22,25'
|
||||
Protocol: TCP
|
||||
- Description: Rule B
|
||||
Action: Allow
|
||||
Direction: Inbound
|
||||
Program: D:\MSSQL\sqlsvr.exe
|
||||
Port: ''
|
||||
Protocol: ''
|
||||
FirewallProfiles:
|
||||
- Name: Domain
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\domainfw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
- Name: Private
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\privatefw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
- Name: Public
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\publicfw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
|
||||
# ---
|
||||
# Variables:
|
||||
# - Name: foo
|
||||
# Expression: |
|
||||
# Write-Host 'bar'
|
||||
@@ -1,140 +0,0 @@
|
||||
#Requires -Modules 'NetSecurity'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetItemSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.yml')
|
||||
}
|
||||
ForEach ($File in (Get-Item @GetItemSplat)) {
|
||||
Try {
|
||||
Write-Host "Loading/parsing file '$($File)' ..."
|
||||
$GetContentSplat = @{
|
||||
Path = $File
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
}
|
||||
Catch {
|
||||
$ParseErrors += "While processing '$($File)': $($_.Exception.Message)"
|
||||
Continue
|
||||
}
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
Try {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
}
|
||||
Catch {
|
||||
$ParseErrors += "While processing '$($File)' (after substitutions): $($_.Exception.Message)"
|
||||
Continue
|
||||
}
|
||||
|
||||
$Settings = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$Settings = $YamlDocuments
|
||||
}
|
||||
|
||||
$NewGPOSplat = @{
|
||||
Name = $Settings.Name
|
||||
}
|
||||
$NewGPO = New-GPO @NewGPOSplat
|
||||
|
||||
$OpenNetGPOSplat = @{
|
||||
PolicyStore = "$($Parameter['addsconfig.domainname'])\$($NewGPO.DisplayName)"
|
||||
}
|
||||
$GPOSession = Open-NetGPO @OpenNetGPOSplat
|
||||
|
||||
ForEach ($Rule in $Settings.FirewallRules) {
|
||||
$NewNetFirewallRuleSplat = @{
|
||||
# Using so-called string formatting with the '-f' operator (looks more complicated than it is) to create consistent policy names:
|
||||
# Examples:
|
||||
# 'DENY: Inbound port 443 (TCP)'
|
||||
# 'ALLOW: Inbound 'D:\MSSQL\bin\sqlservr.exe'
|
||||
DisplayName = ("{0}: {1} {2} {3} {4}" -f
|
||||
$Rule.Action.ToUpper(),
|
||||
$Rule.Direction,
|
||||
("'$($Rule.Program)'", $NULL)[!($Rule.Program)],
|
||||
("Port $($Rule.Port)", $NULL)[!($Rule.Port)],
|
||||
("($($Rule.Protocol))", $NULL)[!($Rule.Protocol)]
|
||||
) -replace '\s+',' '
|
||||
Description = $Rule.Description
|
||||
Action = $Rule.Action
|
||||
Direction = $Rule.Direction
|
||||
Program = ($Rule.Program, 'Any')[!($Rule.Program)]
|
||||
LocalPort = ($Rule.Port.Split(','), 'Any')[!($Rule.Port)]
|
||||
Protocol = ($Rule.Protocol, 'Any')[!($Rule.Protocol)]
|
||||
GPOSession = $GPOSession
|
||||
PolicyStore = $NewGPO.DisplayName
|
||||
Confirm = $False
|
||||
}
|
||||
New-NetFirewallRule @NewNetFirewallRuleSplat
|
||||
}
|
||||
|
||||
ForEach ($Profile in $Settings.FirewallProfiles) {
|
||||
$SetNetFirewallProfileSplat = @{
|
||||
Name = $Profile.Name
|
||||
Enabled = $Profile.Enabled
|
||||
DefaultInboundAction = $Profile.Connections.Inbound
|
||||
DefaultOutboundAction = $Profile.Connections.Outbound
|
||||
LogAllowed = $Profile.Logging.LogSuccessfullConnections
|
||||
LogBlocked = $Profile.Logging.LogDroppedPackets
|
||||
LogFileName = $Profile.Logging.Name
|
||||
LogMaxSizeKilobytes = $Profile.Logging.SizeLimit
|
||||
AllowLocalFirewallRules = $Profile.Settings.ApplyLocalFirewallRules
|
||||
AllowLocalIPsecRules = $Profile.Settings.ApplyLocalConnectionSecurityRules
|
||||
NotifyOnListen = $Profile.Settings.DisplayNotification
|
||||
GPOSession = $GPOSession
|
||||
PolicyStore = $NewGPO.DisplayName
|
||||
Confirm = $False
|
||||
}
|
||||
Set-NetFirewallProfile @SetNetFirewallProfileSplat
|
||||
}
|
||||
|
||||
$SaveNetGPOSplat = @{
|
||||
GPOSession = $GPOSession
|
||||
}
|
||||
Save-NetGPO @SaveNetGPOSplat
|
||||
|
||||
ForEach ($OU in $Settings.LinkedOUs) {
|
||||
If (Test-Path "AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))") {
|
||||
Try {
|
||||
Write-Host "Linking policy '$($NewGPO.DisplayName)' to OU '$($OU)' ..."
|
||||
$NewGPLinkSplat = @{
|
||||
Name = $NewGPO.DisplayName
|
||||
Target = $OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext)
|
||||
}
|
||||
New-GPLink @NewGPLinkSplat | Out-Null
|
||||
}
|
||||
Catch {
|
||||
$ParseErrors += "Could not link GPO '$($NewGPO.DisplayName)' to OU '$($OU)'"
|
||||
Continue
|
||||
}
|
||||
}
|
||||
Else {
|
||||
$ParseErrors += "Path not accessible (referred to by '$($NewGPO.DisplayName)'): 'AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))'"
|
||||
Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
If ($ParseErrors) {
|
||||
Throw "One or more errors occurred:`n$($ParseErrors -join "`n")"
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
Name: 'COMP: Firewall (Servers)'
|
||||
LinkedOUs:
|
||||
- OU=Servers,OU=Computer accounts
|
||||
FirewallRules:
|
||||
- Description: Rule A
|
||||
Action: Block
|
||||
Direction: Inbound
|
||||
Program: ''
|
||||
Port: '21-22,25'
|
||||
Protocol: TCP
|
||||
- Description: Rule B
|
||||
Action: Allow
|
||||
Direction: Inbound
|
||||
Program: D:\MSSQL\sqlsvr.exe
|
||||
Port: ''
|
||||
Protocol: ''
|
||||
FirewallProfiles:
|
||||
- Name: Domain
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\domainfw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
- Name: Private
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\privatefw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
- Name: Public
|
||||
Enabled: 'True'
|
||||
Connections:
|
||||
Inbound: Block
|
||||
Outbound: Allow
|
||||
Settings:
|
||||
DisplayNotification: 'False'
|
||||
ApplyLocalFirewallRules: 'True'
|
||||
ApplyLocalConnectionSecurityRules: 'True'
|
||||
Logging:
|
||||
Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\publicfw.log'
|
||||
SizeLimit: 16384
|
||||
LogDroppedPackets: 'True'
|
||||
LogSuccessfullConnections: 'False'
|
||||
|
||||
# ---
|
||||
# Variables:
|
||||
# - Name: foo
|
||||
# Expression: |
|
||||
# Write-Host 'bar'
|
||||
@@ -1,27 +0,0 @@
|
||||
#Requires -Modules 'DhcpServer'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Configure DHCP (if and only if this server is not already an authorized DHCP server)
|
||||
If ((Get-DHCPServerInDC).IPAddress -NotContains $Parameter['guestinfo.ipaddress']) {
|
||||
# Add DHCP security groups
|
||||
& netsh dhcp add securitygroups
|
||||
|
||||
# Authorize DHCP server
|
||||
$AddDhcpServerInDCSplat = @{
|
||||
DnsName = "$($Parameter['guestinfo.hostname']).$($Parameter['addsconfig.domainname'])"
|
||||
IPAddress = $($Parameter['guestinfo.ipaddress'])
|
||||
Confirm = $False
|
||||
}
|
||||
Add-DhcpServerInDC @AddDhcpServerInDCSplat
|
||||
|
||||
# Notify Server Manager post-install configuration has completed
|
||||
$SetItemPropertySplat = @{
|
||||
Path = 'HKLM:\SOFTWARE\Microsoft\ServerManager\Roles\12'
|
||||
Name = 'ConfigurationState'
|
||||
Value = 2
|
||||
}
|
||||
Set-ItemProperty @SetItemPropertySplat
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#Requires -Modules 'DhcpServer'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on secondary or standalone Domain Controller
|
||||
If (@('secondary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$AddDhcpServerv4ScopeSplat = @{
|
||||
Name = 'Default DHCP scope'
|
||||
StartRange = [ipaddress]$Parameter['dhcpconfig.startip']
|
||||
EndRange = [ipaddress]$Parameter['dhcpconfig.endip']
|
||||
SubnetMask = [ipaddress]$Parameter['dhcpconfig.subnetmask']
|
||||
LeaseDuration = [timespan]$Parameter['dhcpconfig.leaseduration']
|
||||
State = 'Active'
|
||||
PassThru = $True
|
||||
Confirm = $False
|
||||
}
|
||||
$DhcpScope = Add-DhcpServerv4Scope @AddDhcpServerv4ScopeSplat
|
||||
|
||||
$ScopeOptions = @(
|
||||
@{
|
||||
# 003 Router
|
||||
OptionId = 3
|
||||
Value = $Parameter['dhcpconfig.gateway']
|
||||
},
|
||||
@{
|
||||
# 004 Time Server
|
||||
OptionId = 4
|
||||
Value = (Resolve-DnsName -Name $Parameter['addsconfig.domainname']).IPAddress
|
||||
},
|
||||
@{
|
||||
# 006 DNS Server
|
||||
OptionId = 6
|
||||
Value = (Resolve-DnsName -Name $Parameter['addsconfig.domainname']).IPAddress
|
||||
},
|
||||
@{
|
||||
# 015 DNS Domain Name
|
||||
OptionId = 15
|
||||
Value = $Parameter['addsconfig.domainname']
|
||||
}
|
||||
)
|
||||
|
||||
ForEach ($Option in $ScopeOptions) {
|
||||
$SetDhcpServerv4OptionValueSplat = @{
|
||||
ScopeId = $DhcpScope.ScopeId
|
||||
OptionId = $Option.OptionId
|
||||
Value = $Option.Value
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Set-DhcpServerv4OptionValue @SetDhcpServerv4OptionValueSplat
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#Requires -Modules 'DhcpServer'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on secondary Domain Controller
|
||||
If ($Parameter['deployment.type'] -eq 'secondary') {
|
||||
# Wait for secondary DHCP server to be registered in DNS
|
||||
$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 secondary Domain Controller to be registered in DNS."
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
Break
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
} Until ((Get-DhcpServerInDC).Count -gt 1)
|
||||
|
||||
$NewCimSessionSplat = @{
|
||||
Credential = New-Object System.Management.Automation.PSCredential(
|
||||
(Get-ADUser -Filter * | Where-Object {$_.SID -match '-500'}).SamAccountName,
|
||||
(ConvertTo-SecureString $Parameter['addsconfig.administratorpw'] -AsPlainText -Force)
|
||||
)
|
||||
}
|
||||
$AddDhcpServerv4FailoverSplat = @{
|
||||
Name = 'Failover #42'
|
||||
PartnerServer = (Get-DhcpServerInDC).DnsName | Where-Object {$_ -ne "$($Parameter['guestinfo.hostname']).$($Parameter['addsconfig.domainname'])"}
|
||||
ServerRole = 'Active'
|
||||
ScopeId = (Get-DhcpServerv4Scope).ScopeId.IPAddressToString
|
||||
CimSession = New-CimSession @NewCimSessionSplat
|
||||
}
|
||||
Add-DhcpServerv4Failover @AddDhcpServerv4FailoverSplat
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#Requires -Modules 'DnsServer'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on secondary or standalone Domain Controller
|
||||
If (@('secondary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".$($Parameter['deployment.type']).yml")
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression -ErrorAction 'SilentlyContinue')
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
$Records = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$Records = $YamlDocuments
|
||||
}
|
||||
|
||||
ForEach ($Record in $Records.Entries) {
|
||||
$AddDnsServerResourceRecordSplat = @{
|
||||
ComputerName = $Parameter['guestinfo.dnsserver']
|
||||
ZoneName = $Parameter['addsconfig.domainname']
|
||||
Name = [string]$Record.Name
|
||||
TimeToLive = (New-TimeSpan -Hours 1)
|
||||
AgeRecord = $False
|
||||
Confirm = $False
|
||||
}
|
||||
Switch ($Record.Type) {
|
||||
'A' {
|
||||
$AddDnsServerResourceRecordSplat.Add('A', $True)
|
||||
$AddDnsServerResourceRecordSplat.Add('IPv4Address', $Record.Value)
|
||||
}
|
||||
'AAAA' {
|
||||
$AddDnsServerResourceRecordSplat.Add('AAAA', $True)
|
||||
$AddDnsServerResourceRecordSplat.Add('IPv6Address', $Record.Value)
|
||||
}
|
||||
'CNAME' {
|
||||
$AddDnsServerResourceRecordSplat.Add('CNAME', $True)
|
||||
$AddDnsServerResourceRecordSplat.Add('HostNameAlias', $Record.Value)
|
||||
}
|
||||
'MX' {
|
||||
$AddDnsServerResourceRecordSplat.Add('MX', $True)
|
||||
# Value should match pattern '<fqdn>:<preference>'
|
||||
# ie. 'mail.contoso.com:10'
|
||||
$MailExch = $Record.Value -split ':'
|
||||
$AddDnsServerResourceRecordSplat.Add('MailExchange', $MailExch[0])
|
||||
$AddDnsServerResourceRecordSplat.Add('Preference', $MailExch[1])
|
||||
}
|
||||
'NS' {
|
||||
$AddDnsServerResourceRecordSplat.Add('NS', $True)
|
||||
$AddDnsServerResourceRecordSplat.Add('NameServer', $Record.Value)
|
||||
}
|
||||
'SRV' {
|
||||
$AddDnsServerResourceRecordSplat.Add('SRV', $True)
|
||||
# Value should match pattern '<fqdn>:<priority>:<weight>:<port>'
|
||||
# ie. 'sipserver.contoso.com:0:0:5060'
|
||||
$SrvLocator = $Record.Value -split ':'
|
||||
$AddDnsServerResourceRecordSplat.Add('DomainName', $SrvLocator[0])
|
||||
$AddDnsServerResourceRecordSplat.Add('Priority', $SrvLocator[1])
|
||||
$AddDnsServerResourceRecordSplat.Add('Weight', $SrvLocator[2])
|
||||
$AddDnsServerResourceRecordSplat.Add('Port', $SrvLocator[3])
|
||||
}
|
||||
'TXT' {
|
||||
$AddDnsServerResourceRecordSplat.Add('TXT', $True)
|
||||
$AddDnsServerResourceRecordSplat.Add('DescriptiveText', $Record.Value)
|
||||
}
|
||||
}
|
||||
Add-DnsServerResourceRecord @AddDnsServerResourceRecordSplat
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
Entries:
|
||||
- Name: ldap
|
||||
Type: A
|
||||
Value: "{{ primarydc }}"
|
||||
- Name: ldap
|
||||
Type: A
|
||||
Value: "{{ secondarydc }}"
|
||||
- Name: timeserver
|
||||
Type: A
|
||||
Value: "{{ primarydc }}"
|
||||
- Name: timeserver
|
||||
Type: A
|
||||
Value: "{{ secondarydc }}"
|
||||
# - Name: mail
|
||||
# Type: MX
|
||||
# Value: mail.contoso.com:10 # Value should match pattern '<fqdn>:<preference>'
|
||||
# - Name: voipserver
|
||||
# Type: SRV
|
||||
# Value: sip.contoso.com:0:0:5060 # Value should match pattern '<fqdn>:<priority>:<weight>:<port>'
|
||||
---
|
||||
Variables:
|
||||
- Name: primarydc
|
||||
Expression: |
|
||||
(Resolve-DnsName -Name $Parameter['addsconfig.domainname'] | Sort-Object)[0].IPAddress
|
||||
- Name: secondarydc
|
||||
Expression: |
|
||||
(Resolve-DnsName -Name $Parameter['addsconfig.domainname'] | Sort-Object)[1].IPAddress
|
||||
@@ -1,18 +0,0 @@
|
||||
Entries:
|
||||
- Name: ldap
|
||||
Type: A
|
||||
Value: "{{ primarydc }}"
|
||||
- Name: timeserver
|
||||
Type: A
|
||||
Value: "{{ primarydc }}"
|
||||
# - Name: mail
|
||||
# Type: MX
|
||||
# Value: mail.contoso.com:10 # Value should match pattern '<fqdn>:<preference>'
|
||||
# - Name: voipserver
|
||||
# Type: SRV
|
||||
# Value: sip.contoso.com:0:0:5060 # Value should match pattern '<fqdn>:<priority>:<weight>:<port>'
|
||||
---
|
||||
Variables:
|
||||
- Name: primarydc
|
||||
Expression: |
|
||||
(Resolve-DnsName -Name $Parameter['addsconfig.domainname'] | Sort-Object)[0].IPAddress
|
||||
@@ -1,47 +0,0 @@
|
||||
#Requires -Modules 'GPWmiFilter'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.yml')
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
$WmiFilters = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$WmiFilters = $YamlDocuments
|
||||
}
|
||||
|
||||
ForEach ($Filter in $WmiFilters) {
|
||||
$NewGPWmiFilterSplat = @{
|
||||
Name = $Filter.Name
|
||||
Description = $Filter.Description
|
||||
Expression = $Filter.Expressions
|
||||
Server = $Parameter['addsconfig.domainname']
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
New-GPWmiFilter @NewGPWmiFilterSplat
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
- Name: PDC Emulator
|
||||
Description: Primary Domain Controller Emulator only
|
||||
Expressions:
|
||||
- 'SELECT * FROM Win32_ComputerSystem WHERE DomainRole = 5'
|
||||
# ---
|
||||
# Variables:
|
||||
# - Name: foo
|
||||
# Expression: |
|
||||
# Write-Host 'bar'
|
||||
@@ -1,15 +0,0 @@
|
||||
Name: 'COMP: Disable Server Manager at Logon'
|
||||
Type: Object
|
||||
LinkedOUs:
|
||||
- OU=Servers,OU=Computer accounts
|
||||
- OU=Domain Controllers
|
||||
WMIFilters: []
|
||||
RegistryEntries:
|
||||
- Key: HKLM\Software\Microsoft\ServerManager
|
||||
Type: Dword
|
||||
ValueName: DoNotOpenAtServerManagerAtLogon
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Microsoft\ServerManager
|
||||
Type: Dword
|
||||
ValueName: DoNotPopWACConsoleAtSMLaunch
|
||||
Value: 1
|
||||
@@ -1,19 +0,0 @@
|
||||
Name: 'COMP: Loopback processing (Merge)'
|
||||
Type: Object
|
||||
LinkedOUs: []
|
||||
WMIFilters: []
|
||||
RegistryEntries:
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\System
|
||||
Type: Dword
|
||||
ValueName: UserPolicyMode
|
||||
Value: 1
|
||||
---
|
||||
Name: 'COMP: Loopback processing (Replace)'
|
||||
Type: Object
|
||||
LinkedOUs: []
|
||||
WMIFilters: []
|
||||
RegistryEntries:
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\System
|
||||
Type: Dword
|
||||
ValueName: UserPolicyMode
|
||||
Value: 2
|
||||
@@ -1,36 +0,0 @@
|
||||
Name: 'COMP: Timeserver configuration (W32Time)'
|
||||
Type: Object
|
||||
LinkedOUs:
|
||||
- OU=Domain Controllers
|
||||
WMIFilters:
|
||||
- PDC Emulator
|
||||
RegistryEntries:
|
||||
- Key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
|
||||
Type: String
|
||||
ValueName:
|
||||
- Type
|
||||
- NtpServer
|
||||
Value:
|
||||
- NTP
|
||||
- "{{ addsconfig.ntpserver }}"
|
||||
- Key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config
|
||||
Type: DWord
|
||||
ValueName: AnnounceFlags
|
||||
Value: 0xA
|
||||
- Key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config
|
||||
Type: DWord
|
||||
ValueName: MaxPosPhaseCorrection
|
||||
Value: 0xFFFFFFFF
|
||||
- Key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config
|
||||
Type: DWord
|
||||
ValueName: MaxNegPhaseCorrection
|
||||
Value: 0xFFFFFFFF
|
||||
- Key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer
|
||||
Type: DWord
|
||||
ValueName: Enabled
|
||||
Value: 1
|
||||
---
|
||||
Variables:
|
||||
- Name: addsconfig.ntpserver
|
||||
Expression: |
|
||||
($Parameter['addsconfig.ntpserver'] -split ',' | ForEach-Object {'{0},0x1' -f $_}) -join ' '
|
||||
@@ -1,116 +0,0 @@
|
||||
Name: 'COMP: Restrict Internet Communication'
|
||||
Type: Object
|
||||
LinkedOUs:
|
||||
- OU=Servers,OU=Computer accounts
|
||||
WMIFilters: []
|
||||
RegistryEntries:
|
||||
- Key: HKLM\Software\Policies\Microsoft\InternetManagement
|
||||
Type: DWord
|
||||
ValueName: RestrictCommunication
|
||||
Value: 1
|
||||
# All below settings are set such that their respective features cannot access the Internet
|
||||
# If any of these settings are in conflict with the above setting, gpmc.msc will behave erratic!
|
||||
- Key: HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
|
||||
Type: Dword
|
||||
ValueName: NoPublishingWizard
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
|
||||
Type: Dword
|
||||
ValueName: NoWebServices
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
|
||||
Type: DWord
|
||||
ValueName: NoOnlinePrintsWizard
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
|
||||
Type: DWord
|
||||
ValueName: NoInternetOpenWith
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\EventViewer
|
||||
Type: DWord
|
||||
ValueName: MicrosoftEventVwrDisableLinks
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Messenger\Client
|
||||
Type: DWord
|
||||
ValueName: CEIP
|
||||
Value: 2
|
||||
- Key: HKLM\Software\Policies\Microsoft\PCHealth\ErrorReporting
|
||||
Type: DWord
|
||||
ValueName: DoReport
|
||||
Value: 0
|
||||
- Key: HKLM\Software\Policies\Microsoft\PCHealth\HelpSvc
|
||||
Type: DWord
|
||||
ValueName: Headlines
|
||||
Value: 0
|
||||
- Key: HKLM\Software\Policies\Microsoft\PCHealth\HelpSvc
|
||||
Type: DWord
|
||||
ValueName: MicrosoftKBSearch
|
||||
Value: 0
|
||||
- Key: HKLM\Software\Policies\Microsoft\SearchCompanion
|
||||
Type: DWord
|
||||
ValueName: DisableContentFileUpdates
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\SystemCertificates\AuthRoot
|
||||
Type: DWord
|
||||
ValueName: DisableRootAutoUpdate
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\SQMClient\Windows
|
||||
Type: DWord
|
||||
ValueName: CEIPEnable
|
||||
Value: 0
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\DriverSearching
|
||||
Type: DWord
|
||||
ValueName: DontSearchWindowsUpdate
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\HandwritingErrorReports
|
||||
Type: DWord
|
||||
ValueName: PreventHandwritingErrorReports
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\Internet Connection Wizard
|
||||
Type: DWord
|
||||
ValueName: ExitOnMSICW
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\NetworkConnectivityStatusIndicator
|
||||
Type: Dword
|
||||
ValueName: NoActiveProbe
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\Registration Wizard Control
|
||||
Type: DWord
|
||||
ValueName: NoRegistration
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\TabletPC
|
||||
Type: DWord
|
||||
ValueName: PreventHandwritingDataSharing
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\Windows Error Reporting
|
||||
Type: DWord
|
||||
ValueName: Disabled
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate
|
||||
Type: DWord
|
||||
ValueName: DisableWindowsUpdateAccess
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows NT\CurrentVersion\Software Protection Platform
|
||||
Type: DWord
|
||||
ValueName: NoGenTicket
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows NT\Printers
|
||||
Type: DWord
|
||||
ValueName: DisableHTTPPrinting
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\Windows NT\Printers
|
||||
Type: DWord
|
||||
ValueName: DisableWebPnPDownload
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\WindowsMovieMaker
|
||||
Type: DWord
|
||||
ValueName: WebHelp
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\WindowsMovieMaker
|
||||
Type: DWord
|
||||
ValueName: CodecDownload
|
||||
Value: 1
|
||||
- Key: HKLM\Software\Policies\Microsoft\WindowsMovieMaker
|
||||
Type: DWord
|
||||
ValueName: WebPublish
|
||||
Value: 1
|
||||
@@ -1,44 +0,0 @@
|
||||
Name: 'COMP: Example GPO' # Prefix the name with either 'COMP:' or 'USER:'
|
||||
Type: Object # Either 'Object' or 'Preference' (respectively for GPO or GPP)
|
||||
LinkedOUs: # Entries will be concatenated with ',DC=<example>,DC=<org>' automatically
|
||||
- OU=Servers
|
||||
WMIFilters:
|
||||
- FilterA
|
||||
- FilterB
|
||||
RegistryEntries:
|
||||
- Key: HKLM\SOFTWARE\Policies\Microsoft\Windows\System
|
||||
Type: DWord
|
||||
ValueName: PropertyA
|
||||
Value: 1
|
||||
- Key: HKLM\SOFTWARE\Policies\Microsoft\Windows\System
|
||||
Type: DWord
|
||||
ValueName: PropertyB
|
||||
Value: 0xFFFFFFFF # Hexadecimal values are prefixed with '0x'
|
||||
- Key: HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
|
||||
Type: String
|
||||
ValueName: # Multiple entries are possible, but *only* for the data type 'String' and 'ExpandString' (REG_SZ and REG_EXPAND_SZ)
|
||||
- PropertyP
|
||||
- PropertyQ
|
||||
- PropertyR
|
||||
Value: # The amount of entries must match with 'ValueName'
|
||||
- ValueP
|
||||
- ValueQ
|
||||
- ValueR
|
||||
- Key: HKLM\Software\Test
|
||||
Type: String
|
||||
ValueName:
|
||||
- PropertyX
|
||||
- PropertyDate
|
||||
- PropertyOVF
|
||||
Value: # Values can contain variablenames (respective entries must be declared under 'Variables' below)
|
||||
- ValueX
|
||||
- "{{ date }}"
|
||||
- "{{ guestinfo.dnsserver }}"
|
||||
---
|
||||
Variables: # Each variable consists of a name that is used as a placeholder in the yaml file above, and a PowerShell expression
|
||||
- Name: date
|
||||
Expression: | # The PowerShell script's output must evaluate to a [string]
|
||||
Get-Date
|
||||
- Name: guestinfo.dnsserver
|
||||
Expression: | # The variable '$Parameter' will automatically contain all defined OVF Properties
|
||||
$Parameter['guestinfo.dnsserver']
|
||||
@@ -1,34 +0,0 @@
|
||||
Name: 'COMP: Example GPO' # Prefix the name with either 'COMP:' or 'USER:'
|
||||
Type: Preference # Either 'Object' or 'Preference' (respectively for GPO or GPP)
|
||||
LinkedOUs: # Entries will be concatenated with ',DC=<example>,DC=<org>' automatically
|
||||
- OU=Servers
|
||||
WMIFilters:
|
||||
- FilterA
|
||||
- FilterB
|
||||
RegistryEntries:
|
||||
- Key: HKLM\SOFTWARE\Policies\Microsoft\Windows\System
|
||||
Type: DWord
|
||||
ValueName: PropertyA
|
||||
Value: 1
|
||||
Action: Replace # Valid values are: Create, Update, Replace or Delete
|
||||
Context: Computer # Valid values are: User or Computer
|
||||
Disable: False # Change to 'True' when GPP entry should not be applied
|
||||
- Key: HKLM\SOFTWARE\Policies\Microsoft\Windows\System
|
||||
Type: DWord
|
||||
ValueName: PropertyB
|
||||
Value: 0xFFFFFFFF # Hexadecimal values are prefixed with '0x'
|
||||
Action: Replace
|
||||
Context: Computer
|
||||
Disable: False
|
||||
- Key: HKLM\Software\Test
|
||||
Type: String
|
||||
ValueName: PropertyOVF
|
||||
Value: "{{ guestinfo.dnsserver }}" # Values can contain variablenames (respective entries must be declared under 'Variables' below)
|
||||
Action: Replace
|
||||
Context: Computer
|
||||
Disable: False
|
||||
---
|
||||
Variables: # Each variable consists of a name that is used as a placeholder in the yaml file above, and a PowerShell expression
|
||||
- Name: guestinfo.dnsserver
|
||||
Expression: | # The variable '$Parameter' will automatically contain all defined OVF Properties
|
||||
$Parameter['guestinfo.dnsserver']
|
||||
@@ -1,201 +0,0 @@
|
||||
#Requires -Modules 'powershell-yaml'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$NewPSSessionSplat = @{
|
||||
ComputerName = $Parameter['guestinfo.hostname']
|
||||
Credential = New-Object System.Management.Automation.PSCredential(
|
||||
(Get-ADUser -Filter * | Where-Object {$_.SID -match '-500'}).SamAccountName,
|
||||
(ConvertTo-SecureString $Parameter['addsconfig.administratorpw'] -AsPlainText -Force)
|
||||
)
|
||||
}
|
||||
$PSSession = New-PSSession @NewPSSessionSplat
|
||||
|
||||
$ParseErrors = @()
|
||||
$GetItemSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.*.yml')
|
||||
}
|
||||
ForEach ($File in (Get-Item @GetItemSplat)) {
|
||||
Try {
|
||||
Write-Host "Loading/parsing file '$($File)' ..."
|
||||
$GetContentSplat = @{
|
||||
Path = $File
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
}
|
||||
Catch {
|
||||
$ParseErrors += "While processing '$($File)': $($_.Exception.Message)"
|
||||
Continue
|
||||
}
|
||||
|
||||
# Check if the respective .yml file declared substitutions which need to be parsed
|
||||
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
|
||||
Try {
|
||||
ForEach ($Pattern in $YamlDocuments[-1].Variables) {
|
||||
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression)
|
||||
}
|
||||
# Perform conversion to Yaml again, now with parsed file contents
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
}
|
||||
Catch {
|
||||
$ParseErrors += "While processing '$($File)' (after substitutions): $($_.Exception.Message)"
|
||||
Continue
|
||||
}
|
||||
|
||||
$GroupPolicies = $YamlDocuments[0..($YamlDocuments.Count - 2)]
|
||||
}
|
||||
Else {
|
||||
$GroupPolicies = $YamlDocuments
|
||||
}
|
||||
|
||||
ForEach ($GroupPolicy in $GroupPolicies) {
|
||||
Write-Host "Initiating policy '$($GroupPolicy.Name)' ..."
|
||||
$NewGPOSplat = @{
|
||||
Name = $GroupPolicy.Name
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
ErrorVariable = 'Failure'
|
||||
}
|
||||
$NewGPO = New-GPO @NewGPOSplat
|
||||
If ($Failure) {
|
||||
Continue
|
||||
}
|
||||
|
||||
Switch ($GroupPolicy.Type) {
|
||||
'Object' {
|
||||
ForEach ($ValueSet in $GroupPolicy.RegistryEntries) {
|
||||
Write-Host "Adding key/value to policy '$($NewGPO.DisplayName)' ...`n [$($ValueSet.Key)/$($ValueSet.ValueName)]"
|
||||
$SetGPRegistryValueSplat = @{
|
||||
Name = $NewGPO.DisplayName
|
||||
Key = $ValueSet.Key
|
||||
ValueName = $ValueSet.ValueName
|
||||
Type = $ValueSet.Type
|
||||
Value = Switch ($ValueSet.Type) {
|
||||
'Binary' {
|
||||
# Accepted formats:
|
||||
# 000A0F0100
|
||||
# 00 0A 0F 01 00
|
||||
# 00,0A,0F,01,00
|
||||
[byte[]]([regex]::split(($ValueSet.Value -replace '[ ,]'), '([0-9a-eA-E]{2})') | Where-Object {$_} | ForEach-Object {'0x{0}' -f $_})
|
||||
}
|
||||
'DWord' {
|
||||
[uint32]$ValueSet.Value
|
||||
}
|
||||
'QWord' {
|
||||
[uint64]$ValueSet.Value
|
||||
}
|
||||
Default {
|
||||
$ValueSet.Value
|
||||
}
|
||||
}
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Set-GPRegistryValue @SetGPRegistryValueSplat | Out-Null
|
||||
}
|
||||
}
|
||||
'Preference' {
|
||||
ForEach ($ValueSet in $GroupPolicy.RegistryEntries) {
|
||||
Write-Host "Adding key/value to policy '$($NewGPO.DisplayName)' ...`n [$($ValueSet.Key)/$($ValueSet.ValueName)]"
|
||||
$SetGPPrefRegistryValueSplat = @{
|
||||
Name = $NewGPO.DisplayName
|
||||
Key = $ValueSet.Key
|
||||
Context = $ValueSet.Context
|
||||
Action = $ValueSet.Action
|
||||
ValueName = $ValueSet.ValueName
|
||||
Type = $ValueSet.Type
|
||||
Value = Switch ($ValueSet.Type) {
|
||||
'Binary' {
|
||||
# Accepted formats:
|
||||
# 000A0F0100
|
||||
# 00 0A 0F 01 00
|
||||
# 00,0A,0F,01,00
|
||||
[byte[]]([regex]::split(($ValueSet.Value -replace '[ ,]'), '([0-9a-eA-E]{2})') | Where-Object {$_} | ForEach-Object {'0x{0}' -f $_})
|
||||
}
|
||||
'DWord' {
|
||||
[uint32]$ValueSet.Value
|
||||
}
|
||||
'QWord' {
|
||||
[uint64]$ValueSet.Value
|
||||
}
|
||||
Default {
|
||||
$ValueSet.Value
|
||||
}
|
||||
}
|
||||
Disable = [Convert]::ToBoolean($ValueSet.Disable)
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Set-GPPrefRegistryValue @SetGPPrefRegistryValueSplat | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ForEach ($Filter in $GroupPolicy.WMIFilters) {
|
||||
$InvokeCommandSplat = @{
|
||||
Session = $PSSession
|
||||
ArgumentList = $Filter, $Parameter, $NewGPO
|
||||
ScriptBlock = {
|
||||
#Requires -Modules 'GPWmiFilter'
|
||||
Param(
|
||||
$Filter,
|
||||
$Parameter,
|
||||
$NewGPO
|
||||
)
|
||||
|
||||
$GetGPWmiFilterSplat = @{
|
||||
Name = $Filter
|
||||
Server = $Parameter['addsconfig.domainname']
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
If (Get-GPWMIFilter @GetGPWmiFilterSplat) {
|
||||
$SetGPWmiFilterAssignmentSplat = @{
|
||||
Policy = $NewGPO
|
||||
Filter = $Filter
|
||||
EnableException = $True
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Set-GPWmiFilterAssignment @SetGPWmiFilterAssignmentSplat
|
||||
}
|
||||
}
|
||||
}
|
||||
Invoke-Command @InvokeCommandSplat
|
||||
}
|
||||
|
||||
ForEach ($OU in $GroupPolicy.LinkedOUs) {
|
||||
If (Test-Path "AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))") {
|
||||
Try {
|
||||
Write-Host "Linking policy '$($NewGPO.DisplayName)' to OU '$($OU)' ..."
|
||||
$NewGPLinkSplat = @{
|
||||
Name = $NewGPO.DisplayName
|
||||
Target = $OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext)
|
||||
}
|
||||
New-GPLink @NewGPLinkSplat | Out-Null
|
||||
}
|
||||
Catch {
|
||||
$ParseErrors += "Could not link GPO '$($NewGPO.DisplayName)' to OU '$($OU)'"
|
||||
Continue
|
||||
}
|
||||
}
|
||||
Else {
|
||||
$ParseErrors += "Path not accessible (referred to by '$($NewGPO.DisplayName)'): 'AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))'"
|
||||
Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
If ($ParseErrors) {
|
||||
Throw "One or more errors occurred:`n$($ParseErrors -join "`n")"
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#Requires -Modules 'ActiveDirectory','powershell-yaml'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$PSDrive = Get-PSDrive -Name 'AD'
|
||||
If ([boolean]$PSDrive -eq $False) {
|
||||
$NewPSDriveSplat = @{
|
||||
Name = 'ADDS'
|
||||
Root = ''
|
||||
PSProvider = 'ActiveDirectory'
|
||||
}
|
||||
$PSDrive = New-PSDrive @NewPSDriveSplat
|
||||
}
|
||||
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.yml')
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$WhiteList = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
$GetADObjectSplat = @{
|
||||
Filter = '*'
|
||||
SearchBase = 'DC=' + $Parameter['addsconfig.domainname'].Replace('.', ',DC=')
|
||||
SearchScope = 'OneLevel'
|
||||
}
|
||||
$WhiteListedOUs = @()
|
||||
ForEach ($OU in $WhiteList.WhiteListedOUs) {
|
||||
$WhiteListedOUs += Get-ADObject @GetADObjectSplat | Where-Object {
|
||||
$_.DistinguishedName -match $OU
|
||||
}
|
||||
}
|
||||
$ParentContainers = Get-ADObject @GetADObjectSplat | Where-Object {
|
||||
('builtinDomain', 'container', 'organizationalUnit', <#'lostAndFound',#> 'msDS-QuotaContainer', 'msTPM-InformationObjectsContainer') -contains $_.ObjectClass
|
||||
}
|
||||
|
||||
ForEach ($Parent in $ParentContainers) {
|
||||
If ($WhiteListedOUs.DistinguishedName -notcontains $Parent.DistinguishedName) {
|
||||
ForEach ($SecurityPrincipal in $WhiteList.LimitedSecurityPrincipals) {
|
||||
$GetACLSPlat = @{
|
||||
Path = "$($PSDrive.Name):\$($Parent.DistinguishedName)"
|
||||
}
|
||||
$ACL = Get-ACL @GetACLSPlat
|
||||
|
||||
$GetADObjectSplat = @{
|
||||
Filter = "sAMAccountName -eq '$($SecurityPrincipal)'"
|
||||
Properties = 'objectSID'
|
||||
}
|
||||
$NewACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
|
||||
(Get-ADObject @GetADObjectSplat).objectSID,
|
||||
[System.DirectoryServices.ActiveDirectoryRights]"GenericAll",
|
||||
[System.Security.AccessControl.AccessControlType]"Deny",
|
||||
[System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"
|
||||
)
|
||||
$ACL.AddAccessRule($NewACE)
|
||||
|
||||
$SetAclSplat = @{
|
||||
Path = "$($PSDrive.Name):\$($Parent.DistinguishedName)"
|
||||
AclObject = $ACL
|
||||
ErrorAction = 'Continue'
|
||||
}
|
||||
Set-Acl @SetAclSplat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
If ([boolean]$PSDrive.Name -eq 'ADDS') {
|
||||
$RemovePSDriveSplat = @{
|
||||
Name = 'ADDS'
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Remove-PSDrive @RemovePSDriveSplat | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
WhiteListedOUs: [] # Entries will be concatenated with ',DC=<example>,DC=<org>' automatically
|
||||
#- OU=User accounts
|
||||
LimitedSecurityPrincipals: []
|
||||
#- Servicedesk employees
|
||||
@@ -1,34 +0,0 @@
|
||||
#Requires -Modules 'ActiveDirectory'
|
||||
Param(
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$Parameter
|
||||
)
|
||||
|
||||
# Only executed on primary or standalone Domain Controller
|
||||
If (@('primary','standalone') -contains $Parameter['deployment.type']) {
|
||||
$GetContentSplat = @{
|
||||
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".yml")
|
||||
Raw = $True
|
||||
}
|
||||
$RawContent = Get-Content @GetContentSplat
|
||||
$ConvertFromYamlSplat = @{
|
||||
Yaml = $RawContent
|
||||
AllDocuments = $True
|
||||
}
|
||||
$Policy = ConvertFrom-Yaml @ConvertFromYamlSplat
|
||||
|
||||
$SetADDefaultDomainPasswordPolicySplat = @{
|
||||
Identity = $Parameter['addsconfig.domainname']
|
||||
ComplexityEnabled = [Convert]::ToBoolean($Policy.Password.RequireComplexity)
|
||||
LockoutThreshold = [uint32]$Policy.Account.Lockout.Threshold
|
||||
# LockoutDuration = [timespan]$Policy.Account.Lockout.Duration
|
||||
# LockoutObservationWindow = [timespan]$Policy.Account.Lockout.ObservationWindow
|
||||
MaxPasswordAge = [timespan]$Policy.Password.Age.Maximum
|
||||
MinPasswordAge = [timespan]$Policy.Password.Age.Minimum
|
||||
MinPasswordLength = [uint32]$Policy.Password.Length.Minimum
|
||||
PasswordHistoryCount = [uint32]$Policy.Password.History
|
||||
ReversibleEncryptionEnabled = [Convert]::ToBoolean($Policy.Password.ReversibleEncryption)
|
||||
Confirm = $False
|
||||
}
|
||||
Set-ADDefaultDomainPasswordPolicy @SetADDefaultDomainPasswordPolicySplat
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
Account:
|
||||
Lockout:
|
||||
Threshold: 0
|
||||
# Duration: '00:15:00.00'
|
||||
# ObservationWindow: '00:05:00.00'
|
||||
Password:
|
||||
RequireComplexity: True
|
||||
Age:
|
||||
Minimum: 0
|
||||
Maximum: 0
|
||||
Length:
|
||||
Minimum: 10
|
||||
History: 0
|
||||
ReversibleEncryption: False
|
||||
@@ -0,0 +1,8 @@
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
|
||||
netsh advfirewall firewall set rule group="Windows Remote Management" new enable=yes
|
||||
$winrmService = Get-Service -Name WinRM
|
||||
if ($winrmService.Status -eq "Running"){
|
||||
Disable-PSRemoting -Force
|
||||
}
|
||||
Stop-Service winrm
|
||||
Set-Service -Name winrm -StartupType Disabled
|
||||
@@ -0,0 +1,18 @@
|
||||
$NetworkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
|
||||
$Connections = $NetworkListManager.GetNetworkConnections()
|
||||
$Connections | ForEach-Object { $_.GetNetwork().SetCategory(1) }
|
||||
|
||||
Enable-PSRemoting -Force
|
||||
winrm quickconfig -q
|
||||
winrm quickconfig -transport:http
|
||||
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
|
||||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="800"}'
|
||||
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
||||
winrm set winrm/config/service/auth '@{Basic="true"}'
|
||||
winrm set winrm/config/client/auth '@{Basic="true"}'
|
||||
winrm set winrm/config/listener?Address=*+Transport=HTTP '@{Port="5985"}'
|
||||
netsh advfirewall firewall set rule group="Windows Remote Administration" new enable=yes
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=allow
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public new remoteip=any
|
||||
Set-Service winrm -startuptype "auto"
|
||||
Restart-Service winrm
|
||||
@@ -0,0 +1,2 @@
|
||||
@rem Silent mode, basic UI, no reboot
|
||||
e:\setup64 /s /v "/qb REBOOT=R"
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<BlockList>
|
||||
<!-- services to disable -->
|
||||
<Services>
|
||||
<Name>MVMCP2VAgent</Name>
|
||||
<Name>VMTools</Name>
|
||||
<Name> VMUpgradeHelper </Name>
|
||||
<Name> vmvss </Name>
|
||||
<Name>vmdesched</Name>
|
||||
<Name>Virtual Server</Name>
|
||||
<!-- Virtual Machine Helper -->
|
||||
<Name>vmh</Name>
|
||||
<!-- Xen-specific service -->
|
||||
<Name>xensvc</Name>
|
||||
</Services>
|
||||
<!-- drivers to disable -->
|
||||
<Drivers>
|
||||
<Name>vmx_svga</Name>
|
||||
<Name>vmmouse</Name>
|
||||
<Name>vmscsi</Name>
|
||||
<Name>amdpcn</Name>
|
||||
<Name>PCnet</Name>
|
||||
<Name>VMMEMCTL</Name>
|
||||
|
||||
<Name> pvscsi </Name>
|
||||
<Name> vmci </Name>
|
||||
<Name> vmmouse </Name>
|
||||
<Name> vmaudio </Name>
|
||||
<Name> vmrawdsk </Name>
|
||||
<Name> vmxnet </Name>
|
||||
<Name> vmxnet3ndis6 </Name>
|
||||
<Name> vm3dmp </Name>
|
||||
<Name> vmdebug </Name>
|
||||
<Name> vmxnet3ndis5 </Name>
|
||||
|
||||
|
||||
<Name>cirrus</Name>
|
||||
<!-- storage drivers -->
|
||||
<Name>buslogic</Name>
|
||||
<Name>symc810</Name>
|
||||
<Name>cpqarray</Name>
|
||||
<Name>pcntn4m</Name>
|
||||
<Name>cpqnf3</Name>
|
||||
<Name>MRaidNT</Name>
|
||||
<Name>Symc8XX</Name>
|
||||
<!-- VIA chipset drivers -->
|
||||
<Name>viaide</Name>
|
||||
<Name>VIAudio</Name>
|
||||
<Name>VIAPFD</Name>
|
||||
<Name>viafilter</Name>
|
||||
<Name>viaagp</Name>
|
||||
<Name>viaagp1</Name>
|
||||
<!-- network drivers: Intel(R) PRO/100 -->
|
||||
<Name>E100B</Name>
|
||||
<!-- tape drivers -->
|
||||
<Name>4mmdat</Name>
|
||||
<Name>4mmdat-SeSFT</Name>
|
||||
<Name>SCSIChanger</Name>
|
||||
|
||||
<!-- Virtual Machine Monitor -->
|
||||
<Name>vmm</Name>
|
||||
<!-- Xen-specific drivers -->
|
||||
<Name>xenevtchn</Name>
|
||||
<Name>xenvbd</Name>
|
||||
<Name>xennet</Name>
|
||||
</Drivers>
|
||||
<Programs>
|
||||
<Name>ProMON</Name>
|
||||
<Name>s3tray2</Name>
|
||||
<Name>VMwareTray</Name>
|
||||
<Name>VMwareUser</Name>
|
||||
</Programs>
|
||||
</BlockList>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -16,7 +16,7 @@ $PowerCliConfigurationSplat = @{
|
||||
Confirm = $False
|
||||
InvalidCertificateAction = 'Ignore'
|
||||
}
|
||||
Set-PowerCLIConfiguration @PowerCliConfigurationSplat
|
||||
Set-PowerCLIConfiguration @PowerCliConfigurationSplat | Out-Null
|
||||
|
||||
$ConnectVIServerSplat = @{
|
||||
Server = $VSphereFQDN
|
||||
@@ -26,14 +26,26 @@ $ConnectVIServerSplat = @{
|
||||
}
|
||||
Connect-VIServer @ConnectVIServerSplat | Out-Null
|
||||
|
||||
$GetVMSplat = @{
|
||||
Name = "*$($VMName)*"
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
If ([boolean](Get-VM @GetVMSplat)) {
|
||||
$RemoveVMSplat = @{
|
||||
VM = "$($VMName)*"
|
||||
VM = Get-VM @GetVMSplat
|
||||
DeletePermanently = $True
|
||||
Confirm = $False
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Remove-VM @RemoveVMSplat
|
||||
|
||||
# Also delete ISO/floppy?
|
||||
}
|
||||
|
||||
Disconnect-VIServer * -Confirm:$False
|
||||
|
||||
$RemoveItemSplat = @{
|
||||
Path = "/scratch/*"
|
||||
Recurse = $True
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Remove-Item @RemoveItemSplat
|
||||
@@ -0,0 +1,23 @@
|
||||
# You cannot enable Windows PowerShell Remoting on network connections that are set to Public
|
||||
# Spin through all the network locations and if they are set to Public, set them to Private
|
||||
# using the INetwork interface:
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa370750(v=vs.85).aspx
|
||||
# For more info, see:
|
||||
# http://blogs.msdn.com/b/powershell/archive/2009/04/03/setting-network-location-to-private.aspx
|
||||
|
||||
# Network location feature was only introduced in Windows Vista - no need to bother with this
|
||||
# if the operating system is older than Vista
|
||||
if([environment]::OSVersion.version.Major -lt 6) { return }
|
||||
|
||||
# You cannot change the network location if you are joined to a domain, so abort
|
||||
if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { return }
|
||||
|
||||
# Get network connections
|
||||
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
|
||||
$connections = $networkListManager.GetNetworkConnections()
|
||||
|
||||
$connections |foreach {
|
||||
Write-Host $_.GetNetwork().GetName()"category was previously set to"$_.GetNetwork().GetCategory()
|
||||
$_.GetNetwork().SetCategory(1)
|
||||
Write-Host $_.GetNetwork().GetName()"changed to category"$_.GetNetwork().GetCategory()
|
||||
}
|
||||
@@ -1,21 +1,17 @@
|
||||
DeploymentConfigurations:
|
||||
- Id: primary
|
||||
Label: Primary (redundant deployment)
|
||||
Description: Initial Domain Controller with 'PDC Emulator'-role
|
||||
- Id: secondary
|
||||
Label: Secondary (redundant deployment)
|
||||
Description: Additional Domain Controller
|
||||
- Id: domainmember
|
||||
Label: Domain member
|
||||
Description: Windows 10 client joined to an Active Directory domain
|
||||
- Id: standalone
|
||||
Label: Stand-alone (non-redundant deployment)
|
||||
Description: Single Domain Controller
|
||||
Label: Stand-alone
|
||||
Description: Stand-alone Windows 10 client
|
||||
PropertyCategories:
|
||||
- Name: ''
|
||||
- Name: 0) Deployment information
|
||||
ProductProperties:
|
||||
- Key: deployment.type
|
||||
Type: string
|
||||
Value:
|
||||
- primary
|
||||
- secondary
|
||||
- domainmember
|
||||
- standalone
|
||||
UserConfigurable: false
|
||||
- Name: 1) Operating System
|
||||
@@ -27,6 +23,22 @@ PropertyCategories:
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: guestinfo.administratorpw
|
||||
Type: password(7..)
|
||||
Label: Local administrator password*
|
||||
Description: Must meet password complexity rules
|
||||
DefaultValue: password
|
||||
Configurations:
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: guestinfo.ntpserver
|
||||
Type: string(1..)
|
||||
Label: Time server*
|
||||
Description: A comma-separated list of timeservers
|
||||
DefaultValue: 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org
|
||||
Configurations:
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Name: 2) Networking
|
||||
ProductProperties:
|
||||
- Key: guestinfo.ipaddress
|
||||
@@ -46,10 +58,9 @@ PropertyCategories:
|
||||
- Key: guestinfo.dnsserver
|
||||
Type: ip
|
||||
Label: DNS server*
|
||||
Description: Specify IP address of existing primary Domain Controller
|
||||
DefaultValue: '127.0.0.1'
|
||||
Configurations:
|
||||
- secondary
|
||||
Description: ''
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: guestinfo.gateway
|
||||
Type: ip
|
||||
@@ -58,129 +69,31 @@ PropertyCategories:
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Name: 3) Active Directory Domain Services
|
||||
- Name: 3) Active Directory membership
|
||||
ProductProperties:
|
||||
- Key: addsconfig.domainname
|
||||
Type: string(5..)
|
||||
Type: string(1..)
|
||||
Label: Domain name*
|
||||
Description: 'Must be a valid FQDN'
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
Description: Must be able to be resolved through provided DNS server
|
||||
DefaultValue: example.org
|
||||
Configurations:
|
||||
- domainmember
|
||||
UserConfigurable: true
|
||||
- Key: addsconfig.netbiosname
|
||||
Type: string(1..15)
|
||||
Label: Domain short name (NetBIOS)*
|
||||
Description: '(max length: 15 characters)'
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: addsconfig.administratorpw
|
||||
Type: password(7..)
|
||||
Label: Domain Administrator password*
|
||||
Description: Must meet password complexity rules
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: addsconfig.safemodepw
|
||||
Type: password(7..)
|
||||
Label: Safe-mode password*
|
||||
Description: Must meet password complexity rules
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: addsconfig.ntpserver
|
||||
- Key: addsconfig.username
|
||||
Type: string(1..)
|
||||
Label: Time server*
|
||||
Description: A comma-separated list of upstream timeservers
|
||||
DefaultValue: 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org
|
||||
Configurations:
|
||||
- primary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Name: 4) Credential Management
|
||||
ProductProperties:
|
||||
- Key: vault.api
|
||||
Type: string
|
||||
Label: Vault API address
|
||||
Description: The uri on which a HashiCorp Vault REST API can be reached
|
||||
DefaultValue: ''
|
||||
Configurations:
|
||||
- primary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: vault.token
|
||||
Type: password
|
||||
Label: Vault API token
|
||||
Description: An access token which has permissions to read/write to the Vault secrets engine
|
||||
DefaultValue: ''
|
||||
Configurations:
|
||||
- primary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: vault.pwpolicy
|
||||
Type: string
|
||||
Label: Vault password policy
|
||||
Description: A Vault password policy which determines complexity rules for generated passwords
|
||||
DefaultValue: ''
|
||||
Configurations:
|
||||
- primary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: vault.secret
|
||||
Type: string
|
||||
Label: Vault secret name
|
||||
Description: The name of the secret that all generated passwords will be stored in (as key/value pairs)
|
||||
DefaultValue: ''
|
||||
Configurations:
|
||||
- primary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Name: 5) DHCP default scope
|
||||
ProductProperties:
|
||||
- Key: dhcpconfig.startip
|
||||
Type: ip
|
||||
Label: Start IP address
|
||||
Label: Domain account username*
|
||||
Description: ''
|
||||
DefaultValue: '0.0.0.0'
|
||||
DefaultValue: username
|
||||
Configurations:
|
||||
- secondary
|
||||
- standalone
|
||||
- domainmember
|
||||
UserConfigurable: true
|
||||
- Key: dhcpconfig.endip
|
||||
Type: ip
|
||||
Label: End IP address
|
||||
- Key: addsconfig.password
|
||||
Type: password(1..)
|
||||
Label: Domain account password*
|
||||
Description: ''
|
||||
DefaultValue: '0.0.0.0'
|
||||
DefaultValue: password
|
||||
Configurations:
|
||||
- secondary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: dhcpconfig.subnetmask
|
||||
Type: ip
|
||||
Label: Subnet mask
|
||||
Description: ''
|
||||
DefaultValue: '255.255.255.0'
|
||||
Configurations:
|
||||
- secondary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: dhcpconfig.gateway
|
||||
Type: ip
|
||||
Label: Gateway IP address
|
||||
Description: ''
|
||||
DefaultValue: '0.0.0.0'
|
||||
Configurations:
|
||||
- secondary
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Key: dhcpconfig.leaseduration
|
||||
Type: string(1..)
|
||||
Label: Lease duration
|
||||
Description: 'Enter as timestamp format (DD.HH:MM:SS.FFFF), or as a number of seconds'
|
||||
DefaultValue: '01:00:00.00'
|
||||
Configurations:
|
||||
- secondary
|
||||
- standalone
|
||||
- domainmember
|
||||
UserConfigurable: true
|
||||
AdvancedOptions:
|
||||
- Key: appliance.name
|
||||
|
||||
+30
-86
@@ -1,4 +1,3 @@
|
||||
#Requires -Modules 'ADDSDeployment'
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
# No parameters
|
||||
@@ -36,14 +35,11 @@ foreach ($ovfProperty in $ovfProperties) {
|
||||
|
||||
# Check for mandatory values
|
||||
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'), @()
|
||||
'domainmember' {
|
||||
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.gateway', 'addsconfig.domainname', 'addsconfig.username', 'addsconfig.password'), @()
|
||||
}
|
||||
'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'), @()
|
||||
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.gateway', 'guestinfo.administratorpw', 'guestinfo.ntpserver'), @()
|
||||
}
|
||||
default {
|
||||
# Mandatory values missing, cannot provision.
|
||||
@@ -156,12 +152,33 @@ If ((Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration').IPAddress -NotCon
|
||||
$ErrorActionPreference, $OldErrorActionPreference = $OldErrorActionPreference, $NULL
|
||||
}
|
||||
|
||||
# Promote to Domain Controller
|
||||
If ((4,5) -NotContains (Get-WmiObject -Class 'Win32_ComputerSystem').DomainRole) {
|
||||
Switch ($ovfPropertyValues['deployment.type']) {
|
||||
'domainmember' {
|
||||
# Join Active Directory domain as member
|
||||
If (!(Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain) {
|
||||
$AddComputerSplat = @{
|
||||
DomainName = $ovfPropertyValues['addsconfig.domainname']
|
||||
Credential = New-Object System.Management.Automation.PSCredential(
|
||||
$ovfPropertyValues['addsconfig.username'],
|
||||
(ConvertTo-SecureString $ovfPropertyValues['addsconfig.password'] -AsPlainText -Force)
|
||||
)
|
||||
# OUPath = $ovfPropertyValues['addsconfig.organizationalunit']
|
||||
Restart = $True
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Add-Computer @AddComputerSplat
|
||||
|
||||
# Previous cmdlet performs a reboot on completion; so these are commented out
|
||||
# Restart-Computer -Force
|
||||
# Exit
|
||||
}
|
||||
}
|
||||
'standalone' {
|
||||
# Change password of built-in Administrator
|
||||
$BuiltinAdministrator = (Get-LocalUser | Where-Object {$_.SID -match '-500'})
|
||||
$ConvertToSecureStringSplat = @{
|
||||
String = $ovfPropertyValues['addsconfig.administratorpw']
|
||||
String = $ovfPropertyValues['guestinfo.administratorpw']
|
||||
AsPlainText = $True
|
||||
Force = $True
|
||||
}
|
||||
@@ -176,86 +193,13 @@ If ((4,5) -NotContains (Get-WmiObject -Class 'Win32_ComputerSystem').DomainRole)
|
||||
}
|
||||
Set-LocalUser @SetLocalUserSplat
|
||||
|
||||
$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'
|
||||
$EnableLocalUserSplat = @{
|
||||
InputObject = $BuiltinAdministrator
|
||||
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 'FirstBoot' /DISABLE
|
||||
Stop-Computer -Force
|
||||
Exit
|
||||
Enable-LocalUser @EnableLocalUserSplat
|
||||
}
|
||||
}
|
||||
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 'FirstBoot' /DISABLE
|
||||
Stop-Computer -Force
|
||||
Exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Wait for Active Directory to become available
|
||||
$Timestamp, $TimeoutMinutes = (Get-Date), 15
|
||||
Do {
|
||||
If ($Timestamp.AddMinutes($TimeoutMinutes) -lt (Get-Date)) {
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
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)
|
||||
Reference in New Issue
Block a user