added install-dattormm and folded multiple function into it

This commit is contained in:
2025-07-02 21:25:19 -04:00
parent 20d398c57c
commit 50fee51725

View File

@@ -97,19 +97,23 @@
# ─────────────────────────────────────────────────────────
# Datto headless mode
# Both Datto sets share the webhook password
# Shared webhook password for both Datto modes
# ─── DattoFetch & DattoInstall share the webhook creds ─────────────
[Parameter(Mandatory,ParameterSetName='DattoFetch')]
[Parameter(Mandatory,ParameterSetName='DattoInstall')]
[string]$N8nPassword,
# ─────────────────────────────────────────────────────────
# Fetch only set write sites and exit
[Parameter(ParameterSetName='DattoFetch')][switch] $SaveSitesOnly,
[Parameter(ParameterSetName='DattoFetch')][string] $OutputFile = 'datto_sites.csv',
[switch]$UseWebhook,
# ─────────────────────────────────────────────────────────
# Install set: target site must be provided
[Parameter(Mandatory,ParameterSetName='DattoFetch')]
[Parameter(Mandatory,ParameterSetName='DattoInstall')]
[string]$WebhookPassword,
[string]$WebhookUrl = $Global:DattoWebhookUrl,
# ─── only DattoFetch uses these ────────────────────────────────────
[Parameter(ParameterSetName='DattoFetch')][switch]$FetchSites,
[Parameter(ParameterSetName='DattoFetch')][switch] $SaveSitesList,
[Parameter(ParameterSetName='DattoFetch')][ValidatePattern('\.csv$|\.json$','i')][string] $OutputFile = 'datto_sites.csv',
# ─── only DattoInstall uses these ─────────────────────────────────
[Parameter(Mandatory,ParameterSetName='DattoInstall')][string] $SiteUID,
[Parameter(Mandatory,ParameterSetName='DattoInstall')][string] $SiteName,
[Parameter(ParameterSetName='DattoInstall')][switch] $PushSiteVars,
@@ -175,101 +179,7 @@
#endregion SVS Module
#region Get-DattoApiCredentials
function Get-DattoApiCredentials {
[CmdletBinding()]
param (
[Parameter(Mandatory)][string]$Password
)
$headers = @{ "SVSMSPKit" = $Password }
try {
$resp = Invoke-RestMethod -Uri $Global:DattoWebhookUrl `
-Headers $headers `
-Method GET
return @{
ApiUrl = $resp.ApiUrl
ApiKey = $resp.ApiKey
ApiSecretKey = $resp.ApiSecretKey
}
}
catch {
Write-LogHybrid "Failed to fetch API credentials: $($_.Exception.Message)" Error DattoAuth
return $null
}
}
#endregion Get-DattoApiCredentials
#region Get-DattoRMMSites
function Get-DattoRmmSites {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string] $Password,
[Parameter()]
[string] $WebhookUrl = $Global:DattoWebhookUrl
)
# 1) Fetch Datto API credentials from your webhook
Write-Verbose "Fetching Datto API credentials from $WebhookUrl"
try {
$headers = @{ 'SVSMSPKit' = $Password }
$creds = Invoke-RestMethod -Uri $WebhookUrl -Headers $headers -Method GET
}
catch {
Throw "Failed to fetch credentials from webhook: $_"
}
$apiUrl = $creds.ApiUrl
$apiKey = $creds.ApiKey
$apiSecretKey = $creds.ApiSecretKey
# 2) Request an OAuth token
Write-Verbose "Requesting OAuth token from $apiUrl/auth/oauth/token"
try {
$securePwd = ConvertTo-SecureString -String 'public' -AsPlainText -Force
$credObj = New-Object System.Management.Automation.PSCredential('public-client', $securePwd)
$tokenResp = Invoke-RestMethod `
-Uri "$apiUrl/auth/oauth/token" `
-Credential $credObj `
-Method 'POST' `
-ContentType 'application/x-www-form-urlencoded' `
-Body "grant_type=password&username=$apiKey&password=$apiSecretKey"
$token = $tokenResp.access_token
}
catch {
Throw "Failed to obtain OAuth token: $_"
}
# 3) Fetch the list of RMM sites
Write-Verbose "Fetching RMM sites from $apiUrl/api/v2/account/sites"
try {
$authHeader = @{ Authorization = "Bearer $token" }
$sitesResp = Invoke-RestMethod `
-Uri "$apiUrl/api/v2/account/sites" `
-Method 'GET' `
-Headers $authHeader `
-ContentType 'application/json'
$siteList = $sitesResp.sites | Select-Object `
@{ Name = 'Name'; Expression = { $_.name } }, `
@{ Name = 'UID'; Expression = { $_.uid } }
if (-not $siteList) {
Write-Warning "No sites were returned by the API."
return @()
}
return $siteList
}
catch {
Throw "Failed to fetch sites from API: $_"
}
}
#endregion Get-DattoRMMSites
#region Write-Log
@@ -1083,7 +993,6 @@ $script
$Context.Response.OutputStream.Close()
}
# new helper to return JSON
function Respond-JSON {
param($Context, $Object)
$json = $Object | ConvertTo-Json -Depth 5
@@ -1095,266 +1004,289 @@ $script
}
function Handle-FetchSites {
param($Context)
# 1) Read incoming JSON (using block auto-disposes the reader)
$reader = [IO.StreamReader]::new($Context.Request.InputStream)
try {
$raw = $reader.ReadToEnd()
} finally {
$reader.Close()
}
try {
$pw = (ConvertFrom-Json $raw).password
} catch {
Write-LogHybrid "Invalid JSON in /getpw payload: $($_.Exception.Message)" "Error" "FetchSites"
returnRespondEmpty $Context
return
}
# 2) Fetch your Datto API creds from the webhook
Write-LogHybrid "Calling webhook for Datto credentials…" "Info" "FetchSites"
try {
$creds = Get-DattoApiCredentials -Password $pw
if (-not $creds) {
Write-LogHybrid "Webhook returned no credentials" Error FetchSites
returnRespondEmpty $Context 403
return
}
# reuse the same globals from the entrypoint
$Global:ApiUrl = $creds.ApiUrl
$Global:ApiKey = $creds.ApiKey
$Global:ApiSecretKey = $creds.ApiSecretKey
Write-LogHybrid "Fetched and stored API credentials." Success FetchSites
} catch {
Write-LogHybrid "Credential-fetch error: $($_.Exception.Message)" Error FetchSites -LogToEvent
returnRespondEmpty $Context 500
return
}
# 3) Exchange for a bearer token
Write-LogHybrid "Requesting OAuth token" "Info" "FetchSites"
try {
$securePublic = ConvertTo-SecureString 'public' -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential('public-client',$securePublic)
$tokenResp = Invoke-RestMethod `
-Uri "$Global:ApiUrl/auth/oauth/token" `
-Credential $creds `
-Method Post `
-ContentType 'application/x-www-form-urlencoded' `
-Body "grant_type=password&username=$Global:ApiKey&password=$Global:ApiSecretKey"
$token = $tokenResp.access_token
Write-LogHybrid "OAuth token acquired." "Success" "FetchSites"
} catch {
Write-LogHybrid "OAuth request failed: $($_.Exception.Message)" "Error" "FetchSites"
returnRespondEmpty $Context 500
return
}
# 4) Pull the site list
Write-LogHybrid "Fetching Datto RMM site list" "Info" "FetchSites"
try {
$hdr = @{ Authorization = "Bearer $token" }
$sitesResp = Invoke-RestMethod -Uri "$Global:ApiUrl/api/v2/account/sites" `
-Method Get `
-Headers $hdr `
-ContentType 'application/json'
$siteList = $sitesResp.sites | ForEach-Object {
[PSCustomObject]@{ Name = $_.name; UID = $_.uid }
}
Write-LogHybrid "Site list retrieved ($($siteList.Count) sites)." "Success" "FetchSites"
} catch {
Write-LogHybrid "Failed to fetch site list: $($_.Exception.Message)" "Error" "FetchSites"
returnRespondEmpty $Context 500
return
}
# 5) Return JSON array
$json = $siteList | ConvertTo-Json -Depth 2
$bytes = [Text.Encoding]::UTF8.GetBytes($json)
$Context.Response.ContentType = 'application/json'
$Context.Response.ContentLength64 = $bytes.Length
$Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
$Context.Response.OutputStream.Close()
}
# Helper function to consistently return an empty JSON array
function returnRespondEmpty {
param(
[Parameter(Mandatory)][object]$Context,
[Parameter(Mandatory)][ValidateRange(100,599)][int]$StatusCode = 500
)
# Always return an empty JSON array body
$empty = [Text.Encoding]::UTF8.GetBytes("[]")
# Set the desired status code and headers
$Context.Response.StatusCode = $StatusCode
$Context.Response.ContentType = 'application/json'
$Context.Response.ContentLength64 = $empty.Length
# Write and close
$Context.Response.OutputStream.Write($empty, 0, $empty.Length)
$Context.Response.OutputStream.Close()
}
# On-boarding handlers
function Handle-SetSVSPowerPlan {
param($Context)
# 1) call into your module
Set-SVSPowerPlan
# 2) log & write back a simple text response
Write-LogHybrid "PowerPlan set" "Success" "OnBoard"
Respond-Text $Context "PowerPlan applied"
}
function Handle-InstallSVSMSP {
param($Context)
Write-LogHybrid "HTTP trigger: Handle-InstallSVSMSP" "Info" "OnBoard"
try {
Install-SVSMSP -InstallToolkit
Respond-Text $Context "SVSMSP Module installed/updated."
} catch {
Write-LogHybrid "Error in Install-SVSMSP: $_" "Error" "OnBoard"
Respond-Text $Context "ERROR: $_"
}
}
function Handle-InstallCyberQP {
param($Context)
# 1) call into your module
Install-CyberQP
# 2) log & write back a simple text response
Write-LogHybrid "CyberQP installed" "Success" "OnBoard"
Respond-Text $Context "CyberQP installed"
}
function Handle-InstallThreatLocker {
param($Context)
# 1) call into your module
Install-ThreatLocker
# 2) log & write back a simple text response
Write-LogHybrid "ThreatLocker installed" "Success" "OnBoard"
Respond-Text $Context "ThreatLocker installed"
}
function Handle-InstallRocketCyber {
param($Context)
# 1) call into your module
Install-RocketCyber
# 2) log & write back a simple text response
Write-LogHybrid "RocketCyber installed" "Success" "OnBoard"
Respond-Text $Context "RocketCyber installed"
}
function Handle-InstallSVSHelpDesk {
param($Context)
# 1) call into your module
Install-SVSHelpDesk
# 2) log & write back a simple text response
Write-LogHybrid "SVS HelpDesk installed" "Success" "OnBoard"
Respond-Text $Context "SVS HelpDesk installed"
}
function Handle-InstallDattoRMM {
param($Context)
$req = $Context.Request
$resp = $Context.Response
if ($req.HttpMethod -ne 'POST') {
$resp.StatusCode = 405; $resp.ContentType = 'text/plain'
$resp.OutputStream.Write([Text.Encoding]::UTF8.GetBytes('Use POST'),0,7)
$resp.OutputStream.Close(); return
}
# parse JSON body
$body = (New-Object IO.StreamReader $req.InputStream).ReadToEnd()
$data = $body | ConvertFrom-Json
$checked = $data.checkedValues
$uid = $data.UID
$name = $data.Name
try {
Install-DattoRMM `
-ApiUrl $Global:ApiUrl `
-ApiKey $Global:ApiKey `
-ApiSecretKey $Global:ApiSecretKey `
-SiteUID $uid `
-SiteName $name `
-PushSiteVars:($checked -contains 'inputVar') `
-InstallRMM: ($checked -contains 'rmm') `
-SaveCopy: ($checked -contains 'exe')
# 1) Read the incoming JSON payload (contains only the webhook password)
$raw = (New-Object IO.StreamReader $Context.Request.InputStream).ReadToEnd()
$pw = (ConvertFrom-Json $raw).password
Write-LogHybrid "RMM install triggered for $name" "Success" "DattoRMM"
$resp.StatusCode = 200
$responseString = "Triggered DattoRMM for $name"
# 2) Delegate to your unified function
$sites = Install-DattoRMM `
-UseWebhook `
-WebhookPassword $pw `
-FetchSites `
-SaveSitesList:$SaveSitesList `
-OutputFile $OutputFile
# 3) Return JSON array of sites
Respond-JSON $Context $sites
}
catch {
Write-LogHybrid "Error in Install-DattoRMM: $_" "Error" "DattoRMM"
$resp.StatusCode = 500
$responseString = "ERROR: $($_.Exception.Message)"
# Log the exception and return HTTP 500
Write-LogHybrid "Handle-FetchSites error: $($_.Exception.Message)" Error DattoRMM
$Context.Response.StatusCode = 500
Respond-Text $Context "Internal server error fetching sites."
}
}
function Handle-InstallDattoRMM {
param($Context)
try {
if ($Context.Request.HttpMethod -ne 'POST') {
$Context.Response.StatusCode = 405
Respond-Text $Context 'Use POST'
return
}
# 1) Read and parse the JSON body
$body = (New-Object IO.StreamReader $Context.Request.InputStream).ReadToEnd()
$data = ConvertFrom-Json $body
# 2) Delegate to your unified function for the install
Install-DattoRMM `
-UseWebhook `
-WebhookPassword $Global:WebhookPassword `
-SiteUID $data.UID `
-SiteName $data.Name `
-PushSiteVars:($data.checkedValues -contains 'inputVar') `
-InstallRMM: ($data.checkedValues -contains 'rmm') `
-SaveCopy: ($data.checkedValues -contains 'exe')
# 3) Acknowledge to the client
Respond-Text $Context "Triggered DattoRMM for $($data.Name)"
}
catch {
# Log the exception and return HTTP 500
Write-LogHybrid "Handle-InstallDattoRMM error: $($_.Exception.Message)" Error DattoRMM
$Context.Response.StatusCode = 500
Respond-Text $Context "Internal server error during DattoRMM install."
}
}
#endregion Handler Stubs
#region Install-DattoRMM
<#
.SYNOPSIS
Installs/configures the Datto RMM agent, fetches site lists, and optionally saves the site list to disk.
.DESCRIPTION
Centralizes Datto RMM operations in one function:
- Fetch API credentials from a webhook (-UseWebhook)
- Acquire OAuth token
- Fetch site list (-FetchSites)
- Save site list to Desktop as JSON or CSV (-FetchSites + -SaveSitesList)
- Write site variables to registry (-PushSiteVars)
- Download & launch the RMM agent installer (-InstallRMM)
- Save a copy of the installer (-SaveCopy)
.PARAMETER UseWebhook
Fetches ApiUrl, ApiKey, and ApiSecretKey from the webhook when used with WebhookPassword.
.PARAMETER WebhookPassword
Password for authenticating to the credentials webhook.
.PARAMETER WebhookUrl
URL of the credentials webhook. Defaults to $Global:DattoWebhookUrl.
.PARAMETER ApiUrl
Direct Datto API endpoint URL (if not using webhook).
.PARAMETER ApiKey
Direct Datto API key (if not using webhook).
.PARAMETER ApiSecretKey
Direct Datto API secret (if not using webhook).
.PARAMETER FetchSites
Fetches the list of sites and skips all install steps.
.PARAMETER SaveSitesList
Saves the fetched site list to Desktop using OutputFile. Must be used with -FetchSites.
.PARAMETER OutputFile
Filename for saving the site list (.json or .csv). Defaults to 'datto_sites.csv'.
.PARAMETER PushSiteVars
Writes fetched site variables into HKLM:\Software\SVS\Deployment.
.PARAMETER InstallRMM
Downloads and runs the Datto RMM agent installer.
.PARAMETER SaveCopy
Saves a copy of the downloaded agent installer to C:\Temp.
.PARAMETER SiteUID
Unique identifier of the Datto site (required for install and registry push).
.PARAMETER SiteName
Friendly name of the Datto site (used for logging).
.EXAMPLE
# Fetch and save site list via webhook
Install-DattoRMM -UseWebhook -WebhookPassword 'Tndmeeisdwge!' -FetchSites -SaveSitesList -OutputFile 'sites.csv'
.EXAMPLE
# Headless install with site variables
Install-DattoRMM -ApiUrl 'https://api.example.com' -ApiKey 'KeyHere' -ApiSecretKey 'SecretHere' \
-SiteUID 'site-123' -SiteName 'Acme Corp' -PushSiteVars -InstallRMM
.EXAMPLE
# Download and save installer to C:\Temp without installing
Install-DattoRMM -ApiUrl 'https://api.example.com' -ApiKey 'KeyHere' -ApiSecretKey 'SecretHere' \
-SiteUID 'site-123' -SiteName 'Acme Corp' -SaveCopy
#>
function Install-DattoRMM {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param (
[switch]$UseWebhook,
[string]$WebhookPassword,
[string]$WebhookUrl = $Global:DattoWebhookUrl,
[string]$ApiUrl,
[string]$ApiKey,
[string]$ApiSecretKey,
[switch]$FetchSites,
[switch]$SaveSitesList,
[string]$OutputFile = 'datto_sites.csv',
[switch]$PushSiteVars,
[switch]$InstallRMM,
[switch]$SaveCopy,
[string]$SiteUID,
[string]$SiteName
)
# Validate mutually-dependent switches
if ($SaveSitesList -and -not $FetchSites) {
Write-LogHybrid "-SaveSitesList requires -FetchSites." Error DattoRMM; return
}
$b = [Text.Encoding]::UTF8.GetBytes($responseString)
$resp.ContentType = 'text/plain'
$resp.ContentLength64 = $b.Length
$resp.OutputStream.Write($b,0,$b.Length)
$resp.OutputStream.Close()
}
# 1) Optionally fetch credentials from webhook
if ($UseWebhook) {
if (-not $WebhookPassword) {
Write-LogHybrid "Webhook password missing." Error DattoRMM; return
}
try {
$resp = Invoke-RestMethod -Uri $WebhookUrl `
-Headers @{ SVSMSPKit = $WebhookPassword } `
-Method GET
$ApiUrl = $resp.ApiUrl
$ApiKey = $resp.ApiKey
$ApiSecretKey = $resp.ApiSecretKey
Write-LogHybrid "Webhook credentials fetched." Success DattoRMM
} catch {
Write-LogHybrid "Failed to fetch webhook credentials: $($_.Exception.Message)" Error DattoRMM; return
}
}
# 2) Validate API parameters
if (-not $ApiUrl -or -not $ApiKey -or -not $ApiSecretKey) {
Write-LogHybrid "Missing required API parameters." Error DattoRMM; return
}
# Off-boarding handlers
function Handle-UninstallCyberQP {
param($Context)
# 3) Acquire OAuth token
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
$publicCred = New-Object System.Management.Automation.PSCredential(
'public-client', (ConvertTo-SecureString 'public' -AsPlainText -Force)
)
$tokenResp = Invoke-RestMethod -Uri "$ApiUrl/auth/oauth/token" `
-Credential $publicCred `
-Method Post `
-ContentType 'application/x-www-form-urlencoded' `
-Body "grant_type=password&username=$ApiKey&password=$ApiSecretKey"
$token = $tokenResp.access_token
Write-LogHybrid "OAuth token acquired." Success DattoRMM
} catch {
Write-LogHybrid "OAuth token fetch failed: $($_.Exception.Message)" Error DattoRMM; return
}
$headers = @{ Authorization = "Bearer $token" }
# 1) call into your module
Uninstall-CyberQP
# 4) Fetch site list only
if ($FetchSites) {
try {
$sitesResp = Invoke-RestMethod -Uri "$ApiUrl/api/v2/account/sites" -Method Get -Headers $headers
$siteList = $sitesResp.sites | ForEach-Object {
[PSCustomObject]@{ Name = $_.name; UID = $_.uid }
}
Write-LogHybrid "Fetched $($siteList.Count) sites." Success DattoRMM
Write-LogHybrid "CyberQP uninstalled" "Success" "OffBoard"
Respond-Text $Context "CyberQP uninstalled"
}
if ($SaveSitesList) {
$desktop = [Environment]::GetFolderPath('Desktop')
$path = Join-Path $desktop $OutputFile
$ext = [IO.Path]::GetExtension($OutputFile).ToLower()
if ($ext -eq '.json') {
$siteList | ConvertTo-Json -Depth 3 | Out-File -FilePath $path -Encoding UTF8
} else {
$siteList | Export-Csv -Path $path -NoTypeInformation -Encoding UTF8
}
Write-LogHybrid "Wrote $($siteList.Count) sites to $path" Success DattoRMM
}
function Cleanup-SVSMSP {
param($Context)
Write-LogHybrid "SVSMSP cleaned up" "Success" "OffBoard"
Respond-Text $Context "SVSMSP cleaned up"
}
return $siteList
} catch {
Write-LogHybrid "Failed to fetch sites: $($_.Exception.Message)" Error DattoRMM; return @()
}
}
# Tweaks handler
function Disable-Animations {
param($Context)
Write-LogHybrid "Animations disabled" "Success" "Tweaks"
Respond-Text $Context "Animations disabled"
}
# 5) Push site variables to registry
if ($PushSiteVars) {
try {
$varsResp = Invoke-RestMethod -Uri "$ApiUrl/api/v2/site/$SiteUID/variables" -Method Get -Headers $headers
Write-LogHybrid "Fetched variables for '$SiteName'." Success DattoRMM
} catch {
Write-LogHybrid "Variable fetch failed: $($_.Exception.Message)" Error DattoRMM
}
$regPath = "HKLM:\Software\SVS\Deployment"
foreach ($v in $varsResp.variables) {
try {
if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
New-ItemProperty -Path $regPath -Name $v.name -Value $v.value -PropertyType String -Force | Out-Null
Write-LogHybrid "Wrote '$($v.name)' to registry." Success DattoRMM
} catch {
Write-LogHybrid "Failed to write '$($v.name)': $($_.Exception.Message)" Error DattoRMM
}
}
}
# SVSApps handler
function Install-WingetLastPass {
param($Context)
Write-LogHybrid "Winget LastPass installed" "Success" "SVSApps"
Respond-Text $Context "Winget LastPass installed"
}
# 6) Download & install RMM agent
if ($InstallRMM) {
if ($PSCmdlet.ShouldProcess("Site '$SiteName'", "Install RMM agent")) {
try {
$dlUrl = "https://zinfandel.centrastage.net/csm/profile/downloadAgent/$SiteUID"
$tmp = "$env:TEMP\AgentInstall.exe"
Invoke-WebRequest -Uri $dlUrl -OutFile $tmp -UseBasicParsing
Write-LogHybrid "Downloaded agent to $tmp." Info DattoRMM
Start-Process -FilePath $tmp -NoNewWindow
Write-LogHybrid "RMM agent installer launched." Success DattoRMM
} catch {
Write-LogHybrid "Agent install failed: $($_.Exception.Message)" Error DattoRMM
}
}
}
#endregion Handler Stubs
# 7) Save a copy of installer to C:\Temp
if ($SaveCopy) {
try {
$dlUrl = "https://zinfandel.centrastage.net/csm/profile/downloadAgent/$SiteUID"
$path = "C:\Temp\AgentInstall.exe"
if (-not (Test-Path 'C:\Temp')) { New-Item -Path 'C:\Temp' -ItemType Directory | Out-Null }
Invoke-WebRequest -Uri $dlUrl -OutFile $path -UseBasicParsing
Write-LogHybrid "Saved installer copy to $path." Info DattoRMM
} catch {
Write-LogHybrid "Save-copy failed: $($_.Exception.Message)" Error DattoRMM
}
}
# 8) Warn if no action was taken
if (-not ($PushSiteVars -or $InstallRMM -or $SaveCopy)) {
Write-LogHybrid "No action specified. Use -FetchSites, -SaveSitesList, -PushSiteVars, -InstallRMM, or -SaveCopy." Warning DattoRMM
}
}
#endregion Install-DattoRMM
#region Dispatch-Request
@@ -1426,30 +1358,16 @@ $script
'DattoFetch' {
Write-LogHybrid "Fetching site list only…" Info DattoAuth
$sites = Get-DattoRmmSites -Password $N8nPassword
$sites = Install-DattoRMM `
-UseWebhook `
-WebhookPassword $WebhookPassword `
-FetchSites `
-SaveSitesList:$SaveSitesList `
-OutputFile $OutputFile
if ($SaveSitesOnly) {
# If SaveSitesOnly is true, save the output to a file on desktop
$desktopPath = [System.Environment]::GetFolderPath('Desktop')
$filePath = Join-Path -Path $desktopPath -ChildPath $OutputFile
$ext = [IO.Path]::GetExtension($OutputFile).ToLower()
if ($ext -eq '.json') {
# Save the file to the desktop using the full path
$sites | ConvertTo-Json -Depth 3 | Out-File -FilePath $filePath -Encoding UTF8
} else {
# Save the file to the desktop using the full path
$sites | Export-Csv -Path $filePath -NoTypeInformation -Encoding UTF8
}
Write-LogHybrid "Wrote $($sites.Count) sites to $filePath" Success DattoAuth
} else {
# If SaveSitesOnly is not true, just fetch sites (for UI purposes or silent mode without saving)
Write-LogHybrid "Sites fetched successfully, but not saved." Success DattoAuth
}
return
}
Write-LogHybrid "Done." Success DattoAuth
return
}
# ────────────────────────────────────────────
@@ -1458,16 +1376,16 @@ $script
'DattoInstall' {
Write-LogHybrid "Headless DattoRMM deploy" Info DattoAuth
if ($PSCmdlet.ShouldProcess("Datto site '$SiteName'", "Headless install")) {
Install-DattoRMM `
-ApiUrl $Global:ApiUrl `
-ApiKey $Global:ApiKey `
-ApiSecretKey $Global:ApiSecretKey `
-SiteUID $SiteUID `
-SiteName $SiteName `
-PushSiteVars:$PushSiteVars `
-InstallRMM:$InstallRMM `
-SaveCopy:$SaveCopy
-UseWebhook `
-WebhookPassword $WebhookPassword `
-SiteUID $SiteUID `
-SiteName $SiteName `
-PushSiteVars:$PushSiteVars `
-InstallRMM:$InstallRMM `
-SaveCopy:$SaveCopy
}
return
@@ -1532,59 +1450,7 @@ $script
#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 -TypeName 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
}
$getHeaders = @{"Authorization" = "Bearer $requestToken"}
if ($FetchSitesOnly) {
Write-Host "Fetching list of sites from the Datto RMM API..." -ForegroundColor Cyan
try {
$getHeaders = @{"Authorization" = "Bearer $requestToken" }
$getSites = Invoke-WebRequest -Uri "$ApiUrl/api/v2/account/sites" -Method Get -Headers $getHeaders -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 from the API. Details: $($_.Exception.Message)" -ForegroundColor Red
return
}
}
}
#endregion
#region HTTP Listener & Routing