Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1

<#
.SYNOPSIS
Creates a new Azure DevOps authentication provider.
 
.DESCRIPTION
The New-AzDoAuthenticationProvider function configures authentication for Azure DevOps DSC.
It supports multiple authentication methods: Personal Access Token, Managed Identity,
Service Principal with Client Secret, Service Principal with Certificate, Azure CLI, and
Workload Identity Federation (file-based, GitHub Actions OIDC, or a manually-supplied token).
 
.PARAMETER OrganizationName
Specifies the name of the Azure DevOps organization.
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -PersonalAccessToken "myPAT"
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -useManagedIdentity
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -TenantId "..." -ClientId "..." -ClientSecret "..."
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -TenantId "..." -ClientId "..." -CertificateThumbprint "..."
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -useAzureCLI
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -TenantId "..." -ClientId "..." -FederatedTokenFile "/var/run/secrets/azure/tokens/azure-identity-token"
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -TenantId "..." -ClientId "..." -useGitHubActionsOIDC
 
.EXAMPLE
New-AzDoAuthenticationProvider -OrganizationName "Contoso" -TenantId "..." -ClientId "..." -FederatedToken $secureJwt
#>

Function New-AzDoAuthenticationProvider
{
    [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')]
    param (
        # Organization Name
        [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringPersonalAccessToken')]
        [Parameter(Mandatory = $true, ParameterSetName = 'ManagedIdentity')]
        [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringServicePrincipal')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
        [Parameter(Mandatory = $true, ParameterSetName = 'CertificateFile')]
        [Parameter(Mandatory = $true, ParameterSetName = 'AzureCLI')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationFile')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationToken')]
        [Alias('OrgName')]
        [String]
        $OrganizationName,

        # Personal Access Token
        [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')]
        [Alias('PAT')]
        [String]
        $PersonalAccessToken,

        # SecureString Personal Access Token
        [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringPersonalAccessToken')]
        [Alias('SecureStringPAT')]
        [SecureString]
        $SecureStringPersonalAccessToken,

        # Use Managed Identity
        [Parameter(ParameterSetName = 'ManagedIdentity')]
        [Switch]
        $useManagedIdentity,

        # Service Principal parameters
        [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringServicePrincipal')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
        [Parameter(Mandatory = $true, ParameterSetName = 'CertificateFile')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationFile')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationToken')]
        [String]
        $TenantId,

        [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringServicePrincipal')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
        [Parameter(Mandatory = $true, ParameterSetName = 'CertificateFile')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationFile')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationToken')]
        [String]
        $ClientId,

        [Parameter(Mandatory = $true, ParameterSetName = 'ServicePrincipal')]
        [String]
        $ClientSecret,

        [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringServicePrincipal')]
        [SecureString]
        $SecureStringClientSecret,

        # Certificate parameters (Windows cert store thumbprint)
        [Parameter(Mandatory = $true, ParameterSetName = 'Certificate')]
        [String]
        $CertificateThumbprint,

        # Certificate parameters (PFX file, cross-platform)
        [Parameter(Mandatory = $true, ParameterSetName = 'CertificateFile')]
        [String]
        $CertificatePath,

        [Parameter(Mandatory = $true, ParameterSetName = 'CertificateFile')]
        [SecureString]
        $CertificatePassword,

        # Use Azure CLI credentials
        [Parameter(ParameterSetName = 'AzureCLI')]
        [Switch]
        $useAzureCLI,

        # Workload Identity Federation - file-based federated token (e.g. AKS/Kubernetes workload identity)
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationFile')]
        [String]
        $FederatedTokenFile,

        # Workload Identity Federation - acquire the federated token from GitHub Actions' OIDC endpoint
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [Switch]
        $useGitHubActionsOIDC,

        [Parameter(ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [String]
        $GitHubActionsAudience = 'api://AzureADTokenExchange',

        # Workload Identity Federation - a federated token already obtained by the caller
        [Parameter(Mandatory = $true, ParameterSetName = 'WorkloadIdentityFederationToken')]
        [SecureString]
        $FederatedToken,

        # Don't verify the Token
        [Parameter(ParameterSetName = 'ManagedIdentity')]
        [Parameter(ParameterSetName = 'PersonalAccessToken')]
        [Parameter(ParameterSetName = 'ServicePrincipal')]
        [Parameter(ParameterSetName = 'SecureStringServicePrincipal')]
        [Parameter(ParameterSetName = 'Certificate')]
        [Parameter(ParameterSetName = 'CertificateFile')]
        [Parameter(ParameterSetName = 'AzureCLI')]
        [Parameter(ParameterSetName = 'WorkloadIdentityFederationFile')]
        [Parameter(ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [Parameter(ParameterSetName = 'WorkloadIdentityFederationToken')]
        [Switch]
        $NoVerify,

        # Do not export the Token (used by DSC resources that inherit from base class)
        [Parameter(ParameterSetName = 'PersonalAccessToken')]
        [Parameter(ParameterSetName = 'SecureStringPersonalAccessToken')]
        [Parameter(ParameterSetName = 'ManagedIdentity')]
        [Parameter(ParameterSetName = 'ServicePrincipal')]
        [Parameter(ParameterSetName = 'SecureStringServicePrincipal')]
        [Parameter(ParameterSetName = 'Certificate')]
        [Parameter(ParameterSetName = 'CertificateFile')]
        [Parameter(ParameterSetName = 'AzureCLI')]
        [Parameter(ParameterSetName = 'WorkloadIdentityFederationFile')]
        [Parameter(ParameterSetName = 'WorkloadIdentityFederationGitHubActions')]
        [Parameter(ParameterSetName = 'WorkloadIdentityFederationToken')]
        [Switch]
        $isResource

    )

    # Test if $ENV:AZDODSC_CACHE_DIRECTORY is set. If not, throw an error.
    if ($null -eq $ENV:AZDODSC_CACHE_DIRECTORY)
    {
        Throw "[New-AzDoAuthenticationProvider] The Environment Variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the Environment Variable 'AZDODSC_CACHE_DIRECTORY' to the Cache Directory."
    }

    # Set the Global Variables
    $Global:DSCAZDO_OrganizationName = $OrganizationName
    $Global:DSCAZDO_AuthenticationToken = $null

    #
    # If the parameterset is PersonalAccessToken
    if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken')
    {

        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName."

        # if the NoVerify switch is not set, verify the Token.
        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -OrganizationName $OrganizationName
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -Verify -OrganizationName $OrganizationName
        }

    }
    # If the parameterset is ManagedIdentity
    elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity')
    {

        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Azure Managed Identity with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName -Verify
        }

    }
    # If the parameterset is SecureStringPersonalAccessToken
    elseif ($PSCmdlet.ParameterSetName -eq 'SecureStringPersonalAccessToken')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName."
        $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken -OrganizationName $OrganizationName
    }
    # If the parameterset is ServicePrincipal
    elseif ($PSCmdlet.ParameterSetName -eq 'ServicePrincipal')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Service Principal token with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -ClientSecret $ClientSecret
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -ClientSecret $ClientSecret -Verify
        }
    }
    # If the parameterset is SecureStringServicePrincipal
    elseif ($PSCmdlet.ParameterSetName -eq 'SecureStringServicePrincipal')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Service Principal token (SecureString) with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -SecureStringClientSecret $SecureStringClientSecret
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -SecureStringClientSecret $SecureStringClientSecret -Verify
        }
    }
    # If the parameterset is Certificate (thumbprint)
    elseif ($PSCmdlet.ParameterSetName -eq 'Certificate')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Certificate token (thumbprint) with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalCertificateToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $CertificateThumbprint
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalCertificateToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $CertificateThumbprint -Verify
        }
    }
    # If the parameterset is CertificateFile (PFX path)
    elseif ($PSCmdlet.ParameterSetName -eq 'CertificateFile')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Certificate token (PFX file) with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalCertificateToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -CertificatePath $CertificatePath -CertificatePassword $CertificatePassword
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzServicePrincipalCertificateToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -CertificatePath $CertificatePath -CertificatePassword $CertificatePassword -Verify
        }
    }
    # If the parameterset is AzureCLI
    elseif ($PSCmdlet.ParameterSetName -eq 'AzureCLI')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating an Azure CLI token with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzCliToken -OrganizationName $OrganizationName
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzCliToken -OrganizationName $OrganizationName -Verify
        }
    }
    # If the parameterset is WorkloadIdentityFederationFile
    elseif ($PSCmdlet.ParameterSetName -eq 'WorkloadIdentityFederationFile')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Workload Identity Federation token (file) with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzWorkloadIdentityFederationToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -FederatedTokenFile $FederatedTokenFile
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzWorkloadIdentityFederationToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -FederatedTokenFile $FederatedTokenFile -Verify
        }
    }
    # If the parameterset is WorkloadIdentityFederationGitHubActions
    elseif ($PSCmdlet.ParameterSetName -eq 'WorkloadIdentityFederationGitHubActions')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Workload Identity Federation token (GitHub Actions OIDC) with OrganizationName $OrganizationName."

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzWorkloadIdentityFederationToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -GitHubActions -GitHubActionsAudience $GitHubActionsAudience
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzWorkloadIdentityFederationToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -GitHubActions -GitHubActionsAudience $GitHubActionsAudience -Verify
        }
    }
    # If the parameterset is WorkloadIdentityFederationToken
    elseif ($PSCmdlet.ParameterSetName -eq 'WorkloadIdentityFederationToken')
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] Creating a Workload Identity Federation token (manually-supplied) with OrganizationName $OrganizationName."

        $BSTR                = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($FederatedToken)
        $plainFederatedToken = if ($isLinux) {
            [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BSTR)
        } else {
            [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
        }
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

        if ($NoVerify)
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzWorkloadIdentityFederationToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -FederatedToken $plainFederatedToken
        }
        else
        {
            $Global:DSCAZDO_AuthenticationToken = Get-AzWorkloadIdentityFederationToken -OrganizationName $OrganizationName -TenantId $TenantId -ClientId $ClientId -FederatedToken $plainFederatedToken -Verify
        }
    }

    # Export the Token information to the Cache Directory
    if ($isResource.IsPresent)
    {
        Write-Verbose "[New-AzDoAuthenticationProvider] isResource is set. The Token will not be exported."
        return
    }


    # Initialize the Cache
    Get-AzDoCacheObjects | ForEach-Object {
        Initialize-CacheObject -CacheType $_
    }

    # Iterate through Each of the Caching Commands and initalize the Cache.
    Get-Command "AzDoAPI_*" | Where-Object Source -eq 'AzureDevOpsDsc.Common' | ForEach-Object {
        . $_.Name -OrganizationName $AzureDevopsOrganizationName
    }

    # Export the Token to the Cache Directory

    # Create an Object Containing the Organization Name.
    $moduleSettingsPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "ModuleSettings.clixml"
    Write-Verbose "[New-AzDoAuthenticationProvider] Exporting the Module Settings to $moduleSettingsPath."

    $objectSettings = [PSCustomObject]@{
        OrganizationName = (Get-AzDoOrganizationName)
        Token = $Global:DSCAZDO_AuthenticationToken
        SecurityDescriptorTypes = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "SecurityDescriptors.clixml"
    }

    # Export the Object to the Cache Directory
    $objectSettings | Export-Clixml -LiteralPath $moduleSettingsPath -Depth 5

}