Providers/Okta/Public/New-OktaPolicy.ps1
|
function New-OktaPolicy { <# .EXTERNALHELP TestEnvironment-Help.xml .SYNOPSIS Creates the seeded sign-on and password policies, with their rules #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [OutputType([PSCustomObject])] param( [Parameter()] [string[]]$PolicyName, [Parameter()] [switch]$PassThru ) $connection = Get-OktaConnection $dataPath = Get-OktaDataPath $rows = @(Import-Csv -Path (Join-Path $dataPath 'OktaPolicies.csv') -Encoding UTF8) $groupRows = @(Import-Csv -Path (Join-Path $dataPath 'OktaGroups.csv') -Encoding UTF8) if ($PolicyName) { $rows = @($rows | Where-Object { $PolicyName -contains $_.Name }) $unknown = @($PolicyName | Where-Object { $rows.Name -notcontains $_ }) if ($unknown) { throw "No policy definition for: $($unknown -join ', ')" } } $result = [PSCustomObject]@{ TotalPolicies = $rows.Count CreatedPolicies = 0 ExistingPolicies = 0 RulesCreated = 0 Policies = @() Errors = @() } $oktaNameByKey = @{} foreach ($groupRow in $groupRows) { $oktaNameByKey[$groupRow.Name] = '{0}-{1}' -f $connection.Prefix, $groupRow.DisplayName } $groupIdByKey = @{} $seededGroups = @(Get-OktaSeededGroup -Prefix $connection.Prefix -SeedMarker $connection.SeedMarker) foreach ($group in $seededGroups) { $key = @($oktaNameByKey.Keys | Where-Object { $oktaNameByKey[$_] -eq $group.profile.name }) if ($key.Count -eq 1) { $groupIdByKey[$key[0]] = $group.id } } $zoneIdByKey = @{} $liveZones = @(Invoke-OktaRequest -Method GET -Path '/api/v1/zones' ` -Query @{ limit = 200 } -Paginate) foreach ($zone in $liveZones) { if ($zone.name -and $zone.name.StartsWith("$($connection.Prefix)-", [StringComparison]::OrdinalIgnoreCase)) { $zoneIdByKey[$zone.name.Substring($connection.Prefix.Length + 1)] = $zone.id } } $policies = [System.Collections.Generic.List[object]]::new() foreach ($row in $rows) { $name = '{0}-{1}' -f $connection.Prefix, $row.Name $groupIds = @( foreach ($key in @($row.Groups -split ';' | Where-Object { $_ })) { if ($groupIdByKey.ContainsKey($key)) { $groupIdByKey[$key] } else { $message = "Policy '$name' scopes to group '$key', which does not exist. Skipped." $result.Errors += $message Write-Warning $message } } ) if ($groupIds.Count -eq 0) { # A policy with no group condition applies to everybody in the org. Creating one by # accident because a group lookup failed is exactly the kind of mistake that locks # people out, so this refuses rather than falling back to org-wide. $message = ("Policy '$name' resolved no groups, so it was not created. An " + 'unscoped policy would apply to the whole org.') $result.Errors += $message Write-Warning $message continue } if (-not $PSCmdlet.ShouldProcess($name, "Create Okta policy ($($row.Type))")) { continue } try { $existingPolicies = @(Invoke-OktaRequest -Method GET -Path '/api/v1/policies' ` -Query @{ type = $row.Type; limit = 200 } -Paginate | Where-Object { $_.name -eq $name }) if ($existingPolicies.Count -gt 0) { $policy = $existingPolicies[0] $result.ExistingPolicies++ Write-Verbose "Reusing policy $name" } else { $body = [ordered]@{ type = $row.Type name = $name description = '{0} {1}' -f $row.Description, $connection.SeedMarker status = 'ACTIVE' conditions = @{ people = @{ groups = @{ include = $groupIds } } } } if ($row.Type -eq 'PASSWORD') { $complexity = [ordered]@{ minLowerCase = 1; minUpperCase = 1; minNumber = 1; minSymbol = 1 } if ($row.MinLength) { $complexity.minLength = [int]$row.MinLength } $age = [ordered]@{ expireWarnDays = 7; historyCount = 4 } if ($row.MaxAgeDays) { $age.maxAgeDays = [int]$row.MaxAgeDays } $body.settings = @{ password = @{ complexity = $complexity; age = $age } } } $policy = Invoke-OktaRequest -Method POST -Path '/api/v1/policies' -Body $body $result.CreatedPolicies++ Write-Verbose "Created policy $name" } $ruleName = $null if ($row.RuleName) { $ruleName = '{0}-{1}' -f $connection.Prefix, $row.RuleName $existingRules = @(Invoke-OktaRequest -Method GET ` -Path "/api/v1/policies/$($policy.id)/rules" | Where-Object { $_.name -eq $ruleName }) if ($existingRules.Count -eq 0) { $network = @{ connection = 'ANYWHERE' } if ($row.RuleZone) { if ($zoneIdByKey.ContainsKey($row.RuleZone)) { $network = @{ connection = 'ZONE'; include = @($zoneIdByKey[$row.RuleZone]) } } else { $message = ("Policy '$name' rule references zone '$($row.RuleZone)', which " + 'does not exist. The rule was created for ANYWHERE instead.') $result.Errors += $message Write-Warning $message } } $session = [ordered]@{ usePersistentCookie = $false } if ($row.MaxSessionIdleMinutes) { $session.maxSessionIdleMinutes = [int]$row.MaxSessionIdleMinutes } if ($row.MaxSessionLifetimeMinutes) { $session.maxSessionLifetimeMinutes = [int]$row.MaxSessionLifetimeMinutes } $null = Invoke-OktaRequest -Method POST ` -Path "/api/v1/policies/$($policy.id)/rules" -Body @{ type = 'SIGN_ON' name = $ruleName conditions = @{ network = $network; authContext = @{ authType = 'ANY' } } actions = @{ signon = @{ access = $row.RuleAccess requireFactor = $false session = $session } } } $result.RulesCreated++ Write-Verbose "Created rule $ruleName" } } $policies.Add([PSCustomObject]@{ Id = $policy.id Key = $row.Name Name = $name Type = $row.Type Priority = $policy.priority Rule = $ruleName Groups = @($row.Groups -split ';' | Where-Object { $_ }) }) } catch { $message = "Failed to create policy '$name': $($_.Exception.Message)" $result.Errors += $message Write-Error $message } } $result.Policies = $policies.ToArray() if ($PassThru) { return $result } } |