Functions/DevOps.OrganizationPipelinesSettings.ps1

<#
    .SYNOPSIS
    Retrieves Azure DevOps organization-level pipeline settings.

    .DESCRIPTION
    This function calls the internal Contribution HierarchyQuery endpoint used by the Azure DevOps portal
    to retrieve pipeline security, policy, and control settings for the entire organization.

    .PARAMETER Organization
    The name of your Azure DevOps organization (e.g. 'contoso').

    .PARAMETER AccessToken
    A valid Azure DevOps Bearer token with access to organization settings. Accepts a bare token or a complete
    authorization header value ('Bearer ...' or 'Basic ...').

    .EXAMPLE
    Invoke-AdoPipelineSettingsQuery -Organization "MyOrg" -AccessToken $token
#>


function Read-AdoOrganizationPipelinesSettings {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Organization,

        [Parameter(Mandatory)]
        [string]
        $AccessToken
    )

    # Define endpoint for pipeline settings query
    $uri = "https://dev.azure.com/$Organization/_apis/Contribution/HierarchyQuery?api-version=5.0-preview.1"

    # Accept a bare access token as well as a complete authorization header value
    if ($AccessToken -notmatch '^(Bearer|Basic) ') {
        $AccessToken = "Bearer $AccessToken"
    }

    # Set headers for authentication
    $headers = @{
        Authorization  = $AccessToken
        "Content-Type" = "application/json"
    }

    # Build request body for settings query
    $body = @(
        @{
            contributionIds     = @("ms.vss-build-web.pipelines-org-settings-data-provider")
            dataProviderContext = @{
                properties = @{
                    sourcePage = @{
                        url         = "https://dev.azure.com/$Organization/_settings/pipelinessettings"
                        routeId     = "ms.vss-admin-web.collection-admin-hub-route"
                        routeValues = @{
                            adminPivot  = "pipelinessettings"
                            controller  = "ContributedPage"
                            action      = "Execute"
                            serviceHost = "00000000-0000-0000-0000-000000000000 ($Organization)"
                        }
                    }
                }
            }
        }
    ) | ConvertTo-Json -Depth 10

    try {
        # Invoke API to retrieve settings
        $response = Invoke-AzDevOpsOrganizationRequest -Uri $uri -Method Post -Headers $headers -Body $body

        $settings = $response.dataProviders.'ms.vss-build-web.pipelines-org-settings-data-provider'
        $requiredSettings = @(
            'statusBadgesArePrivate', 'enforceSettableVar', 'enforceJobAuthScope', 'enforceJobAuthScopeForReleases',
            'enforceReferencedRepoScopedToken', 'disableStageChooser', 'disableClassicBuildPipelineCreation',
            'disableClassicReleasePipelineCreation', 'disableInBoxTasksVar', 'disableMarketplaceTasksVar',
            'disableNode6TasksVar', 'enableShellTasksArgsSanitizing', 'forkProtectionEnabled', 'buildsEnabledForForks',
            'enforceJobAuthScopeForForks', 'enforceNoAccessToSecretsFromForks', 'disableImpliedYAMLCiTrigger',
            'auditEnforceSettableVar', 'isTaskLockdownFeatureEnabled', 'hasManagePipelinePoliciesPermission',
            'isCommentRequiredForPullRequest', 'requireCommentsForNonTeamMembersOnly',
            'requireCommentsForNonTeamMemberAndNonContributors', 'enableShellTasksArgsSanitizingAudit'
        )
        Test-AzDevOpsOrganizationDataType -Data $settings -BooleanProperties $requiredSettings
        Set-AzDevOpsMissingCollectionData -Data $settings -RequiredProperties $requiredSettings

        # Output settings to console (optional, retained for debugging)
        Write-Host ""
        Write-Host "===== Azure DevOps Pipeline Settings Assessment ====="
        Write-Host ""

        Write-Host "General:"
        Write-Host " - Disable anonymous access to badges: $($settings.statusBadgesArePrivate)"
        Write-Host " - Limit variables that can be set at queue time: $($settings.enforceSettableVar)"
        Write-Host " - Limit job authorization (non-release): $($settings.enforceJobAuthScope)"
        Write-Host " - Limit job authorization (release): $($settings.enforceJobAuthScopeForReleases)"
        Write-Host " - Protect access to repositories in YAML pipelines: $($settings.enforceReferencedRepoScopedToken)"
        Write-Host " - Disable stage chooser: $($settings.disableStageChooser)"
        Write-Host " - Disable creation of classic build pipelines: $($settings.disableClassicBuildPipelineCreation)"
        Write-Host " - Disable creation of classic release pipelines: $($settings.disableClassicReleasePipelineCreation)"

        Write-Host "`nTask Restrictions:"
        Write-Host " - Disable built-in tasks: $($settings.disableInBoxTasksVar)"
        Write-Host " - Disable Marketplace tasks: $($settings.disableMarketplaceTasksVar)"
        Write-Host " - Disable Node 6 tasks: $($settings.disableNode6TasksVar)"
        Write-Host " - Enable shell tasks arguments validation: $($settings.enableShellTasksArgsSanitizing)"

        Write-Host "`nTriggers:"
        Write-Host " - Limit PRs from forks (GitHub): $($settings.forkProtectionEnabled)"
        Write-Host " - Allow builds from forks: $($settings.buildsEnabledForForks)"
        Write-Host " - Enforce job auth for forks: $($settings.enforceJobAuthScopeForForks)"
        Write-Host " - Block secrets access from forks: $($settings.enforceNoAccessToSecretsFromForks)"
        Write-Host " - Disable implied YAML CI trigger: $($settings.disableImpliedYAMLCiTrigger)"

        Write-Host "`nUnmapped / Diagnostic:"
        Write-Host " - Audit settable variable enforcement: $($settings.auditEnforceSettableVar)"
        Write-Host " - Task lockdown feature enabled: $($settings.isTaskLockdownFeatureEnabled)"
        Write-Host " - Has pipeline policies permission: $($settings.hasManagePipelinePoliciesPermission)"
        Write-Host " - Require comments for PRs: $($settings.isCommentRequiredForPullRequest)"
        Write-Host " - Require comments (non-team members): $($settings.requireCommentsForNonTeamMembersOnly)"
        Write-Host " - Require comments (non-team/non-contributors): $($settings.requireCommentsForNonTeamMemberAndNonContributors)"
        Write-Host " - Audit shell argument sanitization: $($settings.enableShellTasksArgsSanitizingAudit)"

        Write-Host ""
        Write-Host "Assessment complete."

        # Return the settings object
        return $settings
    }
    catch {
        throw "Failed to retrieve pipeline settings: $($_.Exception.Message)"
    }
}

Export-ModuleMember -Function Read-AdoOrganizationPipelinesSettings

<#
    .SYNOPSIS
        Exports Azure DevOps organization pipeline settings to a JSON file or returns them as an object.

    .DESCRIPTION
        The Export-AdoOrganizationPipelinesSettings function retrieves pipeline settings for a specified
        Azure DevOps organization using the provided access token. It formats the settings with metadata
        (e.g., ObjectType, ObjectName) and either saves them to a JSON file or returns them as an object,
        depending on the parameters provided. Requires a prior connection to Azure DevOps via Connect-AzDevOps.

    .PARAMETER Organization
        The name of the Azure DevOps organization whose pipeline settings will be exported.

    .PARAMETER AccessToken
        The Bearer token used for authenticating API requests to Azure DevOps.

    .PARAMETER OutputPath
        The file path where the pipeline settings will be saved as a JSON file.
        If not specified, the settings are not saved to a file. Mutually exclusive with -PassThru.

    .PARAMETER PassThru
        If specified, returns the pipeline settings as a PowerShell object instead of saving to a file.

    .EXAMPLE
        Export-AdoOrganizationPipelinesSettings -Organization "MyOrg" -AccessToken "abc123" -OutputPath "C:\Exports"
        Exports the pipeline settings for "MyOrg" to "C:\Exports\pipelineSettings.ado.json".

    .EXAMPLE
        Export-AdoOrganizationPipelinesSettings -Organization "MyOrg" -AccessToken "abc123" -PassThru
        Retrieves and returns the pipeline settings for "MyOrg" as a PowerShell object.

    .NOTES
        - Requires a prior call to Connect-AzDevOps to establish a connection.
        - The function depends on Read-AdoOrganizationPipelinesSettings to retrieve settings.
        - The output JSON file is named "pipelineSettings.ado.json" when using -OutputPath.

    .LINK
        https://docs.microsoft.com/en-us/rest/api/azure/devops/
    #>


function Export-AdoOrganizationPipelinesSettings {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $Organization,
    
        [Parameter(Mandatory)]
        [string]
        $AccessToken,
    
        [Parameter(ParameterSetName = 'JsonFile')]
        [string]
        $OutputPath,
    
        [Parameter(ParameterSetName = 'PassThru')]
        [switch]
        $PassThru
    )
    
    # Check for active connection
    if ($null -eq $script:connection) {
        throw 'Not connected to Azure DevOps. Run Connect-AzDevOps first'
    }
    
    # Retrieve pipeline settings
    try {
        $settings = Read-AdoOrganizationPipelinesSettings -Organization $Organization -AccessToken $AccessToken
        if ($null -eq $settings) {
            if ($null -eq $script:collectionStatus) {
                Write-Error "No Organization pipeline settings returned from Read-AdoOrganizationPipelinesSettings."
            } else {
                Set-AzDevOpsCollectionStatus -Status Unavailable -ReasonCode MissingRequiredData
                return
            }
        }
    }
    catch {
        throw "Failed to get Organization pipeline settings from Azure DevOps: [$($_.Exception.Message)]"
    }
    
    # Process settings into exportable format
    $settingsDetails = @()
    $settingsObject = [PSCustomObject]@{
        statusBadgesArePrivate                            = $settings.statusBadgesArePrivate
        enforceSettableVar                                = $settings.enforceSettableVar
        enforceJobAuthScope                               = $settings.enforceJobAuthScope
        enforceJobAuthScopeForReleases                    = $settings.enforceJobAuthScopeForReleases
        enforceReferencedRepoScopedToken                  = $settings.enforceReferencedRepoScopedToken
        disableStageChooser                               = $settings.disableStageChooser
        disableClassicBuildPipelineCreation               = $settings.disableClassicBuildPipelineCreation
        disableClassicReleasePipelineCreation             = $settings.disableClassicReleasePipelineCreation
        disableInBoxTasksVar                              = $settings.disableInBoxTasksVar
        disableMarketplaceTasksVar                        = $settings.disableMarketplaceTasksVar
        disableNode6TasksVar                              = $settings.disableNode6TasksVar
        enableShellTasksArgsSanitizing                    = $settings.enableShellTasksArgsSanitizing
        forkProtectionEnabled                             = $settings.forkProtectionEnabled
        buildsEnabledForForks                             = $settings.buildsEnabledForForks
        enforceJobAuthScopeForForks                       = $settings.enforceJobAuthScopeForForks
        enforceNoAccessToSecretsFromForks                 = $settings.enforceNoAccessToSecretsFromForks
        disableImpliedYAMLCiTrigger                       = $settings.disableImpliedYAMLCiTrigger
        auditEnforceSettableVar                           = $settings.auditEnforceSettableVar
        isTaskLockdownFeatureEnabled                      = $settings.isTaskLockdownFeatureEnabled
        hasManagePipelinePoliciesPermission               = $settings.hasManagePipelinePoliciesPermission
        isCommentRequiredForPullRequest                   = $settings.isCommentRequiredForPullRequest
        requireCommentsForNonTeamMembersOnly              = $settings.requireCommentsForNonTeamMembersOnly
        requireCommentsForNonTeamMemberAndNonContributors = $settings.requireCommentsForNonTeamMemberAndNonContributors
        enableShellTasksArgsSanitizingAudit               = $settings.enableShellTasksArgsSanitizingAudit
    }
    
    # Add metadata properties
    $settingsObject | Add-Member -MemberType NoteProperty -Name ObjectType -Value 'Azure.DevOps.Organization.Pipelines.Settings' -Force
    $settingsObject | Add-Member -MemberType NoteProperty -Name ObjectName -Value "$($script:connection.Organization).OrganizationPipelineSettings" -Force
    $settingsObject | Add-Member -MemberType NoteProperty -Name name -Value "OrganizationPipelineSettings" -Force
    
    # Create structured id object
    $id = @{
        originalId   = $null
        resourceName = "OrganizationPipelineSettings"
        organization = $script:connection.Organization
    } | ConvertTo-Json -Depth 100
    $settingsObject | Add-Member -MemberType NoteProperty -Name "id" -Value $id -Force
    
    $settingsDetails += $settingsObject
    
    # Output based on parameters
    if ($PassThru) {
        Write-Output $settingsDetails
    }
    else {
        $settingsDetails | ConvertTo-Json -Depth 100 | Out-File -FilePath "$OutputPath\OrganizationPipelineSettings.ado.json"
    }
}
    
Export-ModuleMember -Function Export-AdoOrganizationPipelinesSettings