SyncroRMM.psm1

#Region './Private/Invoke-SyncroRequest.ps1' -1

function Invoke-SyncroRequest {
  <#
  .SYNOPSIS
  Main SyncroRMM API function

  .DESCRIPTION
  Calls SyncroRMM API with token

  .PARAMETER Method
  GET,POST,DELETE,PUT,etc

  .PARAMETER Resource
  Path to API endpoint

  .PARAMETER Params
  Hashtable of parameters

  .PARAMETER Body
  JSON encoded body string

  .PARAMETER Form
  Multipart form data

  .EXAMPLE
  Invoke-SyncroRequest -Resource /customer_assets
  #>


  [CmdletBinding()]
  param (
      [string] $Method = 'GET',
      [ValidateNotNullOrEmpty()]
      [string] $Resource,
      [hashtable] $Params = @{},
      [string] $Body,
      [hashtable] $Form
  )

  $SyncroAPIKey = Get-SyncroAPIKey
  $SyncroSubdomain = Get-syncroSubdomain

  # Assemble parameters
  $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)

  # Sort parameters
  foreach ($Item in ($Params.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) {
    $ParamCollection.Add($Item.Key, $Item.Value)
  }

  # Query string
  $Request = $ParamCollection.ToString()

  $Headers = @{
    'Authorization' = (New-Object PSCredential 'user', $SyncroAPIKey).GetNetworkCredential().Password;
  }

  if (($Script::Int_SyncroCustomHeaders | Measure-Object).count -gt 0) {
    foreach ($Entry in $Int_SyncroCustomHeaders.GetEnumerator()) {
      $Headers[$Entry.Name] = $Entry.Value
    }
  }

  $ContentType = 'application/json; charset=utf-8'

  $Uri = 'https://{0}.syncromsp.com/api/v1{1}' -f $SyncroSubdomain, $Resource
  # Make the API call URI
  if ($Request) {
    $UriBuilder = [System.UriBuilder]$Uri
    $UriBuilder.Query = $Request
    $Uri = $UriBuilder.Uri
  }
  Write-Verbose ('{0} [{1}]' -f $Method, $Uri)

  $RestMethod = @{
    Method = $Method
    Uri = $Uri
    Headers = $Headers
    ContentType = $ContentType
  }

  if ($Body) {
    $RestMethod.Body = $Body
    Write-Verbose $Body
  }

  if ($Form) {
    $RestMethod.Form = $Form
    Write-Verbose ($Form | Out-String)
  }

  try {
    $Results = Invoke-RestMethod @RestMethod
  } catch {
    if ("$_".trim() -eq 'Retry later' -or "$_".trim() -eq 'The remote server returned an error: (429) Too Many Requests.') {
      Write-Warning 'SyncroRMM API Rate limited. Waiting 30 Seconds then trying again'
      Start-Sleep 30
      $Results = Invoke-SyncroRequest @RestMethod
    } else {
      Write-Error "'$_'"
    }
  }

  $Results
}
#EndRegion './Private/Invoke-SyncroRequest.ps1' 104
#Region './Private/Invoke-SyncroRequestPaginated.ps1' -1

function Invoke-SyncroRequestPaginated {
    <#
    .SYNOPSIS
    Paginated requests to SyncroRMM API

    .DESCRIPTION
    Wraps Invoke-SyncroRequest with page sizes

    .PARAMETER SyncroRequest
    Request to paginate

    .PARAMETER Property
    Property name to return (don't specify to return entire response object)

    .PARAMETER PageSize
    Number of results to expect per page (default 25)

    #>

    [CmdletBinding()]
    Param(
        [hashtable]$SyncroRequest,
        [string]$Property
    )

    $i = 1
    do {
      $SyncroRequest.Params.page = $i
      # $HuduRequest.Params.page_size = $PageSize
      $Response = Invoke-SyncroRequest @SyncroRequest

      Write-Verbose ("Got page {0} of {1}" -f $Response.meta.page, $Response.meta.total_pages)
      $i++
      if ($Property) {
        $Response.$Property
      } else {
        $Response
      }
    } while ($Property -and ($Response.meta.page -lt $Response.meta.total_pages))
}
#EndRegion './Private/Invoke-SyncroRequestPaginated.ps1' 40
#Region './Public/Get-SyncroAlert.ps1' -1

function Get-SyncroAlert {
  <#
  .SYNOPSIS
  Gets Alerts from Syncro API

  .DESCRIPTION
  Retrieves RMM alerts from Syncro API

  .PARAMETER Id
  ID of alert to retrieve

  .PARAMETER Status
  Status of alerts to retrieve: Resolved, All, Active

  .EXAMPLE
  # Retrieve a single alert based on ID
  Get-SyncroAlert -Id 12345

  .EXAMPLE
  # Retrieve all active alerts
  Get-SyncroAlert -Status Active
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id,
    [ValidateSet("Resolved", "All", "Active")]
    [String] $Status
  )

  if ($Id) {
    $SyncroRequest = @{
      Resource = "/rmm_alerts/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty rmm_alert
  } else {
    $Params = @{}
    if ($Status) { $Params.status = $Status }

    $SyncroRequest = @{
      Resource = "/rmm_alerts"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property rmm_alerts
  }
}
#EndRegion './Public/Get-SyncroAlert.ps1' 49
#Region './Public/Get-SyncroAPIKey.ps1' -1

function Get-SyncroApiKey {
    <#
    .SYNOPSIS
    Get SyncroRMM API key

    .DESCRIPTION
    Returns SyncroRMM API key in securestring format

    .EXAMPLE
    Get-SyncroApiKey

    #>

    [CmdletBinding()]
    Param()
    if ($null -eq $Int_SyncroAPIKey) {
        Write-Error 'No API key has been set. Please use Set-SyncroAPIKey to set it.'
    } else {
        $Int_SyncroAPIKey
    }
}
#EndRegion './Public/Get-SyncroAPIKey.ps1' 21
#Region './Public/Get-SyncroAppointment.ps1' -1

function Get-SyncroAppointment {
  <#
  .SYNOPSIS
  Gets Appointments from Syncro API

  .DESCRIPTION
  Retrieves Appointments from Syncro API

  .PARAMETER Id
  ID of appointment to retrieve

  .EXAMPLE
  # Retrieve a single appointment by ID
  Get-SyncroAppointment -Id 12345

  .EXAMPLE
  # Retrieve all appointments
  Get-SyncroAppointment
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int64]::MaxValue)]
    [Int64] $Id
  )
  $Resource = "/appointments"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty appointment
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property appointments
  }
}
#EndRegion './Public/Get-SyncroAppointment.ps1' 44
#Region './Public/Get-SyncroAppointmentType.ps1' -1

function Get-SyncroAppointmentType {
  <#
  .SYNOPSIS
  Gets Appointment Types from Syncro API

  .DESCRIPTION
  Retrieves Appointment Types from Syncro API

  .PARAMETER Id
  ID of appointment type to retrieve

  .EXAMPLE
  # Retrieve a single appointment type by ID
  Get-SyncroAppointmentType -Id 12345

  .EXAMPLE
  # Retrieve all appointment types
  Get-SyncroAppointmentType
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/appointment_types"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property appointment_types
  }
}
#EndRegion './Public/Get-SyncroAppointmentType.ps1' 44
#Region './Public/Get-SyncroAsset.ps1' -1

function Get-SyncroAsset {
  <#
  .SYNOPSIS
  Gets SyncroRMM assets

  .DESCRIPTION
  Call SyncroRMM to retrieve assets

  .PARAMETER Id
  Id of requested asset

  .PARAMETER CustomerId
  ID of customer to retrieve assets for

  .PARAMETER AssetTypeId
  ID of asset types to retrieve

  .PARAMETER SNMP
  Retrieve assets with SNMP enabled

  .PARAMETER Query
  Search query to lookup assets

  .EXAMPLE Retrieve a single asset by ID
  Get-SyncroAsset -Id 12345

  .EXAMPLE Get all assets for customer with ID 12345
  Get-SyncroAsset -CustomerId 12345
  #>


  [CmdletBinding()]
  param (
    [ValidateRange(1, [int]::MaxValue)]
    [Int] $Id,
    [ValidateRange(1, [int]::MaxValue)]
    [Int] $CustomerId,
    [ValidateRange(1, [int]::MaxValue)]
    [Int] $AssetTypeId,
    [Alias('snmp_enabled')]
    [switch] $SNMP=$false,
    [String] $Query
  )

  if ($Id) {
    $SyncroRequest = @{
      Resource = "/customer_assets/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty asset
  } else {
    $Params = @{}
    if ($CustomerId) { $Params.customer_id = $CustomerId }
    if ($AssetTypeId) { $Params.asset_type_id = $AssetTypeId }
    if ($SNMP.IsPresent) { $Params.snmp_enabled = $SNMP.IsPresent }
    if ($Query) { $Params.query = $Query }

    $SyncroRequest = @{
      Resource = '/customer_assets'
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property assets
  }
}
#EndRegion './Public/Get-SyncroAsset.ps1' 65
#Region './Public/Get-SyncroAssetType.ps1' -1

function Get-SyncroAssetType {
  <#
  .SYNOPSIS
  Gets SyncroRMM asset types

  .DESCRIPTION
  Uses the SyncroRMM account settings to retrieve asset types

  .PARAM Id
  Retrieve a specific asset type by ID
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '', Scope = 'Function')]

  [CmdletBinding()]
  param (
      [ValidateRange(1, [int]::MaxValue)]
      [Int] $Id
  )

  $Settings = Get-SyncroSettings

  $AssetTypes = @{}
  foreach ($AssetType in $Settings.assets.asset_types.GetEnumerator()) {
    $AssetTypes[[string]$AssetType.id] = $AssetType.name
  }

  if ($Id) {
    $AssetTypes[[string]$Id]
  } else {
    $AssetTypes
  }
}
#EndRegion './Public/Get-SyncroAssetType.ps1' 33
#Region './Public/Get-SyncroContact.ps1' -1

function Get-SyncroContact {
  <#
  .DESCRIPTION
  Get SyncroRMM contacts

  .DESCRIPTION
  Call SyncroRMM to retrieve contacts

  .PARAMETER Id
  ID of requested contact

  .PARAMETER CustomerId
  ID of the customer to request contacts for

  .EXAMPLE Retrieves a single contact by ID
  Get-SyncroContact -Id 12345

  .EXAMPLE Retrieves all contacts for the customer with ID 12345
  Get-SyncroContact -CustomerId 12345
  #>


  [CmdletBinding()]
  param (
    [ValidateRange(1, [int]::MaxValue)]
    [Int] $Id,
    [ValidateRange(1, [int]::MaxValue)]
    [Alias('customer_id')]
    [Int] $CustomerId
  )

  if ($Id) {
    $SyncroRequest = @{
      Resource = "/contacts/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty contact
  } else {
    $Params = @{}
    if ($CustomerId) { $Params.customer_id = $CustomerId }

    $SyncroRequest = @{
      Resource = '/contacts'
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property contacts
  }
}
#EndRegion './Public/Get-SyncroContact.ps1' 49
#Region './Public/Get-SyncroContract.ps1' -1

function Get-SyncroContract {
  <#
  .SYNOPSIS
  Gets Contracts from Syncro API

  .DESCRIPTION
  Retrieves contracts from Syncro API

  .PARAMETER Id
  ID of contract to retrieve

  .EXAMPLE
  # Retrieve a single contract by ID
  Get-SyncroContract -Id 12345

  .EXAMPLE
  # Retrieve all contracts
  Get-SyncroContract
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/contracts"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property contracts
  }
}
#EndRegion './Public/Get-SyncroContract.ps1' 44
#Region './Public/Get-SyncroCustomer.ps1' -1

function Get-SyncroCustomer {
  <#
  .SYNOPSIS
  Gets SyncroRMM customers

  .DESCRIPTION
  Call SyncroRMM to retrieve customers

  .PARAMETER Id
  Id of requested customer

  .PARAMETER FirstName
  First Name of requested customer

  .PARAMETER LastName
  Last Name of requested customer

  .PARAMETER BusinessName
  Business Name of requested customer

  .PARAMETER Email
  Email of requested customer

  .PARAMETER Disabled
  Whether or not to include disabled customers in list

  .PARAMETER Query
  Search query to lookup customers

  .EXAMPLE Retrieve a single customer by ID
  Get-SyncroCustomer -Id 12345

  .EXAMPLE Get all customers with Contoso in their business name
  Get-SyncroCustomer -BusinessName Contoso
  #>


  [CmdletBinding()]
  param (
    [ValidateRange(1, [int]::MaxValue)]
    [Int] $Id,
    [string] $FirstName,
    [string] $LastName,
    [Alias('business_name')]
    [string] $BusinessName,
    [string] $Email,
    [Alias('include_disabled')]
    [switch] $Disabled=$false,
    [String] $Query
  )

  if ($Id) {
    $SyncroRequest = @{
      Resource = "/customers/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty customer
  } else {
    $Params = @{}
    if ($FirstName) { $Params.firstname = $FirstName }
    if ($LastName) { $Params.lastname = $LastName }
    if ($BusinessName) { $Params.business_name = $BusinessName }
    if ($Email) { $Params.email = $Email }
    if ($Disabled.IsPresent) { $Params.include_disabled = $Disabled.IsPresent }
    if ($Query) { $Params.query = $Query }

    $SyncroRequest = @{
      Resource = '/customers'
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property customers
  }
}
#EndRegion './Public/Get-SyncroCustomer.ps1' 74
#Region './Public/Get-SyncroEstimate.ps1' -1

function Get-SyncroEstimate {
  <#
  .SYNOPSIS
  Gets Estimates from Syncro API

  .DESCRIPTION
  Retrieves estimates from Syncro API

  .PARAMETER Id
  ID of estimate to retrieve

  .EXAMPLE
  # Retrieve a single estimate by ID
  Get-SyncroEstimate -Id 12345

  .EXAMPLE
  # Retrieve all estimates
  Get-SyncroEstimate
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/estimates"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty estimate
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property estimates
  }
}
#EndRegion './Public/Get-SyncroEstimate.ps1' 44
#Region './Public/Get-SyncroInvoice.ps1' -1

function Get-SyncroInvoice {
  <#
  .SYNOPSIS
  Gets Invoices from Syncro API

  .DESCRIPTION
  Retrieves invoices from Syncro API

  .PARAMETER Id
  ID of invoice to retrieve

  .EXAMPLE
  # Retrieve a single invoice by ID
  Get-SyncroInvoice -Id 12345

  .EXAMPLE
  # Retrieve all invoices
  Get-SyncroInvoice
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/invoices"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty invoice
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property invoices
  }
}
#EndRegion './Public/Get-SyncroInvoice.ps1' 44
#Region './Public/Get-SyncroLead.ps1' -1

function Get-SyncroLead {
  <#
  .SYNOPSIS
  Gets Leads from Syncro API

  .DESCRIPTION
  Retrieves leads from Syncro API

  .PARAMETER Id
  ID of lead to retrieve

  .EXAMPLE
  # Retrieve a single lead by ID
  Get-SyncroLead -Id 12345

  .EXAMPLE
  # Retrieve all leads
  Get-SyncroLead
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/leads"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty lead
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property leads
  }
}
#EndRegion './Public/Get-SyncroLead.ps1' 44
#Region './Public/Get-SyncroPayment.ps1' -1

function Get-SyncroPayment {
  <#
  .SYNOPSIS
  Gets Payments from Syncro API

  .DESCRIPTION
  Retrieves payments from Syncro API

  .PARAMETER Id
  ID of payment to retrieve

  .EXAMPLE
  # Retrieve a single payment by ID
  Get-SyncroPayment -Id 12345

  .EXAMPLE
  # Retrieve all payments
  Get-SyncroPayment
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/payments"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty payment
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property payments
  }
}
#EndRegion './Public/Get-SyncroPayment.ps1' 44
#Region './Public/Get-SyncroPortalUser.ps1' -1

function Get-SyncroPortalUser {
  <#
  .SYNOPSIS
  Gets SyncroRMM Portal Users

  .DESCRIPTION
  Call SyncroRMM to retrieve portal users

  .PARAMETER CustomerId
  Customer ID to retrieve portal users for

  .PARAMETER Email
  Search for portal users by their email address
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [int]::MaxValue)]
    [Alias('customer_id')]
    [Int] $CustomerId,
    [string] $Email
  )

  $Params = @{}
  if ($CustomerId) { $Params.customer_id = $CustomerId }
  if ($Email) { $Params.email = $Email }

  $SyncroRequest = @{
    Resource = '/portal_users'
    Method = 'GET'
    Params = $Params
  }

  Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property portal_users
}
#EndRegion './Public/Get-SyncroPortalUser.ps1' 35
#Region './Public/Get-SyncroProduct.ps1' -1

function Get-SyncroProduct {
  <#
  .SYNOPSIS
  Gets Products from Syncro API

  .DESCRIPTION
  Retrieves products from Syncro API

  .PARAMETER Id
  ID of product to retrieve

  .EXAMPLE
  # Retrieve a single product by ID
  Get-SyncroProduct -Id 12345

  .EXAMPLE
  # Retrieve all products
  Get-SyncroProduct
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/products"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty product
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property products
  }
}
#EndRegion './Public/Get-SyncroProduct.ps1' 44
#Region './Public/Get-SyncroPurchaseOrder.ps1' -1

function Get-SyncroPurchaseOrder {
  <#
  .SYNOPSIS
  Gets Purchase Orders from Syncro API

  .DESCRIPTION
  Retrieves purchase orders from Syncro API

  .PARAMETER Id
  ID of purchase order to retrieve

  .EXAMPLE
  # Retrieve a single purchase order by ID
  Get-SyncroPurchaseOrder -Id 12345

  .EXAMPLE
  # Retrieve all purchase orders
  Get-SyncroPurchaseOrder
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/purchase_orders"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty purchase_order
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property purchase_orders
  }
}
#EndRegion './Public/Get-SyncroPurchaseOrder.ps1' 44
#Region './Public/Get-SyncroSchedule.ps1' -1

function Get-SyncroSchedule {
  <#
  .SYNOPSIS
  Gets Schedules from Syncro API

  .DESCRIPTION
  Retrieves schedules from Syncro API

  .PARAMETER Id
  ID of schedule to retrieve

  .EXAMPLE
  # Retrieve a single schedule by ID
  Get-SyncroSchedule -Id 12345

  .EXAMPLE
  # Retrieve all schedules
  Get-SyncroSchedule
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/schedules"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty schedule
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property schedules
  }
}
#EndRegion './Public/Get-SyncroSchedule.ps1' 44
#Region './Public/Get-SyncroSettings.ps1' -1

function Get-SyncroSettings {
  <#
  .SYNOPSIS
  Gets SyncroRMM account settings

  .DESCRIPTION
  Calls SyncroRMM API to retrieve account settings

  .EXAMPLE Retrieve all account settings
  Get-SyncroSettings
  #>

  [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Scope = 'Function')]

  Param()

  $SyncroRequest = @{
    Resource = '/settings'
    Method = 'GET'
  }
  Invoke-SyncroRequest @SyncroRequest
}
#EndRegion './Public/Get-SyncroSettings.ps1' 22
#Region './Public/Get-SyncroSubdomain.ps1' -1

function Get-SyncroSubdomain {
    <#
    .SYNOPSIS
    Get SyncroRMM Base URL

    .DESCRIPTION
    Returns SyncroRMM Base URL

    .EXAMPLE
    Get-SyncroSubdomain

    #>

    [CmdletBinding()]
    Param()
    if ($null -eq $Int_SyncroSubdomain) {
        Write-Error 'No subdomain has been set. Please use Set-SyncroSubdomain to set it.'
    } else {
        $Int_SyncroSubdomain
    }
}
#EndRegion './Public/Get-SyncroSubdomain.ps1' 21
#Region './Public/Get-SyncroTicket.ps1' -1

function Get-SyncroTicket {
  <#
  .SYNOPSIS
  Gets tickets from SyncroRMM

  .DESCRIPTION
  Retrieves ticket details from the SyncroRMM API

  .PARAMETER Id
  ID of a specific ticket to retrieve

  .PARAMETER CustomerId
  Customer ID to retrieve tickets for

  .PARAMETER Contact ID
  Contact ID to retrieve tickets for

  .PARAMETER UserId
  Retreive tickets assigned to a specific user

  .PARAMETER TicketSearchId
  Retrieve tickets for a saved search

  .PARAMETER Status
  Retrieve tickets based on their status

  .PARAMETER Number
  Retrieve ticket by the ticket number

  .PARAMETER ResolvedAfter
  Retrieve tickets resolved after a specific date

  .PARAMETER CreatedAfter
  Retrieve tickets created after a specific date

  .PARAMETER SinceUpdatedAt
  Retrieve tickets updated after a specific date

  .PARAMETER Query
  Generic search terms to search for tickets

  .EXAMPLE
  # Get a ticket by it's ID
  Get-SyncroTicket -Id 12345
  #>


  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id,
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $CustomerId,
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $ContactId,
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $UserId,
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $TicketSearchId,
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Number,
    [ValidateSet("New", "In Progress", "Resolved", "Invoiced", "Waiting For Parts", "Waiting on Customer", "Scheduled", "Customer Reply", "Not Closed")]
    [String] $Status,
    [Alias('resolved_after')]
    [datetime] $ResolvedAfter,
    [Alias('created_after')]
    [datetime] $CreatedAfter,
    [Alias('sinced_updated_at')]
    [datetime] $SinceUpdatedAt,
    [string] $Query
  )

  if ($Id) {
    $SyncroRequest = @{
      Resource = "/tickets/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty ticket
  } else {
    $Params = @{}
    if ($CustomerId) { $Params.customer_id = $CustomerId }
    if ($ContactId) { $Params.contact_id = $ContactId }
    if ($UserId) { $Params.user_id = $UserId }
    if ($TicketSearchId) { $Params.ticket_search_id = $TicketSearchId }
    if ($Number) { $Params.number = $Number }
    if ($Status) { $Params.status = $Status }
    if ($ResolvedAfter) {
      $Params.resolved_after = Get-Date $ResolvedAfter -Format "yyyy-MM-dd"
    }
    if ($CreatedAfter) {
      $Params.created_after = Get-Date $CreatedAfter -Format "yyyy-MM-dd"
    }
    if ($SinceUpdatedAt) {
      $Params.since_updated_at = Get-Date $SinceUpdatedAt -Format "yyyy-MM-dd"
    }
    if ($Query) { $Params.query = $Query}


    $SyncroRequest = @{
      Resource = '/tickets'
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property tickets
  }
}
#EndRegion './Public/Get-SyncroTicket.ps1' 106
#Region './Public/Get-SyncroTicketSavedSearch.ps1' -1

function Get-SyncroTicketSavedSearch {
  <#
  .SYNOPSIS
  Get saved ticket searches

  .DESCRIPTION
  Gets a list of saved ticket searches
  #>


  $Settings = Get-SyncroTicketSettings

  $Searches = @{}
  foreach($Search in $Settings.saved_searches.GetEnumerator()) {
    $Searches[[string]$Search.id] = $Search.name
  }

  $Searches
}
#EndRegion './Public/Get-SyncroTicketSavedSearch.ps1' 19
#Region './Public/Get-SyncroTicketSettings.ps1' -1

function Get-SyncroTicketSettings {
  <#
  .SYNOPSIS
  Gets SyncroRMM ticket settings

  .DESCRIPTION
  Calls SyncroRMM API to retrieve account settings

  .EXAMPLE Retrieve all account settings
  Get-SyncroTicketSettings
  #>

  [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Scope = 'Function')]

  Param()

  $SyncroRequest = @{
    Resource = '/tickets/settings'
    Method = 'GET'
  }
  Invoke-SyncroRequest @SyncroRequest
}
#EndRegion './Public/Get-SyncroTicketSettings.ps1' 22
#Region './Public/Get-SyncroVendor.ps1' -1

function Get-SyncroVendor {
  <#
  .SYNOPSIS
  Gets Vendors from Syncro API

  .DESCRIPTION
  Retrieves vendors from Syncro API

  .PARAMETER Id
  ID of vendor to retrieve

  .EXAMPLE
  # Retrieve a single vendor by ID
  Get-SyncroVendor -Id 12345

  .EXAMPLE
  # Retrieve all vendors
  Get-SyncroVendor
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/vendors"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty vendor
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property vendors
  }
}
#EndRegion './Public/Get-SyncroVendor.ps1' 44
#Region './Public/Get-SyncroWikiPage.ps1' -1

function Get-SyncroWikiPage {
  <#
  .SYNOPSIS
  Gets Wiki Pages from Syncro API

  .DESCRIPTION
  Retrieves wiki pages from Syncro API

  .PARAMETER Id
  ID of wiki page to retrieve

  .EXAMPLE
  # Retrieve a single wiki_page by ID
  Get-SyncroWikiPage -Id 12345

  .EXAMPLE
  # Retrieve all wiki_pages
  Get-SyncroWikiPage
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id
  )
  $Resource = "/wiki_pages"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty wiki_page
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property wiki_pages
  }
}
#EndRegion './Public/Get-SyncroWikiPage.ps1' 44
#Region './Public/Get-SyncroWorksheetResult.ps1' -1

function Get-SyncroWorksheetResult {
  <#
  .SYNOPSIS
  Gets Worksheet Results from Syncro API

  .DESCRIPTION
  Retrieves worksheet results from Syncro API

  .PARAMETER TicketId
  Ticket ID to pull worksheets for

  .PARAMETER Id
  ID of worksheet result to retrieve

  .EXAMPLE
  # Retrieve a single worksheet result by ID
  Get-SyncroWorksheetResult -TicketId 98765 -Id 12345

  .EXAMPLE
  # Retrieve all worksheet results for a ticket
  Get-SyncroWorksheetResult -TicketId 12345
  #>

  [CmdletBinding()]
  param (
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $Id,
    [Parameter(mandatory=$true)]
    [ValidateRange(1, [Int]::MaxValue)]
    [Int] $TicketId
  )
  
  $Resource = "/tickets/$TicketId/worksheet_results"

  if ($Id) {
    $SyncroRequest = @{
      Resource = "$Resource/$Id"
      Method = 'GET'
    }
    Invoke-SyncroRequest @SyncroRequest | Select-Object -ExpandProperty worksheet_result
  } else {
    $Params = @{}

    $SyncroRequest = @{
      Resource = "$Resource"
      Method = 'GET'
      Params = $Params
    }
    Invoke-SyncroRequestPaginated -SyncroRequest $SyncroRequest -Property worksheet_results
  }
}
#EndRegion './Public/Get-SyncroWorksheetResult.ps1' 51
#Region './Public/Set-SyncroAPIKey.ps1' -1

function Set-SyncroAPIKey {
  <#
  .SYNOPSIS
  Set SyncroRMM API Key

  .DESCRIPTION
  API keys are required to interact with SyncroRMM

  .PARAM ApiKey
  The API key

  .EXAMPLE
  Set-SyncroAPIKey -ApiKey foobar1234
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope = 'Function')]

  [CmdletBinding()]
  param (
    [Parameter(Mandatory=$False, ValueFromPipeline=$true)]
    [String] $ApiKey
  )
  process {
    if ($ApiKey) {
      $SecApiKey = ConvertTo-SecureString $ApiKey -AsPlainText -Force
    } else {
      $SecApiKey = Read-Host -Prompt 'Please enter your SyncroRMM API key, you can obtain it from https://your-syncro-subdomain.syncromsp.com/api_tokens' -AsSecureString
    }

    Set-Variable -Name 'Int_SyncroAPIKey' -Value $SecApiKey -Visibility Private -Scope script -Force
  }
}
#EndRegion './Public/Set-SyncroAPIKey.ps1' 33
#Region './Public/Set-SyncroSubdomain.ps1' -1

function Set-SyncroSubdomain {
  <#
  .SYNOPSIS
  Set the subdomain for your Syncro instance

  .DESCRIPTION
  In order to access the Syncro API, the subdomain must be set

  .EXAMPLE
  Set-SyncroSubdomain -Subdomain superman
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function')]

  [CmdletBinding()]
  param (
    [Parameter(Mandatory=$false, ValueFromPipeline=$true)]
    [String] $Subdomain
  )
  process {
    if (!$Subdomain) {
      $Subdomain = Read-Host -Prompt 'Please enter your SyncroRMM subdomain, for example superman for https://superman.syncromsp.com:'
    }

    Set-Variable -Name 'Int_SyncroSubdomain' -Value $Subdomain -Visibility Private -Scope script -Force
  }
}
#EndRegion './Public/Set-SyncroSubdomain.ps1' 27