Providers/FreeIPA/Public/New-FreeIPAIdView.ps1

function New-FreeIPAIdView {
    <#
    .EXTERNALHELP TestEnvironment-Help.xml
    .SYNOPSIS
        Creates the seeded ID views and overrides from Data\FreeIPAIdViews.csv and Data\FreeIPAIdOverrides.csv, and applies them to hosts
    #>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [string[]]$ViewName,

        [Parameter()]
        [switch]$PassThru
    )

    $connection = Get-FreeIPAConnection
    $marker = Get-FreeIPASeedMarker -Connection $connection
    $dataPath = Get-FreeIPADataPath

    $viewPath = Join-Path -Path $dataPath -ChildPath 'FreeIPAIdViews.csv'
    $viewRows = @(Import-Csv -Path $viewPath -Encoding UTF8)
    $overrideRows = @(Import-Csv -Path (Join-Path -Path $dataPath -ChildPath 'FreeIPAIdOverrides.csv') -Encoding UTF8)
    if ($ViewName) {
        $viewRows = @($viewRows | Where-Object { $ViewName -contains $_.Name })
        $unknown = @($ViewName | Where-Object { $viewRows.Name -notcontains $_ })
        if ($unknown) { throw "No definition in $viewPath for: $($unknown -join ', ')" }
    }

    $result = [PSCustomObject]@{
        TotalViews       = $viewRows.Count
        CreatedViews     = 0
        UpdatedViews     = 0
        OverridesCreated = 0
        OverridesUpdated = 0
        HostsApplied     = 0
        Views            = @()
        Errors           = @()
    }

    $split = { param($value) @([string]$value -split ';' | Where-Object { $_ }) }
    $resolve = { param($keys, $kind) @(& $split $keys | ForEach-Object { Resolve-FreeIPASeedName -Key $_ -Kind $kind -Marker $marker -Connection $connection }) }
    $record = { param($problem) $result.Errors += $problem; Write-Error $problem }

    $existing = @{}
    foreach ($entry in (Get-FreeIPASeededObject -Type IdViews -Connection $connection)) { $existing[[string](@($entry.cn)[0])] = $entry }

    $views = [System.Collections.Generic.List[object]]::new()
    foreach ($row in $viewRows) {
        $name = Resolve-FreeIPASeedName -Key $row.Name -Marker $marker -Connection $connection
        if (-not $PSCmdlet.ShouldProcess($name, 'Create FreeIPA ID view')) { continue }
        try {
            $options = @{ description = ('{0} {1}' -f $row.Description, $marker.Marker).Trim() }
            if ($existing.ContainsKey($name)) {
                $null = Invoke-FreeIPARequest -Method 'idview_mod' -Arguments $name -Options $options -Connection $connection -IgnoreError 'EmptyModlist'
                $result.UpdatedViews++
            }
            else {
                $null = Invoke-FreeIPARequest -Method 'idview_add' -Arguments $name -Options $options -Connection $connection
                $result.CreatedViews++
                Write-Verbose "Created ID view $name"
            }

            # Overrides inside the view. A user is anchored by login, a group by its name in
            # the realm, and only the fields the row fills are overridden.
            foreach ($override in ($overrideRows | Where-Object { $_.View -eq $row.Name })) {
                $isUser = $override.Kind -eq 'User'
                $anchor = if ($isUser) { $override.Anchor } else { Resolve-FreeIPASeedName -Key $override.Anchor -Marker $marker -Connection $connection }
                $label = '{0} override for {1} in {2}' -f $override.Kind.ToLowerInvariant(), $anchor, $name
                if (-not $PSCmdlet.ShouldProcess($label, 'Create FreeIPA ID override')) { continue }

                $fields = @{}
                if ($isUser) {
                    if ($override.Login) { $fields['uid'] = $override.Login }
                    if ($override.Uid -match '^\d+$') { $fields['uidnumber'] = [int]$override.Uid }
                    if ($override.Gid -match '^\d+$') { $fields['gidnumber'] = [int]$override.Gid }
                    if ($override.Shell) { $fields['loginshell'] = $override.Shell }
                    if ($override.HomeDirectory) { $fields['homedirectory'] = $override.HomeDirectory }
                    if ($override.Gecos) { $fields['gecos'] = $override.Gecos }
                }
                else {
                    if ($override.Login) { $fields['cn'] = $override.Login }
                    if ($override.Gid -match '^\d+$') { $fields['gidnumber'] = [int]$override.Gid }
                }
                if ($override.Description) { $fields['description'] = $override.Description }

                $noun = if ($isUser) { 'idoverrideuser' } else { 'idoverridegroup' }
                $shown = Invoke-FreeIPARequest -Method "${noun}_show" -Arguments @($name, $anchor) -Connection $connection -IgnoreError 'NotFound'
                if ($shown) {
                    $null = Invoke-FreeIPARequest -Method "${noun}_mod" -Arguments @($name, $anchor) -Options $fields -Connection $connection -IgnoreError 'EmptyModlist'
                    $result.OverridesUpdated++
                }
                else {
                    $null = Invoke-FreeIPARequest -Method "${noun}_add" -Arguments @($name, $anchor) -Options $fields -Connection $connection
                    $result.OverridesCreated++
                }
            }

            $targets = @{ host = @(& $resolve $row.Hosts 'Host'); hostgroup = @(& $resolve $row.Hostgroups 'Name') }
            $total = @($targets.Values | ForEach-Object { @($_).Count } | Measure-Object -Sum).Sum
            if ($total -gt 0 -and $PSCmdlet.ShouldProcess($name, "Apply to $total host(s) or host group(s)")) {
                $outcome = Add-FreeIPAMember -Method 'idview_apply' -Name $name -Members $targets -Connection $connection
                $result.HostsApplied += $outcome.Completed
                foreach ($problem in $outcome.Errors) { & $record $problem }
            }

            $views.Add([PSCustomObject]@{ Key = $row.Name; Name = $name; Overrides = @($overrideRows | Where-Object { $_.View -eq $row.Name }).Count })
        }
        catch { & $record "Failed to create ID view '$name': $($_.Exception.Message)" }
    }

    $result.Views = $views.ToArray()
    Write-Verbose "ID views: $($result.CreatedViews) created, $($result.UpdatedViews) updated, $($result.OverridesCreated) overrides, $($result.HostsApplied) applied, $($result.Errors.Count) problems"
    if ($PassThru) { return $result }
}