Skip to content

Instantly share code, notes, and snippets.

@AX-AMaxwell
Last active September 20, 2023 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AX-AMaxwell/f7475ab32bb7b98f5f17a90c624de6b8 to your computer and use it in GitHub Desktop.
Save AX-AMaxwell/f7475ab32bb7b98f5f17a90c624de6b8 to your computer and use it in GitHub Desktop.
Get Software Report
#########################################
# PARAMETERS
# define your Automox API key
$apiKey = ''
# define your Automox organization ID
$orgID = ''
# define the path you'd like
# your CSV report generated to
# the default value will output
# to the same folder as the script,
# or the PowerShell session's current
# directory
$filePath = '.\SoftwareInventory.csv'
#########################################
# VARS
# define the csv column headers
$headers = 'Computer', 'Display Name', 'Version', 'Install Date'
# define the starting page
$page = 0
# define the number of results to return PER-PAGE
$limit = 500
#########################################
# FUNCTIONS
# --! Local Functions !--
function addCSVRow
{
[ CmdletBinding() ]
param (
[ Parameter() ]
[ System.String[] ]
$Values,
[ Parameter() ]
[ System.Management.Automation.SwitchParameter ]
$Overwrite
)
begin
{}
process
{
# define a stringbuilder to construct the row
$builder = [ System.Text.StringBuilder ]::new()
# enumerate the values provided
foreach ( $value in $Values )
{
# define a default value if null
if ( $null -eq $value ) { $value = '' }
# sanitize the string
$value = $value -replace '"', '""'
# append to the stringbuilder
$builder.Append( "`"$value`"," ) | Out-Null
}
if ( $Overwrite )
{
# join and overwrite the csv file
Set-Content -Path $filePath -Value $builder.ToString()
}
else
{
# join and append to the csv file
Add-Content -Path $filePath -Value $builder.ToString()
}
}
end
{}
}
function out
{
[ Alias( 'err' ) ]
param(
[ Parameter( Mandatory, Position = 0, ValueFromPipeline, ValueFromRemainingArguments ) ]
[ System.String[] ]
$Message
)
begin
{
if ( $MyInvocation.InvocationName -eq 'err' )
{
# capture the current console foreground color
$previous = [ System.Console ]::ForegroundColor
# set foreground color for error output
[ System.Console ]::ForegroundColor = 'Red'
# set the stream
$stream = [ System.Console ]::Error
}
else
{
# set the stream
$stream = [ System.Console ]::Out
}
}
process
{
foreach ( $m in $Message )
{
# write our error output
$stream.WriteLine( $m )
}
}
end
{
if ( $stream -eq [ System.Console ]::Error )
{
# restore the original foreground color
[ System.Console ]::ForegroundColor = $previous
}
}
}
#########################################
# REPORT GENERATION
# evaluate required vars
if ( $apiKey -eq [ System.String ]::Empty )
{
err 'The "apiKey" variable is required. Please supply a value for this variable in the "PARAMETERS" section and try again.'
exit 2
}
elseif ( $orgID -eq [ System.String ]::Empty )
{
err 'The "orgID" variable is required. Please supply a value for this variable in the "PARAMETERS" section and try again.'
exit 2
}
# create our header row
addCSVRow $headers -Overwrite
# instantiate a webclient
$client = [ System.Net.WebClient ]::new()
# construct boilerplate headers
$client.Headers.Add( 'User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36' )
$client.Headers.Add( 'Content-Type', 'application/json' )
# inject the bearer token header
$client.Headers.Add( 'Authorization', "Bearer $apiKey" )
for ( $i = $page; $i -lt 10000; $i++ )
{
# assemble the URL
$url = "https://console.automox.com/api/servers?o=$orgID&page=$i&limit=$Limit"
try
{
# retrieve the current page of servers, convert from JSON
$servers = $client.DownloadString( $url ) | ConvertFrom-Json
}
catch [ System.Net.WebException ]
{
$statusCode = [ System.Int32 ] $_.Exception.Response.StatusCode
# attempt to provide some guidance based on status code
$advice = switch ( $statusCode )
{
400
{
@"
Is the requested URL formatted properly?
$url
"@
}
{ $_ -in 401, 403 }
{
# create stringbuilder to censor the api key
$censoredKey = [ System.Text.StringBuilder ]::new()
# enumerate apiKey characters
for ( $i = 0; $i -lt $apiKey.Length; $i++ )
{
# grab the current string character
$chr = $apiKey[ $i ]
# check if we're less than or equal to 6-characters from the end of the string
if ( $i -le $apiKey.Length - 6 )
{
# check if the character is a hyphen
if ( $chr -eq '-' )
{
# append the hyphen
$censoredKey.Append( '-' ) | Out-Null
}
else
{
# append a masked character
$censoredKey.Append( '*' ) | Out-Null
}
}
else
{
# append the current literal apiKey character
$censoredKey.Append( $apiKey[ $i ] ) | Out-Null
}
}
# assemble the constructed masked key
$censoredKey = $censoredKey.ToString()
@"
Authentication failed, is the API token valid?
$censoredKey
"@
}
404
{
@"
The page you requested was not found. Is the endpoint you requested valid?
$url
"@
}
}
err @"
Error: Failed to retrieve the server list.
HTTP Status Code: $statusCode
$advice
"@
}
# break the loop when we run out of results
if ( $servers.Count -eq 0 ) { break }
# enumerate servers returned
foreach ( $server in $servers )
{
try
{
# retrieve packages for the current server
$packages = $client.DownloadString( "https://console.automox.com/api/servers/$( $server.id )/packages?o=$orgID" ) | ConvertFrom-Json | Where-Object { $_.installed -eq $true }
}
catch [ System.Net.WebException ]
{
$statusCode = [ System.Int32 ] $_.Exception.Response.StatusCode
err @"
Error: Failed to retrieve server packages.
Server ID: $( $server.id )
HTTP Status Code: $statusCode
"@
}
# enumerate packages
foreach ( $package in $packages )
{
# add a CSV row for each package
addCSVRow $server.name, $package.display_name, $package.version, ( [ System.DateTime ] $package.create_time ).ToString( 'MM/dd/yy' )
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment