Add-AzureAdClientCertificate.ps1

<#
.SYNOPSIS
    Add a newly generated client certificate to a confidential client in Azure AD.
.EXAMPLE
    PS C:\>Get-AzureADApplication -Filter "AppId eq '00000000-0000-0000-0000-000000000000'" | Add-AzureAdClientCertificate
    Get an Azure AD application registration using Azure AD module, generates a non-exportable client certificate valid for 1 year, and adds it to the Azure AD object.
.EXAMPLE
    PS C:\>Get-AzureADServicePrincipal -Filter "AppId eq '00000000-0000-0000-0000-000000000000'" | Add-AzureAdClientCertificate
    Get an Azure AD service principal using Azure AD module, generates a non-exportable client certificate valid for 1 year, and adds it to the Azure AD object.
.EXAMPLE
    PS C:\>New-AzureAdConfidentialClient | Add-AzureAdClientCertificate
    Creates new Azure AD application registration, generates a non-exportable client certificate valid for 1 year, and adds it to the Azure AD object.
.EXAMPLE
    PS C:\>Add-AzureAdClientCertificate -ObjectId '00000000-0000-0000-0000-000000000000' -MakePrivateKeyExportable -Lifetime (New-TimeSpan -End (Get-Date).AddYears(3))
    Generates an exportable client certificate valid for 3 years and adds it to the Azure AD object.
.INPUTS
    System.String
#>

function Add-AzureAdClientCertificate {
    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2])]
    param(
        # Specifies the object id of the application or service principal.
        [Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [string] $ObjectId,
        # Allows certificate private key to be exported from local machine.
        [Parameter(Mandatory=$false)]
        [switch] $MakePrivateKeyExportable,
        # Valid lifetime of client certificate.
        [Parameter(Mandatory=$false)]
        [timespan] $Lifetime,
        # Specifies the ID of a tenant.
        [Parameter(Mandatory=$false)]
        [string] $TenantId,
        # Specifies the UPN of a user.
        [Parameter(Mandatory=$false)]
        [string] $AccountId
    )

    begin
    {
        $PSModule = Install-AzureAdModule -ErrorAction Stop
        Import-Module -ModuleInfo $PSModule -ErrorAction Stop
        $AzureADSessionInfo = Connect-AzureAdModule -TenantId $TenantId -AccountId $AccountId -ErrorAction Stop

        $InvokeCommandMessage = "`r`n{0}`r`n`r`nDo you want to invoke the above command(s)?"
        [System.Management.Automation.Host.ChoiceDescription[]] $ConfirmChoices = @(
            New-Object System.Management.Automation.Host.ChoiceDescription -ArgumentList "&Yes", "Continue with the operation."
            New-Object System.Management.Automation.Host.ChoiceDescription -ArgumentList "&No", "Do not proceed with the operation."
        )
    }

    process
    {
        ## Lookup Azure AD Object
        $AzureADObject = Get-AzureADObjectByObjectId -ObjectId $ObjectId -ErrorAction Stop

        if ($PSEdition -eq 'Desktop') {
            ## Generate Certificate
            [hashtable] $paramAzureAdClientCertificate = @{}
            if ($MakePrivateKeyExportable) { $paramAzureAdClientCertificate['MakePrivateKeyExportable'] = $MakePrivateKeyExportable }
            if ($Lifetime) { $paramAzureAdClientCertificate['Lifetime'] = $Lifetime }
            [System.Security.Cryptography.X509Certificates.X509Certificate2] $ClientCertificate = New-AzureAdClientCertificate $AzureADObject.DisplayName @paramAzureAdClientCertificate
        }
        else {
            ## Prompt for Certificate Path
            $InputParameters = Write-HostPrompt "Input" "Supply values for the following parameters:" -Fields @(
                New-Object System.Management.Automation.Host.FieldDescription -ArgumentList "CertificatePath"
            )
            if (!$InputParameters['CertificatePath']) {
                $Exception = New-Object System.Management.Automation.PSArgumentException -ArgumentList 'CertificatePath must not be empty or null.'
                Write-Error -Exception $Exception -Category ([System.Management.Automation.ErrorCategory]::InvalidArgument) -CategoryActivity $MyInvocation.MyCommand -ErrorId 'AddAzureAdClientCertificateFailureInvalidArgument' -TargetObject $InputParameters['CertificatePath'] -ErrorAction Stop
            }
            [System.Security.Cryptography.X509Certificates.X509Certificate2] $ClientCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $InputParameters['CertificatePath']
        }
        Write-Output $ClientCertificate

        ## Add Certificate
        $paramKeyCredential = [ordered]@{
            Type = 'AsymmetricX509Cert'
            Usage = 'Verify'
            Value = (ConvertTo-Base64String $ClientCertificate.GetRawCertData())
            StartDate = $ClientCertificate.NotBefore
            EndDate = $ClientCertificate.NotAfter
        }
        switch ($AzureADObject.ObjectType) {
            'Application' {
                $Message = $InvokeCommandMessage -f "New-AzureADApplicationKeyCredential -ObjectId $($AzureADObject.ObjectId) $(ConvertTo-PsParameterString $paramKeyCredential -Compact)"
                $Result = Write-HostPrompt 'Add Client Certificate to Application in Azure AD:' $Message -Choices $ConfirmChoices -DefaultChoice 0
                if ($Result -eq 0) {
                    New-AzureADApplicationKeyCredential -ObjectId $AzureADObject.ObjectId -ErrorAction Stop @paramKeyCredential | Out-Null
                    #Set-AzureADApplication -ObjectId $AzureADObject.ObjectId -PublicClient $false | Out-Null
                }
                else {
                    $Exception = New-Object OperationCanceledException -ArgumentList 'Adding Client Certificate to Application in Azure AD declined by user.'
                    Write-Error -Exception $Exception -Category ([System.Management.Automation.ErrorCategory]::OperationStopped) -CategoryActivity $MyInvocation.MyCommand -ErrorId 'AddAzureAdClientCertificateUserDeclined'
                }
            }
            'ServicePrincipal' {
                $Message = $InvokeCommandMessage -f "New-AzureADServicePrincipalKeyCredential -ObjectId $($AzureADObject.ObjectId) $(ConvertTo-PsParameterString $paramKeyCredential -Compact)"
                $Result = Write-HostPrompt 'Add Client Certificate to Service Principal in Azure AD:' $Message -Choices $ConfirmChoices -DefaultChoice 0
                if ($Result -eq 0) {
                    New-AzureADServicePrincipalKeyCredential -ObjectId $AzureADObject.ObjectId @paramKeyCredential | Out-Null
                }
                else {
                    $Exception = New-Object OperationCanceledException -ArgumentList 'Adding Client Certificate to Service Principal in Azure AD declined by user.'
                    Write-Error -Exception $Exception -Category ([System.Management.Automation.ErrorCategory]::OperationStopped) -CategoryActivity $MyInvocation.MyCommand -ErrorId 'AddAzureAdClientCertificateUserDeclined'
                }
            }
        }
    }

    end {
        #Disconnect-AzureAD -Confirm:$false
    }
}