~/2022/10/12/autotask-powershell-enable-client-portal-for-all-users.md

PowerShell: Autotask – Enable Client Portal for All Users

---
author: 
date: 
updated: 
read: 1 min
in:   [ps, scripts]
tags: [autotask, powershell]
---

$ grep -n '^#' post.md

This is a quick one, it’s been forever since I’ve posted here. After moving back to Autotask, there’s still a ton of things to automate. One of the things that was bugging me was the fact you can’t set the client portal to default. Well, here’s a script you can run periodically to enable all the users to have the simple version of the client portal.

Requirements

  • PowerShell 3.0 or later (the script uses Invoke-RestMethod).
  • An Autotask REST API user with permission to read companies and contacts and to create client portal users.
  • The Autotask REST API base URL for your zone, the API integration code, and the API username and secret, supplied as environment variables (see Parameters).
  • Network access to the Autotask REST API.

Parameters

The script has no param block. It reads its settings from these environment variables, which must be set in the session before it runs.

NameTypeRequiredDescription
at_uriEnvironment variable (string)YesBase URL of your Autotask REST API zone, without /v1.0.
at_integrationcodeEnvironment variable (string)YesAPI integration code (sent as the ApiIntegrationcode header).
at_usernameEnvironment variable (string)YesAutotask API user name.
at_secretEnvironment variable (string)YesSecret for the Autotask API user.

Two values in the script are specific to the author's Autotask instance and must be adjusted before you run it: the companyCategoryID filter and Select-Object -Skip 3. See Notes.

Usage

Set the environment variables for the current session, then run the script. It lists the contacts it enables as it goes.

powershell
$env:at_uri = "<autotask-rest-url>"
$env:at_integrationcode = "<integration-code>"
$env:at_username = "<api-username>"
$env:at_secret = "<api-secret>"

.\Enable-AutotaskClientPortal.ps1

Script

powershell
<#
.SYNOPSIS
    Enables the simple client portal for every active Autotask contact that does not have it yet.
.DESCRIPTION
    Queries Autotask companies (filtered by company category), then the active
    contacts of each company, and compares them with the existing client portal
    users. For every contact without a client portal user it creates one, with
    the contact's e-mail address as the user name and a random 10-character
    password. Settings are read from environment variables, not parameters.
.PARAMETER at_uri
    Environment variable. Base URL of your Autotask REST API zone, without /v1.0.
.PARAMETER at_integrationcode
    Environment variable. API integration code (sent as the ApiIntegrationcode header).
.PARAMETER at_username
    Environment variable. Autotask API user name.
.PARAMETER at_secret
    Environment variable. Secret for the Autotask API user.
.EXAMPLE
    $env:at_uri = "<autotask-rest-url>"
    $env:at_integrationcode = "<integration-code>"
    $env:at_username = "<api-username>"
    $env:at_secret = "<api-secret>"
    .\Enable-AutotaskClientPortal.ps1
.NOTES
    Author  : Thomas Lasswell (https://www.techcolumnist.com)
    Version : 1.0 (2022-10-12)
    Requires: PowerShell 3.0 or later, Autotask REST API user
    Adjust  : Select-Object -Skip 3 skips the first three companies returned and
              companyCategoryID 101 is a category ID from the author's Autotask
              instance. Change or remove both before running.
    Limit   : Only companies with 500 or fewer contacts are handled.
#>
function New-SecurePassword {
    $Password = "!?@#$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".ToCharArray()
    ($Password | Get-Random -Count 10) -join ''
}

$at_uri = $($env:at_uri)
$at_integrationcode = $($env:at_integrationcode)
$at_username = $($env:at_username)
$at_secret = $($env:at_secret)

# Autotask headers.
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("ApiIntegrationcode", "$at_integrationcode")
$headers.Add("Content-Type", 'application/json')
$headers.Add("UserName", "$at_username")
$headers.Add("Secret", "$at_secret")

# Get the companies to process.
$companies = $(Invoke-RestMethod -Uri $($at_uri + '/v1.0/Companies/query?search={"IncludeFields": ["id", "companyName","companyNumber","isActive"],"filter":[{"op":"eq","field":"companyCategoryID","value":"101"}]}') -Headers $headers -Method Get).items

foreach ($company in $companies | Select-Object -Skip 3) {
    # Get the active contacts of the company.
    $contacts = $(Invoke-RestMethod -Uri $($at_uri + '/v1.0/Contacts/query?search={"IncludeFields": ["id", "firstName","lastName","isActive","emailAddress"],"filter":[{"op":"and","items":[{"op":"eq","field":"companyID","value":"' + $($company.id) + '"},{"op":"eq","field":"isActive","value":"true"}]}]}') -Headers $headers -Method Get).items
    $query = $null
    $x = 0
    $y = 0
    $clientportal = @()

    # Get the existing client portal users, 100 contacts per query.
    do {
        foreach ($contact in $contacts) {
            if ($query) {
                $query += ',{"op":"eq","field":"contactID","value":"' + $($contact.id) + '"}'
            }
            if (!$query) {
                $query = '{"op":"eq","field":"contactID","value":"' + $($contact.id) + '"}'
            }
            $y++
            $x++
            if ($x -eq $contacts.Count) {
                $y = 100
            }
            if ($y -eq 100) {
                $postbody = '{"filter":[{"op":"or","items":[' + $query + ']}]}'
                $clientportal += $(Invoke-RestMethod -Uri $($at_uri + '/v1.0/ClientPortalUsers/query') -Body $postbody -Headers $headers -Method Post).items
                $query = $null
                $y = 0
            }
        }
    }
    while ($x -lt $contacts.Count)

    # Create client portal users for the contacts that are missing one.
    if ($clientportal.Count -ne $contacts.Count) {
        Write-Host "Contacts: $($contacts.Count)"
        Write-Host "Enabled: $($clientportal.Count)"
        $missing = $null
        $missing = $contacts.id | Where-Object { $_ -notin $clientportal.contactId }
        foreach ($miss in $missing) {
            $contact = $null
            $contact = $contacts | Where-Object { $miss -eq $_.id }
            Write-Host "$($contact.emailAddress)"
            $json = [PSObject]@{
                contactID            = $($contact.id)
                userName             = "$($contact.emailAddress)"
                securityLevel        = 1
                password             = "$(New-SecurePassword)"
                numberFormat         = 22
                dateFormat           = 1
                timeFormat           = 1
                isClientPortalActive = $true
            }
            $json = $json | ConvertTo-Json
            Start-Sleep -Milliseconds 10
            Invoke-RestMethod -Uri $($at_uri + '/v1.0/ClientPortalUsers') -Method POST -Body $json -Headers $headers
        }
    }
}

Notes

  • There are a few places you might want to update. I use a filter for a specific customer category, so where your $companies variable is, you may want to change or remove this part of the query:
    json
    {"op":"eq","field":"companyCategoryID","value":"101"}
    
  • Category 101 and the Select-Object -Skip 3 (which skips the first three companies returned) are specific to my Autotask instance. Adjust or remove both for yours.
  • The script currently only handles companies with 500 or fewer contacts. I’ll update it with a loop to get all the contacts as well.