Providers/FreeIPA/Public/New-FreeIPACertMapRule.ps1

function New-FreeIPACertMapRule {
    <#
    .EXTERNALHELP TestEnvironment-Help.xml
    .SYNOPSIS
        Creates the seeded certificate identity mapping rules from Data\FreeIPACertMapRules.csv
    #>


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

        [Parameter()]
        [switch]$PassThru
    )

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

    $csvPath = Join-Path -Path (Get-FreeIPADataPath) -ChildPath 'FreeIPACertMapRules.csv'
    $rows = @(Import-Csv -Path $csvPath -Encoding UTF8)
    if ($RuleName) {
        $rows = @($rows | Where-Object { $RuleName -contains $_.Name })
        $unknown = @($RuleName | Where-Object { $rows.Name -notcontains $_ })
        if ($unknown) { throw "No definition in $csvPath for: $($unknown -join ', ')" }
    }

    $result = [PSCustomObject]@{
        TotalRules   = $rows.Count
        CreatedRules = 0
        UpdatedRules = 0
        Rules        = @()
        Errors       = @()
    }

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

    # The seed domain appears in a match rule escaped for a regular expression, so the
    # realm's domain goes in escaped the same way; {realm} is the realm's own name, for the
    # rule that matches the certificates its CA issued.
    $substitute = {
        param($text)
        ([string]$text).Replace([regex]::Escape($script:FreeIPADefaultSeedDomain), [regex]::Escape($connection.Domain)).Replace('{realm}', [regex]::Escape([string]$connection.Realm))
    }

    $rules = [System.Collections.Generic.List[object]]::new()
    foreach ($row in $rows) {
        $name = Resolve-FreeIPASeedName -Key $row.Name -Marker $marker -Connection $connection
        if (-not $PSCmdlet.ShouldProcess($name, 'Create FreeIPA certificate mapping rule')) { continue }
        try {
            $options = @{
                description          = ('{0} {1}' -f $row.Description, $marker.Marker).Trim()
                ipacertmapmatchrule  = & $substitute $row.MatchRule
                ipacertmapmaprule    = $row.MapRule
            }
            if ($row.Priority -match '^\d+$') { $options['ipacertmappriority'] = [int]$row.Priority }

            if ($existing.ContainsKey($name)) {
                $null = Invoke-FreeIPARequest -Method 'certmaprule_mod' -Arguments $name -Options $options -Connection $connection -IgnoreError 'EmptyModlist'
                $result.UpdatedRules++
            }
            else {
                $null = Invoke-FreeIPARequest -Method 'certmaprule_add' -Arguments $name -Options $options -Connection $connection
                $result.CreatedRules++
                Write-Verbose "Created certificate mapping rule $name"
            }

            if ($row.Enabled -eq 'FALSE') {
                $null = Invoke-FreeIPARequest -Method 'certmaprule_disable' -Arguments $name -Connection $connection -IgnoreError 'AlreadyInactive'
            }
            elseif ($existing.ContainsKey($name)) {
                $null = Invoke-FreeIPARequest -Method 'certmaprule_enable' -Arguments $name -Connection $connection -IgnoreError 'AlreadyActive'
            }

            $rules.Add([PSCustomObject]@{ Key = $row.Name; Name = $name; Enabled = ($row.Enabled -ne 'FALSE'); Priority = $row.Priority })
        }
        catch {
            $message = "Failed to create certificate mapping rule '$name': $($_.Exception.Message)"
            $result.Errors += $message
            Write-Error $message
        }
    }

    $result.Rules = $rules.ToArray()
    Write-Verbose "Certificate mapping rules: $($result.CreatedRules) created, $($result.UpdatedRules) updated, $($result.Errors.Count) problems"
    if ($PassThru) { return $result }
}