Skip to content

Instantly share code, notes, and snippets.

@grintor
Last active February 6, 2024 20:41
Show Gist options
  • Save grintor/fdf4d16e724d752c0003889d1580c7c2 to your computer and use it in GitHub Desktop.
Save grintor/fdf4d16e724d752c0003889d1580c7c2 to your computer and use it in GitHub Desktop.
A script for migrating to NinjaOne RMM from other RMMs
# deploy_ninja.ps1: A script for migrating to NinjaOne RMM from other RMMs
#
# This script will silently download and install NinjaOne and place the endpoint into the provided organization/location
# If the location does not exist within the organization in NinjaOne, this script will create it.
# If the organization does not exist in NinjaOne, this script will create it.
# If an installer for the provided organization/location has not yet been built in NinjaOne, this script will build it.
#
# USAGE
# -------
# powershell.exe -file auto_ninja.ps1 "organization name" "location name"
#
# NOTE
# ------
# Environment variables for "ninjaone_access_token" or "ninjaone_client_id" and "ninjaone_client_secret" must be set before running the script.
#
# OBTAINING THE CLIENT ID AND CLIENT SECRET
# -------------------------------------------
# Go to Administration > Apps > API > Client App IDs and click the Add button on the right.
# Select "API Services (machine-to-machine)".
# Select "Monitoring" and "Management" Scopes
# Select "Client Credentials" Allowed Grant Type
#
# LICENSE
# ---------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (c) 2024 Tier2 Technologies (www.tier2.tech)
# Author: Chris Wheeler <chris@tier2.tech>
$faux_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
function Sane-WebRequest {
param (
$method,
$url,
$headers,
$body
)
try {
$ProgressPreference = 'SilentlyContinue'
$response = Invoke-WebRequest -UseBasicParsing -Uri $url -Method $method -Headers $headers -Body $body
return @{
'status' = $response.StatusCode;
'headers' = $response.Headers;
'body' = $response.Content
}
}
catch {
try {
$reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
return @{
'status' = $_.Exception.Response.StatusCode.Value__;
'headers' = $_.Exception.Response.Headers;
'body' = $reader.ReadToEnd()
}
} catch {
return @{
'status' = 99999;
'headers' = '';
'body' = $($error[1])
}
}
}
}
function Ninja-Request {
param (
$method,
$path,
$body
)
$response = Sane-WebRequest -method $method -url "https://app.ninjarmm.com/v2/$path" -headers @{
'Authorization' = "Bearer $env:ninjaone_access_token";
'Content-Type' = 'application/json';
'Accept' = 'application/json'
} -body ($body | ConvertTo-Json -Depth 100)
try {$response.result = ($response.body | ConvertFrom-Json)} catch {}
return $response
}
function Ninja-auth {
param (
$client_id,
$client_secret
)
$response = Sane-WebRequest -method 'post' -url 'https://app.ninjarmm.com/ws/oauth/token' -ContentType 'application/x-www-form-urlencoded' -body @{
'grant_type' = 'client_credentials';
'client_id' = $client_id;
'client_secret' = $client_secret;
'scope' = 'monitoring management';
}
try {$response.result = ($response.body | ConvertFrom-Json)} catch {}
return $response
}
if ((Get-Service -Name 'NinjaRMMAgent' -ea 0) -ne $null){
$host.SetShouldExit(129)
return "NinjaOne is already installed."
}
$target_organization = $args[0]
if ($target_organization -eq $null){
$host.SetShouldExit(127)
return "Argument 1 (organization name) is required."
}
$target_location = $args[1]
if ($target_location -eq $null){
$host.SetShouldExit(128)
return "Argument 2 (location name) is required."
}
if ($env:ninjaone_access_token -eq $null) {
if ($env:ninjaone_client_id -eq $null -or $env:ninjaone_client_secret -eq $null){
$host.SetShouldExit(126)
return "Environment variables for ninjaone_access_token or ninjaone_client_id and ninjaone_client_secret must be populated."
}
$response = Ninja-auth $env:ninjaone_client_id $env:ninjaone_client_secret
if ($response.status -ge 201){
$host.SetShouldExit(125)
return "Could not authenticate. Error $($response.status): $($response.body)"
}
$env:ninjaone_access_token = $response.result.access_token
}
$response = Ninja-Request 'get' 'organizations'
if ($response.status -ge 201){
$host.SetShouldExit(120)
return "Could not obtain organizations. Error $($response.status): $($response.body)"
}
foreach ($organization in $response.result){
if ($organization.name -eq $target_organization){
$organization_id = $organization.id
}
}
if ($organization_id -eq $null) {
Write-Host "Organization ""$target_organization"" does not exist. Creating."
$response = Ninja-Request 'post' 'organizations' @{
'name' = $target_organization;
'locations' = @(
@{ 'name' = $target_location }
)
}
if ($response.status -ge 201){
$host.SetShouldExit(121)
return "Could not create organization ""$target_organization"". Error $($response.status): $($response.body)"
}
$organization_id = $response.result.id
}
Write-Host "Organization ID for ""$target_organization"" is ""$organization_id"""
$response = Ninja-Request 'get' "organization/$organization_id"
if ($response.status -ge 201){
$host.SetShouldExit(122)
return "Could not obtain organization details for organization ID ""$organization_id"", Error $($response.status): $($response.body)"
}
foreach ($location in $response.result.locations){
if ($location.name -eq $target_location){
$location_id = $location.id
}
}
if ($location_id -eq $null) {
Write-Host "Location ""$target_location"" does not exist. Creating."
$response = Ninja-Request 'post' "organization/$organization_id/locations" @{
'name' = $target_location;
}
if ($response.status -ge 201){
$host.SetShouldExit(130)
return "Could not create location ""$target_location"". Error $($response.status): $($response.body)"
}
$location_id = $response.result.id
}
Write-Host "Location ID for ""$target_location"" is ""$location_id"""
Write-Host "Getting installer URL..."
$response = Ninja-Request 'get' "organization/$organization_id/location/$location_id/installer/WINDOWS_MSI"
if ($response.status -ge 201){
$host.SetShouldExit(123)
return "Could not obtain an installer. Error $($response.status): $($response.body)"
}
$installer_url = $response.result.url
Write-Host "the installer for ""$target_organization/$target_location"" is at ""$installer_url"""
Write-Host "Downloading the installer..."
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -UseBasicParsing -Uri $installer_url -headers @{'User-Agent' = $faux_user_agent} -OutFile "$env:temp\ninjaone_installer.msi"
if (-Not (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){
$host.SetShouldExit(124)
return "Not admin. Can't run the installer."
}
Write-Host "Running the installer..."
Start-Process 'msiexec.exe' -ArgumentList "/i $env:temp\ninjaone_installer.msi /qn /norestart" -Wait
Write-Host "Done."
Remove-Item "$env:temp\ninjaone_installer.msi" -ea 0
$host.SetShouldExit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment