AzurePrincipalWithSecret.ps1

[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Scope="Function", Target="*")]
param()

###########################################################
# New-AzurePrincipalWithSecret
###########################################################

function New-AzurePrincipalWithSecret
{
<#
.SYNOPSIS
Adds AzureRM Active Directory Application and persists secrets to Key Vault for it.
 
.DESCRIPTION
1. Creates a new Azure Active Directory Application
2. Creates new secrets in Azure Key Vault for the AAD Application, namely the TenantId, IdentifierUri, ApplicationId and Application Secret
 
.PARAMETER SystemName
The system the application is for.
 
.PARAMETER PrincipalPurpose
The purpose of the principal Authentication or Configuration.
 
.PARAMETER EnvironmentName
The environment the application is for.
 
.PARAMETER PrincipalPassword
The password for the principal.
 
.PARAMETER VaultSubscriptionId
The subscription Id that Key Vault is on.
 
.PARAMETER PrincipalName
The name of the Key Vault principal.
 
.EXAMPLE
New-AzurePrincipalWithSecret -SystemName 'sys1' `
                             -PrincipalPurpose 'Authentication' `
                             -EnvironmentName 'test' `
                             -PrincipalPassword 'something123$' `
                             -VaultSubscriptionId '[ID HERE]' `
                             -PrincipalName 'test'
.NOTES
Currently CmdletBinding doesn't have any internal support built-in.
#>

    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$true, Position=0)]
        [string] $SystemName,

        [parameter(Mandatory=$true, Position=1)]
        [ValidateSet('Configuration','Authentication')]
        [string] $PrincipalPurpose,

        [parameter(Mandatory=$true, Position=2)]
        [string] $EnvironmentName,

        [parameter(Mandatory=$true, Position=4)]
        [string] $PrincipalPassword,

        [parameter(Mandatory=$false, Position=5)]
        [string] $VaultSubscriptionId,

        [parameter(Mandatory=$false, Position=6)]
        [string] $PrincipalName
    )

    # GUARD: There are no non letter characters on the Principal Name
    if(-not [string]::IsNullOrWhiteSpace($PrincipalName) -and -not ($PrincipalName -match "^([A-Za-z])*$")) {
        throw 'The PrincipalName must be letters only, either lower and upper case. Cannot contain any digits or any non-alpha-numeric characters.'
    }

    # Uniform all the namings
    if([string]::IsNullOrWhiteSpace($PrincipalName)) {
        $principalIdDashed = "$($SystemName)-$($PrincipalPurpose)".ToLower()
        $principalIdDotted = "$($SystemName).$($PrincipalPurpose)".ToLower()
        $identifierUri = "https://$($SystemName).$($PrincipalPurpose).$($EnvironmentName)".ToLower()
    }
    else {
        $principalIdDashed = "$($SystemName)-$($PrincipalPurpose)-$($PrincipalName)".ToLower()
        $principalIdDotted = "$($SystemName).$($PrincipalPurpose).$($PrincipalName.ToLower())"
        $identifierUri = "https://$($SystemName).$($PrincipalPurpose).$($EnvironmentName).$($PrincipalName)".ToLower()
    }

    # GUARD: AD application already exists, return the existing ad application
    $previousApplication = Get-AzureRmADApplication -DisplayNameStartWith $principalIdDotted
    if($previousApplication -ne $null) {
        Write-Warning 'An AD Application Already exists that looks identical to what you are trying to create'
        return $previousApplication
    }

    # GUARD: Certificate system vault exists
    $systemVaultName = "$($SystemName)-$($EnvironmentName)".ToLower()
    if((Get-AzureRmKeyVault -VaultName $systemVaultName) -eq $null) {
        throw "The system vault $systemVaultName doesn't exist in the current subscription. Create it before running this cmdlet!"
    }

    # Aquire the tenant ID on the subscription we are creating the principal on, not on the vault subscription!
    $tenantId = (Get-AzureRmContext).Subscription.TenantId

    # Create the Azure Active Directory Application
    $azureAdApplication = New-AzureRmADApplication -DisplayName $principalIdDotted `
                                                   -HomePage $identifierUri `
                                                   -IdentifierUris $identifierUri `
                                                   -Password $PrincipalPassword `
                                                   -Verbose

    Write-Host -ForegroundColor Green  "Application ID: $($azureAdApplication.ApplicationId)"

    # Create the Service Principal and connect it to the Application
    $null = New-AzureRmADServicePrincipal -ApplicationId $azureAdApplication.ApplicationId

    # Switch to the KeyVault Techops-Management subscription
    $currentSubId = (Get-AzureRmContext).Subscription.SubscriptionId
    if(($VaultSubscriptionId -ne $null) -and ($currentSubId -ne $VaultSubscriptionId)) {
        Select-AzureRmSubscription -SubscriptionId $VaultSubscriptionId -ErrorAction Stop
    }

    # Populate the system keyvault with all relevant principal configuration information
    $null = Set-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$principalIdDashed-TenantId" -SecretValue (ConvertTo-SecureString -String $tenantId -AsPlainText –Force)
    $null = Set-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$principalIdDashed-IdentifierUri" -SecretValue (ConvertTo-SecureString -String $identifierUri -AsPlainText –Force)
    $null = Set-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$principalIdDashed-ApplicationId" -SecretValue (ConvertTo-SecureString -String $($azureAdApplication.ApplicationId) -AsPlainText –Force)
    $null = Set-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$principalIdDashed-ApplicationSecret" -SecretValue (ConvertTo-SecureString -String $PrincipalPassword -AsPlainText –Force)

    # Swap back to the subscription the user was in
    if(($VaultSubscriptionId -ne $null) -and ($currentSubId -ne $VaultSubscriptionId)) {
        Select-AzureRmSubscription -SubscriptionId $currentSubId | Out-Null
    }

    return $azureAdApplication
}

###########################################################
# Remove-AzurePrincipalWithSecret
###########################################################

function Remove-AzurePrincipalWithSecret
{
<#
.SYNOPSIS
Removes the Azure Active Directory Application, Principal and any secrets stored for it.
 
.DESCRIPTION
Removes the Azure Active Directory Application, Principal and any secrets stored for it.
 
.PARAMETER ADApplicationId
The Id of the Azure Active Directory Application you with to remove.
 
.PARAMETER ADApplication
The Azure Active Directory Application you with to remove.
 
.PARAMETER VaultSubscriptionId
The subscription Id that Key Vault is on.
 
.EXAMPLE
Remove-AzurePrincipalWithSecret -ADApplicationId '[ID HERE]' -VaultSubscriptionId '[ID HERE]'
 
.NOTES
Currently CmdletBinding doesn't have any internal support built-in.
#>

    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$false, Position=0)]
        [string] $ADApplicationId,

        [parameter(
            Mandatory=$false,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            Position=1)]
        [object] $ADApplication,

        [parameter(Mandatory=$true, Position=2)]
        [string] $VaultSubscriptionId
    )

    # GUARD: At least one parameter is supplied
    if((-not $ADApplicationId) -and (-not $ADApplication)) {
        throw 'You must either supply the PSADApplication object in the pipeline or the ApplicationID guid.'
    }

    if(-not $ADApplication) {
        $adApp = Get-AzureRmADApplication -ApplicationId $ADApplicationId

        # GUARD: If ADApplicationId is supplied, make sure it's a valid one that really exists
        if(-not $adApp) {
            throw "The specified ADApplicationID [$ADApplicationId] doesn't exist"
        }

        $identifierUri = $adApp.IdentifierUris[0]
    }
    else {
        $identifierUri = $ADApplication.IdentifierUris[0]
    }

    # Break the Identifier URI of the AD Application into it's individual components so that we can infer everything else.
    if(-not ($identifierUri -match 'https:\/\/(?<system>[^.]*).(?<purpose>[^.]*).(?<environment>[^.]*).(?<principalName>[^.]*)')) {
        throw "Can't infer the correct system information from the identifier URI [$identifierUri] in the AD Application, was this service principal created with this Module?"
    }

    $systemName = $Matches['system']
    $principalPurpose = $Matches['purpose']
    $environmentName = $Matches['environment']
    $principalName = $Matches['principalName']

    # Uniform all the namings
    if([string]::IsNullOrWhiteSpace($principalName)) {
        $dashName = "$systemName-$principalName"
        $dotName = "$systemName.$principalName"
    }
    else {
        $dashName = "$systemName-$principalPurpose-$principalName"
        $dotName = "$systemName.$principalPurpose.$principalName"
    }

    # Switch to the KeyVault Techops-Management subscription
    $currentSubId = (Get-AzureRmContext).Subscription.SubscriptionId
    if($currentSubId -ne $VaultSubscriptionId) {
        Select-AzureRmSubscription -SubscriptionId $VaultSubscriptionId -ErrorAction Stop
    }

    $systemVaultName = "$($systemName)-$($environmentName)".ToLower() 

    # 1. Remove the principal configuration information from the system keyvault
    Remove-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$dashName-TenantId" -Force -Confirm:$false
    Remove-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$dashName-IdentifierUri" -Force -Confirm:$false
    Remove-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$dashName-ApplicationId" -Force -Confirm:$false
    Remove-AzureKeyVaultSecret -VaultName $systemVaultName -Name "$dashName-ApplicationSecret" -Force -Confirm:$false

    # Swap back to the subscription the user was in
    if($currentSubId -ne $VaultSubscriptionId) {
        Select-AzureRmSubscription -SubscriptionId $currentSubId | Out-Null
    }

    # 2. Remove the AD Service Principal
    $servicePrincipal = Get-AzureRmADServicePrincipal -SearchString $dotName -ErrorAction SilentlyContinue
    if($servicePrincipal) {
        Remove-AzureRmADServicePrincipal -ObjectId $servicePrincipal.Id -Force
    }
    else {
        Write-Warning "Couldn't find any Service Principal using the search string [$dotName]"
    }

    # 3. Remove the AD Application
    $adApplication = Get-AzureRmADApplication -DisplayNameStartWith $dotName -ErrorAction SilentlyContinue
    if($adApplication) {
        Remove-AzureRmADApplication -ObjectId $adApplication.ObjectId -Force
    }
}