Files
SGU-CredentialProvider/scripts/Install-SguEnrollmentGuard.ps1

153 lines
6.5 KiB
PowerShell

[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)]
[string]$PublishPath,
[Parameter(Mandatory)]
[ValidatePattern('^https://')]
[string]$BrokerEndpoint,
[Parameter(Mandatory)]
[ValidatePattern('^[0-9A-Fa-f ]{40,59}$')]
[string]$ClientCertificateThumbprint,
[Parameter(Mandatory)]
[ValidatePattern('^[0-9A-Fa-f ]{40,59}$')]
[string]$ServerCertificateThumbprint,
[string]$DomainNetbios = 'LCI',
[ValidateRange(2, 90)]
[int]$TimeoutSeconds = 90,
[string]$RemoteDesktopPrincipal = 'LCI\SG-Laboratorio-Usuarios-RDP',
[string]$DotNetRuntimeInstallerPath,
[string]$RustDeskServerAddress,
[string]$RustDeskServerPublicKey
)
$ErrorActionPreference = 'Stop'
$taskName = 'SGU-CredentialProvider-EnrollmentGuard'
$enrollmentRoot = Join-Path $env:ProgramData 'SGU\Enrollment'
$sourceScripts = @(
'Install-CredentialProvider.ps1',
'Enable-LabRemoteAccess.ps1',
'Enable-SguClientMonitoring.ps1',
'Install-SguRustDeskClient.ps1',
'Set-SguStandardLocalUser.ps1',
'Test-SguClientEnrollment.ps1',
'Repair-SguClientEnrollment.ps1'
)
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this script from an elevated Windows PowerShell session.'
}
foreach ($scriptName in $sourceScripts) {
if (-not (Test-Path -LiteralPath (Join-Path $PSScriptRoot $scriptName) -PathType Leaf)) {
throw "$scriptName must be beside Install-SguEnrollmentGuard.ps1."
}
}
$requiredProviderFile = Join-Path $PublishPath 'SGU.CredentialProvider.comhost.dll'
if (-not (Test-Path -LiteralPath $requiredProviderFile -PathType Leaf)) {
throw 'PublishPath does not contain the Credential Provider package.'
}
if ($DotNetRuntimeInstallerPath -and
-not (Test-Path -LiteralPath $DotNetRuntimeInstallerPath -PathType Leaf)) {
throw 'DotNetRuntimeInstallerPath does not exist.'
}
if ([string]::IsNullOrWhiteSpace($RustDeskServerAddress) -xor
[string]::IsNullOrWhiteSpace($RustDeskServerPublicKey)) {
throw 'RustDeskServerAddress and RustDeskServerPublicKey must be supplied together.'
}
if ($PSCmdlet.ShouldProcess($enrollmentRoot, 'Install the SGU enrollment repair guard')) {
New-Item -ItemType Directory -Path $enrollmentRoot -Force | Out-Null
$packageId = '{0}-{1}' -f (Get-Date -Format 'yyyyMMddHHmmss'), ([Guid]::NewGuid().ToString('N').Substring(0, 8))
$guardPublishPath = Join-Path $enrollmentRoot "packages\$packageId"
New-Item -ItemType Directory -Path $guardPublishPath -Force | Out-Null
Copy-Item -Path (Join-Path $PublishPath '*') -Destination $guardPublishPath -Recurse -Force
foreach ($scriptName in $sourceScripts) {
Copy-Item -LiteralPath (Join-Path $PSScriptRoot $scriptName) `
-Destination (Join-Path $enrollmentRoot $scriptName) -Force
}
$guardRuntimeInstaller = $null
if ($DotNetRuntimeInstallerPath) {
$runtimeDirectory = Join-Path $enrollmentRoot 'prerequisites'
New-Item -ItemType Directory -Path $runtimeDirectory -Force | Out-Null
$guardRuntimeInstaller = Join-Path $runtimeDirectory (Split-Path $DotNetRuntimeInstallerPath -Leaf)
$sourceRuntimeInstaller = [IO.Path]::GetFullPath($DotNetRuntimeInstallerPath)
$destinationRuntimeInstaller = [IO.Path]::GetFullPath($guardRuntimeInstaller)
if (-not $sourceRuntimeInstaller.Equals($destinationRuntimeInstaller, [StringComparison]::OrdinalIgnoreCase)) {
Copy-Item -LiteralPath $DotNetRuntimeInstallerPath -Destination $guardRuntimeInstaller -Force
}
}
$guardConfiguration = [ordered]@{
PublishPath = $guardPublishPath
BrokerEndpoint = $BrokerEndpoint
ClientCertificateThumbprint = ($ClientCertificateThumbprint -replace ' ', '')
ServerCertificateThumbprint = ($ServerCertificateThumbprint -replace ' ', '')
DomainNetbios = $DomainNetbios
TimeoutSeconds = $TimeoutSeconds
RemoteDesktopPrincipal = $RemoteDesktopPrincipal
DotNetRuntimeInstallerPath = $guardRuntimeInstaller
RustDeskServerAddress = $RustDeskServerAddress
RustDeskServerPublicKey = $RustDeskServerPublicKey
}
$configurationPath = Join-Path $enrollmentRoot 'enrollment.json'
[IO.File]::WriteAllText(
$configurationPath,
($guardConfiguration | ConvertTo-Json),
[Text.UTF8Encoding]::new($false))
$acl = Get-Acl -LiteralPath $enrollmentRoot
$acl.SetAccessRuleProtection($true, $false)
# Well-known SIDs are invariant across localized Windows installations.
$systemSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-18')
$administratorsSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-544')
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
$systemSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'))
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
$administratorsSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'))
Set-Acl -LiteralPath $enrollmentRoot -AclObject $acl
$repairScript = Join-Path $enrollmentRoot 'Repair-SguClientEnrollment.ps1'
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$action = New-ScheduledTaskAction -Execute $powerShell `
-Argument "-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$repairScript`""
$startupTrigger = New-ScheduledTaskTrigger -AtStartup
# Give domain networking and Group Policy time to initialize before the
# repair script resolves domain principals and validates remote access.
$startupTrigger.Delay = 'PT1M'
$triggers = @(
$startupTrigger,
(New-ScheduledTaskTrigger -Daily -At '3:00 AM')
)
$settings = New-ScheduledTaskSettingsSet `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 5) `
-RestartCount 3 `
-RestartInterval (New-TimeSpan -Minutes 1)
Register-ScheduledTask -TaskName $taskName `
-Action $action `
-Trigger $triggers `
-Settings $settings `
-User 'SYSTEM' `
-RunLevel Highest `
-Force | Out-Null
& $repairScript -ConfigurationPath $configurationPath | Out-Null
}
[pscustomobject]@{
EnrollmentRoot = $enrollmentRoot
TaskName = $taskName
TaskState = (Get-ScheduledTask -TaskName $taskName).State
ConfigurationPath = Join-Path $enrollmentRoot 'enrollment.json'
}