functions/public/New-FlexdeskAzureDeployment.ps1

function New-FlexdeskAzureDeployment {
    [cmdletbinding(DefaultParameterSetName="AzureTenantDomain")]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $Location,
        
        [parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('southeastasia','westeurope','northeurope','australiasoutheast')]
        [string] $FallbackLocation,
        
        [parameter()]
        [switch] $AsJob,
        
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $CustomerName,
        
        [parameter(Mandatory=$true)]
        [ValidateSet('small','medium','large','fsmall','fmedium','flarge')]
        [string] $CustomerSize = 'fsmall',
        
        [parameter()]
        [ValidateSet('none','small','medium')]
        [string] $AdditionalVMSize = 'none',
        
        [parameter()]
        [ValidateSet('yes','no')]
        [string] $UseManagedDisks = 'no',
        
        [parameter()]
        [ValidateSet('none','small')]
        [string] $AdditionalSQLInstanceSize = 'none',
        
        [ValidateSet('none','standard')]
        [string] $Backup = 'none',
        
        [ValidateSet('none','basic')]
        [string] $VPN = 'none',
        
        [parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('free')]
        [string] $Management = 'free',
        
        [parameter()]
        [ValidateSet('2012R2','2016')]
        [string] $OS = '2012R2',
        
        [parameter()]
        [string] $SysAdminPassword = $(New-SWRandomPassword),
        
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [pscredential] $Credential = (Get-Credential -Message "Please provide your Partner Credentials"),
        
        [parameter(ParameterSetName="AzureTenantId",Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $TenantId,
        
        [parameter(ParameterSetName="AzureTenantDomain",Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string] $TenantDomain,
        
        [parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $SubscriptionId,
        
        [parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $SubscriptionName,
        
        [parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('p','t','u','d')]
        [string] $NamingEnv = "p",
        
        [parameter()]
        [ValidateLength(1,4)]
        [string] $NamingCust = "demo",
        
        [parameter()]
        [ValidateLength(1,3)]
        [string] $NamingProj = "flx",
        
        [parameter()]
        [switch] $NoUpdateCheck,
        
        [parameter()]
        [ValidateSet('Standard_LRS',"Standard_ZRS","Standard_GRS","Standard_RAGRS","Premium_LRS")]
        [string] $StorageType,
        
        [parameter(DontShow=$true)]
        [string] $Log = $null
    )

<# DynamicParam {
            $ARM = ((invoke-webrequest -Uri $script:TemplateUrl -UseBasicParsing).Content)|ConvertFrom-Json
            # Set the dynamic parameters' name
            $ParameterName = 'StorageType'
             
            # Create the dictionary
            $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
 
            # Create the collection of attributes
            $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
             
            # Create and set the parameters' attributes
            $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
            $ParameterAttribute.Mandatory = $false
            $ParameterAttribute.Position = 1
 
            # Add the attributes to the attributes collection
            $AttributeCollection.Add($ParameterAttribute)
 
            # Generate and set the ValidateSet
            $arrSet = $ARM.Parameters.StorageType.allowedValues
            $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
 
            # Add the ValidateSet to the attributes collection
            $AttributeCollection.Add($ValidateSetAttribute)
 
            # Create and return the dynamic parameter
            $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
            $RunTimeParameter.Value = $ARM.Parameters.StorageType.defaultValue
            $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
            return $RuntimeParameterDictionary
    } #>

    
    begin{
    <# $StorageType = $PSBoundParameters["StorageType"] #>
        $Continue = $true
        $Management = 'free'
        if([string]::IsNullOrEmpty($Log) -eq $false){
            if(test-path $Log){} else {
                $Log = Start-Log
            }
        } else {
            $Log = Start-Log
        }
        $PSDefaultParameterValues = @{"Write-Log:Log"=$Log}
        if(!$PSBoundParameters.ContainsKey('NoUpdateCheck')){
            Test-ModuleVersion -ModuleName FlexdeskBluePrint
        }

        $CustomerNamePrefix = [Regex]::Replace($CustomerName,'[^a-zA-Z0-9]', '')
        $SecurePassword = $SysAdminPassword|ConvertTo-SecureString -AsPlainText -Force
        $ResourceGroupName = "$($NamingEnv)$($NamingCust)$($NamingProj)resg001"
        Write-Log -Message "Using $ResourceGroupName as target resource group"
        $ActiveSubscription = ""
        if($Credential){
            try {
                Connect-MsolService -Credential $Credential
                $Tenant = Get-Tenant -TenantDomain $TenantDomain -TenantId $TenantId
                if($Tenant.Default -eq $false){
                    $null = Add-AzureRMAccount -Credential $Credential -TenantId $TenantId
                } else {
                    $null = Add-AzureRMAccount -Credential $Credential
                }

                if($SubscriptionId){
                    $null = Select-AzureRmSubscription -SubscriptionId $SubscriptionId
                } elseif($SubscriptionName){
                    $null = Select-AzureRmSubscription -SubscriptionName $SubscriptionName
                } else {
                    # use default subscription
                }
            } catch {
                write-log -type error -message "Error during Azure connection: $_"
            }
        }
        try{
            $null = Get-AzureRmContext
        }
        catch {
            Write-Error "No active Azure subscription is present in the session. Please use Login-AzureRMAccount and Select-AzureRMSubscription to set the target subscription, or specify Tenant/Credential information"
            return $null
        }
        if((Get-AzureRmResourceGroup -Name $ResourceGroupName -ErrorAction Ignore) -ne $null){
            Write-Log -Type Error -Message "Resource group already exists, please choose another ResourceGroupName"
            return
        }
        if((Test-AzureRmDnsAvailability -DomainNameLabel $CustomerNamePrefix.ToLower() -Location $Location) -eq $false){
            write-log -type error -Message "Domain Name already taken, please choose another domain name"
            return
        }
        if($CustomerNamePrefix -like "*microsoft*"){
            write-log -type error -message "'Microsoft' can not be a part of the customer name, please choose another customer name"
            return
        }
        if((Test-AADPasswordComplexity -MinimumLength 12 -Password $SysAdminPassword) -eq $false){
                    write-log -type error -message "Password does not meet complexity requirements"
                    return
        }

        $CompatibilityResults = Test-AzureResourceLocation -Location $Location -ResourceFile "$Root\resources"
        $Break = $false
        $Count = 0
        if(((get-variable -name CompatibilityResults -ErrorAction Ignore) -eq $null) -or ($CompatibilityResults -eq $null)){
            $Count = 0
        } else {
            $Count = $CompatibilityResults.Count
        }
        if($Count -gt 0){
            $Continue = $false
            foreach($Result in $CompatibilityResults){
                switch -regex ($Result) {
                    
                    "microsoft\.(automation|operationalinsights|operationsmanagement)"{
                        
                        if(!$FallbackLocation){
                            if($Break){
                                break
                            }
                            $title = "Automation & Monitoring Features Unsupported for this Region"
                            $message = "The selected Azure Region does not support the automation & monitoring features of the Flexdesk Blueprint solution. In which region should they be deployed instead (by re-running this command with the -FallBackLocation Parameter you can automatically deploy non-compatible resources to a fallback region)?" 
                            $choices = new-object System.Collections.ArrayList<System.Management.Automation.Host.ChoiceDescription>
                            $null = $choices.add($(New-Object System.Management.Automation.Host.ChoiceDescription "&Cancel","Aborts the deployment"))
                            $me = get-command -name new-flexdeskazuredeployment
                            #$me.Parameters["FallbackLocation"].Attributes
                            foreach($ChoiceLocation in ((($me.Parameters["FallbackLocation"]).Attributes|?{$_.TypeId.Name -eq 'ValidateSetAttribute'})).ValidValues){
                                $null = $choices.Add($(New-Object System.Management.Automation.Host.ChoiceDescription "&$ChoiceLocation","Deploys the resources in '$ChoiceLocation'"))
                            }
                            $choices = $choices.ToArray([System.Management.Automation.Host.ChoiceDescription])
                            $result = $host.ui.PromptForChoice($title, $message, $choices, 0) 
                            write-log -message "You chose: $($choices[$result].Label.Replace('&',''))"
                            switch ($choices[$result].Label.Replace('&',''))
                            {
                                "Cancel" {write-log -message "Deployment Cancelled";$Continue = $false}
                                default {
                                    
                                    $FallbackLocation = ($choices[$result]).Label.Replace('&','')
                                    write-log -message "Continuing Deployment with Fallback Location: $FallbackLocation"
                                    $Continue = $true
                                }
                            }
                        } else {
                            $Continue = $true
                        }
                    
                    # if($Continue){
                    # $Backup = 'none';$Management = 'none'
                    # }
                    $Break = $true
                    }
                    "microsoft.recoveryservices" {
                        write-log -type warning -message "Backup is not supported at this location. The feature will not be deployed"
                        $Backup = "none"
                    }
                    Default {}
                }
                    
                
            }
        } else {
            # do nothing
        }
        $AzureParameters = @{
            customername = $CustomerNamePrefix
            customersize = $CustomerSize
            useManagedDisks = $UseManagedDisks
            sql = $AdditionalSQLInstanceSize
            vm = $AdditionalVMSize
            backupEnabled = $Backup
            vpnGateway = $VPN
            scheduleid01=$([guid]::NewGuid().ToString())
            scheduleid02=$([guid]::NewGuid().ToString())
            scheduleStartDate = (get-date).AddDays(1).ToString("yyyy/MM/dd") 
            managementEnabled =  $Management
            useFallbackLocation = $(if($FallbackLocation){'yes'}else{'no'})
            fallbackLocation = $(if($FallbackLocation ){$FallbackLocation} else {'westeurope'})
            OSVersion = $OS
            StorageType = $StorageType
            NamingEnv = $NamingEnv
            NamingCust = $NamingCust
            NamingProj = $NamingProj
        }
        $AzureParameters.Add('adminPassword',$SecurePassword)
        
        
        
    }
    process{
        if(!$Continue){return}
        Write-Log "Creating Resourcegroup '$($ResourceGroupName)'"
        try {
            $null = New-AzureRmResourceGroup -Name $ResourceGroupName -Location $Location
        }
        catch {
            Write-log -Type Error -Message "Error while deploying resource group: $_"
            return $null
        }
        write-log "Deploying solution to resource group"
        try{
            if((Get-Variable -name 'SyncHash' -ErrorAction SilentlyContinue) -eq $null){
                #write-log -message "Running in CLI mode"
                $SyncHash = [hashtable]::Synchronized(@{})
                $SyncHash.Root = $script:Root
                $SyncHash.Log = $Log
            } else {
                #write-log -message "Running in GUI mode"
            }

            $SyncHash.ResourceGroupName = $ResourceGroupName
            $SyncHash.DeploymentParameters = $AzureParameters
            $CredentialGuid = [guid]::NewGuid().Guid
            $null = Save-AzureRmProfile -path "$env:TEMP\SBSDeployment-$CredentialGuid.json" -Force

            $SyncHash.DeploymentStart = get-date
            $SyncHash.DeploymentJob = new-object psobject -Property @{
                Type='Azure'
                Duration="00:00:00"
                Status = @{
                    Deployment = @()
                    Configuration = @{
                        Domain="$CustomerNamePrefix.local"
                        Login='sysadmin'
                        Password = $SysAdminPassword
                        ResourceGroup = $ResourceGroupName
                        Connection = "https://$($CustomerNamePrefix.ToLower()).$($Location).cloudapp.azure.com/rdweb"
                    }
                }
                Completed = $false
                Error = $null
                Log = $Log
            }

            $null = invoke-operation -synchash $SyncHash -root $SyncHash.Root -Log $SyncHash.Log -code {
                try{
                    $null = Select-AzureRmProfile -Path "$env:TEMP\SBSDeployment-$CredentialGuid.json"
                    $null = New-AzureRmResourceGroupDeployment -TemplateUri "https://raw.githubusercontent.com/TVDKoni/SMBblueprint-ARM/master/azuredeploy.json" `
                    -TemplateParameterObject $SyncHash.DeploymentParameters -ResourceGroupName $SyncHash.ResourceGroupName
                    if($? -eq $false){
                        throw $Error[1]
                    }
                } catch {
                    $SyncHash.DeploymentJob.Error = $_.Exception
                }
                
            }
            
            $null = Invoke-Operation -synchash $SyncHash -log $SyncHash.Log -root $SyncHash.Root -code {
                try {
                    $null = select-azurermprofile -path "$env:TEMP\SBSDeployment-$CredentialGuid.json"
                    $DeploymentStatus = Get-AzureRmResourceGroupDeployment -ResourceGroupName $SyncHash.ResourceGroupName
                    
                    while(((($DeploymentStatus.where{$_.ProvisioningState -eq 'Running'}).count -gt 0) -or ((new-timespan -start $SyncHash.DeploymentStart -end (get-date)).TotalMinutes -lt 1)) -and ($SyncHash.DeploymentJob.Error -eq $null)){
                        $Start = $SyncHash.DeploymentStart
                        $End = Get-Date
                        $Duration = New-TimeSpan -Start $Start -End $End
                        
                        $SyncHash.DeploymentJob.Duration = $("{0:HH:mm:ss}" -f ([datetime]$Duration.Ticks))
                        $SyncHash.DeploymentJob.Status.Deployment = @()
                        foreach($Item in $DeploymentStatus){
                            $Status = new-object -TypeName psobject -Property @{
                                Name = $Item.DeploymentName
                                Status = $Item.ProvisioningState
                            }
                            $SyncHash.DeploymentJob.Status.Deployment += $Status
                        }
                        start-sleep -Seconds 10
                        $DeploymentStatus = Get-AzureRmResourceGroupDeployment -ResourceGroupName $SyncHash.ResourceGroupName
                    }


                    foreach($Item in $DeploymentStatus){
                        $Status = new-object -TypeName psobject -Property @{
                            Name = $Item.DeploymentName
                            Status = $Item.ProvisioningState
                        }
                        $SyncHash.DeploymentJob.Status.Deployment += $Status
                    }
                    
                } catch {
                    $SyncHash.DeploymentJob.Error = $_
                }
                finally{
                    $Duration = New-TimeSpan -Start $Start -End (get-date) 
                    $SyncHash.DeploymentJob.Duration = $("{0:HH:mm:ss}" -f ([datetime]$Duration.Ticks))
                    $SyncHash.DeploymentJob.Completed = $true
                }


            }
            if($AsJob){
                # return [ref]$SyncHash.DeploymentJob
            }
            else {
                while($SyncHash.DeploymentJob.Completed -ne $true){
                    Write-Progress -id 100 -Activity "Deploying Azure Solution ($($SyncHash.DeploymentJob.Duration))" -PercentComplete -1
                    $i = 0
                    foreach($Item in $SyncHash.DeploymentJob.Status.Deployment){
                        Write-Progress -Activity $Item.Name -Status $Item.Status -ParentId 100 -PercentComplete -1 -id $i
                        $i++
                    }
                    start-sleep -Seconds 10
                }
                
            }
            remove-item -Path "$env:TEMP\SBSDeployment-$CredentialGuid.json" -Force -ErrorAction Ignore
            if($SyncHash.DeploymentJob.Error -ne $null){
                throw $SyncHash.DeploymentJob.Error
            }
        }
        catch {

            # Remove-AzureRmResourceGroup -Name $ResourceGroupName -Force
            write-log -Type Error "Error while deploying solution: $($SyncHash.DeploymentJob.Error)."
        }
        finally{
            ([ref]$SyncHash.DeploymentJob).value
        }
    }
    
    end{} 
}