Examples
Complete PowerShell examples - an RMM alert that raises and resolves a ticket, a caller lookup, an asset sync, a monthly time report and reading a password.
Each example starts with your ITFlow's address and a key. Use a key whose agent has the access the example needs - see Keys and permissions:
$base = 'https://itflow.example.com/api/v1'
$key = 'YOUR_API_KEY'
They were tested with PowerShell 7. Apart from reading a password, they use nothing that's new in PowerShell 7, so they should run in Windows PowerShell 5.1 too.
An RMM alert raises a ticket
The RMM gives the client's name, the device's serial number and the alert. The script finds the client and the device, and raises a ticket for them. It needs Modify on Support, and Read on Clients.
# The alert, as your RMM gives it
$clientName = 'Ravenwood Dental Group'
$serial = '7B5BAE8214'
$alert = 'Disk C: is 94% full'
# The client and the device, by name and serial number
$client = Invoke-RestMethod -Uri "$base/clients/read.php?api_key=$key&client_name=$([uri]::EscapeDataString($clientName))"
if ($client.success -ne 'True') { throw "No client called $clientName" }
$clientId = $client.data[0].client_id
$asset = Invoke-RestMethod -Uri "$base/assets/read.php?api_key=$key&client_id=$clientId&asset_serial=$serial"
$assetId = if ($asset.success -eq 'True') { $asset.data[0].asset_id } else { 0 }
# Raise the ticket
$body = @{
api_key = $key
client_id = $clientId
ticket_subject = "$alert on $($asset.data[0].asset_name)"
ticket_details = "Raised by the RMM: $alert"
ticket_priority = 'High'
ticket_asset_id = $assetId
} | ConvertTo-Json
$ticket = Invoke-RestMethod -Method Post -Uri "$base/tickets/create.php" -ContentType 'application/json' -Body $body
$ticketId = $ticket.data[0].insert_id
"Raised ticket $ticketId"
Keep the ticket's id. When the alert clears, add a note that resolves the ticket:
$ticketId = 1880 # saved when the ticket was raised
$body = @{
api_key = $key
ticket_id = $ticketId
ticket_reply = 'The alert cleared: disk C: is back to 71% after the cleanup task ran.'
ticket_status = 4 # Resolved
} | ConvertTo-Json
$r = Invoke-RestMethod -Method Post -Uri "$base/ticket_replies/create.php" -ContentType 'application/json' -Body $body
if ($r.success -ne 'True') { throw $r.message }
The note is from the key's agent, so the ticket's history shows who - or what - resolved it. Add ticket_reply_time_worked to log time against it too.
Who's calling?
A phone system can look the caller up and show their name and client. ITFlow keeps phone numbers as digits only, so the script takes out everything else first. It needs Read on Clients.
$callerId = '+1 (412) 555-0101'
$digits = $callerId -replace '\D', ''
if ($digits.Length -eq 11 -and $digits.StartsWith('1')) { $digits = $digits.Substring(1) } # ITFlow keeps the country code apart
$r = Invoke-RestMethod -Uri "$base/contacts/read.php?api_key=$key&contact_phone_or_mobile=$digits"
if ($r.success -eq 'True') {
$contact = $r.data[0]
$client = Invoke-RestMethod -Uri "$base/clients/read.php?api_key=$key&client_id=$($contact.contact_client_id)"
"$($contact.contact_name), $($contact.contact_title) at $($client.data[0].client_name)"
} else {
'Unknown caller'
}
Dana Prescott, Office Manager at Ravenwood Dental Group
Keep assets in step with the RMM
For each device the RMM knows about, the script finds the asset by serial number. It updates the asset if ITFlow already has it, and adds it if not. It needs Modify on Support.
$clientId = 1
# What your RMM knows about each device
$devices = @(
@{ name = 'RDG-WS-14'; serial = 'H7Q2LM3'; make = 'Dell'; model = 'OptiPlex 7020'; os = 'Windows 11 Pro'; ip = '192.168.10.64'; mac = 'B0:4F:13:2A:9C:11' }
@{ name = 'RDG-WS-15'; serial = 'H7Q2LM4'; make = 'Dell'; model = 'OptiPlex 7020'; os = 'Windows 11 Pro'; ip = '192.168.10.65'; mac = 'B0:4F:13:2A:9C:12' }
)
foreach ($d in $devices) {
$fields = @{
api_key = $key; client_id = $clientId
asset_name = $d.name; asset_type = 'Desktop'; asset_serial = $d.serial
asset_make = $d.make; asset_model = $d.model; asset_os = $d.os
asset_ip = $d.ip; asset_mac = $d.mac
}
$found = Invoke-RestMethod -Uri "$base/assets/read.php?api_key=$key&client_id=$clientId&asset_serial=$($d.serial)"
if ($found.success -eq 'True') {
$fields.asset_id = $found.data[0].asset_id
$r = Invoke-RestMethod -Method Post -Uri "$base/assets/update.php" -ContentType 'application/json' -Body ($fields | ConvertTo-Json)
# An update that changes nothing comes back "False"
if ($r.success -eq 'True') { "$($d.name): updated" } else { "$($d.name): no changes" }
} else {
$r = Invoke-RestMethod -Method Post -Uri "$base/assets/create.php" -ContentType 'application/json' -Body ($fields | ConvertTo-Json)
"$($d.name): added as asset $($r.data[0].insert_id)"
}
}
RDG-WS-14: added as asset 801
RDG-WS-15: added as asset 802
It sends the IP and MAC address every time, because an update clears them otherwise - see Assets.
Last month's time, by technician
The script saves last month's time to a CSV file, one row per technician per ticket, and totals the hours for each technician. It reads every page - see Lists and paging. It needs Read on Support.
$last = (Get-Date).AddMonths(-1)
$rows = @(); $offset = 0
do {
$r = Invoke-RestMethod -Uri "$base/technicians/time.php?api_key=$key&year=$($last.Year)&month=$($last.Month)&limit=200&offset=$offset"
if ($r.success -ne 'True') { break }
$rows += $r.data
$offset += 200
} while ($r.data.Count -eq 200)
$rows | Export-Csv -NoTypeInformation -Path "time-$($last.ToString('yyyy-MM')).csv"
# Hours per technician
$rows | Group-Object technician | ForEach-Object {
$hours = ($_.Group | ForEach-Object { $h, $m, $s = $_.time_worked -split ':'; [int]$h + [int]$m / 60 + [int]$s / 3600 } | Measure-Object -Sum).Sum
'{0,-15} {1,6:N1} h' -f $_.Name, $hours
}
Alex Morgan 81.0 h
Read a password
Reading a credential needs the key's decryption password too, sent in a JSON body - see Credentials. It needs Read on Credentials, and PowerShell 7.
$decryptPassword = 'YOUR_DECRYPTION_PASSWORD'
# PowerShell 7: a GET request with a JSON body
$body = @{ api_key_decrypt_password = $decryptPassword } | ConvertTo-Json
$r = Invoke-RestMethod -Method Get -Uri "$base/credentials/read.php?api_key=$key&credential_id=1" -ContentType 'application/json' -Body $body
if ($r.success -ne 'True') { throw 'Not found' }
$c = $r.data[0]
"$($c.credential_name): $($c.credential_username) / $($c.credential_password)"
Firewall Administrator: msp-admin / A265CDE06D86CE!aA1
In Windows PowerShell 5.1, use curl.exe instead, as in Credentials.