Providers/Authentik/Private/Get-AuthentikSeededObject.ps1
|
function Get-AuthentikSeededObject { <# .SYNOPSIS Finds the objects of one type that this module created, and nothing else .DESCRIPTION Teardown and the report both need the same answer: which of the objects in the instance are ours. Every type carries the seed prefix on its name, but a name is not proof - an administrator can name a group anything - so each type also has to satisfy the evidence the module wrote when it created the object: - Users are under the seed path AND carry the seed tag in their attributes. The path is what Authentik lets a listing filter on, and the tag is what proves the module put them there. Human user accounts carry no prefix, so this is the only evidence they have. - Groups carry the prefix on the name AND the seed tag in their attributes. - Applications carry the slug prefix AND the bracketed marker in their description, because applications have no attributes. - Providers carry the prefix on the name, and are either attached to a seeded application or attached to nothing. A provider with our prefix that is wired to someone else's application is left alone. - Entitlements belong to a seeded application AND carry the prefix AND the tag in their attributes. They are read per seeded application, which is the only filter the endpoint offers. - Tokens carry the slug prefix on the identifier AND belong to a user that is itself seeded: under the path and tagged. The automation service account's own token is excluded unless -IncludeServiceAccount is passed, because it is the credential the session is using. - Invitations carry the slug prefix on the name AND the tag in their fixed data. - Flows carry the slug prefix on the slug, which is what the instance routes by. - Roles, scope mappings, outposts, certificates, stages, policies, notification rules and transports carry the prefix on the name, which is all those types can hold. A scope mapping or an outpost is additionally required to be unmanaged, since a managed one belongs to Authentik itself. The automation service account is a user with a reserved username and is excluded from Users unless -IncludeServiceAccount is passed, for the same reason the Entra provider excludes its bootstrapped app: deleting the credential mid-teardown strands everything after it. .PARAMETER Type Which objects to find. .PARAMETER IncludeServiceAccount For Users and Tokens: include the module's own automation account, or its token. .PARAMETER Connection The connection to look through. Defaults to the active one. .OUTPUTS System.Object[]. The objects as Authentik returned them, or an empty array. .EXAMPLE PS> Get-AuthentikSeededObject -Type Users DESCRIPTION: Lists the seeded users OUTPUT: Every user under the seed path carrying the tag, minus the automation account USE CASE: Membership resolution in New-AuthentikGroup and the users step of teardown .NOTES Author: Jeffrey Stuhr Blog: https://www.techbyjeff.net LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/ #> [CmdletBinding()] [OutputType([object[]])] param( [Parameter(Mandatory = $true)] [ValidateSet('Users', 'Groups', 'Applications', 'Providers', 'Entitlements', 'ScopeMappings', 'Roles', 'Outposts', 'Certificates', 'Flows', 'Stages', 'Policies', 'NotificationRules', 'NotificationTransports', 'Tokens', 'Invitations')] [string]$Type, [Parameter()] [switch]$IncludeServiceAccount, [Parameter()] [hashtable]$Connection ) if (-not $Connection) { $Connection = Get-AuthentikConnection } $marker = Get-AuthentikSeedMarker -Connection $Connection $prefix = $marker.Prefix $slugPrefix = '{0}-' -f $marker.SlugPrefix $hasTag = { param($object, $property) if (-not $property) { $property = 'attributes' } $attributes = $object.$property $attributes -and $attributes.PSObject.Properties[$marker.Attribute] -and ([string]$attributes.($marker.Attribute)) -eq $marker.Tag } $startsWithPrefix = { param($name) $name -and ([string]$name).StartsWith($prefix, [StringComparison]::OrdinalIgnoreCase) } $startsWithSlugPrefix = { param($name) $name -and ([string]$name).StartsWith($slugPrefix, [StringComparison]::OrdinalIgnoreCase) } $isSeededUser = { param($user) $user -and ([string]$user.path) -eq $marker.UserPath -and (& $hasTag $user) } switch ($Type) { 'Users' { $serviceAccount = Get-AuthentikServiceAccountName -Marker $marker $users = @(Invoke-AuthentikRequest -Method GET -Path '/core/users/' ` -Query @{ path = $marker.UserPath } -Connection $Connection -Paginate) return @($users | Where-Object { (& $hasTag $_) -and ($IncludeServiceAccount -or $_.username -ne $serviceAccount) }) } 'Groups' { $groups = @(Invoke-AuthentikRequest -Method GET -Path '/core/groups/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($groups | Where-Object { (& $startsWithPrefix $_.name) -and (& $hasTag $_) }) } 'Applications' { $applications = @(Invoke-AuthentikRequest -Method GET -Path '/core/applications/' ` -Query @{ search = $marker.SlugPrefix; superuser_full_list = 'true' } -Connection $Connection -Paginate) return @($applications | Where-Object { (& $startsWithSlugPrefix $_.slug) -and $_.meta_description -and $_.meta_description.Contains($marker.Marker) }) } 'Providers' { $ownedSlugs = @((Get-AuthentikSeededObject -Type Applications -Connection $Connection).slug) $providers = @(Invoke-AuthentikRequest -Method GET -Path '/providers/all/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($providers | Where-Object { (& $startsWithPrefix $_.name) -and (-not $_.assigned_application_slug -or $ownedSlugs -contains $_.assigned_application_slug) }) } 'Entitlements' { $applications = @(Get-AuthentikSeededObject -Type Applications -Connection $Connection) $entitlements = foreach ($application in $applications) { @(Invoke-AuthentikRequest -Method GET -Path '/core/application_entitlements/' ` -Query @{ app = [string]$application.pk } -Connection $Connection -Paginate) | Where-Object { (& $startsWithPrefix $_.name) -and (& $hasTag $_) } | ForEach-Object { # The application slug is what the CSV and the report speak in. Add-Member -InputObject $_ -NotePropertyName 'app_slug' -NotePropertyValue $application.slug -Force -PassThru } } return @($entitlements) } 'ScopeMappings' { $mappings = @(Invoke-AuthentikRequest -Method GET -Path '/propertymappings/provider/scope/' ` -Query @{ managed__isnull = 'true' } -Connection $Connection -Paginate) return @($mappings | Where-Object { (& $startsWithPrefix $_.name) -and -not $_.managed }) } 'Roles' { $roles = @(Invoke-AuthentikRequest -Method GET -Path '/rbac/roles/' -Connection $Connection -Paginate) return @($roles | Where-Object { & $startsWithPrefix $_.name }) } 'Outposts' { $outposts = @(Invoke-AuthentikRequest -Method GET -Path '/outposts/instances/' ` -Query @{ name__icontains = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($outposts | Where-Object { (& $startsWithPrefix $_.name) -and -not $_.managed }) } 'Flows' { $flows = @(Invoke-AuthentikRequest -Method GET -Path '/flows/instances/' ` -Query @{ search = $marker.SlugPrefix } -Connection $Connection -Paginate) return @($flows | Where-Object { & $startsWithSlugPrefix $_.slug }) } 'Stages' { $stages = @(Invoke-AuthentikRequest -Method GET -Path '/stages/all/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($stages | Where-Object { & $startsWithPrefix $_.name }) } 'Certificates' { $keypairs = @(Invoke-AuthentikRequest -Method GET -Path '/crypto/certificatekeypairs/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($keypairs | Where-Object { (& $startsWithPrefix $_.name) -and -not $_.managed }) } 'Policies' { $policies = @(Invoke-AuthentikRequest -Method GET -Path '/policies/all/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($policies | Where-Object { & $startsWithPrefix $_.name }) } 'NotificationRules' { $rules = @(Invoke-AuthentikRequest -Method GET -Path '/events/rules/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($rules | Where-Object { & $startsWithPrefix $_.name }) } 'NotificationTransports' { $transports = @(Invoke-AuthentikRequest -Method GET -Path '/events/transports/' ` -Query @{ search = $prefix.TrimEnd('-') } -Connection $Connection -Paginate) return @($transports | Where-Object { & $startsWithPrefix $_.name }) } 'Tokens' { $serviceAccount = Get-AuthentikServiceAccountName -Marker $marker $tokens = @(Invoke-AuthentikRequest -Method GET -Path '/core/tokens/' ` -Query @{ search = $marker.SlugPrefix } -Connection $Connection -Paginate) return @($tokens | Where-Object { (& $startsWithSlugPrefix $_.identifier) -and (& $isSeededUser $_.user_obj) -and ($IncludeServiceAccount -or $_.user_obj.username -ne $serviceAccount) }) } 'Invitations' { $invitations = @(Invoke-AuthentikRequest -Method GET -Path '/stages/invitation/invitations/' ` -Query @{ search = $marker.SlugPrefix } -Connection $Connection -Paginate) return @($invitations | Where-Object { (& $startsWithSlugPrefix $_.name) -and (& $hasTag $_ 'fixed_data') }) } } } |