Files
SGU-CredentialProvider/scripts/Deploy-SguAzureInfrastructure.ps1
T

157 lines
7.5 KiB
PowerShell

#Requires -Version 5.1
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)][string]$SubscriptionId,
[string]$ResourceGroupName = 'rg-sgu-lab',
[string]$Location = 'centralus',
[string]$DeploymentPrefix = 'sgu-lab',
[Parameter(Mandatory)][string]$AdministratorUsername,
[securestring]$AdministratorPassword,
[string]$P2sRootCertificatePath,
[bool]$DeployVpnGateway = $true,
[string]$ComputerName = 'SGU-DC01',
[string]$VmSize = 'Standard_D2s_v5',
[string]$VirtualNetworkAddressPrefix = '10.77.0.0/16',
[string]$DomainControllerSubnetPrefix = '10.77.0.0/24',
[ipaddress]$DomainControllerPrivateIp = '10.77.0.4',
[string]$GatewaySubnetPrefix = '10.77.255.0/27',
[string]$VpnClientAddressPoolPrefix = '172.30.0.0/24',
[string[]]$PublicEnrollmentSourceAddressPrefixes = @(),
[string]$AdministratorSourceAddressPrefix = '',
[string]$TemplateFile = (Join-Path $PSScriptRoot '..\infra\azure\main.bicep')
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
if (-not (Get-Command az -ErrorAction SilentlyContinue)) {
throw 'Azure CLI is required. Install it from https://aka.ms/installazurecliwindows and run az login.'
}
if (-not (Test-Path -LiteralPath $TemplateFile -PathType Leaf)) {
throw "Azure Bicep template not found: $TemplateFile"
}
if (-not $AdministratorPassword) {
$AdministratorPassword = Read-Host 'Password for the local Azure VM administrator' -AsSecureString
}
$rootCertificateData = ''
if ($DeployVpnGateway) {
if (-not $P2sRootCertificatePath -or
-not (Test-Path -LiteralPath $P2sRootCertificatePath -PathType Leaf)) {
throw 'P2sRootCertificatePath is required when DeployVpnGateway is true.'
}
$rootCertificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new(
(Resolve-Path -LiteralPath $P2sRootCertificatePath).Path)
if (-not ($rootCertificate.Extensions | Where-Object {
$_.Oid -and $_.Oid.Value -eq '2.5.29.19' -and $_.Format($false) -match 'CA' })) {
throw 'P2sRootCertificatePath must contain a certificate-authority certificate.'
}
$rootCertificateData = [Convert]::ToBase64String($rootCertificate.RawData)
}
$account = & az account show --output json 2>$null
if ($LASTEXITCODE -ne 0) {
throw 'Azure CLI is not signed in. Run az login, then retry.'
}
& az account set --subscription $SubscriptionId --only-show-errors
if ($LASTEXITCODE -ne 0) {
throw "Could not select Azure subscription $SubscriptionId."
}
$deploymentDescription = if ($DeployVpnGateway) {
'Create Azure VNet, Windows Server 2025 VM, public IP, and P2S VPN Gateway'
}
else {
'Create Azure VNet, Windows Server 2025 VM, and public IP for direct enrollment'
}
if ($PSCmdlet.ShouldProcess("$ResourceGroupName in $Location", $deploymentDescription)) {
& az group create --name $ResourceGroupName --location $Location --only-show-errors --output none
if ($LASTEXITCODE -ne 0) {
throw "Could not create or update resource group $ResourceGroupName."
}
$temporaryRoot = Join-Path ([IO.Path]::GetTempPath()) ("sgu-azure-" + [Guid]::NewGuid().ToString('N'))
$parametersPath = Join-Path $temporaryRoot 'parameters.json'
$passwordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($AdministratorPassword)
try {
New-Item -ItemType Directory -Path $temporaryRoot -Force | Out-Null
$acl = Get-Acl -LiteralPath $temporaryRoot
$acl.SetAccessRuleProtection($true, $false)
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
[Security.Principal.WindowsIdentity]::GetCurrent().User,
[Security.AccessControl.FileSystemRights]::FullControl,
[Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
[Security.AccessControl.PropagationFlags]::None,
[Security.AccessControl.AccessControlType]::Allow))
Set-Acl -LiteralPath $temporaryRoot -AclObject $acl
$plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($passwordPointer)
$parameters = [ordered]@{
'$schema' = 'https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#'
contentVersion = '1.0.0.0'
parameters = [ordered]@{
deploymentPrefix = @{ value = $DeploymentPrefix }
location = @{ value = $Location }
administratorUsername = @{ value = $AdministratorUsername }
administratorPassword = @{ value = $plainPassword }
computerName = @{ value = $ComputerName }
vmSize = @{ value = $VmSize }
virtualNetworkAddressPrefix = @{ value = $VirtualNetworkAddressPrefix }
domainControllerSubnetPrefix = @{ value = $DomainControllerSubnetPrefix }
gatewaySubnetPrefix = @{ value = $GatewaySubnetPrefix }
domainControllerPrivateIp = @{ value = $DomainControllerPrivateIp.IPAddressToString }
vpnClientAddressPoolPrefix = @{ value = $VpnClientAddressPoolPrefix }
deployVpnGateway = @{ value = $DeployVpnGateway }
p2sRootCertificateData = @{ value = $rootCertificateData }
publicEnrollmentSourceAddressPrefixes = @{ value = @($PublicEnrollmentSourceAddressPrefixes) }
administratorSourceAddressPrefix = @{ value = $AdministratorSourceAddressPrefix }
}
}
[IO.File]::WriteAllText(
$parametersPath,
($parameters | ConvertTo-Json -Depth 8),
[Text.UTF8Encoding]::new($false))
$plainPassword = $null
$parameters.parameters.administratorPassword.value = $null
$deploymentName = 'sgu-{0}' -f (Get-Date -Format 'yyyyMMdd-HHmmss')
$deploymentOutput = & az deployment group create `
--name $deploymentName `
--resource-group $ResourceGroupName `
--template-file (Resolve-Path -LiteralPath $TemplateFile).Path `
--parameters "@$parametersPath" `
--only-show-errors `
--output json
if ($LASTEXITCODE -ne 0) {
throw 'Azure deployment failed. Review the Azure CLI error above; no bootstrap credential was persisted by this script.'
}
$deployment = ($deploymentOutput -join [Environment]::NewLine) | ConvertFrom-Json
}
finally {
if ($passwordPointer -ne [IntPtr]::Zero) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($passwordPointer)
}
$AdministratorPassword = $null
if ($temporaryRoot -and (Test-Path -LiteralPath $temporaryRoot)) {
Remove-Item -LiteralPath $temporaryRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
$values = @{}
foreach ($property in $deployment.properties.outputs.PSObject.Properties) {
$values[$property.Name] = $property.Value.value
}
[pscustomobject]@{
ResourceGroupName = $ResourceGroupName
DeploymentName = $deploymentName
DomainControllerName = $values.domainControllerName
DomainControllerPrivateIp = $values.domainControllerPrivateIp
DomainControllerPublicIp = $values.domainControllerPublicIp
VpnGatewayName = $values.vpnGatewayName
VpnClientAddressPoolPrefix = $values.vpnClientAddressPoolPrefix
DeployVpnGateway = $DeployVpnGateway
PublicEnrollmentSourceAddressPrefixes = @($PublicEnrollmentSourceAddressPrefixes)
ServerBootstrapArguments = $values.serverBootstrapArguments
}
}