Add six-month domain and broker monitoring
This commit is contained in:
@@ -46,6 +46,8 @@ param(
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$serviceName = 'SGUAuthBroker'
|
||||
$installPath = Join-Path $env:ProgramFiles 'SGU\AuthBroker'
|
||||
$brokerEventLogName = 'SGU Auth Broker'
|
||||
$brokerEventSource = 'SGU.AuthBroker.Operational'
|
||||
$normalizedClientThumbprints = @($AllowedClientThumbprints | ForEach-Object { $_ -replace ' ', '' })
|
||||
if ($normalizedClientThumbprints.Where({ $_.Length -ne 40 }).Count -gt 0) {
|
||||
throw 'Client certificate thumbprints must contain exactly 40 hexadecimal characters.'
|
||||
@@ -129,6 +131,14 @@ foreach ($file in @('SGU.AuthBroker.exe', 'SGU.AuthBroker.dll', 'appsettings.jso
|
||||
}
|
||||
|
||||
$productionSettings = @{
|
||||
Logging = @{
|
||||
EventLog = @{
|
||||
LogLevel = @{
|
||||
Default = 'Information'
|
||||
'Microsoft.AspNetCore' = 'Warning'
|
||||
}
|
||||
}
|
||||
}
|
||||
Kestrel = @{
|
||||
Endpoints = @{
|
||||
Https = @{
|
||||
@@ -143,6 +153,9 @@ $productionSettings = @{
|
||||
}
|
||||
}
|
||||
Broker = @{
|
||||
Diagnostics = @{
|
||||
UseDedicatedEventLog = $true
|
||||
}
|
||||
Tls = @{
|
||||
AllowedClientThumbprints = $normalizedClientThumbprints
|
||||
CheckCertificateRevocation = -not $DisableCertificateRevocationCheckForLab
|
||||
@@ -198,6 +211,18 @@ if ($PSCmdlet.ShouldProcess($installPath, 'Install the SGU Authentication Broker
|
||||
$settingsJson,
|
||||
$utf8WithoutBom)
|
||||
|
||||
if ([Diagnostics.EventLog]::SourceExists($brokerEventSource)) {
|
||||
$registeredLog = [Diagnostics.EventLog]::LogNameFromSourceName($brokerEventSource, '.')
|
||||
if (-not $registeredLog.Equals($brokerEventLogName, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Event source $brokerEventSource is already registered to $registeredLog."
|
||||
}
|
||||
}
|
||||
else {
|
||||
New-EventLog -LogName $brokerEventLogName -Source $brokerEventSource
|
||||
}
|
||||
Limit-EventLog -LogName $brokerEventLogName -MaximumSize 268435456 `
|
||||
-OverflowAction OverwriteAsNeeded
|
||||
|
||||
if (-not (Get-Service -Name $serviceName -ErrorAction SilentlyContinue)) {
|
||||
New-Service -Name $serviceName `
|
||||
-DisplayName 'SGU Authentication Broker' `
|
||||
@@ -249,4 +274,5 @@ if ($PSCmdlet.ShouldProcess($installPath, 'Install the SGU Authentication Broker
|
||||
Start-Service -Name $serviceName
|
||||
}
|
||||
|
||||
Get-Service -Name $serviceName | Select-Object Name, Status, StartType
|
||||
Get-Service -Name $serviceName | Select-Object Name, Status, StartType,
|
||||
@{ Name = 'EventLog'; Expression = { $brokerEventLogName } }
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$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.'
|
||||
}
|
||||
|
||||
# Use invariant audit subcategory GUIDs so this works on English and Spanish
|
||||
# installations. Logon, logoff, and other logon/logoff events provide the
|
||||
# session identifiers required to correlate usage centrally.
|
||||
$auditSubcategories = @(
|
||||
'{0CCE9215-69AE-11D9-BED3-505054503030}', # Logon
|
||||
'{0CCE9216-69AE-11D9-BED3-505054503030}', # Logoff
|
||||
'{0CCE921C-69AE-11D9-BED3-505054503030}' # Other Logon/Logoff Events
|
||||
)
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Enable SGU session auditing and event forwarding prerequisites')) {
|
||||
foreach ($subcategory in $auditSubcategories) {
|
||||
& auditpol.exe /set "/subcategory:$subcategory" /success:enable /failure:enable | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "auditpol failed for subcategory $subcategory with exit code $LASTEXITCODE."
|
||||
}
|
||||
}
|
||||
|
||||
# Security events are read by the Windows Event Forwarding plug-in under
|
||||
# NETWORK SERVICE. Resolve both principals by SID for localized Windows.
|
||||
$eventLogReadersSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-573')
|
||||
$networkServiceSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-20')
|
||||
$members = @(Get-LocalGroupMember -SID $eventLogReadersSid -ErrorAction SilentlyContinue)
|
||||
$eventLogReaderMembershipChanged = $false
|
||||
if ($members.SID.Value -notcontains $networkServiceSid.Value) {
|
||||
$networkServiceAccount = $networkServiceSid.Translate([Security.Principal.NTAccount]).Value
|
||||
Add-LocalGroupMember -SID $eventLogReadersSid -Member $networkServiceAccount
|
||||
$eventLogReaderMembershipChanged = $true
|
||||
}
|
||||
|
||||
Set-Service WinRM -StartupType Automatic
|
||||
if ((Get-Service WinRM).Status -ne 'Running') {
|
||||
Start-Service WinRM
|
||||
}
|
||||
elseif ($eventLogReaderMembershipChanged) {
|
||||
Restart-Service WinRM -Force
|
||||
}
|
||||
|
||||
& wevtutil.exe set-log Security /maxsize:268435456 /retention:false /autobackup:false
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "wevtutil failed to configure the local Security log with exit code $LASTEXITCODE."
|
||||
}
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
ComputerName = $env:COMPUTERNAME
|
||||
WinRM = (Get-Service WinRM).Status.ToString()
|
||||
SecurityLogMaximumBytes = (Get-WinEvent -ListLog Security).MaximumSizeInBytes
|
||||
AuditSubcategories = $auditSubcategories
|
||||
EventForwardingPolicy = Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\EventLog\EventForwarding\SubscriptionManager'
|
||||
}
|
||||
@@ -39,7 +39,8 @@ foreach ($scriptName in @(
|
||||
'Install-SguEnrollmentGuard.ps1',
|
||||
'Test-SguClientEnrollment.ps1',
|
||||
'Repair-SguClientEnrollment.ps1',
|
||||
'Enable-LabRemoteAccess.ps1')) {
|
||||
'Enable-LabRemoteAccess.ps1',
|
||||
'Enable-SguClientMonitoring.ps1')) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $PSScriptRoot $scriptName) -PathType Leaf)) {
|
||||
throw "$scriptName must be beside Enroll-SguDomainClient.ps1."
|
||||
}
|
||||
@@ -96,6 +97,7 @@ if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Install and verify SGU before jo
|
||||
& (Join-Path $PSScriptRoot 'Enable-LabRemoteAccess.ps1') `
|
||||
-RemoteDesktopPrincipal $RemoteDesktopPrincipal `
|
||||
-EnableAdministrativeFirewallGroups | Out-Null
|
||||
& (Join-Path $PSScriptRoot 'Enable-SguClientMonitoring.ps1') | Out-Null
|
||||
return & (Join-Path $PSScriptRoot 'Test-SguClientEnrollment.ps1') `
|
||||
-RequireDomainJoined `
|
||||
-RequireRemoteAccess `
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#Requires -Version 5.1
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[datetime]$Since = (Get-Date).AddDays(-183),
|
||||
[datetime]$Until = (Get-Date),
|
||||
[string]$UserName,
|
||||
[ValidateSet('Critical','Error','Warning','Information','Verbose')]
|
||||
[string]$Level,
|
||||
[int[]]$EventId,
|
||||
[string]$Text,
|
||||
[string]$MonitoringRoot = 'C:\ProgramData\SGU\Monitoring',
|
||||
[string]$BrokerEventLogName = 'SGU Auth Broker',
|
||||
[string]$OutputCsv
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$events = [Collections.Generic.List[object]]::new()
|
||||
$eventNames = @{
|
||||
900 = 'BrokerStarted'
|
||||
1000 = 'AuthenticationAuthorized'
|
||||
1001 = 'AuthenticationRejected'
|
||||
1002 = 'AuthenticationUnavailable'
|
||||
1003 = 'AuthenticationInvalidRequest'
|
||||
1100 = 'SguAuthenticationAccepted'
|
||||
1101 = 'SguAuthenticationTimeout'
|
||||
1102 = 'SguAuthenticationNetworkFailure'
|
||||
1200 = 'ProfileEnrichmentCompleted'
|
||||
1201 = 'ProfileHtmlUnexpected'
|
||||
1202 = 'ProfileEnrichmentTimeout'
|
||||
1203 = 'ProfileEnrichmentFailure'
|
||||
1204 = 'ProfilePageUnavailable'
|
||||
1300 = 'DirectorySynchronizationFailure'
|
||||
1301 = 'DirectoryOptionalMetadataFailure'
|
||||
1302 = 'DirectoryGroupMembershipFailure'
|
||||
}
|
||||
|
||||
# Keep these reads unfiltered. Besides making archived and current logs behave
|
||||
# identically, this avoids the Windows Server 2025 ForwardedEvents query defect.
|
||||
if (Get-WinEvent -ListLog $BrokerEventLogName -ErrorAction SilentlyContinue) {
|
||||
Get-WinEvent -LogName $BrokerEventLogName -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.TimeCreated -ge $Since -and $_.TimeCreated -le $Until } |
|
||||
ForEach-Object { $events.Add($_) }
|
||||
}
|
||||
|
||||
$brokerArchiveRoot = Join-Path $MonitoringRoot 'Archive\Broker'
|
||||
Get-ChildItem -LiteralPath $brokerArchiveRoot -Filter '*.evtx' -File -ErrorAction SilentlyContinue |
|
||||
Where-Object LastWriteTime -ge $Since.AddDays(-1) |
|
||||
ForEach-Object {
|
||||
try {
|
||||
Get-WinEvent -Path $_.FullName -Oldest -ErrorAction Stop |
|
||||
Where-Object { $_.TimeCreated -ge $Since -and $_.TimeCreated -le $Until } |
|
||||
ForEach-Object { $events.Add($_) }
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Could not read broker archive $($_.FullName): $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
$result = @($events | Where-Object {
|
||||
(-not $UserName -or $_.Message -like "*$UserName*") -and
|
||||
(-not $Level -or $_.LevelDisplayName -eq $Level) -and
|
||||
(-not $EventId -or $_.Id -in $EventId) -and
|
||||
(-not $Text -or $_.Message -like "*$Text*")
|
||||
} | Sort-Object TimeCreated -Descending | ForEach-Object {
|
||||
[pscustomobject]@{
|
||||
TimeCreated = $_.TimeCreated
|
||||
Level = $_.LevelDisplayName
|
||||
EventId = $_.Id
|
||||
EventName = $eventNames[[int]$_.Id]
|
||||
Provider = $_.ProviderName
|
||||
Message = $_.Message
|
||||
}
|
||||
})
|
||||
|
||||
if ($OutputCsv) {
|
||||
$resolvedOutput = [IO.Path]::GetFullPath($OutputCsv)
|
||||
New-Item -ItemType Directory -Path (Split-Path $resolvedOutput -Parent) -Force | Out-Null
|
||||
$result | Export-Csv -LiteralPath $resolvedOutput -NoTypeInformation -Encoding UTF8
|
||||
}
|
||||
|
||||
$result
|
||||
@@ -0,0 +1,176 @@
|
||||
#Requires -Version 5.1
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[datetime]$Since = (Get-Date).AddDays(-183),
|
||||
[datetime]$Until = (Get-Date),
|
||||
[string]$UserName,
|
||||
[string]$ComputerName,
|
||||
[string]$MonitoringRoot = 'C:\ProgramData\SGU\Monitoring',
|
||||
[string]$OutputCsv
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$eventIds = @(4624,4625,4634,4647,4778,4779,6005,6006,6008)
|
||||
$events = [Collections.Generic.List[object]]::new()
|
||||
|
||||
try {
|
||||
# Windows Server 2025 can crash the Windows Event Log service when a
|
||||
# structured query is evaluated against ForwardedEvents (wevtsvc.dll,
|
||||
# exception 0xc0000420). Read the channel without a server-side query and
|
||||
# apply every predicate in this process instead.
|
||||
Get-WinEvent -LogName 'ForwardedEvents' -ErrorAction Stop |
|
||||
Where-Object {
|
||||
$_.Id -in $eventIds -and
|
||||
$_.TimeCreated -ge $Since -and
|
||||
$_.TimeCreated -le $Until
|
||||
} |
|
||||
ForEach-Object { $events.Add($_) }
|
||||
}
|
||||
catch [System.Exception] {
|
||||
if ($_.FullyQualifiedErrorId -notlike 'NoMatchingEventsFound*') {
|
||||
Write-Verbose $_.Exception.Message
|
||||
}
|
||||
}
|
||||
|
||||
$archiveRoot = Join-Path $MonitoringRoot 'Archive'
|
||||
Get-ChildItem -LiteralPath $archiveRoot -Filter '*.evtx' -File -ErrorAction SilentlyContinue |
|
||||
Where-Object LastWriteTime -ge $Since.AddDays(-1) |
|
||||
ForEach-Object {
|
||||
try {
|
||||
Get-WinEvent -Path $_.FullName -Oldest -ErrorAction Stop |
|
||||
Where-Object { $_.Id -in $eventIds -and $_.TimeCreated -ge $Since -and $_.TimeCreated -le $Until } |
|
||||
ForEach-Object { $events.Add($_) }
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Could not read archive $($_.FullName): $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-EventData {
|
||||
param([Parameter(Mandatory)]$EventRecord)
|
||||
|
||||
$xml = [xml]$EventRecord.ToXml()
|
||||
$data = @{}
|
||||
foreach ($item in @($xml.Event.EventData.Data)) {
|
||||
if ($item.Name) {
|
||||
$data[[string]$item.Name] = [string]$item.'#text'
|
||||
}
|
||||
}
|
||||
[pscustomobject]@{
|
||||
Computer = [string]$xml.Event.System.Computer
|
||||
Data = $data
|
||||
}
|
||||
}
|
||||
|
||||
$openSessions = @{}
|
||||
$rows = [Collections.Generic.List[object]]::new()
|
||||
$ignoredUsers = @('ANONYMOUS LOGON','DWM-1','DWM-2','DWM-3','LOCAL SERVICE','NETWORK SERVICE','SYSTEM','UMFD-0','UMFD-1','UMFD-2','UMFD-3')
|
||||
|
||||
foreach ($eventRecord in @($events | Sort-Object TimeCreated,RecordId)) {
|
||||
$parsed = Get-EventData -EventRecord $eventRecord
|
||||
$machine = ($parsed.Computer -split '\.')[0].ToUpperInvariant()
|
||||
$data = $parsed.Data
|
||||
|
||||
if ($eventRecord.Id -in 6005,6006,6008) {
|
||||
foreach ($key in @($openSessions.Keys | Where-Object { $_ -like "$machine|*" })) {
|
||||
$session = $openSessions[$key]
|
||||
$rows.Add([pscustomobject]@{
|
||||
User = $session.User
|
||||
Computer = $machine
|
||||
StartedAt = $session.StartedAt
|
||||
EndedAt = $eventRecord.TimeCreated
|
||||
Duration = $eventRecord.TimeCreated - $session.StartedAt
|
||||
DurationMinutes = [math]::Round(($eventRecord.TimeCreated - $session.StartedAt).TotalMinutes, 2)
|
||||
LogonType = $session.LogonType
|
||||
Result = 'Interrumpida por apagado o reinicio'
|
||||
FailureStatus = $null
|
||||
})
|
||||
$openSessions.Remove($key)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ($eventRecord.Id -eq 4625) {
|
||||
$failedUser = [string]$data.TargetUserName
|
||||
if ($failedUser -and $failedUser -notlike '*$' -and $failedUser.ToUpperInvariant() -notin $ignoredUsers) {
|
||||
$rows.Add([pscustomobject]@{
|
||||
User = if ($data.TargetDomainName) { "$($data.TargetDomainName)\$failedUser" } else { $failedUser }
|
||||
Computer = $machine
|
||||
StartedAt = $eventRecord.TimeCreated
|
||||
EndedAt = $eventRecord.TimeCreated
|
||||
Duration = [timespan]::Zero
|
||||
DurationMinutes = 0
|
||||
LogonType = [string]$data.LogonType
|
||||
Result = 'Fallida'
|
||||
FailureStatus = "$($data.Status)/$($data.SubStatus)"
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ($eventRecord.Id -eq 4624) {
|
||||
$logonType = [string]$data.LogonType
|
||||
$targetUser = [string]$data.TargetUserName
|
||||
if ($logonType -notin @('2','10','11') -or -not $targetUser -or $targetUser -like '*$' -or
|
||||
$targetUser.ToUpperInvariant() -in $ignoredUsers) {
|
||||
continue
|
||||
}
|
||||
$logonId = [string]$data.TargetLogonId
|
||||
$key = "$machine|$logonId"
|
||||
$openSessions[$key] = [pscustomobject]@{
|
||||
User = if ($data.TargetDomainName) { "$($data.TargetDomainName)\$targetUser" } else { $targetUser }
|
||||
StartedAt = $eventRecord.TimeCreated
|
||||
LogonType = $logonType
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ($eventRecord.Id -in 4634,4647) {
|
||||
$logonId = if ($eventRecord.Id -eq 4634) { [string]$data.TargetLogonId } else { [string]$data.SubjectLogonId }
|
||||
$key = "$machine|$logonId"
|
||||
if ($openSessions.ContainsKey($key)) {
|
||||
$session = $openSessions[$key]
|
||||
$rows.Add([pscustomobject]@{
|
||||
User = $session.User
|
||||
Computer = $machine
|
||||
StartedAt = $session.StartedAt
|
||||
EndedAt = $eventRecord.TimeCreated
|
||||
Duration = $eventRecord.TimeCreated - $session.StartedAt
|
||||
DurationMinutes = [math]::Round(($eventRecord.TimeCreated - $session.StartedAt).TotalMinutes, 2)
|
||||
LogonType = $session.LogonType
|
||||
Result = 'Completada'
|
||||
FailureStatus = $null
|
||||
})
|
||||
$openSessions.Remove($key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($key in $openSessions.Keys) {
|
||||
$session = $openSessions[$key]
|
||||
$machine = ($key -split '\|', 2)[0]
|
||||
$rows.Add([pscustomobject]@{
|
||||
User = $session.User
|
||||
Computer = $machine
|
||||
StartedAt = $session.StartedAt
|
||||
EndedAt = $null
|
||||
Duration = $Until - $session.StartedAt
|
||||
DurationMinutes = [math]::Round(($Until - $session.StartedAt).TotalMinutes, 2)
|
||||
LogonType = $session.LogonType
|
||||
Result = 'Sesión posiblemente activa'
|
||||
FailureStatus = $null
|
||||
})
|
||||
}
|
||||
|
||||
$result = @($rows | Where-Object {
|
||||
(-not $UserName -or $_.User -like "*$UserName*") -and
|
||||
(-not $ComputerName -or $_.Computer -like "*$ComputerName*")
|
||||
} | Sort-Object StartedAt -Descending)
|
||||
|
||||
if ($OutputCsv) {
|
||||
$resolvedOutput = [IO.Path]::GetFullPath($OutputCsv)
|
||||
New-Item -ItemType Directory -Path (Split-Path $resolvedOutput -Parent) -Force | Out-Null
|
||||
$result | Export-Csv -LiteralPath $resolvedOutput -NoTypeInformation -Encoding UTF8
|
||||
}
|
||||
|
||||
$result
|
||||
@@ -348,6 +348,10 @@ foreach ($requiredPath in @(
|
||||
(Join-Path $scriptsRoot 'Set-SguDomainComputerPolicies.ps1'),
|
||||
(Join-Path $scriptsRoot 'Set-SguDomainUserPolicies.ps1'),
|
||||
(Join-Path $scriptsRoot 'Enable-SguServerRemoteManagement.ps1'),
|
||||
(Join-Path $scriptsRoot 'Install-SguDomainMonitoring.ps1'),
|
||||
(Join-Path $scriptsRoot 'Invoke-SguMonitoringMaintenance.ps1'),
|
||||
(Join-Path $scriptsRoot 'Get-SguUsageReport.ps1'),
|
||||
(Join-Path $scriptsRoot 'Get-SguBrokerLog.ps1'),
|
||||
(Join-Path $brokerPublishPath 'SGU.AuthBroker.exe'))) {
|
||||
if (-not (Test-Path -LiteralPath $requiredPath -PathType Leaf)) {
|
||||
throw "The server bootstrap package is incomplete: $requiredPath"
|
||||
@@ -606,8 +610,15 @@ else {
|
||||
-RemoteAddress $privateSubnet | Out-Null
|
||||
}
|
||||
|
||||
$collectorFqdn = "$env:COMPUTERNAME.$DomainName"
|
||||
& (Join-Path $scriptsRoot 'Set-SguDomainComputerPolicies.ps1') `
|
||||
-TargetOuDn $laboratoryOuDn -DomainController $env:COMPUTERNAME | Out-Null
|
||||
-TargetOuDn $laboratoryOuDn `
|
||||
-DomainController $env:COMPUTERNAME `
|
||||
-EventCollectorFqdn $collectorFqdn | Out-Null
|
||||
& (Join-Path $scriptsRoot 'Install-SguDomainMonitoring.ps1') `
|
||||
-CollectorFqdn $collectorFqdn `
|
||||
-ComputerOuDn $laboratoryOuDn `
|
||||
-RetentionDays 183 | Out-Null
|
||||
$userPolicyParameters = @{
|
||||
TargetOuDn = $usersOuDn
|
||||
DomainController = $env:COMPUTERNAME
|
||||
@@ -632,6 +643,9 @@ $validation = [ordered]@{
|
||||
BrokerPortListening = [bool](Get-NetTCPConnection -LocalPort 8443 -State Listen -ErrorAction SilentlyContinue)
|
||||
WinRM = (Get-Service WinRM).Status.ToString()
|
||||
RemoteDesktop = (Get-Service TermService).Status.ToString()
|
||||
EventCollector = (Get-Service Wecsvc).Status.ToString()
|
||||
EventSubscription = @(& wecutil.exe enum-subscription) -contains 'SGU-Lab-Monitoring'
|
||||
MonitoringRetentionDays = 183
|
||||
PackageShare = "\\$env:COMPUTERNAME\Packages"
|
||||
LaboratoryOu = $laboratoryOuDn
|
||||
UsersOu = $usersOuDn
|
||||
@@ -643,7 +657,9 @@ $validation = [ordered]@{
|
||||
if ($validation.BrokerService -ne 'Running' -or
|
||||
-not $validation.BrokerPortListening -or
|
||||
$validation.WinRM -ne 'Running' -or
|
||||
$validation.RemoteDesktop -ne 'Running') {
|
||||
$validation.RemoteDesktop -ne 'Running' -or
|
||||
$validation.EventCollector -ne 'Running' -or
|
||||
-not $validation.EventSubscription) {
|
||||
throw 'Server finalization did not pass service validation. Review bootstrap.log and re-run the bootstrap.'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#Requires -Version 5.1
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[string]$CollectorFqdn = "$env:COMPUTERNAME.$env:USERDNSDOMAIN",
|
||||
[string]$ComputerOuDn = 'OU=Laboratorio,DC=lci,DC=lasalle,DC=mx',
|
||||
[string]$MonitoringRoot = 'C:\ProgramData\SGU\Monitoring',
|
||||
[ValidateRange(30, 730)]
|
||||
[int]$RetentionDays = 183
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$subscriptionId = 'SGU-Lab-Monitoring'
|
||||
$maintenanceScriptName = 'Invoke-SguMonitoringMaintenance.ps1'
|
||||
$reportScriptName = 'Get-SguUsageReport.ps1'
|
||||
$brokerReportScriptName = 'Get-SguBrokerLog.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 on the domain event collector.'
|
||||
}
|
||||
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
Get-ADOrganizationalUnit -Identity $ComputerOuDn -ErrorAction Stop | Out-Null
|
||||
|
||||
foreach ($requiredScript in $maintenanceScriptName,$reportScriptName,$brokerReportScriptName) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $PSScriptRoot $requiredScript) -PathType Leaf)) {
|
||||
throw "$requiredScript must be beside Install-SguDomainMonitoring.ps1."
|
||||
}
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Install the SGU domain monitoring collector')) {
|
||||
Set-Service EventLog -StartupType Automatic
|
||||
if ((Get-Service EventLog).Status -ne 'Running') {
|
||||
Start-Service EventLog
|
||||
}
|
||||
& wecutil.exe quick-config /quiet
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "wecutil quick-config failed with exit code $LASTEXITCODE."
|
||||
}
|
||||
Set-Service Wecsvc -StartupType Automatic
|
||||
Start-Service Wecsvc
|
||||
& wevtutil.exe set-log ForwardedEvents /enabled:true /maxsize:536870912 /retention:false /autobackup:false
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "wevtutil failed to configure ForwardedEvents with exit code $LASTEXITCODE."
|
||||
}
|
||||
|
||||
$query = @'
|
||||
<QueryList>
|
||||
<Query Id="0">
|
||||
<Select Path="Security">*[System[(EventID=4624 or EventID=4625 or EventID=4634 or EventID=4647 or EventID=4778 or EventID=4779)]]</Select>
|
||||
<Select Path="System">*[System[(EventID=12 or EventID=13 or EventID=41 or EventID=1074 or EventID=6005 or EventID=6006 or EventID=6008)]]</Select>
|
||||
</Query>
|
||||
</QueryList>
|
||||
'@
|
||||
$escapedQuery = [Security.SecurityElement]::Escape($query)
|
||||
$subscriptionXml = @"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Subscription xmlns="http://schemas.microsoft.com/2006/03/windows/events/subscription">
|
||||
<SubscriptionId>$subscriptionId</SubscriptionId>
|
||||
<SubscriptionType>SourceInitiated</SubscriptionType>
|
||||
<Description>SGU interactive sessions, failures, reconnects, and workstation power state.</Description>
|
||||
<Enabled>true</Enabled>
|
||||
<Uri>http://schemas.microsoft.com/wbem/wsman/1/windows/EventLog</Uri>
|
||||
<ConfigurationMode>Custom</ConfigurationMode>
|
||||
<Delivery Mode="Push">
|
||||
<Batching><MaxItems>5</MaxItems><MaxLatencyTime>30000</MaxLatencyTime></Batching>
|
||||
<PushSettings><Heartbeat Interval="60000"/></PushSettings>
|
||||
</Delivery>
|
||||
<Query>$escapedQuery</Query>
|
||||
<ReadExistingEvents>false</ReadExistingEvents>
|
||||
<TransportName>HTTP</TransportName>
|
||||
<ContentFormat>Events</ContentFormat>
|
||||
<Locale Language="es-MX"/>
|
||||
<LogFile>ForwardedEvents</LogFile>
|
||||
<AllowedSourceDomainComputers>O:NSG:NSD:(A;;GA;;;DC)(A;;GA;;;NS)</AllowedSourceDomainComputers>
|
||||
</Subscription>
|
||||
"@
|
||||
|
||||
New-Item -ItemType Directory -Path $MonitoringRoot -Force | Out-Null
|
||||
$subscriptionPath = Join-Path $MonitoringRoot 'SGU-Lab-Monitoring.xml'
|
||||
[IO.File]::WriteAllText($subscriptionPath, $subscriptionXml, [Text.UTF8Encoding]::new($true))
|
||||
$existingSubscriptions = @(& wecutil.exe enum-subscription 2>$null)
|
||||
if ($existingSubscriptions -contains $subscriptionId) {
|
||||
& wecutil.exe delete-subscription $subscriptionId
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not replace the existing $subscriptionId subscription."
|
||||
}
|
||||
}
|
||||
& wecutil.exe create-subscription $subscriptionPath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not create the $subscriptionId subscription."
|
||||
}
|
||||
|
||||
foreach ($scriptName in $maintenanceScriptName,$reportScriptName,$brokerReportScriptName) {
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot $scriptName) `
|
||||
-Destination (Join-Path $MonitoringRoot $scriptName) -Force
|
||||
}
|
||||
|
||||
$configuration = [ordered]@{
|
||||
CollectorFqdn = $CollectorFqdn
|
||||
ComputerOuDn = $ComputerOuDn
|
||||
RetentionDays = $RetentionDays
|
||||
SubscriptionId = $subscriptionId
|
||||
}
|
||||
[IO.File]::WriteAllText(
|
||||
(Join-Path $MonitoringRoot 'monitoring.json'),
|
||||
($configuration | ConvertTo-Json),
|
||||
[Text.UTF8Encoding]::new($false))
|
||||
|
||||
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
|
||||
$maintenanceScript = Join-Path $MonitoringRoot $maintenanceScriptName
|
||||
$inventoryAction = New-ScheduledTaskAction -Execute $powerShell -Argument (
|
||||
"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$maintenanceScript`" " +
|
||||
"-MonitoringRoot `"$MonitoringRoot`" -ComputerOuDn `"$ComputerOuDn`" -RetentionDays $RetentionDays -InventoryOnly")
|
||||
$inventoryTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) `
|
||||
-RepetitionInterval (New-TimeSpan -Minutes 5) `
|
||||
-RepetitionDuration (New-TimeSpan -Days 3650)
|
||||
$taskSettings = New-ScheduledTaskSettingsSet -StartWhenAvailable `
|
||||
-ExecutionTimeLimit (New-TimeSpan -Minutes 10) -RestartCount 2 `
|
||||
-RestartInterval (New-TimeSpan -Minutes 1)
|
||||
Register-ScheduledTask -TaskName 'SGU-Monitoring-Inventory' -Action $inventoryAction `
|
||||
-Trigger $inventoryTrigger -Settings $taskSettings -User 'SYSTEM' -RunLevel Highest -Force | Out-Null
|
||||
|
||||
$retentionAction = New-ScheduledTaskAction -Execute $powerShell -Argument (
|
||||
"-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$maintenanceScript`" " +
|
||||
"-MonitoringRoot `"$MonitoringRoot`" -ComputerOuDn `"$ComputerOuDn`" -RetentionDays $RetentionDays")
|
||||
$retentionTrigger = New-ScheduledTaskTrigger -Daily -At '12:10 AM'
|
||||
Register-ScheduledTask -TaskName 'SGU-Monitoring-Retention' -Action $retentionAction `
|
||||
-Trigger $retentionTrigger -Settings $taskSettings -User 'SYSTEM' -RunLevel Highest -Force | Out-Null
|
||||
|
||||
& $maintenanceScript -MonitoringRoot $MonitoringRoot -ComputerOuDn $ComputerOuDn `
|
||||
-RetentionDays $RetentionDays -InventoryOnly | Out-Null
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
Collector = $CollectorFqdn
|
||||
CollectorService = (Get-Service Wecsvc).Status.ToString()
|
||||
SubscriptionId = $subscriptionId
|
||||
SubscriptionEnabled = @(& wecutil.exe enum-subscription) -contains $subscriptionId
|
||||
RetentionDays = $RetentionDays
|
||||
InventoryTask = (Get-ScheduledTask -TaskName 'SGU-Monitoring-Inventory').State
|
||||
RetentionTask = (Get-ScheduledTask -TaskName 'SGU-Monitoring-Retention').State
|
||||
MachineStatusPath = Join-Path $MonitoringRoot 'Reports\machine-status.json'
|
||||
UsageReportCommand = "& '$MonitoringRoot\$reportScriptName'"
|
||||
BrokerLogCommand = "& '$MonitoringRoot\$brokerReportScriptName'"
|
||||
}
|
||||
@@ -28,6 +28,7 @@ $enrollmentRoot = Join-Path $env:ProgramData 'SGU\Enrollment'
|
||||
$sourceScripts = @(
|
||||
'Install-CredentialProvider.ps1',
|
||||
'Enable-LabRemoteAccess.ps1',
|
||||
'Enable-SguClientMonitoring.ps1',
|
||||
'Test-SguClientEnrollment.ps1',
|
||||
'Repair-SguClientEnrollment.ps1'
|
||||
)
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#Requires -Version 5.1
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$MonitoringRoot = 'C:\ProgramData\SGU\Monitoring',
|
||||
[string]$ComputerOuDn = 'OU=Laboratorio,DC=lci,DC=lasalle,DC=mx',
|
||||
[ValidateRange(30, 730)]
|
||||
[int]$RetentionDays = 183,
|
||||
[string]$BrokerEventLogName = 'SGU Auth Broker',
|
||||
[switch]$InventoryOnly
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
|
||||
foreach ($serviceName in 'EventLog','Wecsvc') {
|
||||
Set-Service -Name $serviceName -StartupType Automatic
|
||||
if ((Get-Service $serviceName).Status -ne 'Running') {
|
||||
Start-Service $serviceName
|
||||
}
|
||||
}
|
||||
|
||||
$archiveRoot = Join-Path $MonitoringRoot 'Archive'
|
||||
$brokerArchiveRoot = Join-Path $archiveRoot 'Broker'
|
||||
$reportRoot = Join-Path $MonitoringRoot 'Reports'
|
||||
New-Item -ItemType Directory -Path $archiveRoot,$brokerArchiveRoot,$reportRoot -Force | Out-Null
|
||||
|
||||
function Test-TcpEndpoint {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ComputerName,
|
||||
[int]$Port = 5985,
|
||||
[int]$TimeoutMilliseconds = 900
|
||||
)
|
||||
|
||||
$client = [Net.Sockets.TcpClient]::new()
|
||||
try {
|
||||
$pending = $client.BeginConnect($ComputerName, $Port, $null, $null)
|
||||
if (-not $pending.AsyncWaitHandle.WaitOne($TimeoutMilliseconds)) {
|
||||
return $false
|
||||
}
|
||||
$client.EndConnect($pending)
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
return $false
|
||||
}
|
||||
finally {
|
||||
$client.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $InventoryOnly) {
|
||||
$forwardedLog = Get-WinEvent -ListLog ForwardedEvents -ErrorAction Stop
|
||||
if ($forwardedLog.RecordCount -gt 0) {
|
||||
$archivePath = Join-Path $archiveRoot ("ForwardedEvents-{0:yyyyMMdd-HHmmss}.evtx" -f (Get-Date))
|
||||
& wevtutil.exe clear-log ForwardedEvents "/backup:$archivePath"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not archive ForwardedEvents; wevtutil returned exit code $LASTEXITCODE."
|
||||
}
|
||||
}
|
||||
|
||||
$brokerLog = Get-WinEvent -ListLog $BrokerEventLogName -ErrorAction SilentlyContinue
|
||||
if ($brokerLog -and $brokerLog.RecordCount -gt 0) {
|
||||
$brokerArchivePath = Join-Path $brokerArchiveRoot ("SguAuthBroker-{0:yyyyMMdd-HHmmss}.evtx" -f (Get-Date))
|
||||
& wevtutil.exe clear-log $BrokerEventLogName "/backup:$brokerArchivePath"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not archive $BrokerEventLogName; wevtutil returned exit code $LASTEXITCODE."
|
||||
}
|
||||
}
|
||||
|
||||
$cutoff = (Get-Date).AddDays(-$RetentionDays)
|
||||
Get-ChildItem -LiteralPath $archiveRoot -Filter '*.evtx' -File -Recurse -ErrorAction SilentlyContinue |
|
||||
Where-Object LastWriteTime -lt $cutoff |
|
||||
ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force }
|
||||
}
|
||||
|
||||
$computers = @(Get-ADComputer -SearchBase $ComputerOuDn -SearchScope Subtree -Filter * `
|
||||
-Properties DNSHostName,IPv4Address,OperatingSystem,LastLogonDate,Enabled |
|
||||
Sort-Object Name)
|
||||
|
||||
$inventory = @(foreach ($computer in $computers) {
|
||||
$target = if ($computer.DNSHostName) { $computer.DNSHostName } else { $computer.Name }
|
||||
$online = Test-TcpEndpoint -ComputerName $target
|
||||
[pscustomobject]@{
|
||||
ComputerName = $computer.Name
|
||||
DNSHostName = $computer.DNSHostName
|
||||
IPv4Address = $computer.IPv4Address
|
||||
OperatingSystem = $computer.OperatingSystem
|
||||
Enabled = [bool]$computer.Enabled
|
||||
Status = if ($online) { 'Encendida' } else { 'Apagada o inaccesible' }
|
||||
WinRMReachable = [bool]$online
|
||||
LastDomainLogon = if ($computer.LastLogonDate) {
|
||||
$computer.LastLogonDate.ToUniversalTime().ToString('o')
|
||||
} else { $null }
|
||||
CheckedAt = (Get-Date).ToUniversalTime().ToString('o')
|
||||
}
|
||||
})
|
||||
|
||||
$jsonPath = Join-Path $reportRoot 'machine-status.json'
|
||||
$csvPath = Join-Path $reportRoot 'machine-status.csv'
|
||||
[IO.File]::WriteAllText($jsonPath, (ConvertTo-Json -InputObject $inventory -Depth 4), [Text.UTF8Encoding]::new($false))
|
||||
$inventory | Export-Csv -LiteralPath $csvPath -NoTypeInformation -Encoding UTF8
|
||||
|
||||
[pscustomobject]@{
|
||||
CheckedAt = (Get-Date).ToUniversalTime().ToString('o')
|
||||
ComputerCount = @($inventory).Count
|
||||
OnlineCount = @($inventory | Where-Object WinRMReachable).Count
|
||||
OfflineCount = @($inventory | Where-Object { -not $_.WinRMReachable }).Count
|
||||
RetentionDays = $RetentionDays
|
||||
StatusJson = $jsonPath
|
||||
StatusCsv = $csvPath
|
||||
}
|
||||
@@ -96,6 +96,7 @@ Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguClientEnrollment.cm
|
||||
-Destination (Join-Path $clientRoot 'Start-SguClientEnrollment.cmd')
|
||||
$clientScripts = @(
|
||||
'Enable-LabRemoteAccess.ps1',
|
||||
'Enable-SguClientMonitoring.ps1',
|
||||
'Enroll-SguDomainClient.ps1',
|
||||
'Install-CredentialProvider.ps1',
|
||||
'Install-SguEnrollmentGuard.ps1',
|
||||
@@ -126,6 +127,10 @@ Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguServerBootstrap.cmd
|
||||
$serverScripts = @(
|
||||
'Deploy-AuthBroker.ps1',
|
||||
'Enable-SguServerRemoteManagement.ps1',
|
||||
'Get-SguUsageReport.ps1',
|
||||
'Get-SguBrokerLog.ps1',
|
||||
'Install-SguDomainMonitoring.ps1',
|
||||
'Invoke-SguMonitoringMaintenance.ps1',
|
||||
'New-LabCertificate.ps1',
|
||||
'Register-SguClientCertificate.ps1',
|
||||
'Set-LabBrokerDns.ps1',
|
||||
|
||||
@@ -100,6 +100,7 @@ Bootstrap reproducible para el laboratorio SGU.
|
||||
- **Advertencia:** el bootstrap de servidor crea un bosque nuevo. No restaura los SID, contraseñas ni relaciones de confianza del bosque anterior; para conservarlos se requiere una recuperación de bosque desde una copia de estado del sistema.
|
||||
- `sgu-server-bootstrap-$Version.zip`: crea el bosque AD/DNS, OUs, grupo RDP, GPO, recurso `Packages`, broker mTLS y administración remota; se reanuda solo después del reinicio.
|
||||
- `sgu-client-bootstrap-$Version.zip`: registra un certificado mTLS único, instala y valida el Credential Provider antes de unir el equipo al dominio, habilita RDP/WinRM y se repara al arranque.
|
||||
- El servidor configura WEF/WEC para registrar sesiones y fallos, inventariar el estado alcanzable de las máquinas cada cinco minutos y conservar durante 183 días tanto esos eventos como el diagnóstico estructurado del Auth Broker.
|
||||
- Windows Home se detecta y se rechaza con una explicación, ya que no admite unión a Active Directory ni RDP host.
|
||||
|
||||
Las contraseñas se solicitan de forma interactiva y no se escriben en archivos ni en la línea de comandos. Verifique los ZIP con `SHA256SUMS-$Version.txt`.
|
||||
|
||||
@@ -9,6 +9,7 @@ $enrollmentRoot = Split-Path $ConfigurationPath -Parent
|
||||
$testScript = Join-Path $enrollmentRoot 'Test-SguClientEnrollment.ps1'
|
||||
$installScript = Join-Path $enrollmentRoot 'Install-CredentialProvider.ps1'
|
||||
$remoteAccessScript = Join-Path $enrollmentRoot 'Enable-LabRemoteAccess.ps1'
|
||||
$monitoringScript = Join-Path $enrollmentRoot 'Enable-SguClientMonitoring.ps1'
|
||||
|
||||
$before = & $testScript
|
||||
if (-not $before.IsValid) {
|
||||
@@ -32,6 +33,7 @@ if ($computer.PartOfDomain) {
|
||||
& $remoteAccessScript `
|
||||
-RemoteDesktopPrincipal ([string]$configuration.RemoteDesktopPrincipal) `
|
||||
-EnableAdministrativeFirewallGroups | Out-Null
|
||||
& $monitoringScript | Out-Null
|
||||
}
|
||||
|
||||
$verificationParams = @{}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
param(
|
||||
[string]$TargetOuDn = 'OU=Laboratorio,DC=lci,DC=lasalle,DC=mx',
|
||||
[string]$GpoName = 'SGU - Windows client experience',
|
||||
[string]$DomainController = $env:COMPUTERNAME
|
||||
[string]$DomainController = $env:COMPUTERNAME,
|
||||
[string]$EventCollectorFqdn
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
@@ -24,6 +25,14 @@ if (-not $domainDn) {
|
||||
throw 'TargetOuDn does not contain a domain distinguished name.'
|
||||
}
|
||||
$domainName = ($domainDn -replace ',DC=', '.')
|
||||
if (-not $EventCollectorFqdn) {
|
||||
$collectorComputer = Get-ADComputer -Identity $DomainController -Properties DNSHostName `
|
||||
-Server $DomainController -ErrorAction Stop
|
||||
$EventCollectorFqdn = $collectorComputer.DNSHostName
|
||||
}
|
||||
if (-not $EventCollectorFqdn) {
|
||||
throw 'Could not determine the event collector FQDN.'
|
||||
}
|
||||
|
||||
$gpo = Get-GPO -Name $GpoName -Domain $domainName -Server $DomainController -ErrorAction SilentlyContinue
|
||||
if (-not $gpo -and $PSCmdlet.ShouldProcess($GpoName, 'Create the SGU Windows client policy GPO')) {
|
||||
@@ -64,6 +73,8 @@ $powerPolicyRoot = 'HKLM\Software\Policies\Microsoft\Power\PowerSettings'
|
||||
$credentialProviderPolicyKey = 'HKLM\Software\Policies\Microsoft\Windows\System'
|
||||
$interactiveLogonPolicyKey = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System'
|
||||
$accountPicturePolicyKey = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer'
|
||||
$eventForwardingPolicyKey = 'HKLM\Software\Policies\Microsoft\Windows\EventLog\EventForwarding\SubscriptionManager'
|
||||
$auditPolicyKey = 'HKLM\System\CurrentControlSet\Control\Lsa'
|
||||
$providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}'
|
||||
$policies = @(
|
||||
@{ Key = $dataCollectionKey; Name = 'AllowTelemetry'; Type = 'DWord'; Value = 0 },
|
||||
@@ -83,7 +94,12 @@ $policies = @(
|
||||
|
||||
# Use Windows' native default account image for named user tiles. LogonUI
|
||||
# retains ownership of the anonymous Other user tile and its circular mask.
|
||||
@{ Key = $accountPicturePolicyKey; Name = 'UseDefaultTile'; Type = 'DWord'; Value = 1 }
|
||||
@{ Key = $accountPicturePolicyKey; Name = 'UseDefaultTile'; Type = 'DWord'; Value = 1 },
|
||||
|
||||
# Source-initiated Windows Event Forwarding. Kerberos authenticates domain
|
||||
# computers to the collector; no SGU password or reusable secret is logged.
|
||||
@{ Key = $eventForwardingPolicyKey; Name = '1'; Type = 'String'; Value = "Server=http://${EventCollectorFqdn}:5985/wsman/SubscriptionManager/WEC,Refresh=300" },
|
||||
@{ Key = $auditPolicyKey; Name = 'SCENoApplyLegacyAuditPolicy'; Type = 'DWord'; Value = 1 }
|
||||
)
|
||||
|
||||
$powerSettingIds = @(
|
||||
@@ -134,5 +150,6 @@ $linkEnabled = $link -and (
|
||||
TargetOu = $TargetOuDn
|
||||
LinkEnabled = [bool]$linkEnabled
|
||||
PolicyCount = $configuredPolicies.Count
|
||||
EventCollector = $EventCollectorFqdn
|
||||
Policies = [pscustomobject]$configuredPolicies
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user