Providers/Entra/Public/New-EntraAuthenticationStrength.ps1

function New-EntraAuthenticationStrength {
    <#
    .EXTERNALHELP TestEnvironment-Help.xml
    .SYNOPSIS
        Creates custom authentication strength policies
    #>


    [CmdletBinding(SupportsShouldProcess)]
    [OutputType('EntraAuthenticationStrength')]
    param(
        [Parameter()]
        [string[]]$StrengthKey,

        [Parameter()]
        [switch]$ShowProgress,

        [Parameter()]
        [switch]$PassThru
    )

    $connection = Get-EntraConnection
    $marker = Get-EntraSeedMarker -Connection $connection

    $definitions = @(Get-EntraSeedData -Name 'EntraAuthenticationStrengths')
    if ($StrengthKey) {
        $definitions = @($definitions | Where-Object { $StrengthKey -contains $_.Key })
        $missing = @($StrengthKey | Where-Object { $definitions.Key -notcontains $_ })
        if ($missing) {
            Write-Error "No seed definition for strength key(s): $($missing -join ', ')" -ErrorAction Stop
            return
        }
    }

    $existing = @(Get-EntraSeededObject -Type AuthenticationStrengths -Connection $connection)
    $created = [System.Collections.Generic.List[object]]::new()
    $index = 0

    foreach ($definition in $definitions) {
        $index++
        $displayName = '{0}{1}' -f $marker.Prefix, $definition.DisplayName

        Write-TestProgress -Activity 'Seeding authentication strengths' -Status $displayName `
            -PercentComplete ([int](100 * $index / [Math]::Max(1, $definitions.Count))) -ShowProgress:$ShowProgress

        $already = $existing | Where-Object { $_.displayName -eq $displayName } | Select-Object -First 1
        if ($already) {
            Write-Verbose "Authentication strength '$displayName' already exists"
            $created.Add([PSCustomObject]@{
                    PSTypeName          = 'EntraAuthenticationStrength'
                    Key                 = $definition.Key
                    Id                  = $already.id
                    DisplayName         = $displayName
                    AllowedCombinations = @($already.allowedCombinations)
                    Purpose             = $definition.Purpose
                })
            continue
        }

        # Semicolon, not comma. A single allowed combination is itself a comma-joined string
        # such as 'password,sms', so splitting on commas would send Entra a list of names it
        # rejects one at a time.
        $combinations = @($definition.AllowedCombinations -split ';' | Where-Object { $_ } | ForEach-Object { $_.Trim() })
        if ($combinations.Count -eq 0) {
            Write-Error "Authentication strength '$($definition.Key)' lists no allowed combinations." -ErrorAction Continue
            continue
        }

        # Checked before sending, because the limit is not documented on the request and the
        # failure names a length rather than the prefix that caused it. 30 characters is the
        # whole displayName, so a longer -Prefix eats into the seed data's own names: with the
        # default ENTRALAB- that leaves 21.
        if ($displayName.Length -gt 30) {
            Write-Error ("Authentication strength name '$displayName' is $($displayName.Length) characters and Entra " +
                "caps it at 30. Shorten the DisplayName in the seed data, or connect with a shorter -Prefix " +
                "(the prefix '$($marker.Prefix)' uses $($marker.Prefix.Length) of the 30).") -ErrorAction Continue
            continue
        }

        if (-not $PSCmdlet.ShouldProcess($displayName, 'Create authentication strength')) { continue }

        try {
            $strength = Invoke-EntraRequest -Method POST -Connection $connection `
                -Path '/identity/conditionalAccess/authenticationStrength/policies' -Body @{
                displayName         = $displayName
                description         = $marker.Description
                allowedCombinations = $combinations
            }
        }
        catch {
            Write-Error "Failed to create authentication strength '${displayName}': $($_.Exception.Message)"
            continue
        }

        $created.Add([PSCustomObject]@{
                PSTypeName          = 'EntraAuthenticationStrength'
                Key                 = $definition.Key
                Id                  = $strength.id
                DisplayName         = $displayName
                AllowedCombinations = $combinations
                Purpose             = $definition.Purpose
            })

        Write-Verbose "Created authentication strength '$displayName' ($($strength.id))"
    }

    Write-TestProgress -Activity 'Seeding authentication strengths' -Completed -ShowProgress:$ShowProgress

    if ($PassThru) { return $created.ToArray() }
}