You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

105 lines
6.5 KiB

[CmdletBinding()]
param (
[Parameter(Mandatory)] [string] $LogPath,
[string] $LabName = 'NtfsSecurityOsMatrixLab',
[string] $DomainName = 'osmatrix.net',
[string] $VmPath = 'V:\AutomatedLab-VMs',
[string] $AddressSpace = '192.168.12.0/24',
[string] $PesterModulePath = 'V:\Git\WindowsAccessControl\output\RequiredModules\Pester\5.7.1',
[string] $PowerShell7Msi = 'V:\LabSources\SoftwarePackages\PowerShell-7.6.3-win-x64.msi'
)
# Deploys an isolated AutomatedLab lab for the NTFSSecurity operating-system matrix (Decision 24): one domain controller, three file
# servers (Server 2019, 2022, 2025), and a Windows 11 client, in a domain and on a switch of their own. It touches none of the
# existing labs, machines, switches, or domains, never calls Remove-Lab, and refuses to run when the lab or a machine name exists.
# The installation password is generated here, kept in memory, and stored only where AutomatedLab stores it for every lab.
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$stamp = '[{0:yyyy-MM-dd HH:mm:ss}Z]'
function Write-Step { param ([string] $Message) ($stamp -f [DateTime]::UtcNow) + ' ' + $Message | Add-Content -LiteralPath $LogPath }
$principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'This script must run in an elevated PowerShell session.' }
($stamp -f [DateTime]::UtcNow) + " START deploy-os-matrix-lab lab=$LabName" | Set-Content -LiteralPath $LogPath
try {
Import-Module -Name AutomatedLab -ErrorAction Stop
foreach ($path in $PesterModulePath, $PowerShell7Msi) { if (-not (Test-Path -LiteralPath $path)) { throw "Missing payload: $path" } }
$machines = @(
@{ Name = 'OSDC1'; Os = 'Windows Server 2025 Datacenter (Desktop Experience)'; Roles = @('RootDC'); Memory = 4GB; Address = '192.168.12.10' }
@{ Name = 'OSFile25'; Os = 'Windows Server 2025 Datacenter (Desktop Experience)'; Roles = @('FileServer'); Memory = 3GB; Address = '192.168.12.25' }
@{ Name = 'OSFile22'; Os = 'Windows Server 2022 Datacenter (Desktop Experience)'; Roles = @('FileServer'); Memory = 3GB; Address = '192.168.12.22' }
@{ Name = 'OSFile19'; Os = 'Windows Server 2019 Datacenter (Desktop Experience)'; Roles = @('FileServer'); Memory = 3GB; Address = '192.168.12.19' }
@{ Name = 'OSWin11'; Os = 'Windows 11 Pro'; Roles = @(); Memory = 4GB; Address = '192.168.12.11' }
)
# Collision checks from AutomatedLab metadata and from Hyper-V; the existing labs are only read.
$existingNames = New-Object System.Collections.Generic.List[string]
$labs = @(Get-Lab -List)
if ($labs -contains $LabName) { throw "The lab '$LabName' exists already. Refusing to redefine it." }
foreach ($existing in $labs) {
Import-Lab -Name $existing -NoValidation -NoDisplay -ErrorAction Stop
foreach ($vm in Get-LabVM -IncludeLinux) { $existingNames.Add($vm.Name) }
}
foreach ($vm in Get-VM) { $existingNames.Add($vm.Name) }
$collisions = @($machines.Name | Where-Object { $_ -in $existingNames })
if ($collisions) { throw "Machine name collision: $($collisions -join ', ')" }
if (Get-VMSwitch -Name $LabName -ErrorAction SilentlyContinue) { throw "A virtual switch named '$LabName' exists already." }
$usedAddresses = @(Get-NetIPAddress -AddressFamily IPv4 | ForEach-Object { $_.IPAddress })
if ($usedAddresses | Where-Object { $_ -like '192.168.12.*' }) { throw 'The address space 192.168.12.0/24 is in use on the host.' }
Write-Step ('preflight ok; existing labs: {0}; existing machine names: {1}' -f ($labs -join ', '), $existingNames.Count)
$characters = ([char[]](48..57) + [char[]](65..90) + [char[]](97..122) + '!', '#', '%', '+', '-', '=')
# A cryptographic generator, without the bias of a remainder: this is the installation and domain administrator password of the lab.
$generator = [Security.Cryptography.RandomNumberGenerator]::Create()
$limit = 256 - (256 % $characters.Count)
$buffer = New-Object -TypeName 'byte[]' -ArgumentList 1
$chosen = New-Object -TypeName 'System.Text.StringBuilder'
while ($chosen.Length -lt 24) {
$generator.GetBytes($buffer)
if ($buffer[0] -lt $limit) { $null = $chosen.Append($characters[$buffer[0] % $characters.Count]) }
}
$password = 'Aa1!' + $chosen.ToString()
New-LabDefinition -Name $LabName -DefaultVirtualizationEngine HyperV -VmPath $VmPath
Add-LabVirtualNetworkDefinition -Name $LabName -AddressSpace $AddressSpace
Add-LabDomainDefinition -Name $DomainName -AdminUser 'install' -AdminPassword $password
Set-LabInstallationCredential -Username 'install' -Password $password
foreach ($definition in $machines) {
$parameters = @{
Name = $definition.Name; DomainName = $DomainName; OperatingSystem = $definition.Os; Memory = $definition.Memory
Processors = 2; Network = $LabName; IpAddress = $definition.Address
}
if ($definition.Roles.Count -gt 0) { $parameters.Roles = $definition.Roles }
if ($definition.Os -like 'Windows 11*') {
$parameters.HypervProperties = @{ EnableSecureBoot = 'On'; SecureBootTemplate = 'MicrosoftWindows'; EnableTpm = 'true' }
}
Add-LabMachineDefinition @parameters
}
Write-Step 'lab defined; installing network switches and base images'
Install-Lab -NetworkSwitches -BaseImages
Write-Step 'network switches and base images done'
Install-Lab
Write-Step 'machines, domain, and roles done'
$labMachines = Get-LabVM
Install-LabSoftwarePackage -ComputerName $labMachines -Path $PowerShell7Msi -CommandLine '/quiet /norestart ADD_PATH=1' -Timeout 30
Write-Step 'PowerShell 7 installed'
foreach ($modulesRoot in 'C:\Program Files\WindowsPowerShell\Modules', 'C:\Program Files\PowerShell\Modules') {
$destination = Join-Path $modulesRoot 'Pester'
Invoke-LabCommand -ComputerName $labMachines -ActivityName 'Create the Pester module directory' -ScriptBlock { param ($Path) $null = New-Item -Path $Path -ItemType Directory -Force } -ArgumentList $destination -NoDisplay
Copy-LabFileItem -Path $PesterModulePath -ComputerName $labMachines -DestinationFolderPath $destination -Recurse
}
Write-Step 'Pester 5.7.1 copied'
Show-LabDeploymentSummary -Summary
Write-Step "deploy-os-matrix-lab-DONE"
exit 0
}
catch {
Write-Step ("deploy-os-matrix-lab-FAILED: {0}" -f $_)
$_ | Format-List -Property * -Force | Out-String | Add-Content -LiteralPath $LogPath
exit 1
}