Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoPipelineAuthorization/Get-AzDoPipelineAuthorization.ps1

<#
.SYNOPSIS
Retrieves the current pipeline authorization state for a protected Azure DevOps resource.
 
.DESCRIPTION
Reads the live pipelinePermissions state for a service connection, agent queue, variable group,
secure file, environment or repository, and compares it against the desired AllPipelines flag and
AuthorizedPipelines list. In additive mode (ExclusiveList = $false) the resource is Unchanged as
soon as every desired pipeline is already authorized, even if other pipelines are authorized too;
in exclusive mode, any pipeline authorized on the resource but absent from AuthorizedPipelines also
counts as drift. The live state is always read directly from the API rather than from a cache,
since pipeline authorizations are commonly changed from the Azure DevOps portal out of band.
 
.PARAMETER ProjectName
The name of the Azure DevOps project.
 
.PARAMETER ResourceType
The protected resource type: endpoint, queue, variablegroup, securefile, environment or repository.
 
.PARAMETER TargetResourceName
The name of the protected resource.
 
.PARAMETER AuthorizedPipelines
The pipeline paths that should be authorized to use the resource.
 
.PARAMETER AllPipelines
Whether all pipelines in the project should be authorized to use the resource.
 
.PARAMETER ExclusiveList
Whether a pipeline authorized on the resource but absent from AuthorizedPipelines should be
reported as drift.
 
.PARAMETER LookupResult
(Optional) A hashtable to store lookup results.
 
.PARAMETER Ensure
(Optional) Specifies the desired state of the resource.
 
.PARAMETER Force
(Optional) A switch parameter to force the operation.
 
.OUTPUTS
System.Management.Automation.PSObject[]
 
.EXAMPLE
Get-AzDoPipelineAuthorization -ProjectName 'MyProject' -ResourceType 'variablegroup' -TargetResourceName 'Prod Secrets' -AuthorizedPipelines @('\Platform\deploy-infra')
#>

Function Get-AzDoPipelineAuthorization
{
    [CmdletBinding()]
    [OutputType([System.Management.Automation.PSObject[]])]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ProjectName,

        [Parameter(Mandatory = $true)]
        [ValidateSet('endpoint', 'queue', 'variablegroup', 'securefile', 'environment', 'repository')]
        [string]$ResourceType,

        [Parameter(Mandatory = $true)]
        [string]$TargetResourceName,

        [Parameter()]
        [string[]]$AuthorizedPipelines,

        [Parameter()]
        [bool]$AllPipelines = $false,

        [Parameter()]
        [bool]$ExclusiveList = $false,

        [Parameter()]
        [HashTable]$LookupResult,

        [Parameter()]
        [Ensure]$Ensure,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $Force
    )

    Write-Verbose "[Get-AzDoPipelineAuthorization] Started."

    $result = @{
        Ensure            = [Ensure]::Present
        propertiesChanged = @()
        status            = $null
        reason            = $null
    }

    $resourceId = Resolve-AzDoPipelineAuthorizationResource -ProjectName $ProjectName -ResourceType $ResourceType -TargetResourceName $TargetResourceName

    if (-not $resourceId)
    {
        Write-Warning "[Get-AzDoPipelineAuthorization] Resource '$TargetResourceName' of type '$ResourceType' was not found in project '$ProjectName'."
        $result.status = [DSCGetSummaryState]::Error
        $result.reason = "Resource '$TargetResourceName' of type '$ResourceType' was not found in project '$ProjectName'."
        return $result
    }

    $result.ResourceId = $resourceId

    $OrgName = Get-AzDoOrganizationName
    try
    {
        $permission = Get-DevOpsPipelinePermission -ApiUri "https://dev.azure.com/$OrgName" -ProjectName $ProjectName -ResourceType $ResourceType -ResourceId $resourceId
    }
    catch
    {
        Write-Warning "[Get-AzDoPipelineAuthorization] Failed to retrieve pipeline permissions: $_"
        $result.status = [DSCGetSummaryState]::Error
        $result.reason = "Failed to retrieve pipeline permissions: $_"
        return $result
    }

    $result.liveCache = $permission

    $currentAllPipelines = [bool]$permission.allPipelines.authorized
    [Array]$currentAuthorizedIds = @($permission.pipelines | Where-Object { $_.authorized } | ForEach-Object { $_.id })

    # Resolve the desired pipeline paths to ids so they can be compared against the ids the API
    # returns. An unresolved path is drift that Set can never satisfy, not something to skip.
    [Array]$desiredTargets = @()
    if ($AuthorizedPipelines)
    {
        $desiredTargets = @(Resolve-AzDoPipelineAuthorizationTargets -ProjectName $ProjectName -PipelinePaths $AuthorizedPipelines)
    }
    $result.DesiredTargets = $desiredTargets

    $unresolved = @($desiredTargets | Where-Object { -not $_.Id })
    if ($unresolved.Count -gt 0)
    {
        $unresolvedPaths = ($unresolved | ForEach-Object { $_.Path }) -join ', '
        Write-Warning "[Get-AzDoPipelineAuthorization] Could not resolve pipeline path(s): $unresolvedPaths"
        $result.status = [DSCGetSummaryState]::Error
        $result.reason = "Could not resolve pipeline path(s): $unresolvedPaths"
        return $result
    }

    [Array]$desiredIds = @($desiredTargets | ForEach-Object { $_.Id })

    $propertiesChanged = [System.Collections.Generic.List[string]]::new()

    if ($currentAllPipelines -ne $AllPipelines)
    {
        $propertiesChanged.Add('AllPipelines')
    }

    $missing = @($desiredIds | Where-Object { $currentAuthorizedIds -notcontains $_ })
    if ($missing.Count -gt 0)
    {
        $propertiesChanged.Add('AuthorizedPipelines')
    }

    if ($ExclusiveList -and -not $propertiesChanged.Contains('AuthorizedPipelines'))
    {
        $extra = @($currentAuthorizedIds | Where-Object { $desiredIds -notcontains $_ })
        if ($extra.Count -gt 0)
        {
            $propertiesChanged.Add('AuthorizedPipelines')
        }
    }

    $result.propertiesChanged = $propertiesChanged.ToArray()
    $result.status = if ($propertiesChanged.Count -gt 0) { [DSCGetSummaryState]::Changed } else { [DSCGetSummaryState]::Unchanged }

    Write-Verbose "[Get-AzDoPipelineAuthorization] Result status: $($result.status)"

    return $result
}