### To Modify as of January 27 2025 ### let's start thinking about the write-log -TaskCategory "On-boarding" or "Off-boarding" ### need RGB color codes from john, once we picked the RGBA colors ### add the .NET silent install tweaks to toolkit ### for the reg tweak need to do/undo function maybe it should have its own check box list ### added offboard check boxes for DattoRMM, DattoDEB, RocketCyber, CyberQP, SVSHelpdesk and Splashtop ### need to fix path in the uninstall-DattoEDR - ####### ❌ [Error] [GeneralTask] Uninstallation command 'C:\Program Files\Infocyte\Agent\agent.exe' not found. (Event ID: 3000) - bad path # --------------------------------------------------------------------------- # 1) CREATE A GLOBAL LOG CACHE (NEW) # --------------------------------------------------------------------------- if (-not $Global:LogCache -or -not ($Global:LogCache -is [System.Collections.ArrayList])) { $Global:LogCache = New-Object System.Collections.ArrayList } #region Write-LogHelper if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction SilentlyContinue)) { function Write-LogHelper { param ( [string]$Message, [ValidateSet("Info", "Warning", "Error", "Success", "General")] [string]$Level = "Info", [string]$TaskCategory = "GeneralTask", [switch]$LogToEvent = $false, [string]$EventSource = "SVSMSP_Module", [string]$EventLog = "Application", [int]$CustomEventID ) $EventID = switch ($Level) { "Info" { 1000 } "Warning" { 2000 } "Error" { 3000 } "Success" { 4000 } "General" { 1000 } } $Icon = switch ($Level) { "Info" { [System.Char]::ConvertFromUtf32(0x1F4CB) } "Warning" { ([char]0x26A0) } "Error" { ([char]0x274C) } "Success" { ([char]0x2705) } "General" { ([char]0x1F4E6) } } $logEntry = [PSCustomObject]@{ Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") Level = $Level Message = "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)" } [void]$Global:LogCache.Add($logEntry) if ($LogToEvent) { $EntryType = switch ($Level) { "Info" { "Information" } "Warning" { "Warning" } "Error" { "Error" } default { "Information" } } try { if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) { New-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue } $EventMessage = "TaskCategory: $TaskCategory | Message: $Message" Write-EventLog -LogName $EventLog -Source $EventSource -EntryType $EntryType -EventId $EventID -Message $EventMessage } catch { Write-Host "⚠ [Warning] [EventLog] Failed to write to Event Log: $($_.Exception.Message)" -ForegroundColor Yellow } } } function Write-LogHybrid { param ( [string]$Message, [ValidateSet("Info", "Warning", "Error", "Success", "General")] [string]$Level = "Info", [string]$TaskCategory = "GeneralTask", [switch]$LogToEvent = $false, [string]$EventSource = "SVSMSP_Module", [string]$EventLog = "Application", [int]$CustomEventID ) Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory ` -LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog ` -CustomEventID $CustomEventID } } else { function Write-LogHybrid { param ( [string]$Message, [ValidateSet("Info", "Warning", "Error", "Success", "General")] [string]$Level = "Info", [string]$TaskCategory = "GeneralTask", [switch]$LogToEvent = $false, [string]$EventSource = "SVSMSP_Module", [string]$EventLog = "Application", [int]$CustomEventID ) Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory ` -LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog ` -CustomEventID $CustomEventID } } Write-LogHybrid -Message "Starting SVS TaskGate" -Level "Info" -TaskCategory "SVSTaskGate" -LogToEvent:$true #endregion #region Install-DattoRMM-Helper function Install-DattoRMM-Helper { param ( [string]$ApiUrl, [string]$ApiKey, [string]$ApiSecretKey, [switch]$FetchSitesOnly, [string]$SiteName, [string]$SiteUID ) if (-not $ApiUrl -or -not $ApiKey -or -not $ApiSecretKey) { Write-LogHybrid -Message "Missing required parameters. Please provide ApiUrl, ApiKey, and ApiSecretKey." -Level "Error" -LogToEvent return } [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Write-LogHybrid -Message "Fetching OAuth token..." -Level "Info" try { $securePassword = ConvertTo-SecureString -String 'public' -AsPlainText -Force $apiGenToken = Invoke-WebRequest -Credential (New-Object System.Management.Automation.PSCredential -ArgumentList ('public-client', $securePassword)) ` -Uri ("{0}/auth/oauth/token" -f $ApiUrl) ` -Method 'POST' ` -ContentType 'application/x-www-form-urlencoded' ` -Body ("grant_type=password&username={0}&password={1}" -f $ApiKey, $ApiSecretKey) ` | ConvertFrom-Json $requestToken = $apiGenToken.access_token Write-LogHybrid -Message "OAuth token fetched successfully." -Level "Success" -LogToEvent } catch { Write-LogHybrid -Message "Failed to fetch OAuth token. Details: $($_.Exception.Message)" -Level "Error" -LogToEvent return } if ($FetchSitesOnly) { Write-Host "Fetching list of sites from the Datto RMM API..." -ForegroundColor Cyan try { $getSites = Invoke-WebRequest -Uri "$ApiUrl/api/v2/account/sites" -Method Get -Headers @{ "Authorization" = "Bearer $requestToken" } -ContentType "application/json" $sitesJson = $getSites.Content | ConvertFrom-Json $siteList = $sitesJson.sites | ForEach-Object { [PSCustomObject]@{ Name = $_.name; UID = $_.uid } } Write-Host "Successfully fetched list of sites." -ForegroundColor Green return $siteList } catch { Write-Host "Failed to fetch sites: $($_.Exception.Message)" -ForegroundColor Red return } } } #endregion #region LastPass Extensions function ForceInstall-LastPassChrome { $ExtensionID = "hdokiejnpimakedhajhdlcegeplioahd" $UpdateURL = "https://clients2.google.com/service/update2/crx" $RegPath = "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist" try { New-Item -Path (Split-Path $RegPath) -Force -ErrorAction SilentlyContinue | Out-Null New-Item -Path $RegPath -Force -ErrorAction SilentlyContinue | Out-Null Set-ItemProperty -Path $RegPath -Name "1" -Value "$ExtensionID;$UpdateURL" -ErrorAction Stop Write-Host "Configured LastPass Chrome extension." } catch { Write-Host "Failed Chrome config: $($_.Exception.Message)" -ForegroundColor Red } } function ForceInstall-LastPassEdge { $ExtensionID = "bbcinlkgjjkejfdpemiealijmmooekmp" $UpdateURL = "https://edge.microsoft.com/extensionwebstorebase/v1/crx" $RegPath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallForcelist" try { New-Item -Path (Split-Path $RegPath) -Force -ErrorAction SilentlyContinue | Out-Null New-Item -Path $RegPath -Force -ErrorAction SilentlyContinue | Out-Null Set-ItemProperty -Path $RegPath -Name "1" -Value "$ExtensionID;$UpdateURL" -ErrorAction Stop Write-Host "Configured LastPass Edge extension." } catch { Write-Host "Failed Edge config: $($_.Exception.Message)" -ForegroundColor Red } } function Install-LastPassExtensions { param([switch]$Chrome,[switch]$Edge) if ($Chrome) { ForceInstall-LastPassChrome } if ($Edge) { ForceInstall-LastPassEdge } } #endregion #region SVS Module function Install-SVSMSP { param ( [switch]$Cleanup, [switch]$InstallToolkit, [array]$AllModules = @(@{ModuleName="SVS_Toolkit"},@{ModuleName="SVSMSP"}), [string]$NewModuleName = "SVSMSP", [array]$AllRepositories = @(@{RepoName="SVS_Repo"},@{RepoName="SVS_Toolkit"}), [string]$NewRepositoryName = "SVS_Repo", [string]$NewRepositoryURL = "http://proget.svstools.ca:8083/nuget/SVS_Repo/", [string]$LogFilePath = "$env:SVSMSP\svstoolkit.log" ) function Perform-Cleanup { Write-LogHybrid -Message "Cleanup mode enabled..." -Level Info -LogToEvent foreach ($m in $AllModules) { if (Get-Module -Name $m.ModuleName -ListAvailable) { Write-LogHybrid -Message "Removing $($m.ModuleName)..." -Level Warning -LogToEvent try { Uninstall-Module -Name $m.ModuleName -AllVersions -Force; Write-LogHybrid -Message "Removed $($m.ModuleName)" -Level Success -LogToEvent } catch { Write-LogHybrid -Message "Failed to remove $($m.ModuleName): $_" -Level Error -LogToEvent } } } foreach ($r in $AllRepositories) { if (Get-PSRepository -Name $r.RepoName -ErrorAction SilentlyContinue) { Write-LogHybrid -Message "Unregistering $($r.RepoName)..." -Level Warning -LogToEvent try { Unregister-PSRepository -Name $r.RepoName -ErrorAction Stop; Write-LogHybrid -Message "Unregistered $($r.RepoName)" -Level Success -LogToEvent } catch { Write-LogHybrid -Message "Failed to unregister $($r.RepoName): $_" -Level Error -LogToEvent } } } Write-LogHybrid -Message "Cleanup complete." -Level Success -LogToEvent } function Perform-ToolkitInstallation { Perform-Cleanup if ((Get-ExecutionPolicy -Scope LocalMachine) -ne "RemoteSigned") { Write-LogHybrid -Message "Setting execution policy to RemoteSigned..." -Level Warning -LogToEvent try { Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force; Write-LogHybrid -Message "Policy set." -Level Success -LogToEvent } catch { Write-LogHybrid -Message "Failed to set policy: $_" -Level Error -LogToEvent; return } } Install-PackageProvider -Name NuGet -Force -Scope AllUsers -Confirm:$false if (-not (Get-PSRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue)) { Write-LogHybrid -Message "Registering $NewRepositoryName..." -Level Info -LogToEvent try { Register-PSRepository -Name $NewRepositoryName -SourceLocation $NewRepositoryURL -InstallationPolicy Trusted; Write-LogHybrid -Message "Registered." -Level Success -LogToEvent } catch { Write-LogHybrid -Message "Register failed: $_" -Level Error -LogToEvent } } Write-LogHybrid -Message "Installing $NewModuleName..." -Level Info -LogToEvent try { Install-Module -Name $NewModuleName -Repository $NewRepositoryName -Scope AllUsers -Force; Write-LogHybrid -Message "Installed." -Level Success -LogToEvent } catch { Write-LogHybrid -Message "Install failed: $_" -Level Error -LogToEvent } } Write-LogHybrid -Message "Install-SVSMSP started." -Level Info -LogToEvent if ($Cleanup) { Perform-Cleanup; return } if ($InstallToolkit) { Perform-ToolkitInstallation; return } Perform-ToolkitInstallation } #endregion #region HTTP Listener Setup try { $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add("http://localhost:8081/") Write-LogHybrid -Message "Listener prefix added." -Level Info $listener.Start() Write-LogHybrid -Message "Listener started." -Level Info } catch { Write-LogHybrid -Message "Listener init error: $($_.Exception.Message)" -Level Error throw } #endregion function Get-N8nWebhookData { param ([string]$AuthHeaderValue) $url = "https://automate.svstools.ca/webhook/svsmspkit" $headers = @{ "SVSMSPKit" = $AuthHeaderValue } try { $resp = Invoke-RestMethod -Uri $url -Headers $headers -Method Get foreach ($prop in $resp.PSObject.Properties) { Set-Variable -Name $prop.Name -Value $prop.Value -Scope Global } return $resp } catch { Write-Host "Webhook GET error: $($_.Exception.Message)" -ForegroundColor Red return $null } } #region HTML Content function GetHtmlContent { @" SVS TaskGate
SVS Logo

On-Boarding

This new deployment method ensures everything is deployed smoothly!


Off-Boarding

Tweaks

System Optimizations

Additional Tweaks

Miscellaneous

SVS APPs

Winget Apps

Extensions

Logs will appear here...

"@ } # Launch the UI Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/" #region HTTP Listener Route Handling try { while ($listener.IsListening) { $ctx = $listener.GetContext() $req = $ctx.Request $res = $ctx.Response switch ($req.Url.AbsolutePath) { "/" { $html = GetHtmlContent $bytes = [System.Text.Encoding]::UTF8.GetBytes($html) $res.ContentType = "text/html" $res.ContentLength64 = $bytes.Length $res.OutputStream.Write($bytes,0,$bytes.Length) $res.OutputStream.Close() } "/getn8npw" { if ($req.HttpMethod -eq "POST") { $body = (New-Object IO.StreamReader $req.InputStream).ReadToEnd() | ConvertFrom-Json Get-N8nWebhookData -AuthHeaderValue $body.password $sites = Install-DattoRMM-Helper -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey -FetchSitesOnly if ($sites) { $json = $sites | ConvertTo-Json $bytes = [System.Text.Encoding]::UTF8.GetBytes($json) $res.ContentType = "application/json" $res.ContentLength64 = $bytes.Length $res.OutputStream.Write($bytes,0,$bytes.Length) } else { $res.StatusCode = 500 $res.OutputStream.Write(([System.Text.Encoding]::UTF8.GetBytes("No sites found")),0,14) } $res.OutputStream.Close() } } "/installSVSMSPModule" { if ($req.HttpMethod -eq "GET") { try { Install-SVSMSP -InstallToolkit; $msg="SVS Module triggered"; $code=200 } catch { $msg="Error: $_"; $code=500 } $res.StatusCode = $code $bytes = [System.Text.Encoding]::UTF8.GetBytes($msg) $res.OutputStream.Write($bytes,0,$bytes.Length) $res.OutputStream.Close() } } # … include the rest of your endpoints (/installrmm, /setSVSPowerplan, /installCyberQP, etc.) exactly as before … "/quit" { if ($req.HttpMethod -eq "GET") { $res.OutputStream.Write(([System.Text.Encoding]::UTF8.GetBytes("Shutting down")),0,12) $res.OutputStream.Close() $listener.Stop() break } } default { $res.StatusCode = 404 $res.OutputStream.Write(([System.Text.Encoding]::UTF8.GetBytes("Not Found")),0,9) $res.OutputStream.Close() } } } } finally { if ($listener) { Write-LogHybrid -Message "Stopping listener." -Level Info $listener.Stop() $listener.Close() } }