PSAtera.psm1

Add-Type -AssemblyName System.Web

<#
  .Synopsis
  Generic command for making GET requests against the Atera API
 
  .Parameter Endpoint
  Endpoint to request, beginning with a /
 
  .Parameter Paginate
  Whether to paginate the request. If set to false, will only query the first page of results.
 
  .Example
  New-AteraGetRequest -Endpoint "/customers/3"
  # Get a customer
 
  .Example
  New-AteraGetRequest -Endpoint "/customvalues/ticketfield/2/Product%20Family" -Paginate $false
  # Get a custom value (which needs to not paginate)
#>

function New-AteraGetRequest {
  [CmdletBinding()]
  param (
    [Parameter(Mandatory)]
    [string] $Endpoint,
    [Parameter()]
    [bool] $Paginate=$true,
    [Parameter()]
    [Hashtable] $Query
  )
  $Headers = @{
    "accept" = "application/json"
    "X-API-KEY" = Get-AteraAPIKey
  }
  $ItemsInPage = 50
  $QueryString = Format-QueryString -Query $Query
  $Uri = "https://app.atera.com/api/v3$($endpoint)?itemsInPage=$ItemsInPage&$QueryString"
  $items = @()
  $index = 0

  do {
    Write-Debug "[PSAtera] Request for $Uri"
    $data = Invoke-RestMethod -Uri $Uri -Method "GET" -Headers $Headers
    if (!$Paginate) { return $data }
    $items += $data.items
    $index += 1
    $Uri = $data.nextLink
  } while ($Uri -ne "" -and $index -lt [math]::ceiling($RecordLimit / $ItemsInPage))
  return $items
}

<#
  .Synopsis
  Generic command for making POST requests against the Atera API
 
  .Parameter Endpoint
  Endpoint to request, beginning with a /
 
  .Parameter Body
  Hashtable of data to send in POST request
 
  .Example
  New-AteraPostRequest -Endpoint "/contacts" -Body @{CustomerID=5; Email="john@example.com"}
  # Create a new Contact
#>

function New-AteraPostRequest {
  [CmdletBinding()]
  param (
    [Parameter(Mandatory)]
    [string] $Endpoint,
    [Parameter(Mandatory, ValueFromPipeline)]
    [Hashtable] $Body
  )
  $Headers = @{
    "accept" = "application/json"
    "X-API-KEY" = Get-AteraAPIKey
  }
  $Uri = "https://app.atera.com/api/v3$($endpoint)"
  Write-Debug "[PSAtera] Request for $Uri"
  $Body = ConvertTo-Json $Body
  $data = Invoke-RestMethod -Uri $Uri -Method "POST" -Headers $Headers -Body ([System.Text.Encoding]::UTF8.GetBytes($Body))
  return $data
}

<#
  .Synopsis
  Generic command for making PUT requests against the Atera API. Basically the same as the
  New-AteraPostRequest command.
 
  .Parameter Endpoint
  Endpoint to request, beginning with a /
 
  .Parameter Body
  Hashtable of data to send in PUT request
 
  .Example
  New-AteraPutRequest -Endpoint "/customvalues/customerfield/1/age/5%20Years"
  # Updates the age field on customer 1 to "5 Years"
#>

function New-AteraPutRequest {
  [CmdletBinding()]
  param (
    [Parameter(Mandatory)]
    [string] $Endpoint,
    [Parameter(ValueFromPipeline)]
    [Object] $Body
  )
  $Headers = @{
    "accept" = "application/json"
    "content-type" = "application/json"
    "X-API-KEY" = Get-AteraAPIKey
  }
  $Uri = "https://app.atera.com/api/v3$($endpoint)"
  Write-Debug "[PSAtera] Request for $Uri"
  $Body = ConvertTo-Json $Body
  $data = Invoke-RestMethod -Uri $Uri -Method "Put" -Headers $Headers -Body ([System.Text.Encoding]::UTF8.GetBytes($Body))
  return $data
}


$AteraAPIKey = $env:ATERAAPIKEY
<#
  .Synopsis
  Set the Atera API Key used by the module. If none set, the ATERAAPIKEY environment variable will be used instead.
 
  .Parameter APIKey
  Atera API Key which can be found at https://app.atera.com/Admin#/admin/api
#>

function Set-AteraAPIKey {
  param(
    # Atera API Key
    [string]$APIKey
  )
  $script:AteraAPIKey = $APIKey
}

<#
  .Synopsis
  Get the Atera API Key in use by the module.
#>

function Get-AteraAPIKey {
  if (!$AteraAPIKey) { throw "`$AteraAPIKey not set. Set it with either Set-AteraAPIKey or `$env:ATERAAPIKEY" }
  return $AteraAPIKey
}

$RecordLimit = 1000
<#
  .Synopsis
  Set the maximum number of records returned by API calls. Default is set at 1,000.
 
  .Parameter Limit
  Number of total records returned by Get- command
#>

function Set-AteraRecordLimit {
  param(
    # Maximum records returned
    [Parameter(Mandatory=$true)]
    [int]$Limit
  )
  $script:RecordLimit = $Limit
}
<#
  .Synopsis
  Get the maximum number of records returned by API calls.
#>

function Get-AteraRecordLimit {
  $RecordLimit
}

<#
  .Synopsis
  Installs the Atera Agent for a device
 
  .Parameter Subdomain
  Atera instances subdomain (ex. for https://example.atera.com/GetAgent... the subdomain is 'example')
 
  .Parameter IntegratorLogin
  Your Atera username
 
  .Parameter CustomerID
  Customer ID to assign agent to. If not provided, assigned agent to the Unassigned customer in Atera
 
  .Example
  Install-AteraAgent -Subdomain example -IntegratorLogin john@example.com -CustomerID 2
  # Install agent
 
  .Example
  Get-AteraCustomers | Where CustomerName -eq "Contoso" | Install-AteraAgent -Subdomain example -IntegratorLogin john@example.com
  # Search for a customer to install the agent for
#>

function Install-AteraAgent {
  param (
    [Parameter(Mandatory)]
    [string] $Subdomain,
    [Parameter(Mandatory)]
    [string] $IntegratorLogin,
    [Parameter(ValueFromPipelineByPropertyName)]
    [int] $CustomerID = 0
  )
  if (Get-Service -Name "AteraAgent" -ErrorAction SilentlyContinue) {
    Write-Output "Atera Agent already installed."
    return
  }

  Write-Debug "Downloading Atera Installer"
  $TempFile = Join-Path -Path $env:TEMP -ChildPath "AteraAgent.msi"
  Invoke-WebRequest -Uri "http://$Subdomain.atera.com/GetAgent/Msi/?CustomerID=$CustomerID&IntegratorLogin=$IntegratorLogin" -OutFile $TempFile
  Write-Debug "Installing Atera"
  $proc = Start-Process (Join-Path -Path $env:SystemRoot -ChildPath "system32\msiexec.exe") -PassThru -Wait -ArgumentList "/I","$TempFile","/quiet"
  Write-Debug "Exit code: $($proc.ExitCode)"
  if ($proc.ExitCode -eq 0) { Write-Output "Atera Agent installed" -ForegroundColor Green }
  else { Write-Error "Installation failed with exit code $($proc.ExitCode)" -Category InvalidResult }
}

function Format-QueryString($Query) {
  $qs = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
  foreach($item in $Query.Keys) {
    $qs.Add($item, $Query[$item])
  }
  $qs.ToString()
}

Get-ChildItem -Path $PSScriptRoot/endpoints | ForEach-Object { . $_.PSPath }

Export-ModuleMember -Function Install-AteraAgent,Get-Atera*,Set-Atera*,New-Atera*