Providers/Okta/Public/New-OktaGroup.ps1
|
function New-OktaGroup { <# .EXTERNALHELP TestEnvironment-Help.xml .SYNOPSIS Creates the seeded Okta groups and assigns their members #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([PSCustomObject])] param( [Parameter()] [string[]]$GroupName, [Parameter()] [switch]$SkipMemberAssignment, [Parameter()] [switch]$PassThru ) $connection = Get-OktaConnection $csvPath = Join-Path -Path (Get-OktaDataPath) -ChildPath 'OktaGroups.csv' $rows = @(Import-Csv -Path $csvPath -Encoding UTF8) if ($GroupName) { $rows = @($rows | Where-Object { $GroupName -contains $_.Name }) $unknown = @($GroupName | Where-Object { $rows.Name -notcontains $_ }) if ($unknown) { throw "No definition in $csvPath for: $($unknown -join ', ')" } } $result = [PSCustomObject]@{ TotalGroups = $rows.Count CreatedGroups = 0 UpdatedGroups = 0 MembersAdded = 0 Groups = @() Errors = @() } # One listing up front rather than a lookup per member. Membership is expressed as logins # in the CSV and ids in the API, and at eight users the whole mapping costs one call. $userIdByLogin = @{} if (-not $SkipMemberAssignment) { $seeded = @(Get-OktaSeededUser -Prefix $connection.Prefix ` -EmailDomain $connection.EmailDomain) foreach ($user in $seeded) { $userIdByLogin[$user.profile.login] = $user.id } } $groups = [System.Collections.Generic.List[object]]::new() foreach ($row in $rows) { # The display name is what goes to Okta, so the accented names actually reach the # directory. The CSV Name column stays ASCII and is the key that rules and member # lists refer to. $oktaName = '{0}-{1}' -f $connection.Prefix, $row.DisplayName $description = '{0} {1}' -f $row.Description, $connection.SeedMarker if (-not $PSCmdlet.ShouldProcess($oktaName, 'Create Okta group')) { continue } try { $existing = @(Invoke-OktaRequest -Method GET -Path '/api/v1/groups' ` -Query @{ q = $oktaName; limit = 50 } -Paginate | Where-Object { $_.profile.name -eq $oktaName }) if ($existing.Count -gt 0) { $group = Invoke-OktaRequest -Method PUT -Path "/api/v1/groups/$($existing[0].id)" ` -Body @{ profile = @{ name = $oktaName; description = $description } } $result.UpdatedGroups++ Write-Verbose "Updated group $oktaName" } else { $group = Invoke-OktaRequest -Method POST -Path '/api/v1/groups' ` -Body @{ profile = @{ name = $oktaName; description = $description } } $result.CreatedGroups++ Write-Verbose "Created group $oktaName" } $assigned = @() if (-not $SkipMemberAssignment) { foreach ($memberPrefix in @($row.Members -split ';' | Where-Object { $_ })) { $login = '{0}@{1}' -f $memberPrefix, $connection.EmailDomain if (-not $userIdByLogin.ContainsKey($login)) { $message = "Group '$oktaName' lists '$login', which does not exist. Skipped." $result.Errors += $message Write-Warning $message continue } # PUT is idempotent here: adding a user who is already a member returns # 204 rather than an error, so a re-run does not need a membership check. $null = Invoke-OktaRequest -Method PUT ` -Path "/api/v1/groups/$($group.id)/users/$($userIdByLogin[$login])" $assigned += $login $result.MembersAdded++ } } $groups.Add([PSCustomObject]@{ Id = $group.id Key = $row.Name Name = $oktaName Category = $row.Category Assignment = $row.Assignment Members = $assigned }) } catch { $message = "Failed to create group '$oktaName': $($_.Exception.Message)" $result.Errors += $message Write-Error $message } } $result.Groups = $groups.ToArray() Write-Verbose ("Groups: $($result.CreatedGroups) created, $($result.UpdatedGroups) updated, " + "$($result.MembersAdded) memberships, $($result.Errors.Count) problems") if ($PassThru) { return $result } } |