Public/Deploy-B42VM.ps1

function Deploy-B42VM {
    <#
        .SYNOPSIS
        Deploys a VM.
        .DESCRIPTION
        The Deploy-B42VM function serves as a one touch deploy point for an Azure Virtual Machine
        .EXAMPLE
        Deploy-B42VM
        .NOTES
        Run this function after establishing an Az context using Connect-AzAccount
    #>

    [CmdletBinding()]
    param (
        # The destination Resource Group Name
        [Parameter(Mandatory=$true)]
        [string] $ResourceGroupName,

        # The destination Azure region
        [Parameter(Mandatory=$false)]
        [string] $Location,

        # Parameters used for VM creation
        [Parameter(Mandatory = $false)]
        [System.Collections.Specialized.OrderedDictionary] $VMParameters = [ordered]@{},

        # If true, a Public IP and NSG will be created for the VM
        [Parameter(Mandatory = $false)]
        [switch] $IncludePublicInterface,

        # If true, the vm will use linux specific configuraiton settings. If false, the vm will use windows specific configuration settings.
        [Parameter (Mandatory = $false)]
        [switch] $IsLinux
    )

    begin {
        Write-Verbose ("{0} started at {1} " -f $MyInvocation.MyCommand, (Get-Date).ToString())
    }

    process {
        # The parameters in VirtualNetworkParameters are required. If not provided, create some defaults.
        if (!($VMParameters.Contains("vnetResourceGroupName") -and $VMParameters.Contains("vnetName") -and $VMParameters.Contains("subnetName"))) {
            $vnetReportCard = Deploy-B42VNet -ResourceGroupName $ResourceGroupName -Location "$Location" -VNetParameters $VMParameters
        }

        # A KeyVault is required, if one wasn't supplied create it then add the admin user and password.
        if (!($VMParameters.Contains("keyVaultResourceGroupName") -and $VMParameters.Contains("keyVaultName"))) {
            $keyVaultReportCard = Deploy-B42KeyVault -ResourceGroupName $ResourceGroupName -Location "$Location" -IncludeCurrentUserAccess -KeyVaultParameters ([ordered]@{keyVaultEnabledForDeployment = $true; keyVaultEnabledForTemplateDeployment = $true})
            # These values are required
            $VMParameters.Add("keyVaultResourceGroupName", $ResourceGroupName)
            $VMParameters.Add("keyVaultName", $keyVaultReportCard.Parameters.keyVaultName)
        }
        # Should the VM just reference the password in the keyVault?
        $userSecret = Add-Secret -KeyVaultName $VMParameters.keyVaultName -SecretName "AdminUsername" -SecretValue "azdam"
        $passSecret = Add-Secret -KeyVaultName $VMParameters.keyVaultName -SecretName "AdminPassword" -SecretValue (New-B42Password)
        $VMParameters.Add("vmAdminUsername", $userSecret.SecretValueText)
        $VMParameters.Add("vmAdminPassword", $passSecret.SecretValueText)

        # Deploy VM Specific pre-reqs.
        $prereqTemplates = @()
        if ($IncludePublicInterface) {
            $prereqTemplates += "PublicIP"
            $VMParameters.Add("nsgSecurityRules", (Get-NSGList -IsLinux:$IsLinux))
            $prereqTemplates += "NSG"
        }
        $prereqTemplates += "NetworkInterface"
        $prereqDeployments = New-B42Deployment -ResourceGroupName $ResourceGroupName -Location "$Location" -Templates $prereqTemplates -TemplateParameters $VMParameters
        $prereqReportCard = Test-B42Deployment -ResourceGroupName $ResourceGroupName -Templates $prereqTemplates -TemplateParameters $VMParameters -Deployments $prereqDeployments

        if ($prereqReportCard.SimpleReport() -ne $true) {
            throw "Failed to deploy Pre-Reqs"
        }
        $VMParameters.Add("networkInterfaceName", $prereqReportCard.Parameters.networkInterfaceName)

        # TODO: More tokens for resourceid
        if ($VMParameters.Contains("imageName")) {
            $VMParameters.Add("vmImagePublisher", "")
            $VMParameters.Add("vmImageOffer", "")
            $VMParameters.Add("vmImageSKU", "")
        }

        # Deploy the actual VM
        $requiredTemplates = @()
        if ($IsLinux) {
            $requiredTemplates += "LinVM"
            # TODO generate the SSH bits here.
        } else {
            $requiredTemplates += "WinVM"

            # Create a self-signed cert for use with WinRM over HTTPS
            $certPath = ("{0}\Blue42VM.pfx" -f (Convert-Path -Path ".\"))
            $certForms = Get-B42CertificateForms -CertificatePath $certPath -DomainNames @("testing.local")
            # TODO Why is this still here? Should the helper delete it?
            $null = Remove-Item $certPath

            $null = Add-Secret -KeyVaultName $VMParameters.keyVaultName -SecretName "CertPassword" -SecretValue $certForms.Password
            $certSecret = Add-Secret -KeyVaultName $VMParameters.keyVaultName -SecretName "Cert" -SecretValue $certForms.JsonArray
            $VMParameters.Add("vmCertificateSecretUrl", $certSecret.Id)
        }
        $requiredDeployments = New-B42Deployment -ResourceGroupName $ResourceGroupName -Location "$Location" -Templates $requiredTemplates -TemplateParameters $VMParameters
        $requiredReportCard = Test-B42Deployment -ResourceGroupName $ResourceGroupName -Templates $requiredTemplates -TemplateParameters $VMParameters -Deployments $requiredDeployments

        if ($requiredReportCard.SimpleReport() -ne $true) {
            throw "Failed to deploy the VM"
        }

        if($requiredReportCard.Parameters.vmIdentity -eq "SystemAssigned"){
            # Find the Managed Service Identity PrincipalId and grant it permission to query the secrets.
            $vmInfoPS = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $requiredReportCard.Parameters.vmName
            if (![string]::IsNullOrEmpty($vmInfoPS.Identity.PrincipalId)) {
                Set-AzKeyVaultAccessPolicy -VaultName $VMParameters.keyVaultName -PermissionsToSecrets get, list -ObjectId $vmInfoPS.Identity.PrincipalId
            }
        }
        # This report card only contains the results of the VM deployment.
        $requiredReportCard
    }

    end {
        Write-Verbose ("{0} ended at {1} " -f $MyInvocation.MyCommand, (Get-Date).ToString())
    }
}