Public/Repair-TakeControlAgent.ps1
|
function Repair-TakeControlAgent { <# .SYNOPSIS Diagnoses, repairs, and reinstalls the N-able Take Control (N-central) Agent. .DESCRIPTION The Repair-TakeControlAgent tool is a robust recovery utility designed for MSP environments. It addresses "Phantom Agent" scenarios where the service appears installed but fails to start. OPERATION MODES (-OperationMode): ------------------------------------------------------------------------- * Audit : Non-destructive health check, connectivity test, and log analysis. * Repair : (Default) Smart repair. Reinstalls only if health check fails or config is corrupt. * ForceReinstall : Reinstalls regardless of state. Preserves config (MSPID) if valid. * CleanInstall : "Scorched Earth". Wipes registry and files before fresh install. ------------------------------------------------------------------------- Key Capabilities: 1. Forensics: Detects missing binaries (AV Quarantine) & corrupt config (Zombie Agent). 2. Provisioning: Waits for N-central to inject MSPID after restart. 3. File Locking: Identifies security products (SentinelOne, Defender) locking files. 4. Connectivity: Validates TCP/TLS connection to N-able global gateways. .PARAMETER OperationMode Selects the repair strategy. See DESCRIPTION for list. .PARAMETER TargetVersion Optional specific version (e.g., '6.00.00'). Defaults to latest manifest. .PARAMETER RestartNcentralAgent Restarts 'Windows Agent Service' AFTER successful install. Recommended for 'ConfigCorrupt' scenarios to force ID reprovisioning. .NOTES SECURITY & APPLICATION CONTROL WARNING: --------------------------------------- This script downloads the installer to the user's %TEMP% directory. 1. Blackpoint Cyber (SnapAgent): May classify the reinstall behavior as an unauthorized RMM installation. 2. ThreatLocker / AppLocker: Will block execution from %TEMP% unless the "N-able Technologies Ltd" certificate is whitelisted or the agent is in Learning Mode. Ensure your EDR/App Control is configured to allow this activity before running. .OUTPUTS PSCustomObject #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Position = 0)] [ValidateSet('Audit', 'Repair', 'ForceReinstall', 'CleanInstall')] [string]$OperationMode = 'Repair', [Parameter(Mandatory = $false)] [string]$TargetVersion, [Parameter(Mandatory = $false)] [switch]$RestartNcentralAgent ) # Ensure TLS 1.2 is enabled for current session (Vital for older OS/PS versions) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Load Configuration $Config = Get-TakeControlConfig # --- Main Execution Block --- # SMART SILENCE: If non-interactive, suppress prompts. if ([Console]::IsOutputRedirected) { $PSDefaultParameterValues['*:Confirm'] = $false } if (-not (Test-TakeControlIsAdmin)) { Write-Error "Requires Administrator privileges."; exit 1 } Write-TakeControlLog -Message "Starting Take Control Maintenance. Mode: $OperationMode" -LogPath $Config.LogPath Write-TakeControlLog -Message "Log file: $($Config.LogPath)" -LogPath $Config.LogPath # LOCK FILE CHECK (Safety First) Test-TakeControlInstallLock -Config $Config try { $downloadUrl = Get-TakeControlDownloadUrl -Config $Config -TargetVersion $TargetVersion $webReq = Invoke-RestMethod -Uri $downloadUrl -ErrorAction Stop } catch { Write-TakeControlLog -Message "Failed to contact N-able CDN." -Level Error -LogPath $Config.LogPath exit 1 } $initialHealth = Get-TakeControlAgentHealth -Config $Config # Standard check (VERBOSE) Write-Verbose "Initial Health State: $($initialHealth | Out-String)" if ($OperationMode -eq 'Audit') { Write-Host "--- Audit Report ---" -ForegroundColor Cyan $initialHealth | Format-List Test-TakeControlGatewayConnection -Config $Config Invoke-TakeControlLogAnalysis -Config $Config return } # Decision Matrix $needsAction = $false if ($OperationMode -in @('ForceReinstall', 'CleanInstall')) { $needsAction = $true } elseif (-not $initialHealth.ServicesRunning -or -not $initialHealth.SignatureValid -or $initialHealth.QuarantineSuspect -or $initialHealth.ConfigCorrupt) { Write-TakeControlLog -Message "Health check failed. Initiating repair." -Level Warning -LogPath $Config.LogPath $needsAction = $true } if (-not $needsAction) { Write-TakeControlLog -Message "Agent is healthy. No action required." -Level Success -LogPath $Config.LogPath return } # --- PROACTIVE RESTART PROMPT --- if ($initialHealth.ConfigCorrupt -and -not $RestartNcentralAgent) { Write-TakeControlLog -Message "DETECTION: Corrupt Configuration (Zombie Agent). Windows Agent Service (N-central) must be restarted to reprovision ID." -Level Warning -LogPath $Config.LogPath if ($PSCmdlet.ShouldProcess("Windows Agent Service (N-central)", "Enable Post-Install Restart to fix corruption")) { $RestartNcentralAgent = $true Write-TakeControlLog -Message "Windows Agent Service restart enabled." -Level Info -LogPath $Config.LogPath } } if ($PSCmdlet.ShouldProcess("Local Machine", "Install Take Control Agent")) { $tempInstaller = Join-Path $env:TEMP "TC_Installer_$(Get-Random).exe" try { # ASYNC DOWNLOAD: Using Start-Job to keep UI responsive for Animation. # Note: This has overhead but is required for the requested UX. # Since Start-Job creates a new process, we need to handle Download inside it carefully. # But wait, Start-Job doesn't inherit modules easily. # The original script used `Invoke-WebRequest` inside the job block. # It didn't depend on helpers. $job = Start-Job -ScriptBlock { param($Url, $Path) $ProgressPreference = 'SilentlyContinue' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Invoke-WebRequest -Uri $Url -OutFile $Path -UseBasicParsing -ErrorAction Stop } -ArgumentList $webReq.url, $tempInstaller Wait-TakeControlAnimation -Activity "Downloading Installer" -TimeoutSeconds 300 -Condition { return ($job.State -ne 'Running') } -Config $Config | Out-Null $jobState = $job.State $jobError = Receive-Job -Job $job -Wait -AutoRemoveJob -ErrorAction SilentlyContinue if ($jobState -ne 'Completed') { throw "Download job failed or timed out: $jobError" } if (-not (Test-Path $tempInstaller)) { throw "Installer missing after download." } if ((Get-FileHash $tempInstaller).Hash -ne $webReq.expected_hash) { throw "Hash mismatch" } } catch { Write-TakeControlLog -Message "Download failed: $_" -Level Error -LogPath $Config.LogPath exit 1 } # CRITICAL: If config is corrupt, we MUST delete the file so installer/N-central can regenerate it. if ($initialHealth.ConfigCorrupt) { Write-TakeControlLog -Message "Detected Corrupt Configuration. Deleting BASupSrvc.ini..." -Level Warning -LogPath $Config.LogPath $badIni = Join-Path $env:ProgramData "GetSupportService_N-Central\BASupSrvc.ini" if (Test-Path $badIni) { try { Remove-Item -Path $badIni -Force -ErrorAction Stop } catch { Write-TakeControlLog -Message "Failed to delete corrupt INI: $_" -Level Error -LogPath $Config.LogPath } } } Invoke-TakeControlCleanup -Config $Config -OperationMode $OperationMode Wait-TakeControlAnimation -Activity "Installing Agent" -TimeoutSeconds 10 -Condition { $false } -Config $Config | Out-Null $exitCode = Invoke-TakeControlInstaller -InstallerPath $tempInstaller -Config $Config Remove-Item $tempInstaller -ErrorAction SilentlyContinue if ($exitCode -ne 0) { Write-TakeControlLog -Message "Installer returned non-zero exit code: $exitCode" -Level Error -LogPath $Config.LogPath } } $success = Wait-TakeControlAnimation -Activity "Verifying Services" -TimeoutSeconds 60 -Condition { $running = (Get-Service -Name $Config.Services -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Running' }).Count return ($running -eq 2) } -Config $Config if ($success) { if ($RestartNcentralAgent) { Write-TakeControlLog -Message "Take Control Services are running. Restarting Windows Agent Service (N-central)..." -Level Info -LogPath $Config.LogPath try { Restart-Service -Name $Config.NcentralService -Force -ErrorAction Stop Write-TakeControlLog -Message "Windows Agent Service restarted successfully." -Level Success -LogPath $Config.LogPath # Wait for MSPID provisioning # UPDATED: Checks for either MSPID OR ServerUniqueID to prevent false wait loops $provisioned = Wait-TakeControlAnimation -Activity "Provisioning Configuration" -TimeoutSeconds 120 -Condition { $iniPath = Join-Path $env:ProgramData "GetSupportService_N-Central\BASupSrvc.ini" if (Test-Path $iniPath) { $c = Get-Content $iniPath -Raw -ErrorAction SilentlyContinue return (($c -match "(?ms)^\[Main\].*?^MSPID=[a-zA-Z0-9\-_]{10,}") -or ($c -match "(?ms)^\[Main\].*?^ServerUniqueID=[a-zA-Z0-9\-_]{10,}")) } return $false } -Config $Config if ($provisioned) { Write-TakeControlLog -Message "Configuration successfully provisioned by N-central." -Level Success -LogPath $Config.LogPath } else { Write-TakeControlLog -Message "Timed out waiting for Configuration (120s). It may appear after the next Agent check-in." -Level Warning -LogPath $Config.LogPath } } catch { Write-TakeControlLog -Message "Failed to restart N-central Agent: $_" -Level Error -LogPath $Config.LogPath } } else { # Case: Services Repaired, but User declined Restart Write-TakeControlLog -Message "Take Control Services are running." -Level Success -LogPath $Config.LogPath $currentHealth = Get-TakeControlAgentHealth -Silent -Config $Config if ($currentHealth.ConfigCorrupt) { # FINAL UX: Explicit confirmation that the service is up but waiting on N-central Write-TakeControlLog -Message "SUCCESS: Services have been repaired and are running." -Level Success -LogPath $Config.LogPath Write-TakeControlLog -Message "NOTE: Configuration (MSPID) is currently pending." -Level Info -LogPath $Config.LogPath Write-TakeControlLog -Message " The N-central 'Windows Agent Service' will automatically provision this during its next check-in (approx 10-15 mins)." -Level Info -LogPath $Config.LogPath Write-TakeControlLog -Message " To force this immediately, you can restart the 'Windows Agent Service'." -Level Info -LogPath $Config.LogPath } } } else { Write-TakeControlLog -Message "Recovery Failed. Services did not start." -Level Error -LogPath $Config.LogPath Write-TakeControlLog -Message "Triggering Automatic Log Analysis..." -Level Warning -LogPath $Config.LogPath Invoke-TakeControlLogAnalysis -Config $Config $av = Get-TakeControlRunningAV -Config $Config if ($av) { Write-TakeControlLog -Message "POSSIBLE CAUSE: Active Security Software ($av)" -Level Warning -LogPath $Config.LogPath if ($av -like "*SnapAgent*" -or $av -like "*ThreatLocker*" -or $av -like "*AEService*" -or $av -like "*AirlockEnforcement*") { Write-TakeControlLog -Message "Application Control Detected. Ensure RMM tools are whitelisted/learning mode is active." -Level Warning -LogPath $Config.LogPath } } } # Final Report (SILENT) $finalHealth = Get-TakeControlAgentHealth -Silent -Config $Config return [PSCustomObject]@{ Timestamp = Get-Date Operation = $OperationMode Success = $finalHealth.ServicesRunning ServicesUp = $finalHealth.ServicesRunning QuarantineSuspect = $finalHealth.QuarantineSuspect ConfigCorrupt = $finalHealth.ConfigCorrupt RestartedNcentral = $RestartNcentralAgent LogFile = $Config.LogPath } } |