Last active
August 26, 2024 08:00
-
-
Save plmcgrn/6686a92b90dc522a566f0f47b52148a1 to your computer and use it in GitHub Desktop.
Genesys Cloud Automate WebRTC Phone Creation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################## | |
# Author: Paul McGurn | |
# Created: August 3, 2022 | |
# | |
# This script is intended to run from within an Azure Automation Runbook. It queries Genesys CLoud | |
# for Users, determines if each has a WebRTC phone assigned, and either assigns or creates one as needed. | |
# There are Azure Active Directory integrations to cover mapping from AAD Office Location to Genesys Cloud Site, since | |
# the Genesys SCIM integration doesn't support syncing Office Location. | |
################################## | |
$sep = "**********************************" | |
################################## | |
# Update values in this section as appropriate for the environment | |
################################## | |
$cloudenv = 'usw2.pure.cloud' # 'usw2.pure.cloud' or 'mypurecloud.com', etc. | |
$credenv = 'qa' #Set to 'qa' or 'prod' depending on which environment we're connecting to | |
$azureaccountid = 'xxxxxxxxx' #GUID of the Managed Identity impersonation account | |
$defaultsite = 'NORTHAMERICA' | |
#Mappings from Azure AD User location to Genesys Cloud Site for the WebRTC phone. | |
$locationmap = $( | |
@{name = "Amsterdam"; cloudlocation = "EUROPE"; }, | |
@{name = "Budapest"; cloudlocation = "EUROPE"; }, | |
@{name = "Field (Australia)"; cloudlocation = "AUSTRALIA"; }, | |
@{name = "Field (Germany)"; cloudlocation = "EUROPE"; }, | |
@{name = "Field (Hungary)"; cloudlocation = "EUROPE"; }, | |
@{name = "Field (Ireland)"; cloudlocation = "EUROPE"; }, | |
@{name = "Field (Netherlands)"; cloudlocation = "EUROPE"; }, | |
@{name = "Field (UK)"; cloudlocation = "EUROPE"; }, | |
@{name = "Dublin"; cloudlocation = "EUROPE"; }, | |
@{name = "Sao Paulo"; cloudlocation = "SOUTHAMERICA"; }, | |
@{name = "Singapore"; cloudlocation = "AUSTRALIA"; }, | |
@{name = "Sydney, Australia"; cloudlocation = "AUSTRALIA"; }, | |
@{name = "Theaterstr. 6, Dresden"; cloudlocation = "EUROPE"; } | |
) | |
#Azure AD connection, comment out before -Identity to run locally | |
try { | |
Write-Output "Connecting to Azure AD" | |
Connect-AzAccount -Identity -Account $azureaccountid | Out-Null | |
} | |
catch { | |
Write-Output "Failed to connect to Azure AD, aborting provisioning run" | |
Write-Output $_ | |
Write-Error "Failed to connect to Azure AD, aborting provisioning run" | |
Write-Error $_ | |
Exit 1 | |
} | |
################################## | |
# No edits below here | |
################################## | |
#Get clientid and clientsecret from Azure Keyvault. We store both QA and Prod values in the same vault | |
#ex. clientid-prod and clientsecret-prod | |
try { | |
Write-Output "Getting clientid and clientsecret from Azure Keyvault" | |
$clientid = Get-AzKeyVaultSecret -VaultName "genesyscloud-prov" -Name "clientid-$($credenv)" -AsPlainText | |
$clientsecret = Get-AzKeyVaultSecret -VaultName "genesyscloud-prov" -Name "clientsecret-$($credenv)" -AsPlainText | |
Write-Output "Client ID: ($($clientid))" | |
} | |
catch { | |
Write-Output "Failed to get Azure Keyvautl entries for Genesys credentials. Aborting." | |
Write-Output $_ | |
Write-Error "Failed to get Azure Keyvautl entries for Genesys credentials. Aborting." | |
Write-Error $_ | |
Exit 1 | |
} | |
$pair = "$($clientid):$($clientsecret)" | |
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) | |
$base64 = [System.Convert]::ToBase64String($bytes) | |
#Get Genesys Cloud OAuth token | |
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" | |
$headers.Add("Authorization", "Basic $base64") | |
$headers.Add("Content-Type", "application/x-www-form-urlencoded") | |
$body = "grant_type=client_credentials" | |
Write-Output "Logging in to Genesys Cloud API" | |
try { | |
$response = Invoke-RestMethod "https://login.$cloudenv/oauth/token" -Method 'POST' -Headers $headers -Body $body | |
} | |
catch { | |
Write-Output "Failed to log in to Genesys Cloud API. Aborting." | |
Write-Output $_ | |
Write-Error "Failed to log in to Genesys Cloud API. Aborting." | |
Write-Error $_ | |
Exit 1 | |
} | |
#set bearer based on returned access token | |
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" | |
$headers.Add("Authorization", "Bearer $($response.access_token)") | |
#get user list | |
Write-Output $sep | |
Write-Output "Getting User List with Stations" | |
$users = Invoke-RestMethod "https://api.$cloudenv/api/v2/users?pageSize=250&expand=station" -Method 'GET' -Headers $headers | |
#add users to separate collection, in case we have more pages to fetch | |
$userlist = [System.Collections.ArrayList]::new() | |
foreach ($u in $users.entities) { | |
$userlist.add($u) | Out-Null | |
} | |
#see if we have additional pages of users, if so, get them all | |
if ($false -eq [string]::IsNullOrEmpty($users.nextUri)) { | |
Write-Output "User list has multiple pages ($($users.pageCount))" | |
$selfUri = $users.selfUri | |
$nextUri = $users.nextUri | |
$lastUri = $users.lastUri | |
while ($selfUri -ne $lastUri) { | |
Write-Output "Getting next page of users ($($users.pageNumber + 1))" | |
$users = Invoke-RestMethod "https://api.$($cloudenv)$($nextUri)" -Method 'GET' -Headers $headers | |
#add additional users to collection | |
foreach ($u in $users.entities) { | |
$userlist.add($u) | Out-Null | |
} | |
$selfUri = $users.selfUri | |
$nextUri = $users.nextUri | |
} | |
} | |
#Parse list of users that that do not have DefaultStation set to a phone | |
$toprovision = [System.Collections.ArrayList]::New() | |
foreach ($u in $userlist) { | |
if (!$u.station.defaultStation) { | |
$toprovision.add($u) | Out-Null | |
} | |
} | |
if ($toprovision.count -lt 1) { | |
Write-Output "No users need a WebRTC phone. Exiting." | |
Exit 0 | |
} | |
else { | |
Write-Output "Phones to provision: $($toprovision.count)" | |
} | |
#get site list. Note, we expect a default site to be called "NORTHAMERICA" unless changed above. | |
$sitelist = Invoke-RestMethod "https://api.$($cloudenv)/api/v2/telephony/providers/edges/sites" -Method 'GET' -Headers $headers | |
if (!$sitelist.entities) { | |
Write-Error "No Sites found defined, exiting" | |
Exit 1 | |
} | |
else { | |
$sitelist = $sitelist.entities | |
} | |
#get edge group list, we should only have one, but we need the GUID for it | |
$edgegrouplist = Invoke-RestMethod "https://api.$($cloudenv)/api/v2/telephony/providers/edges/edgegroups" -Method 'GET' -Headers $headers | |
if ($edgegrouplist.entities) { | |
$edgegroupid = $edgegrouplist.entities[0].id | |
#Write-Output "Edge Group ID: $($edgegroupid)" | |
} | |
else { | |
Write-Error "No edge group found, exiting" | |
Exit 1 | |
} | |
#get phone base settings list and ID for WebRTC entry | |
$phonebasesettingslist = Invoke-RestMethod "https://api.$($cloudenv)/api/v2/telephony/providers/edges/phonebasesettings?name=WebRTC" -Method 'GET' -Headers $headers | |
if ($phonebasesettingslist.entities) { | |
$phonebasesettingsid = $null | |
$webrtc = $phonebasesettingslist.entities | Where-Object -Property Name -Match "WebRTC" | |
if ($webrtc) { | |
$phonebasesettingsid = $webrtc.id | |
#Write-Output "Phone Base Settings ID: $9$phonebasesettingsid)" | |
} | |
else { | |
Write-Error "No WebRTC config found in Phone Base Settings" | |
Exit 1 | |
} | |
} | |
else { | |
Write-Error "No phone base settings found, exiting" | |
Exit 1 | |
} | |
#get line base settings list and ID for WebRTC entry | |
$linebasesettingslist = Invoke-RestMethod "https://api.$cloudenv/api/v2/telephony/providers/edges/linebasesettings" -Method 'GET' -Headers $headers | |
if ($linebasesettingslist.entities.count -gt 0) { | |
$line = $linebasesettingslist.entities | Where-Object Name -Match "WebRTC" | |
if ($line) { | |
$linebasesettingsid = $line.id | |
#Write-Output "Line Base Settings ID: $($linebasesettingsid)" | |
} | |
else { | |
Write-Error "No line config found for Line Base Settings 'WebRTC'" | |
Exit 1 | |
} | |
} | |
else { | |
Write-Error "No line base settings found, exiting" | |
Exit 1 | |
} | |
#add the webrtc softphones | |
$headers.Add("Content-Type", "application/json") | |
foreach ($u in $toprovision) { | |
Write-Output $sep | |
$email = $u.email | |
#try to find an existing WebRTC phone based on user ID first | |
Write-Output "$($email): Check for existing WebRTC phone" | |
$alreadyexists = $false | |
$phonelookupresp = Invoke-RestMethod "https://api.$cloudenv/api/v2/telephony/providers/edges/phones?webRtcUser.id=$($u.id)" -Method 'GET' -Headers $headers | |
if ($phonelookupresp.total -gt 0) { | |
$webrtcid = $phonelookupresp.entities[0].Lines[0].id | |
Write-Output "$($email): phone exists ($($webrtcid)), but not set as a default." | |
$alreadyexists = $true | |
} | |
#phone doesn't already exist, create one | |
if ($false -eq $alreadyexists) { | |
Write-Output "$($email): WebRTC phone doesn't exist, creating one." | |
Write-Output "$($email): Looking up User in AD to map WebRTC Site" | |
$user = Get-AzAdUser -Filter "mail eq '$($email)'" | |
if ($user) { | |
#User found, map them | |
$location = $user.OfficeLocation | |
$mappedlocation = $locationmap | Where-Object -Property Name -eq $location | |
if ($mappedlocation) { | |
$site = $sitelist | Where-Object -Property Name -eq $mappedlocation.cloudlocation | |
Write-Output "$($email): Site mapped to ($($site.name))" | |
$siteid = $site.id | |
} | |
else { | |
$site = $sitelist | Where-Object -Property Name -eq $defaultsite | |
Write-Output "$($email): Site not mapped, using default ($($defaultsite))" | |
$siteid = $site.id | |
} | |
} | |
else { | |
Write-Output "$($email): User not found in AD (local user?). Using default ($($defaultsite))." | |
$site = $sitelist | Where-Object -Property Name -eq $defaultsite | |
$siteid = $site.id | |
} | |
#build WebRTC request payload | |
$body = [PSCustomObject]@{ | |
name = "$($u.name) WebRTC"; | |
edgeGroup = @{ id = $edgegroupid }; | |
site = @{ id = $siteid }; | |
phoneBaseSettings = @{ id = $phonebasesettingsid }; | |
webRtcUser = @{ id = $u.id }; | |
lines = @( | |
@{ | |
name = "$($u.name) WebRTC"; | |
lineBaseSettings = @{ id = $linebasesettingsid } | |
} | |
) | |
} | |
$phonejson = $body | ConvertTo-Json -depth 8 | |
$phoneresp = Invoke-RestMethod "https://api.$cloudenv/api/v2/telephony/providers/edges/phones" -Method 'POST' -Headers $headers -Body $phonejson -ErrorAction SilentlyContinue | |
$webrtcid = $phoneresp.Lines[0].id | |
} | |
#update each user's Default Station | |
Write-Output "$($u.email): Set default station to $($webrtcid)" | |
$response = Invoke-RestMethod "https://api.$cloudenv/api/v2/users/$($u.id)/station/defaultstation/$($webrtcid)" -Method 'PUT' -Headers $headers | |
Write-Output $sep | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment