# -------------------------------------------------- # Author: Gavin Stone (NinjaOne) # Status: Work in Progress / Draft / Testing # Attribution: Luke Whitelock (NinjaOne) for his work on the Authentication Functions # Date: 20th June 2024 # Description: Tries to be clever and get the policy structure from NinjaOne. Lists out an overview of the policy structure, # including the number of devices and snowflake devices for each policy. Also lists out the snowflake devices for each policy. # A snowflake device is a device that has a policy override applied to it. # The output is grouped by node class and the policies are listed in a hierarchical structure. # The output is written to a file in C:\Temp\NinjaOnePolicyOutput_.html # The output is also printed to the console. # Version: 1.0 - Original Version # Version: 2.0 - Added HTML output and improved formatting. Made interactive with collapsible tree view. # Version: 2.0.1 - Removed hardcoded URL and used variable instead # -------------------------------------------------- # User editable variables: $NinjaOneInstance = '' # Please replace with the region instance you login to (app.ninjarmm.com, us2.ninjarmm.com, eu.ninjarmm.com, ca.ninjarmm.com, oc.ninjarmm.com) $NinjaOneClientId = '' $NinjaOneClientSecret = '' # Functions for Authentication function Get-NinjaOneToken { [CmdletBinding()] param() if ($Script:NinjaOneInstance -and $Script:NinjaOneClientID -and $Script:NinjaOneClientSecret ) { if ($Script:NinjaTokenExpiry -and (Get-Date) -lt $Script:NinjaTokenExpiry) { return $Script:NinjaToken } else { if ($Script:NinjaOneRefreshToken) { $Body = @{ 'grant_type' = 'refresh_token' 'client_id' = $Script:NinjaOneClientID 'client_secret' = $Script:NinjaOneClientSecret 'refresh_token' = $Script:NinjaOneRefreshToken } } else { $body = @{ grant_type = 'client_credentials' client_id = $Script:NinjaOneClientID client_secret = $Script:NinjaOneClientSecret scope = 'monitoring management' } } $token = Invoke-RestMethod -Uri "https://$($Script:NinjaOneInstance -replace '/ws','')/ws/oauth/token" -Method Post -Body $body -ContentType 'application/x-www-form-urlencoded' -UseBasicParsing $Script:NinjaTokenExpiry = (Get-Date).AddSeconds($Token.expires_in) $Script:NinjaToken = $token Write-Host 'Fetched New Token' return $token } else { Throw 'Please run Connect-NinjaOne first' } } } function Connect-NinjaOne { [CmdletBinding()] param ( [Parameter(mandatory = $true)] $NinjaOneInstance, [Parameter(mandatory = $true)] $NinjaOneClientID, [Parameter(mandatory = $true)] $NinjaOneClientSecret, $NinjaOneRefreshToken ) $Script:NinjaOneInstance = $NinjaOneInstance $Script:NinjaOneClientID = $NinjaOneClientID $Script:NinjaOneClientSecret = $NinjaOneClientSecret $Script:NinjaOneRefreshToken = $NinjaOneRefreshToken try { $Null = Get-NinjaOneToken -ea Stop } catch { Throw "Failed to Connect to NinjaOne: $_" } } function Invoke-NinjaOneRequest { param( $Method, $Body, $InputObject, $Path, $QueryParams, [Switch]$Paginate, [Switch]$AsArray ) $Token = Get-NinjaOneToken if ($InputObject) { if ($AsArray) { $Body = $InputObject | ConvertTo-Json -depth 100 if (($InputObject | Measure-Object).count -eq 1 ) { $Body = '[' + $Body + ']' } } else { $Body = $InputObject | ConvertTo-Json -depth 100 } } try { if ($Method -in @('GET', 'DELETE')) { if ($Paginate) { $After = 0 $PageSize = 1000 $NinjaResult = do { $Result = Invoke-WebRequest -uri "https://$($Script:NinjaOneInstance)/api/v2/$($Path)?pageSize=$PageSize&after=$After$(if ($QueryParams){"&$QueryParams"})" -Method $Method -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json' -UseBasicParsing $Result $ResultCount = ($Result.id | Measure-Object -Maximum) $After = $ResultCount.maximum } while ($ResultCount.count -eq $PageSize) } else { $NinjaResult = Invoke-WebRequest -uri "https://$($Script:NinjaOneInstance)/api/v2/$($Path)$(if ($QueryParams){"?$QueryParams"})" -Method $Method -Headers @{Authorization = "Bearer $($token.access_token)" } -ContentType 'application/json; charset=utf-8' -UseBasicParsing } } elseif ($Method -in @('PATCH', 'PUT', 'POST')) { $NinjaResult = Invoke-WebRequest -uri "https://$($Script:NinjaOneInstance)/api/v2/$($Path)$(if ($QueryParams){"?$QueryParams"})" -Method $Method -Headers @{Authorization = "Bearer $($token.access_token)" } -Body $Body -ContentType 'application/json; charset=utf-8' -UseBasicParsing } else { Throw 'Unknown Method' } } catch { Throw "Error Occured: $_" } try { return $NinjaResult.content | ConvertFrom-Json -ea stop } catch { return $NinjaResult.content } } # Connect to NinjaOne API try { Connect-NinjaOne -NinjaOneInstance $NinjaOneInstance -NinjaOneClientID $NinjaOneClientId -NinjaOneClientSecret $NinjaOneClientSecret } catch { Write-Output "Failed to connect to NinjaOne API: $_" exit 1 } # Get the current date and time for the file name $currentDateTime = Get-Date -Format "yyyyMMdd_HHmmss" $outputFilePath = "C:\Temp\NinjaOnePolicyOutput_$currentDateTime.html" # Create a new file and ensure it's empty if it already exists (clean slate) New-Item -Path $outputFilePath -ItemType File -Force | Out-Null # Function to write output to both console and file function Write-OutputAndFile { param ( [string]$message ) Write-Output $message Add-Content -Path $outputFilePath -Value $message } # Function to generate HTML for the header # Function to generate HTML for the header # Function to generate HTML for the header # Function to generate HTML for the header function Get-HTMLHeader { return @"
NinjaOne Logo

Policy Structure Overview

    "@ } # Function to generate HTML for the footer function Get-HTMLFooter { return @"
"@ } # Function to generate HTML for a snowflake device function Get-SnowFlakeDeviceHTML { param ( [string]$deviceLink, [string]$systemName, [array]$overrides ) $overrideBadges = $overrides | ForEach-Object { " $_" } $overrideHTML = [string]::Join("", $overrideBadges) return "
  • Device Override: $systemName $overrideHTML
  • " } # Function to generate HTML for child policies # Function to generate HTML for child policies function Get-ChildPoliciesHTML { param ( [int]$policyId, [int]$depth ) $childPolicies = $Policies | Where-Object { $_.parentPolicyId -eq $policyId } $html = "