Managing SailPoint IdentityNow Sources via the API with PowerShell

Back again with another post in my series detailing accessing SailPoint IdentityNow via the API using the unpublished and undocumented APIs. Previous posts detail;

This post also assumes you are able to access the IdentityNow APIs as detailed in this post here. You will need to use that process to access the Sources APIs. You will also need to update the Headers for “Content-Type” for the Get API calls and again for the Post API call. Add this line to your script to allow the query and return of Source Details


This post details:

  • Getting a List of Sources
  • Getting the Details of a Source
  • Getting the Schema of a Source
  • Updating the Details of a Source

Getting a List of Sources


The API call shown above will return all Sources configured in the queried IdentityNow Tenant. For each Source a limited set of configuration information is returned. Below is an example for a Delimited File Source File Source Type.

id : 36666
version : 2
name : Privileged Access Management
description : Cyberark PAM
owner :
lastUpdated : 2018-10-05T00:39:33Z
scriptName : delimitedfile
definitionName : Delimited File
appCount : 0
userCount : 0
sourceConnected : False
sourceConnectorName : Delimited File
supportsEntitlementAggregation : true
externalId : 2c918086663fbbc0016641aa51041603
icon :
health : @{hostname=564c355e916f; lastSeen=1538699972555; org=orgName; healthy=True; lastChanged=1538699972555;
isAuthoritative=false; id=36777; type=C:173-delimited-file; status=SOURCE_STATE_UNCHECKED_SOURCE_NO_ACCOUNTS;
useForAuthentication : False
useForAccounts : False
useForProvisioning : False
useForPasswordManagement : False
iqServiceDownloadUrl :

PowerShell Example

The following will return the list of Sources in an IdentityNow Tenant where $orgName is the Organisation Name for your IdentityNow Tenant.

$IDNSources=Invoke-RestMethod-Method Get -Uri "https://$($orgName)"-WebSession $IDN
write-host -ForegroundColor Green "$($IDNSources.Count) Sources found"

Getting the Details of a Source


The API call shown above will return all the details for the specified Source. Below is an example of the full details for the same Delimited File Source File Source Type above.

id : 36666
version : 3
name : Privileged Access Management
description : Cyberark PAM 
owner : @{id=1084412; name=IDN Admin}
lastUpdated : 2018-10-22T21:29:12Z
scriptName : delimitedfile
definitionName : Delimited File
appCount : 0
userCount : 0
sourceConnected : False
sourceConnectorName : Delimited File
supportsEntitlementAggregation : true
externalId : 2c918086663fbbc0016641aa51041603
icon :
health : @{hostname=564c355e916f; lastSeen=1538699972555; org=orgName; healthy=True; lastChanged=1538699972555;
isAuthoritative=false; id=36777; type=C:173-delimited-file; status=SOURCE_STATE_UNCHECKED_SOURCE_NO_ACCOUNTS;
useForAuthentication : False
useForAccounts : False
useForProvisioning : False
useForPasswordManagement : False
iqServiceDownloadUrl :
entitlementsCount : 0
accountsCount : 0
hasValidAccountProfile : False
correlationConfig : @{attributeAssignments=; id=; name=}
sourceConfigFrom : Mantis Config: Cloud Connector
isAuthoritative : False
accessProfilesCount : 0
connector_delimiter : ,
connector_commentCharacter : #
connector_numberOfLinesToSkip :
connector_filterString :
cloudDisplayName : Privileged Access Management
cloudExternalId : 36777
cloudOriginalApplicationType : Delimited File
deleteThresholdPercentage : 10
file : /var/lib/identityiq_workspace/f8001b46-4fab-4e0b-ad15-18f53dc1507c-accounts.csv
filetransport : local
filterEmptyRecords : True
formPath :
group.columnNames : {id, name, displayName, created...}
group.delimiter : ,
group.file : /var/lib/identityiq_workspace/156524b6-9513-404c-8063-40275edfa575-groups.csv
group.filetransport : local
group.filterEmptyRecords : True
group.hasHeader : True : local
group.indexColumn : id
group.mergeColumns : {entitlements, groups, permissions}
group.mergeRows : True
group.partitionMode : disabled
hasHeader : True
host : local
indexColumn : id
managerCorrelationFilter :
mergeColumns : {groups}
mergeRows : True
partitionMode : disabled
templateApplication : DelimitedFile Template

PowerShell Example

The following will return the details for all sources in an IdentityNow Tenant where $orgName is the Organisation Name for your IdentityNow Tenant and IDNSources is the collection of Sources returned from the List Sources API call above.

foreach ($idnSource in $IDNSources){
    # Get Source Details
    $sourceInfo=Invoke-RestMethod-Method Get -uri "$($sourceDetailsURI)/$($"-WebSession $IDN

Getting the Schema of a Source


The API call shown above will return the Schema for the specified Source. Below is an example of the Schema for the same Delimited File Source File Source Type above.

attributes : {@{description=The unique ID for the account; displayAttribute=False; entitlement=False; identityAttribute=True; managed=False;
    minable=False; multi=False; name=id; type=string}, @{description=The name of the account - typical username etc;
    displayAttribute=True; entitlement=False; identityAttribute=False; managed=False; minable=False; multi=False; name=name;
    type=string}, @{description=The first or given name of the user associated with the account; displayAttribute=False;
    entitlement=False; identityAttribute=False; managed=False; minable=False; multi=False; name=givenName; type=string},
    @{description=The last, family name, or surname of the user associated with the account; displayAttribute=False; entitlement=False;
    identityAttribute=False; managed=False; minable=False; multi=False; name=familyName; type=string}...}
displayAttribute : name
groupAttribute : groups
identityAttribute : id
nativeObjectType : User
objectType : account

PowerShell Example

The following will return the schema for all sources in an IdentityNow Tenant where $orgName is the Organisation Name for your IdentityNow Tenant and IDNSources is the collection of Sources returned from the List Sources API call above.

foreach ($idnSource in $IDNSources){
   # Get Source Schema Details
   $sourceSchema=Invoke-RestMethod-Uri "$($sourceSchemaURI)/$($"-WebSession $IDN

Updating  the Details for a Source


The API endpoint above is called to update the Details for a Source. In conjunction with calling the API endpoint a Body needs to be provided to update the Source Details. Below is an example of updating the Owner and the Description of a Source.


  • The Content-Type needs to be updated for “application/x-www-form-urlencoded; charset=UTF-8”
  • You only need to specify the attributes you wish to change and append them to each other with the separator ‘&
  • The body with the udpate(s) needs to be URLEncoded. PowerShell Invoke-RestMethod handles that
$sourceUpdateURI = "https://$($orgName)"
$Global:IDN.Headers.Add('Content-Type', "application/x-www-form-urlencoded; charset=UTF-8")
$sourceDesscription = "CyberArk"
$sourceOwnerID = "1089912"
$sourceDetailsBody = "description=$($sourceDesscription)&ownerId=$($sourceOwnerID)"
$updateSource = Invoke-RestMethod -Method Post -Uri "$($sourceUpdateURI)/$($sourceID)" -Body $sourceDetailsBody -WebSession $Global:IDN

If you set a variable to the POST webRequest you get the updated object returned following a successful update. A snippet of the response is below. The version updates with each update.

id : 36666
version : 7
name : Privileged Access Management
description : CyberArk
owner : @{id=1089123; name=Bob Smith}
lastUpdated : 2018-10-23T01:15:26Z
scriptName : delimitedfile
definitionName : Delimited File


In this post I showed using PowerShell to access the Sources APIs to List Sources, Get full details for a Source, Get the Schema of a Source and Update the Details for a Source. In my next post I’ll show generating HTML Reports for the configuration of Sources.

Here is the snippet of the calls as listed in this post. As per the introduction it assumes you are authenticated and re-using your WebSession.

Azure Active Directory B2B Pending and Accepted User Reports

One of the benefits of Cloud Services is the continual enhancements that vendors provide based on feedback from their customers. One such item of feedback that Microsoft has heard often is the request to know what state a Guest user in Azure AD is in. In the last couple of days Microsoft exposed two additional attributes on the User objectClass in Azure AD;

  • externalUserState
  • externalUserStateChangeDateTime

B2B State Tweet.PNG

This means we can now query the Microsoft Graph for B2B users and understand if they have Accepted or are PendingAcceptance, and the datetime of the last change.

My customers have been wanting such information and would like to report on it. Here is an example PowerShell script I’ve put together that queries Microsoft Graph to locate B2B Users in the Accepted and PendingAcceptance states and outputs summary information into two HTML Reports. One for Accepted and one for PendingAcceptance.

Update the following script;

  • Line 2 for a Microsoft Azure PowerShell Module such as AzureAD that has the Microsoft.IdentityModel.Clients.ActiveDirectory.dll library in it
  • Line 5 for your Tenant name
  • Lines 15 and 16 for a Username and Password
  • Line 19 for where you want the Reports written to

Running the Script will then generate the Accepted and Pending HTML Reports.

Output Reports.PNG

Here is an example of the AcceptedB2BUsers HTML Report.

Accepted Report.png


With the addition of these two additional attributes to the Microsoft Graph we can now query for B2B users based on their State and using PowerShell quickly report on them.

Managing SailPoint IdentityNow Applications via API with PowerShell

The SailPoint IdentityNow Request Center comes pre-populated with 130 Applications (as shown below) that by default are visible to users in the Dashboard and can be requested via the Request Center. Whilst this is great the majority are not often applicable and you need to configure each individual application to remove visibility and requestablity. You could of course ask your IdentityNow Support representative to do this for you, or you could manage it yourself. Lets go with option B and I’ll show you how.

Application List.PNG

To disable visibility of an Application, and to also remove it from being requested through the Request Center there are two options that need to be toggled off. Enabled For Users, and Visible in the Request Center. 

Application Settings 1.PNG

Say you want to remove all from being visible and requestable. You will need to open each app, toggle the slider and the radio button and select save. That’s a minimum of 4 mouse clicks and some mouse scrolling x 130, or do it via the IdentityNow API in < 60 seconds. Option B please.

Retrieving Applications

The URI to return all IdentityNow Applications is


Before you can call that URI you will need to be authenticated to IdentityNow. Follow this post and make sure you have the headers in the WebSession configured with the Bearer Access Token.

Then using PowerShell you can return all Applications with;

$appList = Invoke-RestMethod -Uri $appListURI -Method Get -WebSession $IDN

If you want to find a single app, find it by name using Select-Object | Where-Object

$myApp = $appList | Select-Object | Where-Object {$ -eq "New York Times"}

The Application PowerShell Object for the New York Times looks like;

id : 24184
appId : 11
serviceId : 32896
serviceAppId : 24184
name : New York Times
description : American daily newspaper
appCenterEnabled : False
provisionRequestEnabled : False
controlType : PERSONAL
mobile : True
privateApp : False
scriptName : C:1-new-york-times
status : ACTIVE
icon :
health : @{status=HEALTHY; lastChanged=1539766560496; since=0; healthy=True}
enableSso : True
ssoMethod : PASSWORD
hasLinks : True
hasAutomations : True
primaryLink :
primaryMobileLink :
stepUpAuthData :
stepUpAuthType : NONE
usageAnalytics : False
usageCertRequired : False
usageCertText :
launchpadEnabled : False
passwordManaged : False
owner :
dateCreated : 1522393052000
lastUpdated : 1539766536000
defaultAccessProfile :
service : New York Times
selectedSsoMethod : PASSWORD
supportedSsoMethods : 2
authenticationCookie : []
directoryPassword_supported : false
none_supported : true
passwordReplay_supported : true
proxy_supported : false
saml_supported : false
wsfed_supported : false
accountServiceId : -1
launcherCount : 0
accountServiceName :
accountServiceExternalId :
accountServiceMatchAllAccounts : True
externalId :
passwordServiceId : -1

Removing Applications from User Visibility

Let’s remove all Applications from user visibility (and the Dashboard). The process is simply to retrieve all Applications, then update each one to toggle off the options for visibility. The following script does just that.

After updating each app the Request Center is empty. Much quicker than hundreds of mouse clicks.

Request Center Empty.PNG


With the ability to retrieve Applications and update them via the API repetitive configuration becomes super quick.

Obtaining Workday HR Supervisory Hierarchy, Provisioning Flags and Photos with PowerShell

A few weeks back I posted this regarding using PowerShell and the Granfeldt PowerShell Management Agent to interface Microsoft Identity Manager with Workday HR. The core of this functionality is the WorkdayAPI PowerShell Module which I forked from Nathan and added additional functionality.

New WorkdayAPI PowerShell Module Cmdlets

This post details additional functionality I’ve added to the WorkdayAPI PowerShell Module. Updates include the following additional cmdlets;


Implementations of Workday obviously vary from organisation to organisation. Workday HR being an authoritative source of identity means it is also often the initiator of processes for Identity Management. For one of our customers base entitlements are derived from Workday Job Profile and may specify an Active Directory Account and an Office 365 License.

The Get-WorkdayWorkerProvData cmdlet is invoked from when calling Get_WorkdayWorkerAdv with the -IncludeWork flag. The result now returns ProvisioningGroup information as part of the returned PowerShell Object. As shown below Workday has indicated my Identity requires Active Directory and Office 365 (email).

Workday Provisioning Group.PNG

If you just want to return the Account Provisioning information you can with Get-WorkdayWorkerProvData.
Get-WorkdayWorkerProvData -WorkerId 181123 -WorkerType Employee_ID
Provisioning_Group Status   Last_Changed
------------------ ------   ------------
Office 365 (email) Assigned 1/05/2017 9:31:21 PM
Active Directory   Assigned 1/05/2017 9:57:37 PM


The Get-WorkdayWorkerMgmtData cmdlet is invoked from when calling Get_WorkdayWorkerAdv with the -IncludeWork flag. The result now returns MgmtData information as part of the returned PowerShell Object. The collection is an ordered list of the Supervisory Hierarchy for the object.

Mgmt Data.PNG

Expanding the collection we can see the Supervisory Hierarchy. Note: the top of the hierarchy is listed twice as in this organisation the top reports to himself. 

Mgmt Heirarchy 2.PNG

If you just want to return the Management Hierarchy you can with Get-WorkdayWorkerMgmtData.

Get-WorkdayWorkerMgmtData -WorkerId 1234 -WorkerType Employee_ID


The Get-WorkdayWorkerPhoto cmdlet is invoked from when calling Get_WorkdayWorkerAdv with the -IncludePhoto and -PhotoPath flags. The result will then output the photo to the path provided in the -PhotoPath option.

If you just want to export the Photo for a single user you can with Get-WorkdayWorkerPhoto.

Get-WorkdayWorkerPhoto -WorkerId 1234 -WorkerType Employee_ID -PhotoPath 'C:\temp\workday'


Using the WorkdayAPI PowerShell Module we can now access information to drive the provisioning process as well as understand identities placement in the Supervisory hierarchy. We can also obtain their Workday Profile photo and sync that to other places if required.

Pi-Hole; Increasing my browsing speed and reducing my web data consumption

Why is my internet connection flooded and older devices slowing up?

Like any IT savvy person I have a wide range of devices that cross the spectrum of operating systems and hardware from Linux, PC, Mac and embedded operating systems (mostly Linux). A number are re-purposed for new tasks when their ability to perform their originally intended duties can no longer be efficiently fulfilled.

Think an 11-year-old 27″ iMac now predominantly used for web browsing, reading the news and research (it has been upgraded to SSD for its primary HDD), a 12-year-old IBM ThinkPad now running Ubuntu and providing internet access duties from the pilot brewery, a 5-year-old Lenovo Laptop now primarily used as a Slicer for 3D Print jobs; and another half-dozen or so similar stories.

However each has a bottle neck when accessing the internet. Page loads seem to be way slower than optimum usability. Combined with internet usage in my house with other devices managing media, internet connected door bells, security cameras, a couple of teenagers with multiple devices, gaming and social media etc, the internet connection often bursts to full capacity. The tweet below from a week ago, just randomly in time shows the internet connection usage at 75%.


A Solution

Taking advantage of being confined indoors on a Sunday and having an older Raspberry Pi Model B lying around I took the time to explore Pi-Hole that I’d recently been hearing some great reviews about. I already had an Ad-Blocker configured in my primary browser (Chrome) on a number of computers, but that wasn’t close to being 100% effective and that option wasn’t necessarily available for all the devices. Could Pi-Hole be the solution?

The premise of Pi-Hole is that it blocks the Advertisements that litter many websites (particularly News Websites). Pi-Hole becomes your local DNS Lookup proxy that blocks Advertisements and allows other DNS requests through.

The Setup

I got the latest Raspbian Image from Raspberry Pi Org and burned the image to the SD Card. I dropped a text file named ‘ssh’ onto the Boot partition to enable SSH straight away as I was going to be doing a headless install. Concise details are available here if you’re new to Raspberry Pi. After putting the SD Card into my Raspberry Pi, plugging in an Ethernet cable and powering it up I was halfway there.

For Pi-Hole I went the simple route of the fully automated install as detailed perfectly here. Going straight for Gold I updated my Unifi Networking Config to set the DHCP provided DNS Server to be the Pi-Hole Raspberry Pi. Within a couple of hours the majority of the devices on my network had picked up the new DNS address and started using Pi-Hole for filtering out unwanted Internet traffic.

Pi-Hole Dashboard Summary 1.PNG

Does Pi-Hole Work?

Using the oldest devices as the best indicators of the functionality I started browsing my commonly visited sites that have a plethora of advertisements. Sporting News, IT News, Social Media etc. Sure enough the Ubuntu driven brewery laptop that was always the most sluggish was slick and fast. Surprisingly fast. The 11-year-old iMac was also instantly more responsive. I then dug out a Surface RT device I’d been given as a reward many years ago that sits idle in a cupboard as its pretty much unusable. Hitting up News websites it too was more spritely than before, but would still only be considered everyday usable as a last resort. The graphic below is after having the Pi-Hole online for ~4 hours. Already close to 3800 DNS queries blocked equating to just under 25% of all queries. Think of it this way, that’s almost a quarter of the requests that would have gone to sites that return traffic, but with no value to me. WOW!!

Pi-Hole Dashboard Summary 3.PNG


Another wet Sunday has come to an end, I’ve had an opportunity to mess around with a new tool and put an old Raspberry Pi back to work and made a few of my older computers feel like new again. That’s pretty much win-win-win. Here is a summary of traffic from the Pi-Hole dashboard after only 9 hours. Needless to say it’ll remain part of my home network.

Pi-Hole Summary.png




Managing SailPoint IdentityNow Roles via API and PowerShell

Managing SailPoint IdentityNow Role Groups typically involves leveraging the SailPoint IdentityNow Portal for the creation and on-going management. That’s because the API for Roles is not published or documented.

What happens then if you have many to create, update/manage? How does the IdentityNow Portal use the non-published undocumented API’s to create and manage them? I’ve worked it out and am documenting it here in the interim until the API’s get versioned and published.

Note: There is a chance that the Roles API may change, 
so use at your own risk. 

Roles API Authentication

First up the Roles API URI looks like this.

The /cc/ part is a good indication that the API isn’t documented, but also that the Authentication mechanism to interact with it is using a JWT Bearer Token. I covered how to obtain a JWT Bearer Token specifically for interacting with these API’s in this post here. I’m not going to cover that here so read that post to get up to speed with that process.

Associated with managing the criteria of Roles we will also need to reference IdentityNow Sources. We can do that via a non-published API too.

Listing all IdentityNow Roles

Similar to Governance Groups that I detailed in this post Roles can be returned on an individual basis (if you know the ID of the Role which you won’t). So the simplest way is to query and return them all (no, the Search (BETA) doesn’t support Roles either). The API to list all roles is:

Doing this then via PowerShell to return all roles looks like this:

Roles = Invoke-RestMethod -Uri "" -WebSession $IDN
and finding Roles you are interested in can be done using Where-Object
$Roles.items | Select-Object | Where-Object {$_.displayName -like "Kloud*"}

SailPoint Roles.PNG

Creating Roles

The example script below shows the generation of information for a Role Object. Specifically;

  • name
  • displayname
  • description
  • disabled (whether the Role is enabled or disabled)
  • owner (the IdentityNow NAME attribute value of the owner of the Role)

Executing this creates the Role.

Role Created.PNG

Looking in the Portal we can then see the Role.

Role Created in Portal.PNG

Managing Role Criteria

The Role Criteria will obviously depend on what your criteria is. Is it based on Standard Group Rules, a List of Identities or Custom? I’ll cover a List of Identities and Group based criteria.

Adding a List of Identities to a Role

This isn’t too dis-similar to Governance Roles. We search for the list of users we want to add to the Role and add them to a collection. The key difference is that we use Name instead of ID. We then specify the type of Criteria (Identity_List) and the ID of the Group to update.

Executing the Script

The example script below switches the Headers for Basic Auth to use the v2 endpoint to search and locate the users to add to the Role. It then switches the Headers over to JWT Bearer Auth, generates the body for the request to update the Role Group and updates the Role Group.

Executing the script looks as per below and runs through successfully.

Adding IdentityList Members to Role Group.PNG

Checking the Role Group in the IdentityNow Portal

Identity List Updated - Portal.PNG

Adding a Members Based on Criteria to a Role

Adding users to a Role based on Criteria is similar to above except rather than searching for Identities we are adding the criteria for a role.

The criteria can be based on an Identity Attribute an Account Attribute or an Entitlement. I’ll show using an Identity Attribute and an Account Attribute.

Account Attribute based Criteria

Account criteria is based on attributes available in the Schema of a Source. In order to reference the Source we need the ID of the Source. This is available via the non-published Source API cc/api/source

The example script below shows getting all Sources and finding the one you want to base your Role Criteria off (based on name). You can of course mix criteria across many sources, but I’m showing you doing it from one. Running the following script will return your Sources. You require the ID of the Source you want to leverage attributes from to base your criteria off. The attribute that contains this used further along in these examples is $RoleSource 

Now that we have the Source ID for the Source that contains the attribute that we want to base our Role Criteria on, we need to build the criteria. Let’s start with a simple one and Single Criteria.

Here is an example JSON document with a Single Criteria that contains the variables for the Role that we just created (Role ID), the Source ID for the Source with the attribute we are using for the criteria and the value we want to match (RoleCriteriaValue). The Attribute from my Source that I’m using is Supplier. Update for the attribute you’re using on your Source.

After converting the JSON object to a PowerShell Object using ConvertFrom-JSON it looks like this:
Single Role Criteria.PNG

Having it as a PowerShell Object also makes it easy to modify. If you wanted to change the criteria to match against, the Operator and/or the operation then just change the value. e.g. the following will change the value to match from “Kloud Solutions Pty Ltd” to “Darren J Robinson

$RoleCriteria.selector.complexRoleCriterion.children.value = "Darren J Robinson"

Convert back to JSON for Post via the webrequest to the API. That and updating the Role with the criteria thereby is;

Update Single Criteria.PNG

In the Portal we can see that the Criteria has been added.

Update Single Criteria - Portal.PNG

And you will notice I have a Role Refresh call after the update to update the Role Membership as it is now based on Criteria. If you are updating/adding criteria to numerous roles only call the Refresh at the end.

Role Refresh.PNG

Adding Multiple Criterion

Multiple criterion is just more information to pass to the update. The format can be a little confusing so here is a template for three criteria showing the three Operators (Equals, Contains and Not_Equals).

$RoleCriteria = "{`"id`":`"$($`",`"accessProfileIds`":[],`"selector`":{`"type`":`"COMPLEX_CRITERIA`",`"entitlementIds`":[],`"aliasList`":[],`"valueMap`":[],`"complexRoleCriterion`":{`"operation`":`"OR`",`"children`":[{`"operation`":`"AND`",`"children`":[{`"operation`":`"EQUALS`",`"key`":{`"type`":`"ACCOUNT`",`"property`":`"attribute.Supplier`",`"sourceId`":`"$($RoleSource)`"},`"value`":`"$($RoleCriteriaValue)`"},{`"operation`":`"NOT_EQUALS`",`"key`":{`"type`":`"ACCOUNT`",`"property`":`"`",`"sourceId`":`"$($RoleSource)`"},`"value`":`"New Zealand`"},{`"operation`":`"CONTAINS`",`"key`":{`"type`":`"ACCOUNT`",`"property`":`"`",`"sourceId`":`"$($RoleSource)`"},`"value`":`"`"}]}]}}}"

When converted to a PowerShell Object and displayed it looks like this;

3 Account Criteria Options.PNG

Identity Attribute based Criteria

The last example is Identity Attribute Criteria. This is simpler than the Account Attribute Criteria as we just need to reference the Identity Attribute and don’t need to reference a Source. A single criteria for isEmployee  = True looks like this;


As a second Criteria Group though in the Portal it looks like this;

2nd Criteria Group.PNG

and for the JSON object (RAW without variablizing) looks like this;

"{`"id`":`"2c918086663fbbd0016612345678909876`",`"accessProfileIds`":[],`"selector`":{`"type`":`"COMPLEX_CRITERIA`",`"entitlementIds`":[],`"aliasList`":[],`"valueMap`":[],`"complexRoleCriterion`":{`"operation`":`"OR`",`"children`":[{`"operation`":`"AND`",`"children`":[{`"operation`":`"EQUALS`",`"key`":{`"type`":`"ACCOUNT`",`"property`":`"attribute.Supplier`",`"sourceId`":`"2c91808365f742620165f9ba0e831bf8`"},`"value`":`"Kloud Solutions Pty Ltd`"},{`"operation`":`"NOT_EQUALS`",`"key`":{`"type`":`"ACCOUNT`",`"property`":`"`",`"sourceId`":`"2c91808365f742620165f9ba0e831bf8`"},`"value`":`"New Zealand`"},{`"operation`":`"CONTAINS`",`"key`":{`"type`":`"ACCOUNT`",`"property`":`"`",`"sourceId`":`"2c91808365f742620165f9ba0e831bf8`"},`"value`":`"`"}]},{`"operation`":`"EQUALS`",`"key`":{`"type`":`"IDENTITY`",`"property`":`"attribute.isemployee`"},`"value`":`"true`"}]}}}"


Using the cc/api/role APIs we can Create Role Groups, Update Roles Groups to contain Criteria and Refresh Role Groups to have the membership calculated. With the base functionality detailed we can now use this to create and manage our Role Groups through automation. Happy Days.

Managing SailPoint IdentityNow Governance Groups via the API with PowerShell

In this post I detail the management of SailPoint IdentityNow Governance Groups using the IdentityNow v2 API as the functions associated with Governance Groups is not currently detailed in the v2 API Documentation here (9 Oct 2018).

In order to interact with the v2 API you will need to use Basic Authentication which I detail in this post here. The common authentication/authorization piece from that post is also shown below.

Retrieving Governance Groups

Now that you’re authorized to IdentityNow using Basic Authentication we can look to retrieve Governance Groups. This can be achieved by calling the /v2/workgroups API.

Using PowerShell all Governance Groups can be returned by making the following API call.

$GovGroups = Invoke-RestMethod -Method Get -Uri "$($baseURI)/v2/workgroups?&org=$($orgName)" -Headers @{Authorization = "Basic $($encodedAuth)"}

To retrieve an individual group you need to know the ID of the Group. You can then retrieve it directly using the v2/workgroups API  e.g.
Doing that via PowerShell looks like this:
Invoke-RestMethod -uri "" -Method Get -Headers @{Authorization = "Basic $($encodedAuth)"}

It would be nice to search Governance Groups using the new Search (BETA) feature. But currently the only Groups that are returned via it are Entitlement Groups.

Searching for Governance Groups

As mentioned above the new Search Beta only returns Entitlement Groups. Retrieving Governance Groups via the Governance Group ID is fine, if you know it (which you won’t).  So here is my workaround for this. Retrieve all Governance Groups as detailed above using PowerShell and then use the power of PowerShell (Where-Object) to search and find the group you want.

$GovGroups = Invoke-RestMethod -Method Get -Uri "$($baseURI)/v2/workgroups?&org=$($orgName)" -Headers @{Authorization = "Basic $($encodedAuth)"}
$myGovGroup = $GovGroups | Select-Object | Where-Object {$_.description -like "Kloud*"}

The above looks through each of the Governance Groups to find the ones that contain the word Kloud in the Description field. 53 Groups returned and 2 meet the criteria.

Search Governance Groups.PNG

Creating IdentityNow Governance Groups

To create a Governance Group you will/should provide:

  • IdentityNow Governance Group
    • name (e.g Vendor XX)
    • description (e.g Vendo)
    • owner
      • displayName (e.g Vendor Admin)
      • emailaddress (e.g
      • id (eg 2c918084624f8b59016275c123456789)
      • name (e.g Vendor_Admin)

My approach is;

  • Use the Search API to search and find the user account that will be the Owner for the Governance Group
  • Create the Governance Group assigning the Owner

Here is an example of creating a single Governance Group implementing the approach above.

Executing the script successfully creates the group.

Create Governance Group.PNG

Looking at the Group in the Portal I can see that it has been created with the correct owner.

Governance Group Created

Now if you are like me and you have numerous Governance Groups to create you can of course have a list of Governance Groups to be created and loop through creating each one. Brilliant.

Managing Members of Governance Groups

Updating a group for membership is a simple case of sending through a collection of members to add/remove. This isn’t a replace operation but an addition. So if you want to add a single member, just send through the details to add that member and they will be added to the Governance Group. Likewise for removal.

Process overview;

  • taking a group that was just created we use the ID of that group to then update the membership
  • searching IdentityNow we find the members to add
  • generate the collection to add
  • update the group

Here is a sample wscript the performs that process.

And looking at the Governance Group in the IdentityNow Portal we can see the membership has been updated. Obtaining the Group (using the search method above) also allows for easy removal of all/any members.

Members Added to Governance Group.PNG


Using the v2/workgroups IdentityNow API we can create and manage Governance Groups. This is extremely powerful when you have many to create and manage.

Leveraging v1, v2 and non-Published SailPoint IdentityNow API’s with PowerShell

This post supersedes my previous posts on leveraging the IdentityNow API’s in relation to API Authentication/Authorization;

Using this Compass document as my guide (which takes a bit of finding) I’ve automated the process of being able to use PowerShell to leverage the non versioned/published API’s. The method I show below is also valid for the documented and versioned API’s.

I show how I generate the necessary credentials required for:

  • IdentityNow Basic Authentication
  • IdentityNow Cookie Authentication
  • IdentityNow JWT oAuth Bearer Authentication

I also show working calls using Basic and JWT oAuth Authentication. The CCSessionID is extracted for use in Cookie Authentication for completeness, but everything can be accomplished using Basic and JWT oAuth Authentication. This document on SailPoint Compass provides more details on each.

Script Overview

Using PowerShell I;

  • authenticate to the IdentityNow Portal
  • elevate via authentication to the Admin section of the IdentityNow Portal
  • obtain the CCSESSIONID Cookie and the CSRF Token
  • provide the ability to add into the Web Session Header IdentityNow Basic or JWT oAuth Authentication credentials

That then allows access to the non-published API’s that are leveraged via the IdentityNow Portal and published API’s.

Obtaining the CSRF Token, JWT Access Token and Session Cookie Programatically with PowerShell

Update the Strong Authentication Methods for IdentityNow Admins

As we need to programatically authenticate to the Portal itself to get the necessary information required for API authorization, we need a mechanism that supports that. The only one that does easily is password. So you will need to enable that.

  • NOTE: chances are you’re using programmatic access to configure/ingest data or configuration as part of service enablement. Once that is done, REMOVE “re-entering password” as a Strong Authentication Method.

In the IdentityNow Portal go to Admin => Identities => Identity Profiles => IdentityNow Admins =>Enable By re-entering their password

Admin Centre Strong Auth Options.PNG

Using a Chrome Browser head to the IdentityNow Login screen for your Tenant. e.g. Press F12 for Developer Tools and then enter the Username and Password for an Admin account. Press the Record button (highlighted below, if it isn’t already red) to start recording and then press the Sign In button.

Portal Login Window

When the browser has logged you in, select the Network tab and then right-click on the ‘login’ entry in the trace. Select Copy and then Copy as PowerShell which will copy the webrequest that you just performed to login. Paste this into the script below on Line 25.

WebRequest Login

Next in the browser select Admin and then any of the Admin menu items. e.g. Dashboard => Overview. Select Enter password and check Remember my preference then Continue.

Record the Strong Auth Screen.PNG

The next screen will give you the prompt to supply your password again. Enter the password and then clear the traces from the Developer Tools pane and then make sure Record is enabled, then press Authenticate.

Record Password 2nd Screen.PNG

Select the strongAuthn trace from the Network tab, right-click and select Copy and then Copy as PowerShell. Paste this into the script below on Line 43. Don’t forget to edit it for the CSRF Token variable.  

Strong Auth Capture.PNG

The Script

Update the script below for your environment by:

  • Line 3 with your IdentityNow Organisation name
  • Line 9 with your IdentityNow API Client ID
  • Line 11 with your IdentityNow API Client Secret
  • Line 16 with your Admin login name for the IdentityNow Portal
  • Line 25 with the first PowerShell Webrequest you copied above
  • Line 43 update with the second PowerShell Webrequest you copied above. Make sure to go back and add in the variable for the CSRF Token as shown below. $($orgName) is optional.
    • example $admPortalAuthN = Invoke-WebRequest -Uri $adminStrongAuthURI -Method “POST” -Headers @{“Origin” = “https://$($orgName)”; “Accept-Encoding” = “gzip, deflate, br”; “X-CSRF-Token” = $($csrf); “Accept-Language” = “en-US,en;q=0.9”; “User-Agent” = “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36”; “Accept” = “*/*”; “Referer” = “https://$($orgname)”; “X-Requested-With” = “XMLHttpRequest”} -ContentType “application/x-www-form-urlencoded; charset=UTF-8” -Body “password=f2dbeb5a897abeeecafc9deee5612345678903456789bdedf7d42” -WebSession $IDN

Output from the Script

The screenshot below shows successfully making API calls to IdentityNow across the v1, v2 and the non versioned API’s after generating the necessary Authentication credentials.

Note that after calling the v1 and non versioned API’s I change the Headers to remove the Bearer Authorization Token and replace it with the Basic Authorization Token so that the v2 API’s can be called.

Examples Output.PNG


Optional: Obtaining the CSRF Token Manually

If you do want to obtain the CSRF Token manually then using a web browser.

  • Login to IdentityNow Portal main Dashboard with an Admin account
    • A direct URI looks like this;
  • Head to the Admin Dashboard and you will be prompted for Strong Authentication
    • At this point though you will also be able to retrieve the CSRF Token

Using the Chrome Developer Tools (e.g. pressing F12 in Chrome) select Console, then at the prompt type SLPT.CSRFToken You will then see your CSRF Token for that session.

CSRFToken via Browser.PNG


With the ability to programatically generate the necessary Authorization tokens for IdentityNow we can interact with the documented API’s along with the API’s that are being leveraged by the IdentityNow Admin Portal itself.

Querying for updates/changes in Workday HR using PowerShell

Nathan Hartley has an awesome PowerShell Module for Workday that you can find here. I detailed how I’m using that module in this post here Building a Microsoft Identity Manager PowerShell Management Agent for Workday HR.

A large portion of that post detailed the nuances of working with the Worday API especially for implementations at scale. Those are constraints I have. Specifically I was looking for a couple more functions;

  • Changes since the last time I queried the API
  • Changes including those who are now Inactive workers*
  • in the summary PowerShell Object return Hire Date, Start Date, Active Status and Supplier

Not wanting to re-invent the wheel I forked Nathan’s Project and added those enhancements. The Github link is here

Querying changes from Workday using PowerShell

The following assumes you’ve imported the WorkdayAPI PowerShell Module and provided connection information for your Workday Tenant. e.g

The following will then give you a summary of the changes in the last 28 days.
Update line 35 to change this date range.

The output looks like this:

Delta Changes.PNG

To query for Contingent Workers updated within a date range (e.g. the last 28 days) use use the new Get-WorkdayWorkerAdv cmdlet with the -FromDate and -ToDate as shown below.

$datenow = Get-Date
$now = $datenow.AddMinutes(-1).ToString('o')
$from = $datenow.AddDays(-28).ToString('o')
$contractors = Get-WorkdayWorkerAdv -WorkerType Contingent_Worker_ID -FromDate $from -ToDate $now

Querying Inactive Users from Workday using PowerShell

-ExcludeInactive is the new switch. The default is still true. To return Inactive workers set it to false. e.g -ExcludeInactive ‘false’. This is exactly the same as the -Force* switch, it is just a bit more obvious for me.

$contractor = Get-WorkdayWorkerAdv -WorkerType Contingent_Worker_ID 123456 -ExcludeInactive 'false'

However another change is to return additional information for the worker such as Hire_Date, Start_Date, Active (boolean) and Supplier (for Contractors/Contingent Workers).

Extended PSObject Attributes.PNG


With a few modifications I’ve been able to get the additional information I require from Workday to drive a number of business functions. Having Nathan’s module and therefore working examples made it very easy to get up to speed with the Workday WebService. Thank’s Nathan.


Azure Sphere – Initial Setup, Configuration and First Impressions

In April this year, Microsoft announced Azure Sphere. This was the same week as I’d be preparing for a presentation I was giving on Azure IoT at the Sydney location for the Global Azure Bootcamp. When pre-orders became available from Seeed Studio I naturally signed up as I’ve previously bought many IoT related pieces of hardware from Seeed Studio.

Fast forward to this week and the Azure Sphere MT3620 device shipped. It’s a long weekend here in Sydney Australia and delivery wasn’t due until after the long weekend, but by some miracle the packaged was delivered on the Friday by DHL after only leaving China 3-4 days earlier.

What a great opportunity then to un-box it, get it configured and build the sample “Hello World” (Blinky) project.

Getting Started

Following the “Get Started Guide” here I was straight away perplexed as to why Visual Studio was required, when I’ve made the complete transition to Visual Studio Code.

It seems there isn’t support in the IoT Workbench Extension in VS Code for the MT3620 yet.

Azure IoT Workbench.PNG

After patching and updating my now out-of-date Visual Studio installation I was finally able to install the VS Tools for Azure Sphere.

Azure Sphere VS Tools.PNG

which also comes with the TAP Driver for communicating with the device via the USB port, which is necessary for setup.

TAP Driver.PNG

With that all done it needs to be connected to Azure Active Directory. For that I created a new user for use with Azure Sphere in my Azure AD Tenant and then proceeded to login to Azure AD with that account.

azsphere login

azsphere login.PNG


Successfully logged in (if you try with a Microsoft Account you’ll get a message indicating Azure AD is required), it prompts you to create an Azure Sphere Tenant.

Create Tenant

NOTE: Claiming the Device

Claiming the Device.PNG

With the Azure Sphere Device connected the Windows 10 computer you are executing the command from, as this is the first time setup an Azure Sphere Tenant needs to be created and the device claimed.

azsphere tenant create --name 
azsphere device claim

Claim Device.PNG

Connecting to Wifi

With the Azure Sphere Tenant created and the device claimed its time to connect it to Wifi.

azsphere device wifi show-status
azsphere device wifi add --ssid  --key

Connect Azure Sphere to Wifi.PNG

Checking the Wifi Connection Status after connecting provides the device connection status.

azsphere device wifi show-status

Azure Sphere Wifi Status.PNG

Checking the Azure Sphere OS Version against what is available shows it’s on the latest.

azsphere device show-ota-status

Azure Sphere OS Version.PNG

Blink Example Project

With the device now configured it was time to try out the sample project. Again following the instructions I first Enabled Debugging.

azsphere device prep-debug

Enable Azure Sphere Debugging.PNG

Following the example as per the Getting Started Guide I built the Blink Example project.

New Project Azure Sphere Blink Example.PNG

and ran it. It all worked as per the instructions. Pressing the A button with debugging enabled allow the state of the device (button) to be read and output.

Blink Example with Debugging.PNG


The setup was very quick, completely painless and just worked. So initial impressions are positive. My only gripe is that the Azure IoT Workbench Extension for VS Code doesn’t support the hardware. I’m hoping that comes soon.

Now to build something with it. What to build ……..