[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, 60)] [int]$TimeoutSeconds = 35, [string]$RemoteDesktopPrincipal = 'LCI\SG-Laboratorio-Usuarios-RDP', [string]$DotNetRuntimeInstallerPath ) $ErrorActionPreference = 'Stop' $taskName = 'SGU-CredentialProvider-EnrollmentGuard' $enrollmentRoot = Join-Path $env:ProgramData 'SGU\Enrollment' $sourceScripts = @( 'Install-CredentialProvider.ps1', 'Enable-LabRemoteAccess.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 ($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) 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 } $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) $acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new( 'SYSTEM', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) $acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new( 'BUILTIN\Administrators', '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' }