Update SVSTaskGate.ps1

This commit is contained in:
2025-01-02 23:37:30 -05:00
parent a59201c1ce
commit f4beac86ac

View File

@@ -1,11 +1,13 @@
### now that we have a field for user to enter N8N password we need to figure out how we can make the fetch site button refresh the downdown after
### add tweek to set default provider, add to toolkit?
### power settings in tweeks or onboarding?
### make script install the toolkit
### test the write-log in the log box and in the event viewer
### need to see if there's anything else we could take from Theo script?
### make when using select all the datto rmm pus var runs before install-splashtop
# ---------------------------------------------------------------------------
# 1) CREATE A GLOBAL LOG CACHE (NEW)
# ---------------------------------------------------------------------------
$Global:LogCache = New-Object System.Collections.ArrayList
# Check if the Write-Log function exists
if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction SilentlyContinue)) {
@@ -37,7 +39,7 @@ if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction Silentl
"Warning" { ([char]0x26A0) } # Warning icon
"Error" { ([char]0x274C) } # Error icon
"Success" { ([char]0x2705) } # Success icon
"General" { ([char]0x1F4E6) } # Package icon for generic tasks/operations
"General" { ([char]0x1F4E6) } # Package icon
}
# Map levels to colors
@@ -49,9 +51,20 @@ if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction Silentl
"General" { "White" }
}
# Include Task Category, Icon, and Message in the console output
# Write to the PowerShell console
Write-Host "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)" -ForegroundColor $Color
# -------------------------------------------------------------------
# 2) ALSO STORE THE LOG IN OUR GLOBAL LOG CACHE (NEW)
# -------------------------------------------------------------------
$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)
# -------------------------------------------------------------------
# Optionally log to the Windows Event Log
if ($LogToEvent) {
$EntryType = switch ($Level) {
@@ -62,23 +75,18 @@ if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction Silentl
}
try {
# Check if the event source exists, create it if necessary
if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) {
New-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue
}
# Include Task Category in the Event Log Message
$EventMessage = "TaskCategory: $TaskCategory | Message: $Message"
# Write the event log
Write-EventLog -LogName $EventLog -Source $EventSource -EntryType $EntryType -EventId $EventID -Message $EventMessage
} catch {
}
catch {
Write-Host "([char]0x26A0) [Warning] [EventLog] Failed to write to Event Log: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
}
}
# Define a fallback Write-LogHybrid function that uses Write-LogHelper
function Write-LogHybrid {
param (
[string]$Message,
@@ -90,8 +98,6 @@ if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction Silentl
[string]$EventLog = "Application",
[int]$CustomEventID
)
# Use the helper function to log
Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory `
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
-CustomEventID $CustomEventID
@@ -110,8 +116,6 @@ else {
[string]$EventLog = "Application",
[int]$CustomEventID
)
# Use the existing Write-Log function
Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory `
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
-CustomEventID $CustomEventID
@@ -120,6 +124,7 @@ else {
# Example usage of Write-LogHybrid
Write-LogHybrid -Message "This is a test log message. Write-log2 does exist" -Level "Info" -TaskCategory "TestCategory" -LogToEvent:$true
Write-Log -Message "This is a test log message. Write-log4444 does exist" -Level "Info" -TaskCategory "TestCategory" -LogToEvent:$true
function Install-SVSMSP {
@@ -178,7 +183,6 @@ function Install-SVSMSP {
[string]$ApiSecretKey = "YOUR_API_SECRET_HERE"
)
# Helper function: Perform Cleanup
function Perform-Cleanup {
Write-LogHybrid -Message "Cleanup mode enabled. Starting cleanup process..." -Level "Info" -LogToEvent -EventID 1502
@@ -225,9 +229,8 @@ function Install-SVSMSP {
Write-LogHybrid -Message "Cleanup process completed successfully." -Level "Success" -LogToEvent -EventID 1510
}
# Helper function: Perform Toolkit Installation
function Perform-ToolkitInstallation {
# Perform cleanup to remove old modules and repositories
# Perform cleanup
Perform-Cleanup
# Step 1: Set Execution Policy
@@ -282,7 +285,6 @@ function Install-SVSMSP {
Write-LogHybrid -Message "Toolkit installation process completed successfully." -Level "Success" -LogToEvent -EventID 1510
}
# Main Logic
Write-LogHybrid -Message "Install-SVSMSP function started." -Level "Info" -LogToEvent -EventID 1500
if ($Cleanup) {
@@ -299,7 +301,7 @@ function Install-SVSMSP {
Perform-ToolkitInstallation
}
#Install-SVSMSP -InstallToolkit
Install-SVSMSP -InstallToolkit
# ----------------------------------------------------------------------------------
@@ -316,21 +318,17 @@ function Get-N8nWebhookData {
[string]$AuthHeaderValue
)
# Define the URL and headers
$url = "https://automate.svstools.ca/webhook/svsmspkit"
$headers = @{
"SVSMSPKit" = $AuthHeaderValue
}
# Make the GET request to the N8N webhook
try {
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
Write-Host "Response received successfully:" -ForegroundColor Green
# Convert the JSON response to a PowerShell object
$data = $response
# Map each field to a variable (if needed)
# Map fields
$global:Comment_SVSmodule = $data._Comment_SVSmodule
$global:ModuleName = $data.ModuleName
$global:RepositoryURL = $data.RepositoryURL
@@ -343,18 +341,8 @@ function Get-N8nWebhookData {
$global:ApiKey = $data.ApiKey
$global:ApiSecretKey = $data.ApiSecretKey
# Print the values
Write-Output "Module Name: $ModuleName"
Write-Output "Repository URL: $RepositoryURL"
Write-Output "Old Repo: $OldRepo"
Write-Output "New Repo: $NewRepo"
Write-Output "Commands To Check: $($CommandsToCheck -join ', ')"
Write-Output "Log File Path: $LogFilePath"
Write-Output "API URL: $ApiUrl"
Write-Output "API Key: $ApiKey"
Write-Output "API Secret Key: $ApiSecretKey"
# Return the parsed data object
return $data
}
catch {
@@ -370,11 +358,11 @@ function GetHtmlContent {
@"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>On-Boarding & Off-Boarding</title>
<title>SVS TaskGate</title>
<link rel="icon" href="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Favicon.ico" type="image/x-icon">
<style>
body {
font-family: Arial, sans-serif;
@@ -383,18 +371,24 @@ function GetHtmlContent {
background-color: #1e1e1e;
color: #ffffff;
}
.logo-container {
text-align: center;
padding: 20px;
background-color: #1e1e1e; /* Matches the background color */
}
.logo-container img {
max-width: 300px; /* Adjust size as needed */
height: auto;
}
.container {
display: flex;
height: 100vh;
}
.sidebar {
width: 200px;
background-color: #2e2e2e;
background-color: #1e1e1e;
padding: 10px;
}
.sidebar button {
display: block;
width: 100%;
@@ -408,30 +402,24 @@ function GetHtmlContent {
text-align: left;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.sidebar button.active {
background-color: #007bff;
}
.sidebar button:hover {
background-color: #0056b3;
transform: scale(1.05);
}
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.checkbox-group label {
margin: 0;
padding: 0;
@@ -439,7 +427,6 @@ function GetHtmlContent {
display: flex;
align-items: center;
}
.password-input,
.dropdown {
width: 30%;
@@ -450,12 +437,10 @@ function GetHtmlContent {
background-color: #2e2e2e;
color: white;
}
.button-group {
margin-top: 20px;
text-align: right;
}
.button-group button {
padding: 10px 20px;
margin-left: 10px;
@@ -463,24 +448,20 @@ function GetHtmlContent {
border-radius: 5px;
cursor: pointer;
}
.install-button {
background-color: #28a745;
color: white;
}
.install-button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
.exit-button {
background-color: #dc3545;
color: white;
}
.log {
width: 50%;
width: 80%;
margin-top: 20px;
padding: 10px;
background-color: #333;
@@ -489,20 +470,20 @@ function GetHtmlContent {
max-height: 300px;
overflow-y: auto;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
.sidebar {
width: 100%;
}
}
</style>
</head>
<body>
<div class="logo-container">
<img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Logo.png" alt="SVS Logo">
</div>
<div class="container">
<div class="sidebar">
<button class="tab-button active" data-tab="onboardTab" aria-expanded="true">On-Boarding</button>
@@ -560,25 +541,21 @@ function GetHtmlContent {
<input type="checkbox" name="dattoRMMOption" value="exe">
Download.exe
</label>
<br>
<br>
<br><br>
</div>
</div>
<div id="n8nPasswordContainer" style="display: none;">
<label for="n8nPassword">Enter n8n Password:</label>
<br>
<label for="n8nPassword">Enter n8n Password:</label><br>
<input type="password" id="n8nPassword" class="password-input" placeholder="Enter N8N Password">
</div>
<br>
<div id="DattoRMMContainer" style="display: none;">
<label for="dattoRmmDropdown">Select a Datto RMM site:</label>
<br>
<label for="dattoRmmDropdown">Select a Datto RMM site:</label><br>
<select id="dattoRmmDropdown" class="dropdown">
<option value="">Fetching sites...</option>
$siteOptions
</select>
</div>
<div class="button-group">
<button class="install-button" id="fetchSitesButton" onclick="fetchSites()" disabled>Fetch
Sites</button>
@@ -591,6 +568,7 @@ function GetHtmlContent {
</div>
</div>
</div>
<script>
function toggleOnboardCheckboxes(selectedCheckbox) {
const checkboxes = document.querySelectorAll('#onboardTab input[type="checkbox"]');
@@ -605,7 +583,6 @@ function GetHtmlContent {
}
});
// Check if "Select All" is checked and handle the Datto RMM-specific logic
if (dattoRMMCheckbox.checked) {
optionsContainer.style.display = 'block';
n8nPasswordContainer.style.display = 'block';
@@ -621,16 +598,11 @@ function GetHtmlContent {
const checkbox = document.getElementById('installDattoRMMCheckbox');
const optionsContainer = document.getElementById('dattoRMMOptionsContainer');
const n8nPasswordContainer = document.getElementById('n8nPasswordContainer');
const DattoRMMContainer = document.getElementById('DattoRMMContainer')
const DattoRMMContainer = document.getElementById('DattoRMMContainer');
// Show or hide the Datto RMM options
optionsContainer.style.display = checkbox.checked ? 'block' : 'none';
// Show or hide the password field
n8nPasswordContainer.style.display = checkbox.checked ? 'block' : 'none';
// Show or Hide the DattoRMM site list
DattoRMMContainer.style.display = checkbox.checked ? 'block' : 'none'
DattoRMMContainer.style.display = checkbox.checked ? 'block' : 'none';
}
const tabButtons = document.querySelectorAll('.tab-button');
@@ -668,7 +640,6 @@ function GetHtmlContent {
try {
appendLog("Fetching sites...", "yellow");
// Example fetch request (update URL and payload as needed)
const response = await fetch('/getn8npw', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -679,24 +650,20 @@ function GetHtmlContent {
throw new Error('Failed to fetch sites. Please try again.');
}
// Parse the JSON response into an array (or object)
const sites = await response.json();
// Clear out existing options in the dropdown
dropdown.innerHTML = '';
// Populate dropdown with new options
sites.forEach(site => {
const option = document.createElement('option');
option.value = site.UID; // or whatever property holds the site ID
option.textContent = site.Name; // or the relevant display name
// Adjust property names based on your actual data
option.value = site.UID;
option.textContent = site.Name;
dropdown.appendChild(option);
});
appendLog("Sites fetched successfully, please select a site!", "green");
} catch (error) {
// Handle errors and display a fallback option
}
catch (error) {
dropdown.innerHTML = '<option value="">Fetching sites failed</option>';
appendLog('Error:' + error.message, "red");
}
@@ -707,7 +674,7 @@ function GetHtmlContent {
const UID = dropdown.options[dropdown.selectedIndex].value;
const Name = dropdown.options[dropdown.selectedIndex].text;
const installDattoRMM = document.querySelector('input[name="installDattoRMM"]')
const installDattoRMM = document.querySelector('input[name="installDattoRMM"]');
const installCyberQP = document.querySelector('input[name="installCyberQP"]');
const installSplashtop = document.querySelector('input[name="installSplashtop"]');
const installSVSHelpDesk = document.querySelector('input[name="installSVSHelpDesk"]');
@@ -717,15 +684,11 @@ function GetHtmlContent {
if (installDattoRMM.checked) {
const DattoRMMCheckbox = document.querySelectorAll('input[name="dattoRMMOption"]:checked');
appendLog("Installing selected site RMM...", "cyan");
const checkedValues = Array.from(DattoRMMCheckbox).map(c => c.value);
// Initialize the PowerShell command
let installRMMCommand = 'Install-DattoRMM -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey';
// Check individual selections and append flags to the command
if (checkedValues.includes('inputVar')) {
installRMMCommand += ' -PushSiteVars';
}
@@ -736,83 +699,98 @@ function GetHtmlContent {
installRMMCommand += ' -SaveCopy';
}
const response = fetch('/installrmm', {
fetch('/installrmm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
installRMMCommand,
UID,
Name
})
body: JSON.stringify({ installRMMCommand, UID, Name })
});
}
if (installCyberQP.checked) {
fetch('/installCyberQP', { method: 'GET' })
if (installCyberQP.checked) {
fetch('/installCyberQP', { method: 'GET' });
appendLog("Installing CyberQP", "cyan");
}
if (installSplashtop.checked) {
fetch('/installSplashtop', { method: 'GET' });
appendLog("Installing Splashtop", "cyan");
}
if (installSVSHelpDesk.checked) {
fetch('/installSVSHelpDesk', { method: 'GET' });
appendLog("Installing SVSHelpdesk", "cyan");
}
if (installSVSWatchtower.checked) {
fetch('/installSVSWatchtower', { method: 'GET' });
appendLog("Installing SVSWatchtower", "cyan");
}
if (installThreatLocker.checked) {
fetch('/installThreatLocker', { method: 'GET' });
appendLog("Installing ThreatLocker", "cyan");
}
if (installRocketCyber.checked) {
fetch('/installRocketCyber', { method: 'GET' });
appendLog("Installing RocketCyber", "cyan");
}
}
function endSession() {
appendLog("Session ended. Closing application...", "yellow");
fetch('/quit', { method: 'GET' })
.then(response => {
if (!response.ok) {
throw new Error('Failed to end session');
}
// Optionally, close the window or do something else
window.close()
window.close();
})
.catch(error => {
appendLog("Error ending session: " + error.message, "red");
});
}
function appendLog(message, color = "white") {
const log = document.createElement('p');
log.style.color = color;
log.textContent = message;
logArea.appendChild(log);
document.getElementById('logArea').appendChild(log);
}
// -------------------------------------------------------------------
// 3) POLL THE SERVER LOGS (NEW)
// -------------------------------------------------------------------
let lastLogCount = 0;
async function fetchLogs() {
try {
const resp = await fetch('/getLogs');
if (!resp.ok) return;
const logs = await resp.json(); // array of {Timestamp, Level, Message}
// Append only new messages
for (let i = lastLogCount; i < logs.length; i++) {
// We'll display each new line in "white" or a color of your choice
appendLog(logs[i].Message, "white");
}
lastLogCount = logs.length;
} catch (err) {
console.error("Error fetching logs:", err);
}
}
// Poll logs every 3 seconds (feel free to adjust)
setInterval(fetchLogs, 3000);
// -------------------------------------------------------------------
</script>
</body>
</html>
"@
}
# Save and launch the HTML
Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/"
try {
while ($listener.IsListening) {
# Wait for an incoming request
$context = $listener.GetContext()
$request = $context.Request
$response = $context.Response
@@ -820,7 +798,6 @@ try {
switch ($request.Url.AbsolutePath) {
"/" {
# Serve the main HTML
$htmlContent = GetHtmlContent
$buffer = [System.Text.Encoding]::UTF8.GetBytes($htmlContent)
$response.ContentType = "text/html"
@@ -831,17 +808,13 @@ try {
"/getn8npw" {
if ($request.HttpMethod -eq "POST") {
# Parse the received JSON
$bodyStream = New-Object IO.StreamReader $request.InputStream
$body = $bodyStream.ReadToEnd()
$data = ConvertFrom-Json $body
$password = $data.password
Get-N8nWebhookData -AuthHeaderValue $password
# Fetch the list of Datto RMM client sites
$sites = Install-DattoRMM -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey -FetchSitesOnly
if (-not $sites) {
Write-Host "No sites returned. Please check the API." -ForegroundColor Red
$response.StatusCode = 500
@@ -850,15 +823,10 @@ try {
$response.OutputStream.Close()
continue
}
# Convert the array (or object) in $sites to JSON
$responseData = $sites | ConvertTo-Json
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseData)
# Set the response headers
$response.ContentType = "application/json"
$response.ContentLength64 = $buffer.Length
$response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.OutputStream.Close()
}
@@ -871,7 +839,6 @@ try {
$selectedSite = ConvertFrom-Json $body
Invoke-Expression $selectedSite.installRMMCommand
# Return a simple success response
$responseString = "RMM install triggered."
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
$response.ContentType = "text/plain"
@@ -885,7 +852,6 @@ try {
if ($request.HttpMethod -eq "GET") {
Install-CyberQP
}
# Return something
$responseString = "Install CyberQP triggered."
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
$response.ContentType = "text/plain"
@@ -954,13 +920,23 @@ try {
$response.OutputStream.Close()
}
# ----------------------------------------------------------------
# 4) NEW ROUTE: /getLogs
# Returns $Global:LogCache as JSON for the polling function
# ----------------------------------------------------------------
"/getLogs" {
if ($request.HttpMethod -eq "GET") {
$jsonLogs = $Global:LogCache | ConvertTo-Json
$logBuffer = [System.Text.Encoding]::UTF8.GetBytes($jsonLogs)
$response.ContentType = "application/json"
$response.ContentLength64 = $logBuffer.Length
$response.OutputStream.Write($logBuffer, 0, $logBuffer.Length)
$response.OutputStream.Close()
}
}
"/quit" {
if ($request.HttpMethod -eq "GET") {
# Return a "shutting down" message
$responseString = "Server shutting down."
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
$response.ContentType = "text/plain"
@@ -969,26 +945,25 @@ try {
$response.OutputStream.Close()
Write-Host $responseString
$listener.stop()
break # exit the while loop
break
}
}
default {
# Handle unknown routes
$response.StatusCode = 404
$response.StatusDescription = "Not Found"
$buffer = [System.Text.Encoding]::UTF8.GetBytes("404 - Not Found")
$response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.OutputStream.Close()
}
} # end switch
} # end while
}
}
}
catch {
Write-Host "Error: $($_.Exception.Message)"
}
finally {
$listener.Stop()
$listener.Close()
}