Providers/PingOne/Public/New-PingOneUser.ps1
|
function New-PingOneUser { <# .EXTERNALHELP TestEnvironment-Help.xml .SYNOPSIS Creates the seeded people in their populations, tags them, and applies their group memberships #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([PSCustomObject])] param( [Parameter()] [string[]]$Username, [Parameter()] [ValidateSet('Core', 'Bulk')] [string[]]$Tier, [Parameter()] [switch]$ShowProgress, [Parameter()] [switch]$PassThru ) $connection = Get-PingOneConnection $marker = Get-PingOneSeedMarker -Prefix $connection.Prefix $attributeName = $script:PingOneSeedAttributeName $rows = @(Import-Csv -LiteralPath (Join-Path (Get-PingOneDataPath) 'PingOneUsers.csv') -Encoding UTF8) if ($Tier) { $rows = @($rows | Where-Object { $Tier -contains $_.Tier }) } if ($Username) { $rows = @($rows | Where-Object { $Username -contains $_.Key }) } # A population's display name comes from the populations file rather than from its key, # because 'empty-hold' is 'Offboarding Hold' and no casing rule turns one into the other. $populationNameByKey = @{} foreach ($populationRow in (Import-Csv -LiteralPath (Join-Path (Get-PingOneDataPath) 'PingOnePopulations.csv') -Encoding UTF8)) { $populationNameByKey[$populationRow.Key] = Resolve-PingOneSeedName -Key $populationRow.Name ` -Kind DisplayName -Connection $connection } # Populations and groups, looked up once by the name the seed gave them. $populationId = @{} foreach ($population in (Invoke-PingOneRequest -Method GET -Path 'populations' -Paginate -Connection $connection)) { $populationId[[string]$population.name] = $population.id } $groupRows = @{} foreach ($groupRow in (Import-Csv -LiteralPath (Join-Path (Get-PingOneDataPath) 'PingOneGroups.csv') -Encoding UTF8)) { $groupRows[$groupRow.Key] = $groupRow } $groupByName = @{} foreach ($group in (Invoke-PingOneRequest -Method GET -Path 'groups' -Paginate -Connection $connection)) { $groupByName[[string]$group.name] = $group } $created = [System.Collections.Generic.List[object]]::new() $reused = [System.Collections.Generic.List[object]]::new() $errors = [System.Collections.Generic.List[string]]::new() $membershipsApplied = 0 $index = 0 foreach ($row in $rows) { $index++ $login = Resolve-PingOneSeedName -Key $row.Key -Kind Username -Connection $connection Write-TestProgress -Activity 'Creating PingOne users' -Status $login ` -PercentComplete ([int](100 * $index / [Math]::Max(1, $rows.Count))) -ShowProgress:$ShowProgress $populationName = $populationNameByKey[$row.Population] if (-not $populationName -or -not $populationId.ContainsKey($populationName)) { $errors.Add("Population '$populationName' for $login does not exist; run New-PingOnePopulation first") continue } $existing = @(Invoke-PingOneRequest -Method GET -Path 'users' -Connection $connection -Paginate ` -Query @{ filter = ('username eq "{0}"' -f $login) }) $user = $null if ($existing) { Write-Verbose "User $login already exists; reusing" $user = $existing[0] $reused.Add([PSCustomObject]@{ Key = $row.Key; Username = $login; Id = $user.id }) } else { if (-not $PSCmdlet.ShouldProcess($login, 'Create PingOne user')) { continue } $body = @{ username = $login email = Resolve-PingOneSeedName -Key $row.Key -Kind Email -Connection $connection population = @{ id = $populationId[$populationName] } enabled = [bool]::Parse($row.Enabled) name = @{} } if ($row.GivenName) { $body.name['given'] = $row.GivenName } if ($row.FamilyName) { $body.name['family'] = $row.FamilyName } if ($row.Title) { $body['title'] = $row.Title } $body[$attributeName] = $marker.Tag if ($row.BadgeId) { $body['labBadgeId'] = $row.BadgeId } $entitlements = @(($row.Entitlements -split ';') | Where-Object { $_ }) if ($entitlements) { $body['labEntitlements'] = $entitlements } # Text on purpose, and lower case: see the description. $body['labContractor'] = $row.Contractor.ToLowerInvariant() if ($row.Department) { $body['labProfile'] = @{ department = $row.Department; tier = $row.Tier } } try { $user = Invoke-PingOneRequest -Method POST -Path 'users' -Body $body -Connection $connection $created.Add([PSCustomObject]@{ Key = $row.Key; Username = $login; Id = $user.id }) } catch { $errors.Add("Could not create user ${login}: $($_.Exception.Message)") Write-Warning "Could not create user ${login}: $($_.Exception.Message)" continue } # MFA is off on a created user and has its own endpoint rather than a field. if ([bool]::Parse($row.MfaEnabled)) { try { $null = Invoke-PingOneRequest -Method PUT -Path "users/$($user.id)/mfaEnabled" ` -Body @{ mfaEnabled = $true } -Connection $connection } catch { $errors.Add("Could not enable MFA for ${login}: $($_.Exception.Message)") } } } foreach ($groupKey in @(($row.Groups -split ';') | Where-Object { $_ })) { $groupRow = $groupRows[$groupKey] if (-not $groupRow) { $errors.Add("Group key '$groupKey' on $login is not in the groups file") continue } if ($groupRow.UserFilter) { # PingOne maintains a filtered group's membership and refuses a manual addition. continue } $groupName = Resolve-PingOneSeedName -Key $groupRow.Name -Kind DisplayName -Connection $connection $group = $groupByName[$groupName] if (-not $group) { $errors.Add("Group '$groupName' for $login does not exist; run New-PingOneGroup first") continue } if (-not $PSCmdlet.ShouldProcess("$login -> $groupName", 'Add PingOne group membership')) { continue } try { $null = Invoke-PingOneRequest -Method POST -Path "users/$($user.id)/memberOfGroups" ` -Body @{ id = $group.id } -Connection $connection -IgnoreError 'UNIQUENESS_VIOLATION' $membershipsApplied++ } catch { $errors.Add("Could not add $login to ${groupName}: $($_.Exception.Message)") } } } Write-TestProgress -Activity 'Creating PingOne users' -Completed -ShowProgress:$ShowProgress if ($PassThru) { return [PSCustomObject]@{ TotalUsers = @($rows).Count CreatedUsers = $created.Count ReusedUsers = $reused.Count MembershipsApplied = $membershipsApplied Users = (@($created) + @($reused)) Errors = $errors.ToArray() } } } |