Harnessing the Power of Pipelines and the PowerShell SDK

Background

PowerShell is a powerful language that can be executed on any OS nowadays, Windows of course, but also Linux and MacOS.

It has a powerful pipeline concept. Back in the day (previous century), Linux users were having a lot of fun with command line “piping” commands like cat, grep, awk, sed, tr, etc.

PowerShell is superior to this approach in a way because PowerShell can pipe objects instead of just strings.

We will see how to benefit from pipes in practical examples using the PowerShell SDK.

Examples

View sources

Let’s say I want to get all source ids, ordered by name:

❯ Get-V2024Sources |  select name, id | sort name

name                                    id
----                                    --
Active Directory                        da8b3d55ba26439b90caabb9f3c4e6c1
Airgap Application                      501c51c32bf641308ce5d3fa10730fd1
Badging                                 03b2420339544c76a028b2680fb0ff7f
PRISM                                   bec1159ffd0d457080afc3355dafbb82
TRAKK-WS                                4d235626b57b4a43b732f44e5b896c01
...

select (which is actually the alias for Select-Object) allows getting properties of objects passed to it.

sort (which is actually the alias for Sort-Object) allows sorting of objects based on a property.

Another interesting cmdlet is Where-Object, or its alias ?:

❯ Get-V2024Sources | ? type -like "Active Directory*"    

Name                           Value
----                           -----
description                    Active Directory Users
owner                          {[type, IDENTITY], [id, 8430ce98aa5441f09f6204a5700bf46b], [name, yannick.beot]}
cluster                        {[type, CLUSTER], [id, c67e76b7de49457b8652adb44594d964], [name, AWS Cluster]}
...

This allows us to have a quick and efficient way of filtering the data “client side.” Of course, if possible, it’s better to rely on filters for server-side filtering.

Update Entitlements in Bulk

Get Entitlements

First, let’s get the entitlements:

$Parameters = @{
    "Filters" = 'source.id eq "b4f1d383e2be4a3f92291fbdc89ece51"'
}

# List entitlements
$entitlements = Invoke-Paginate -Function "Get-V2024Entitlements" -Parameters $Parameters

Here, I’m using a variable to save the entitlements, as I want to reuse it several times. I could have piped the Invoke-Paginate cmdlet to another cmdlet. But let’s check if I got what I wanted with the cmdlet Measure-Object.

❯ $entitlements | Measure-Object

Count             : 3524
Average           :
Sum               :
Maximum           :
Minimum           :
StandardDeviation :
Property          :

3524 entitlements. That’s perfect.

Mark Entitlement as Requestable

Make a Chunk of 50

I want to mark all these entitlements as requestable. Identity Security Cloud has a “bulk update” for entitlements available. But first, I need to get “chunks” of entitlements, a set of 50 max entitlements.

I will use the following function I have found on Stackoverflow

function Split-Collection {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object[]] $InputObject,

        [Parameter(Position = 0)]
        [ValidateRange(1, [int]::MaxValue)]
        [int] $ChunkSize = 50
    )

    begin {
        $list = [System.Collections.Generic.List[object]]::new()
    }
    process {
        foreach($item in $InputObject) {
            $list.Add($item)
            if($list.Count -eq $ChunkSize) {
                $PSCmdlet.WriteObject($list.ToArray())
                $list.Clear()
            }
        }
    }
    end {
        if($list.Count) {
            $PSCmdlet.WriteObject($list.ToArray())
        }
    }
}

The parameter InputObject is actually using the property ValueFromPipeline. This means that the value can be passed through the pipeline as an input.

There are various ways to write to the pipeline as an output. Here the function is using $PSCmdlet.WriteObject to push objects to the pipeline. Specifically, it will push the array of 50 objects max.

Set Requestable

I will now define a new function to update the requestable flag:

Function Set-Requestable {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, ValueFromPipeline)]
    [string[]]$EntitlementIds
  )

  PROCESS {
    
    $EntitlementBulkUpdateRequest = [PSCustomObject]@{
      "entitlementIds" = $EntitlementIds
      "jsonPatch"      = , @{
        "op"    = "replace"
        "path"  = "/requestable"
        "value" = $true
      }
      
    }
    Update-V2024EntitlementsInBulk -EntitlementBulkUpdateRequest $EntitlementBulkUpdateRequest
  }

}

$EntitlementIds will be my chunk of 50 entitlements and can be passed through the pipeline. How convenient!

The “body” of the cmdlet must be passed in PROCESS to be executed for each chunk.

Plumbing

Now, let’s use all these cmdlets and pipes. But first, a small test of 5 entitlements only:

$entitlements | select -First 5 -ExpandProperty id | Split-Collection | Set-Requestable

Again, I’m using select to “extract” the id from the entitlements. As I’m using the parameter -First 5, only the first 5 entitlement ids will be pushed to the pipeline. And now that I know it’s working, let’s try it for all entitlements:

$entitlements | select -ExpandProperty id | Split-Collection | Set-Requestable

This is equivalent to this beautiful one-liner:

Invoke-Paginate -Function "Get-V2024Entitlements" -Parameters @{"Filters" = 'source.id eq "b4f1d383e2be4a3f92291fbdc89ece51"'} | select -ExpandProperty id | Split-Collection | Set-Requestable

Update the Entitlement Request Config

It’s not possible to update the Entitlement Request Config in bulk (see put-entitlement-request-config | SailPoint Developer Community).

Set Entitlement Request Config

As for the requestable flag, we could have written a small function and passed my list of entitlements in the pipeline.

I will adopt another approach here by using ForEach-Object, whose alias is %.

When using ForEach-Object, the object in the pipeline is passed to the argument $_:

# Define approval by manager for all of my entitlements
$EntitlementRequestConfig = [PSCustomObject]@{
  "accessRequestConfig" = @{
    "approvalSchemes" = , @{
      "approverType" = "MANAGER"
    }
  }
}

# Test First
$entitlements | select -First 5 | % {
    Send-V2024EntitlementRequestConfig -Id $_.id -EntitlementRequestConfig $EntitlementRequestConfig
}

Again, this can be a one-liner. This is equivalent to this beautiful one-liner:

Invoke-Paginate -Function "Get-V2024Entitlements" -Parameters @{"Filters" = 'source.id eq "b4f1d383e2be4a3f92291fbdc89ece51"'} | % { Send-V2024EntitlementRequestConfig -Id $_.id -EntitlementRequestConfig $EntitlementRequestConfig } | Out-Null

As the cmdlet Send-V2024EntitlementRequestConfig is returning the updated config data I don’t need, I will just ignore it with Out-Null

Conclusion

The use of pipelines in PowerShell allows us to have a concise and powerful suite of commands, relying on reusable cmdlets. It allows all sort of transformation and processing without relying on for-loops.

Happy PowerShell!

6 Likes