Introduzione
Il mantenimento dell’integrità dell’ambiente Active Directory (AD) è fondamentale per la stabilità e la sicurezza dei sistemi IT. Una AD non integra può causare errori di autenticazione, problemi di replica e interruzioni più ampie nella rete di un’organizzazione. Questo script PowerShell è stato progettato per semplificare il processo di valutazione e risoluzione dei problemi durante il controllo dell’integrità di Active Directory, offrendo un metodo strutturato e automatizzato per i professionisti IT e i Managed Service Provider (MSP).
Contesto
Active Directory è la spina dorsale di molti ambienti IT aziendali e gestisce l’autenticazione degli utenti, i criteri di gruppo e i servizi di directory. Tuttavia, assicurarne il corretto funzionamento può essere complesso, dato il numero di potenziali punti di guasto. Questo script sfrutta l’utility con riga di comando DCDiag per analizzare lo stato di un controller di dominio, identificare potenziali problemi e riportare i risultati. Per gli MSP e gli amministratori IT, questo strumento è di valore inestimabile, in quanto fornisce un’istantanea chiara dell’integrità di AD e facilita la manutenzione proattiva.
Lo script per verificare l’integrità di Active Directory:
#Requires -Version 5.1 <# .SYNOPSIS Analyzes the state of a domain controller and reports any problems to help with troubleshooting. Optionally, set a WYSIWYG custom field. By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). .DESCRIPTION Analyzes the state of a domain controller and reports any problems to help with troubleshooting. Optionally, set a WYSIWYG custom field. .EXAMPLE (No Parameters) Retrieving Directory Server Diagnosis Test Results. Passing Tests: CheckSDRefDom, Connectivity, CrossRefValidation, DFSREvent, FrsEvent, Intersite, KccEvent, KnowsOfRoleHolders, MachineAccount, NCSecDesc, NetLogons, ObjectsReplicated, Replications, RidManager, Services, SystemLog, SysVolCheck, VerifyReferences [Alert] Failed Tests Detected! Failed Tests: Advertising, LocatorCheck ### Detailed Output ### Directory Server Diagnosis Performing initial setup: Trying to find home server... Home Server = SRV16-DC2-TEST * Identified AD Forest. Done gathering initial info. Doing initial required tests Testing server: Default-First-Site-Name\SRV16-DC2-TEST Starting test: Connectivity ......................... SRV16-DC2-TEST passed test Connectivity Doing primary tests Testing server: Default-First-Site-Name\SRV16-DC2-TEST Starting test: Advertising Warning: SRV16-DC2-TEST is not advertising as a time server. ......................... SRV16-DC2-TEST failed test Advertising Running partition tests on : ForestDnsZones Running partition tests on : DomainDnsZones Running partition tests on : Schema Running partition tests on : Configuration Running partition tests on : test Running enterprise tests on : test.lan Directory Server Diagnosis Performing initial setup: Trying to find home server... Home Server = SRV16-DC2-TEST * Identified AD Forest. Done gathering initial info. Doing initial required tests Testing server: Default-First-Site-Name\SRV16-DC2-TEST Starting test: Connectivity ......................... SRV16-DC2-TEST passed test Connectivity Doing primary tests Testing server: Default-First-Site-Name\SRV16-DC2-TEST Running partition tests on : ForestDnsZones Running partition tests on : DomainDnsZones Running partition tests on : Schema Running partition tests on : Configuration Running partition tests on : test Running enterprise tests on : test.lan Starting test: LocatorCheck Warning: DcGetDcName(TIME_SERVER) call failed, error 1355 A Time Server could not be located. The server holding the PDC role is down. Warning: DcGetDcName(GOOD_TIME_SERVER_PREFERRED) call failed, error 1355 A Good Time Server could not be located. ......................... test.lan failed test LocatorCheck PARAMETER: -wysiwygCustomField "ReplaceMeWithaWYSIWYGcustomField" Name of a WYSIWYG custom field to optionally save the results to. .NOTES Minimum OS Architecture Supported: Windows 10, Windows Server 2016 Release Notes: Initial Release #> [CmdletBinding()] param ( [Parameter()] [String]$wysiwygCustomField ) begin { # If script form variables are used, replace command line parameters with their value. if ($env:wysiwygCustomFieldName -and $env:wysiwygCustomFieldName -notlike "null") { $wysiwygCustomField = $env:wysiwygCustomFieldName } # Function to test if the current machine is a domain controller function Test-IsDomainController { $OS = if ($PSVersionTable.PSVersion.Major -lt 5) { Get-WmiObject -Class Win32_OperatingSystem } else { Get-CimInstance -ClassName Win32_OperatingSystem } # Check if the OS is a domain controller (ProductType 2) if ($OS.ProductType -eq "2") { return $true } } function Get-DCDiagResults { # Define the list of DCDiag tests to run $DCDiagTestsToRun = "Connectivity", "Advertising", "FrsEvent", "DFSREvent", "SysVolCheck", "KccEvent", "KnowsOfRoleHolders", "MachineAccount", "NCSecDesc", "NetLogons", "ObjectsReplicated", "Replications", "RidManager", "Services", "SystemLog", "VerifyReferences", "CheckSDRefDom", "CrossRefValidation", "LocatorCheck", "Intersite" foreach ($DCTest in $DCDiagTestsToRun) { # Run DCDiag for the current test and save the output to a file $DCDiag = Start-Process -FilePath "DCDiag.exe" -ArgumentList "/test:$DCTest", "/f:$env:TEMP\dc-diag-$DCTest.txt" -PassThru -Wait -NoNewWindow # Check if the DCDiag test failed if ($DCDiag.ExitCode -ne 0) { Write-Host "[Error] Running $DCTest!" exit 1 } # Read the raw results from the output file and filter out empty lines $RawResult = Get-Content -Path "$env:TEMP\dc-diag-$DCTest.txt" | Where-Object { $_.Trim() } # Find the status line indicating whether the test passed or failed $StatusLine = $RawResult | Where-Object { $_ -match "\. .* test $DCTest" } # Extract the status (passed or failed) from the status line $Status = $StatusLine -split ' ' | Where-Object { $_ -like "passed" -or $_ -like "failed" } # Create a custom object to store the test results [PSCustomObject]@{ Test = $DCTest Status = $Status Result = $RawResult } # Remove the temporary output file Remove-Item -Path "$env:TEMP\dc-diag-$DCTest.txt" } } function Set-NinjaProperty { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String]$Name, [Parameter()] [String]$Type, [Parameter(Mandatory = $True, ValueFromPipeline = $True)] $Value, [Parameter()] [String]$DocumentName ) $Characters = $Value | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters if ($Characters -ge 200000) { throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded: the value is greater than or equal to 200,000 characters.") } # If requested to set the field value for a Ninja document, specify it here. $DocumentationParams = @{} if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName } # This is a list of valid fields that can be set. If no type is specified, assume that the input does not need to be changed. $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG" if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type. Please check here for valid types: https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" } # The field below requires additional information to set. $NeedsOptions = "Dropdown" if ($DocumentName) { if ($NeedsOptions -contains $Type) { # Redirect error output to the success stream to handle errors more easily if nothing is found or something else goes wrong. $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1 } } else { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1 } } # If an error is received with an exception property, exit the function with that error information. if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions } # The types below require values not typically given to be set. The code below will convert whatever we're given into a format ninjarmm-cli supports. switch ($Type) { "Checkbox" { # Although it's highly likely we were given a value like "True" or a boolean data type, it's better to be safe than sorry. $NinjaValue = [System.Convert]::ToBoolean($Value) } "Date or Date Time" { # Ninjarmm-cli expects the GUID of the option to be selected. Therefore, match the given value with a GUID. $Date = (Get-Date $Value).ToUniversalTime() $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date $NinjaValue = $TimeSpan.TotalSeconds } "Dropdown" { # Ninjarmm-cli expects the GUID of the option we're trying to select, so match the value we were given with a GUID. $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name" $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID if (-not $Selection) { throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown options.") } $NinjaValue = $Selection } default { # All the other types shouldn't require additional work on the input. $NinjaValue = $Value } } # Set the field differently depending on whether it's a field in a Ninja Document or not. if ($DocumentName) { $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1 } else { $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2>&1 } if ($CustomField.Exception) { throw $CustomField } } function Test-IsElevated { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) } if (!$ExitCode) { $ExitCode = 0 } } process { # Check if the script is run with Administrator privileges if (!(Test-IsElevated)) { Write-Host -Object "[Error] Access Denied. Please run with Administrator privileges." exit 1 } # Check if the script is run on a Domain Controller if (!(Test-IsDomainController)) { Write-Host -Object "[Error] This script needs to be run on a Domain Controller." exit 1 } # Initialize lists to store passing and failing tests $PassingTests = New-Object System.Collections.Generic.List[object] $FailedTests = New-Object System.Collections.Generic.List[object] # Notify the user that the tests are being retrieved Write-Host -Object "`nRetrieving Directory Server Diagnosis Test Results." $TestResults = Get-DCDiagResults # Process each test result foreach ($Result in $TestResults) { $TestFailed = $False # Check if any status in the result indicates a failure $Result.Status | ForEach-Object { if ($_ -notmatch "pass") { $TestFailed = $True } } # Add the result to the appropriate list if ($TestFailed) { $FailedTests.Add($Result) } else { $PassingTests.Add($Result) } } # Optionally set a WYSIWYG custom field if specified if ($wysiwygCustomField) { try { Write-Host -Object "`nBuilding HTML for Custom Field." # Create an HTML report for the custom field $HTML = New-Object System.Collections.Generic.List[object] $HTML.Add("<h1 style='text-align: center'>Directory Server Diagnosis Test Results (DCDiag.exe)</h1>") $FailedPercentage = $([math]::Round((($FailedTests.Count / ($FailedTests.Count + $PassingTests.Count)) * 100), 2)) $SuccessPercentage = 100 - $FailedPercentage $HTML.Add( @" <div class='p-3 linechart'> <div style='width: $FailedPercentage%; background-color: #C6313A;'></div> <div style='width: $SuccessPercentage%; background-color: #007644;'></div> </div> <ul class='unstyled p-3' style='display: flex; justify-content: space-between; '> <li><span class='chart-key' style='background-color: #C6313A;'></span><span>Failed ($($FailedTests.Count))</span></li> <li><span class='chart-key' style='background-color: #007644;'></span><span>Passed ($($PassingTests.Count))</span></li> </ul> "@ ) # Add failed tests to the HTML report $FailedTests | Sort-Object Test | ForEach-Object { $HTML.Add( @" <div class='info-card error'> <i class='info-icon fa-solid fa-circle-exclamation'></i> <div class='info-text'> <div class='info-title'>$($_.Test)</div> <div class='info-description'> $($_.Result | Out-String) </div> </div> </div> "@ ) } # Add passing tests to the HTML report $PassingTests | Sort-Object Test | ForEach-Object { $HTML.Add( @" <div class='info-card success'> <i class='info-icon fa-solid fa-circle-check'></i> <div class='info-text'> <div class='info-title'>$($_.Test)</div> <div class='info-description'> Test passed. </div> </div> </div> "@ ) } # Set the custom field with the HTML report Write-Host -Object "Attempting to set Custom Field '$wysiwygCustomField'." Set-NinjaProperty -Name $wysiwygCustomField -Value $HTML Write-Host -Object "Successfully set Custom Field '$wysiwygCustomField'!" } catch { Write-Host -Object "[Error] $($_.Exception.Message)" $ExitCode = 1 } } # Display the list of passing tests if ($PassingTests.Count -gt 0) { Write-Host -Object "" Write-Host -Object "Passing Tests: " -NoNewline Write-Host -Object ($PassingTests.Test | Sort-Object) -Separator ", " Write-Host -Object "" } # Display the list of failed tests with detailed output if ($FailedTests.Count -gt 0) { Write-Host -Object "[Alert] Failed Tests Detected!" Write-Host -Object "Failed Tests: " -NoNewline Write-Host -Object ($FailedTests.Test | Sort-Object) -Separator ", " Write-Host -Object "`n### Detailed Output ###" $FailedTests | Sort-Object Test | ForEach-Object { Write-Host -Object "" Write-Host -Object ($_.Result | Out-String) Write-Host -Object "" } } else { Write-Host -Object "All Directory Server Diagnosis Tests Pass!" } exit $ExitCode } end { }
Risparmia tempo con gli oltre 300 script del Dojo NinjaOne.
Analisi dettagliata
Funzionalità dello script per verificare l’integrità di Active Directory
- Controllo dei privilegi di amministratore
Lo script inizia verificando che sia in esecuzione con privilegi elevati, assicurandosi di avere le autorizzazioni necessarie per eseguire i comandi diagnostici. - Convalida del controller di dominio
Controlla se lo script per verificare l’integrità di Active Directory viene eseguito su un controller di dominio. In caso contrario, esce con un messaggio di errore appropriato. - Esecuzione dei test DCDiag
Viene eseguita una serie di test DCDiag critici, tra cui controlli di connettività, advertising, replica, registri di sistema e altro ancora. I risultati vengono analizzati per separare i test che hanno risultato positivo da quelli che invece falliscono. - Categorizzazione dei risultati
I risultati sono suddivisi in categorie di superamento e fallimento, con output dettagliati per ogni test fallito. Questo aiuta gli amministratori a concentrarsi sui problemi che richiedono un’attenzione immediata. - Campi personalizzati opzionali per il reporting
Lo script supporta un parametro opzionale per visualizzare i risultati in un campo personalizzato WYSIWYG (What You See Is What You Get), utile per l’integrazione con strumenti come NinjaOne. - Generazione di report HTML
Per una maggiore leggibilità, lo script per verificare l’integrità di Active Directory genera un report HTML che riassume i risultati dei test in un formato visivamente intuitivo, con le percentuali di successo e di fallimento visibili.
Riepilogo del flusso di lavoro
- Convalida i prerequisiti: Privilegi di amministratore e ambiente del controller di dominio.
- Esegue i test DCDiag e acquisisce i risultati.
- Categorizza e visualizza i risultati.
- Genera un report HTML opzionale per l’integrazione dei campi personalizzati.
Casi d’uso potenziali
Scenario concreto
Immagina un amministratore IT responsabile della gestione di un ambiente AD multi-sito. Uno dei controller di dominio inizia a evidenziare errori di autenticazione intermittenti. L’amministratore esegue questo script per verificare l’integrità di Active Directory sul server interessato per diagnosticare il problema. L’output evidenzia i fallimenti dei test Advertising e LocatorCheck , indicando problemi di sincronizzazione temporale e disponibilità del ruolo PDC. L’output dettagliato aiuta l’amministratore a identificare e risolvere rapidamente la causa principale, ripristinando le normali operazioni.
Confronti
Controlli manuali sull’integrità di AD
L’esecuzione manuale dei test DCDiag richiede un notevole investimento di tempo, soprattutto per l’analisi e l’interpretazione dei risultati. Questo script automatizza l’intero processo, fornendo output strutturati e opzioni di integrazione.
Strumenti basati su GUI
Sebbene strumenti GUI come AD Manager o RSAT forniscano un’interfaccia facile da usare per i controlli dell’integrità di AD, spesso mancano della flessibilità e delle capacità di automazione degli script PowerShell. Questo script per verificare l’integrità di Active Directory è particolarmente vantaggioso per gli MSP che gestiscono più ambienti di clienti.
Domande frequenti
- Questo script per verificare l’integrità di Active Directory può essere eseguito su qualsiasi server?
No, deve essere eseguito su un controller di dominio. Gli ambienti non-DC generano un errore. - Quali autorizzazioni sono necessarie?
I privilegi di amministratore sono obbligatori per eseguire lo script per verificare l’integrità di Active Directory con successo. - I risultati possono essere esportati in un file?
Sì, lo script per verificare l’integrità di Active Directory include un’opzione per generare un report HTML per l’integrazione con i campi personalizzati. - Come vengono evidenziati i test falliti?
I test falliti sono elencati separatamente con messaggi diagnostici dettagliati, per facilitare la definizione delle priorità di risoluzione dei problemi.
Implicazioni
I risultati di questo script per verificare l’integrità di Active Directory possono rivelare vulnerabilità critiche nell’ambiente AD, come errori di replica o ruoli mal configurati. Se non risolti, questi problemi possono portare a problemi di autenticazione diffusi, ad applicazioni errate delle policy e a rischi per la sicurezza.
Raccomandazioni
- Esegui lo script regolarmente: Pianifica l’esecuzione periodica dello script per verificare l’integrità di Active Directory per mantenere un controllo continuo dell’integrità di AD.
- Analizza le tendenze: Confronta i risultati nel tempo per identificare i problemi ricorrenti.
- Integralo con gli strumenti di monitoraggio: Utilizza l’opzione del campo personalizzato per integrare i risultati in strumenti come NinjaOne per un monitoraggio centralizzato.
Considerazioni finali
Questo script PowerShell offre una soluzione semplificata e automatizzata per verificare l’integrità di Active Directory, riducendo il tempo e l’impegno necessari per la diagnostica manuale. Identificando tempestivamente i problemi, i professionisti IT possono affrontare in modo proattivo le potenziali interruzioni, garantendo un ambiente Active Directory stabile e sicuro. Per gli MSP, strumenti come NinjaOne sono complementari a questo script e forniscono una piattaforma centralizzata per la gestione e il monitoraggio delle operazioni IT. L’utilizzo combinato di script come questo e di una piattaforma RMM come NinjaOne permette di mettere insieme una solida strategia per mantenere l’integrità IT dell’organizzazione.