AzureRM.Websites.Experiments.psm1

# ----------------------------------------------------------------------------------
#
# Copyright Microsoft Corporation
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------------

function New-AzWebAppJustDoIt
{
    [CmdletBinding()]
    [OutputType([Microsoft.Azure.Management.WebSites.Models.Site])]
    param(
    [string][Parameter(Mandatory=$false)][alias("Name")]$WebAppName,
    [string][Parameter(Mandatory=$false)][alias("Group")]$ResourceGroupName,
    [string][Parameter(Mandatory=$false)][alias("Plan")]$AppServicePlan
    )
    DynamicParam{
        #Set the dynamic parameters' name
        $ParamName_location = 'Location'
        # 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
        # Add the attributes to the attributes collection
        $AttributeCollection.Add($ParameterAttribute) 
        # Create the dictionary
        $RuntimeParameterDictionary = New-Object  System.Management.Automation.RuntimeDefinedParameterDictionary
        #Generate and set the ValidateSet
        $providerNamespace = "Microsoft.Web"
        try
        {
            $availableLocations = $(Get-AzureRmResourceProvider | Where-Object {$_.ProviderNamespace -eq $providerNamespace}).Locations   
        }
        catch
        {
            throw $_        
        }     
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($availableLocations)
        # Add the ValidateSet to the attributes collection
        $AttributeCollection.Add($ValidateSetAttribute)
        # Create and return the dynamic parameter
        $RuntimeParameter = New-Object  System.Management.Automation.RuntimeDefinedParameter($ParamName_location, [string],  $AttributeCollection)        
        $RuntimeParameterDictionary.Add($ParamName_location, $RuntimeParameter)  

        return $RuntimeParameterDictionary
    }

    BEGIN {         
        $context = Get-Context
        $webSitesClient = Get-WebSitesClient $context
        $resourceManagementClient = Get-ResourceManagementClient $context
    }

    PROCESS {
        $mainActivity = "Create Azure Web App" 
        [string]$Location = $PSBoundParameters[$ParamName_location]
        Write-Progress `
            -Activity "Some Activity." `
            -CurrentOperation "Getting App Name information."   
        [string]$appName = Get-WebAppName $PSBoundParameters $webSitesClient
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting Resource Group information."  
        $operationNumber = 2 
        [hashtable]$groupInfo = Get-ResourceGroupInfo $PSBoundParameters $appName $resourceManagementClient
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Service Plan information." 
        $operationNumber = 3   
        [hashtable]$appPlanInfo = Get-AppServicePlanInfo $PSBoundParameters $appName $groupInfo.Name        
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Location information."  
        $operationNumber++   
        [string]$appLocation = Get-AppLocation $PSBoundParameters $groupInfo.Name $groupInfo.Exists $availableLocations
        if ($groupInfo.Exists) {          
            $appGroup = Get-AzureRmResourceGroup `
                            -Name $groupInfo.Name 
            $message = "Using resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'."    
            Write-Information -MessageData $message -InformationAction Continue            
        } else {           
            $appGroup =  New-AzureRmResourceGroup `
                            -Name $groupInfo.Name `
                            -Location $appLocation 
            $message = "Created resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'."
            Write-Information -MessageData $message -InformationAction Continue   
        }

        $operationNumber = 4 
        if ($appPlanInfo.Exists) {            
            $appPlan = Get-AzureRmAppServicePlan `
                        -Name $appPlanInfo.Name `
                        -ResourceGroupName $appPlanInfo.ResourceGroup
            $message = "Using app service plan '$($appPlan.Name)' in location '$($appPlan.Location)'." 
            Write-Information -MessageData $message -InformationAction Continue       
        } else {           
            $defaultTier = "Free"
            $appPlan = New-AzureRmAppServicePlan `
                        -Name $appPlanInfo.Name `
                        -Location $appLocation `
                        -Tier $defaultTier `
                        -ResourceGroupName $appPlanInfo.ResourceGroup
            $message = "Created app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." 
            Write-Information -MessageData $message -InformationAction Continue        
        } 
          
        $webapp =  New-AzureRmWebApp `
                    -Name $appName `
                    -AppServicePlan $appPlan.Id `
                    -ResourceGroupName $appGroup.ResourceGroupName `
                    -Location $appLocation   
         
        Write-Output $webapp            
    }

    END {}
}

<#
.ExternalHelp help\AzureRM.Websites.Experiments-help.xml
#>

function New-AzWebApp
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    [OutputType([Microsoft.Azure.Management.WebSites.Models.Site])]
    param(
    [string][Parameter(Mandatory=$false)][alias("Name")]$WebAppName,
    [string][Parameter(Mandatory=$false)][alias("Group")]$ResourceGroupName,
    [string][Parameter(Mandatory=$false)][alias("Plan")]$AppServicePlan,
    [switch][Parameter(Mandatory=$false)]$Auto,
    [switch][Parameter(Mandatory=$false)]$AddRemote,
    [string][Parameter(Mandatory=$false)]$GitRepositoryPath
    )
    DynamicParam{
        #Set the dynamic parameters' name
        $ParamName_location = 'Location'
        # 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
        # Add the attributes to the attributes collection
        $AttributeCollection.Add($ParameterAttribute) 
        # Create the dictionary
        $RuntimeParameterDictionary = New-Object  System.Management.Automation.RuntimeDefinedParameterDictionary
        #Generate and set the ValidateSet
        $providerNamespace = "Microsoft.Web"
        try
        {
            $availableLocations = $(Get-AzureRmResourceProvider | Where-Object {$_.ProviderNamespace -eq $providerNamespace}).Locations   
        }
        catch
        {
            throw $_        
        }     
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($availableLocations)
        # Add the ValidateSet to the attributes collection
        $AttributeCollection.Add($ValidateSetAttribute)
        # Create and return the dynamic parameter
        $RuntimeParameter = New-Object  System.Management.Automation.RuntimeDefinedParameter($ParamName_location, [string],  $AttributeCollection)        
        $RuntimeParameterDictionary.Add($ParamName_location, $RuntimeParameter)  

        return $RuntimeParameterDictionary
    }

    BEGIN {         
        $context = Get-Context
        $webSitesClient = Get-WebSitesClient $context
        $resourceManagementClient = Get-ResourceManagementClient $context
    }

    PROCESS {
        #Validate Parameters
        if (-not $PSBoundParameters.ContainsKey('Auto')) {
            $parametersNotProvided = @()
            if(-not $PSBoundParameters.ContainsKey('WebAppName')){
                $parametersNotProvided += 'WebAppName'
            }

            if(-not $PSBoundParameters.ContainsKey('ResourceGroupName')){
                $parametersNotProvided += 'ResourceGroupName'
            }

            if(-not $PSBoundParameters.ContainsKey('AppServicePlan')){
                $parametersNotProvided += 'AppServicePlan'
            }

            if(-not $PSBoundParameters.ContainsKey('Location')){
                $parametersNotProvided += 'Location'
            }        
               
            $message = "The following parameters were not provided: " 
            $message +=  $($parametersNotProvided -join ',') + " ."
            $message += "You can provide -Auto switch to use Smart Defaults."
            $exception = New-Object -TypeName System.Exception -ArgumentList $message
            throw $exception                   
        }

        $mainActivity = "Create Azure Web App" 
        [string]$Location = $PSBoundParameters[$ParamName_location]
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Name information."   
        [string]$appName = Get-WebAppName `
                            -ProvidedParameters $PSBoundParameters `
                            -WebSitesClient $webSitesClient
     if ($PSCmdlet.ShouldProcess($appName, "Create an Azure Web App")) {
       Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting Resource Group information."  
        [hashtable]$groupInfo = Get-ResourceGroupInfo `
                                    -ProvidedParameters $PSBoundParameters `
                                    -WebAppName $appName `
                                    -ResourceManagementClient $resourceManagementClient
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Service Plan information."  
        [hashtable]$appPlanInfo = Get-AppServicePlanInfo `
                                    -ProvidedParameters $PSBoundParameters `
                                    -WebAppName $appName `
                                    -ResourceGroupName $groupInfo.Name        
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Location information."  
        [string]$appLocation = Get-AppLocation `
                                -ProvidedParameters $PSBoundParameters `
                                -ResourceGroupName $groupInfo.Name`
                                -ResourceGroupExists $groupInfo.Exists `
                                -AvailableLocations $availableLocations            
        if ($groupInfo.Exists) {          
            $appGroup = Get-AzureRmResourceGroup `
                            -Name $groupInfo.Name 
            $message = "Using resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'."    
            Write-Information -MessageData $message -InformationAction Continue            
        } else {           
            $appGroup =  New-AzureRmResourceGroup `
                            -Name $groupInfo.Name `
                            -Location $appLocation 
            $message = "Created resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'."
            Write-Information -MessageData $message -InformationAction Continue   
        }

        if ($appPlanInfo.Exists) {            
            $appPlan = Get-AzureRmAppServicePlan `
                        -Name $appPlanInfo.Name `
                        -ResourceGroupName $appPlanInfo.ResourceGroup
            $message = "Using app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." 
            Write-Information -MessageData $message -InformationAction Continue       
        } else {           
            $defaultTier = "Free"
            $appPlan = New-AzureRmAppServicePlan `
                        -Name $appPlanInfo.Name `
                        -Location $appLocation `
                        -Tier $defaultTier `
                        -ResourceGroupName $appPlanInfo.ResourceGroup
            $message = "Created app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." 
            Write-Information -MessageData $message -InformationAction Continue        
        } 
          
        $webapp =  New-AzureRmWebApp `
                    -Name $appName `
                    -AppServicePlan $appPlan.Id `
                    -ResourceGroupName $appGroup.ResourceGroupName `
                    -Location $appLocation   
         
        Write-Output $webapp
        
        if (($PSBoundParameters.ContainsKey('AddRemote') -or $PSBoundParameters.ContainsKey('Auto')) -and $webapp) { 
            Add-Remote -ProvidedParameters $PSBoundParameters -WebApp $webapp -GitRepositoryPath $GitRepositoryPath                   
        }  
      }          
    }

    END {}
}

function Add-Remote 
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [Microsoft.Azure.Management.WebSites.Models.Site][Parameter()]$WebApp,
    [string][Parameter()] $GitRepositoryPath 
    )
    [bool]$repoDetected = $true
    [bool]$repoAdded = $true
    $OriginalErrorActionPreference = $ErrorActionPreference

    if(-Not $ProvidedParameters.ContainsKey('GitRepositoryPath')){
        $GitRepositoryPath = (Get-Location).Path 
    }

    try
    {
        $ErrorActionPreference = 'Stop'
        git -C $GitRepositoryPath status | Out-Null
    }
    catch
    {          
        $repoDetected = $false
    }
    finally
    {
        $ErrorActionPreference = $OriginalErrorActionPreference
    }

    if ($repoDetected) { 
        $message = "A git repository has been detected. " 
        try
        {               
            $ErrorActionPreference = 'Stop'
            # Get app-level deployment credentials
            $xml = [xml](Get-AzureRmWebAppPublishingProfile -Name $WebApp.Name -ResourceGroupName $WebApp.ResourceGroup -OutputFile null)
            $username = [System.Uri]::EscapeDataString($xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userName").value)
            $password = [System.Uri]::EscapeDataString($xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userPWD").value)
            $remoteName = "azure"
            $url = ("https://$username" + ':' + "$password@$($WebApp.EnabledHostNames[1])")
            # Add the Azure remote to a local Git respository
            $command = "git -C $GitRepositoryPath remote add $remoteName $url"
            Invoke-Expression -Command $command | Out-Null
            if ($gitOutPut) {
                $repoAdded = $false
            }
        }
        catch
        {
            $repoAdded = $false
        }
        finally 
        {
            $ErrorActionPreference = $OriginalErrorActionPreference
        }
        
        if ($repoAdded) {        
            $message += "Added remote '$($remoteName)'. Push your code by running the command 'git push $($remoteName) master.' "            
        } else {
            $message += "However, remote '$($remoteName)' could not be added. "
        }

        Write-Information $message -InformationAction Continue
    }
}

function Get-WebAppName 
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient][Parameter()]$WebSitesClient
    )
    [string]$name = ""
    [bool]$nameIsAvailable = $false
 
    if ($ProvidedParameters.ContainsKey('WebAppName')) {      
        $name = $ProvidedParameters.WebAppName
        $nameIsAvailable = Test-NameAvailability $name $WebSitesClient
        if (-not $NameIsAvailable) {
            $message = "Website with given name '$name' already exists."
            $exception = New-Object -TypeName System.Exception -ArgumentList $message
            throw $exception             
        }
    } else {
        for ($i = 0; $i -le 2; $i++) {
            $name ="WebApp$(Get-Random -max 1000000)"
            $nameIsAvailable = Test-NameAvailability $name $WebSitesClient
            if ($NameIsAvailable) {
                 break
            }
        }
    }
    return $name
}

function Test-NameAvailability
{
    param(
    [string][Parameter()]$WebAppName,
    [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient][Parameter()]$WebSitesClient
    )
    [string]$resourceType = "Site"
    return $WebSitesClient.CheckNameAvailabilityWithHttpMessagesAsync($WebAppName, $resourceType).Result.Body.NameAvailable
}

function Get-ResourceGroupInfo
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [string][Parameter(Mandatory=$false)]$WebAppName,    
    [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient][Parameter()]$ResourceManagementClient
    )
    [hashtable]$resourceGroupInfo = @{Name="";Exists=$false}
    $defaultName = $WebAppName 

    if ($ProvidedParameters.ContainsKey('ResourceGroupName')) {
        $resourceGroupInfo.Name = $ProvidedParameters.ResourceGroupName 
    } else {
        $resourceGroupInfo.Name = $defaultName 
    }

    $resourceGroupInfo.Exists = Test-ResourceGroupExistence $resourceGroupInfo.Name $ResourceManagementClient

    return $resourceGroupInfo
}

function Test-ResourceGroupExistence 
{
    param(
    [string][Parameter()]$ResourceGroupName,    
    [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient][Parameter()]$ResourceManagementClient
    )
    return $ResourceManagementClient.ResourceGroups.CheckExistenceWithHttpMessagesAsync($ResourceGroupName).Result.Body   
}

function Get-AppServicePlanInfo
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [string][Parameter()]$WebAppName,
    [string][Parameter()]$ResourceGroupName
    )
    [hashtable]$appServicePlanInfo = @{Name="";ResourceGroup="";IsDefaultPlan=$false;Exists=$false}   
    [object[]]$appServicePlans = Get-AzureRmAppServicePlan
    $defaultName = $WebAppName 

    if ($ProvidedParameters.ContainsKey('AppServicePlan')) {  
         $regexp = '/subscriptions/[-A-Za-z0-9]+/resourceGroups/[-\w\._\(\)]+/providers/Microsoft.Web/serverfarms/[-\w\._\(\)]+$'
         $idWasProvided = $ProvidedParameters.AppServicePlan -imatch $regexp
         if ($idWasProvided) {
            $parsedId = $ProvidedParameters.AppServicePlan.split('/')            
            $appServicePlanInfo.Name = $parsedId[$parsedId.Length - 1]
            $appServicePlanInfo.ResourceGroup =  $parsedId[4] 
            $existingPlan = $appServicePlans | Where-Object {$_.Id -eq $ProvidedParameters.AppServicePlan}
            if (-not $existingPlan) {
                $message = "The app service plan with the id provided does not exist."
                $exception = New-Object -TypeName System.Exception -ArgumentList $message
                throw $exception
            }                                
        } else {
            $appServicePlanInfo.Name = $ProvidedParameters.AppServicePlan
        }
            $appServicePlanInfo.IsDefaultPlan = $false
    } else {
        $existentDefaultPlan = Get-DefaultAppServicePlan $AppServicePlans
        if ($existentDefaultPlan) {        
            $appServicePlanInfo.Name = $existentDefaultPlan.Name 
            $appServicePlanInfo.ResourceGroup = $existentDefaultPlan.ResourceGroup
            $appServicePlanInfo.IsDefaultPlan = $true   
            $appServicePlanInfo.Exists = $true
        } else {                        
            $appServicePlanInfo.Name = $defaultName
            $appServicePlanInfo.IsDefaultPlan = $false
        }
    }

    if (-not $appServicePlanInfo.IsDefaultPlan) {
        [object[]]$appServicePlansWithProvidedName = $appServicePlans | Where-Object {$_.Name -eq $appServicePlanInfo.Name}
        if ($appServicePlansWithProvidedName) {
            $appServicePlanInfo.Exists = $true
            $appServicePlanWithProvidedNameAndGroup =  $appServicePlansWithProvidedName | Where-Object {$_.ResourceGroup -eq $ResourceGroupName}
            if ($appServicePlanWithProvidedNameAndGroup) {
                $appServicePlanInfo.ResourceGroup = $appServicePlanWithProvidedNameAndGroup.ResourceGroup
            } else {
                if ($appServicePlansWithProvidedName.Count -gt 1) {
                    $message = "There are various App Service Plans with that name. An existing Resource Group name should be provided."
                    $exception = New-Object -TypeName System.Exception -ArgumentList $message
                    throw $exception
                } else {                    
                    $appServicePlanInfo.ResourceGroup = $appServicePlansWithProvidedName.ResourceGroup
                }                
            }
        } else {            
            $appServicePlanInfo.Exists = $false
            $appServicePlanInfo.ResourceGroup = $ResourceGroupName
        }
    }  

    return $appServicePlanInfo
}

function Get-DefaultAppServicePlan
{
    param(
    [object[]][Parameter()]$AppServicePlans
    )
    [object[]]$appServicePlanMatches = $AppServicePlans | Where-Object {$_.Sku.Tier -eq "Free"}
    if($appServicePlanMatches){
        return $appServicePlanMatches[0]
    } else {
        return $null
    }
}

function Get-AppLocation 
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [string][Parameter()]$ResourceGroupName,
    [bool][Parameter()]$ResourceGroupExists,
    [string[]][Parameter()]$AvailableLocations
    )
    [string]$location = ""

    if ($ProvidedParameters.ContainsKey('Location')) {
        $location = $ProvidedParameters.Location
    } else {
        if ($ResourceGroupExists) {
            $location = $(Get-AzureRmResourceGroup -Name $ResourceGroupName).Location
        } else {
            $location = Get-DefaultLocation $AvailableLocations
        }
    }
    return $location
}

function Get-DefaultLocation
{
    param(
    [string[]][Parameter()]$AvailableLocations
    )
    # TODO: figure out a way to get a 'Smart Default Location'
    return $AvailableLocations[0]
}

function Get-Context
{
      return [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
}

function Get-ResourceManagementClient
{
    param(
    [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context
    )
    $factory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.ClientFactory
    [System.Type[]]$types = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext], [string]
    $resourceManagementClient = [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient]
    $method = [Microsoft.Azure.Commands.Common.Authentication.IClientFactory].GetMethod("CreateArmClient", $types)
    
    $closedMethod = $method.MakeGenericMethod($resourceManagementClient)
    $arguments = $Context, [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureEnvironment+Endpoint]::ResourceManager
    $client = $closedMethod.Invoke($factory, $arguments)

    return $client
}

function Get-WebSitesClient
{
    param(
    [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context
    )
    $factory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.ClientFactory
    [System.Type[]]$types = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext], [string]
    $webSitesClient = [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient]
    $method = [Microsoft.Azure.Commands.Common.Authentication.IClientFactory].GetMethod("CreateArmClient", $types)

    $closedMethod = $method.MakeGenericMethod($webSitesClient)
    $arguments = $context, [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureEnvironment+Endpoint]::ResourceManager
    $client = $closedMethod.Invoke($factory, $arguments)

    return $client
}

<#
.ExternalHelp help\AzureRM.Websites.Experiments-help.xml
#>

function New-AzWebAppGrayParam
{
    [CmdletBinding(SupportsShouldProcess=$true)]        
    [OutputType([Microsoft.Azure.Management.WebSites.Models.Site])]
    param(
    [string][Parameter(Mandatory=$false)][alias("Name")]$WebAppName,
    [string][Parameter(Mandatory=$false)][alias("Group")]$ResourceGroupName,
    [string][Parameter(Mandatory=$false)][alias("Plan")]$AppServicePlan,
    [switch][Parameter(Mandatory=$false)]$Auto,
    [switch][Parameter(Mandatory=$false)]$AddRemote,
    [string][Parameter(Mandatory=$false)]$GitRepositoryPath
    )
    DynamicParam{
        #Set the dynamic parameters' name
        $ParamName_location = 'Location'
        # 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
        if ($PSBoundParameters.ContainsKey('Auto')) {
            $ParameterAttribute.Mandatory = $false
        } else {             
            $ParameterAttribute.Mandatory = $true
        }
        # Add the attributes to the attributes collection
        $AttributeCollection.Add($ParameterAttribute) 
        # Create the dictionary
        $RuntimeParameterDictionary = New-Object  System.Management.Automation.RuntimeDefinedParameterDictionary
        #Generate and set the ValidateSet
        $providerNamespace = "Microsoft.Web"
        try
        {
            $availableLocations = $(Get-AzureRmResourceProvider | Where-Object {$_.ProviderNamespace -eq $providerNamespace}).Locations   
        }
        catch
        {
            throw $_        
        }     
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($availableLocations)
        # Add the ValidateSet to the attributes collection
        $AttributeCollection.Add($ValidateSetAttribute)
        # Create and return the dynamic parameter
        $RuntimeParameter = New-Object  System.Management.Automation.RuntimeDefinedParameter($ParamName_location, [string],  $AttributeCollection)        
        $RuntimeParameterDictionary.Add($ParamName_location, $RuntimeParameter)  

        return $RuntimeParameterDictionary
    }

    BEGIN {         
        $context = Get-Context
        $webSitesClient = Get-WebSitesClient $context
        $resourceManagementClient = Get-ResourceManagementClient $context
    }

    PROCESS {
        $mainActivity = "Create Azure Web App" 
        [string]$Location = $PSBoundParameters[$ParamName_location]
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Name information."   
        [string]$appName = Get-WebAppNameGrayParam $PSBoundParameters $webSitesClient
    if ($PSCmdlet.ShouldProcess($appName, "Create an Azure Web App")) {

        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting Resource Group information." 
        [hashtable]$groupInfo = Get-ResourceGroupInfoGrayParam $PSBoundParameters $appName $resourceManagementClient
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Service Plan information."   
        [hashtable]$appPlanInfo = Get-AppServicePlanInfoGrayParam $PSBoundParameters $appName $groupInfo.Name        
        Write-Progress `
            -Activity $mainActivity `
            -CurrentOperation "Getting App Location information."   
        [string]$appLocation = Get-LocationGrayParam $PSBoundParameters $groupInfo.Name $groupInfo.Exists $availableLocations
        if ($groupInfo.Exists) {          
            $appGroup = Get-AzureRmResourceGroup `
                            -Name $groupInfo.Name 
            $message = "Using resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'."    
            Write-Information -MessageData $message -InformationAction Continue            
        } else {           
            $appGroup =  New-AzureRmResourceGroup `
                            -Name $groupInfo.Name `
                            -Location $appLocation 
            $message = "Created resource group '$($appGroup.ResourceGroupName)' in location '$($appGroup.Location)'."
            Write-Information -MessageData $message -InformationAction Continue   
        }

        if ($appPlanInfo.Exists) {            
            $appPlan = Get-AzureRmAppServicePlan `
                        -Name $appPlanInfo.Name `
                        -ResourceGroupName $appPlanInfo.ResourceGroup
            $message = "Using app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." 
            Write-Information -MessageData $message -InformationAction Continue       
        } else {           
            $defaultTier = "Free"
            $appPlan = New-AzureRmAppServicePlan `
                        -Name $appPlanInfo.Name `
                        -Location $appLocation `
                        -Tier $defaultTier `
                        -ResourceGroupName $appPlanInfo.ResourceGroup
            $message = "Created app service plan '$($appPlan.Name)' in location '$($appPlan.Location)' with Tier '$($appPlan.Sku.Tier)'." 
            Write-Information -MessageData $message -InformationAction Continue        
        } 
          
        $webapp =  New-AzureRmWebApp `
                    -Name $appName `
                    -AppServicePlan $appPlan.Id `
                    -ResourceGroupName $appGroup.ResourceGroupName `
                    -Location $appLocation  
         
        Write-Output $webapp 

        Add-RemoteGrayParam -ProvidedParameters $PSBoundParameters -WebApp $webapp -GitRepositoryPath $GitRepositoryPath   
      }         
    }

    END {}
}

function Add-RemoteGrayParam 
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [Microsoft.Azure.Management.WebSites.Models.Site][Parameter()]$WebApp,
    [string][Parameter()] $GitRepositoryPath 
    )
    [bool]$repoDetected = $true
    [bool]$repoAdded = $true
    $OriginalErrorActionPreference = $ErrorActionPreference

    if(-Not $ProvidedParameters.ContainsKey('GitRepositoryPath')){
        $GitRepositoryPath = (Get-Location).Path 
    }

    try
    {
        $ErrorActionPreference = 'Stop'
        git -C $GitRepositoryPath status | Out-Null
    }
    catch
    {          
        $repoDetected = $false
    }
    finally
    {
        $ErrorActionPreference = $OriginalErrorActionPreference
    }

    if ($repoDetected) { 
        $message = "A git repository has been detected. "         
        try
        {              
            $ErrorActionPreference = 'Stop'

            # Get app-level deployment credentials
            $xml = [xml](Get-AzureRmWebAppPublishingProfile -Name $WebApp.Name -ResourceGroupName $WebApp.ResourceGroup -OutputFile null)
            $username = $xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userName").value
            $password = $xml.SelectNodes("//publishProfile[@publishMethod=`"MSDeploy`"]/@userPWD").value
            
            # Add remote azure
            $remoteName = "azure"
            if ($ProvidedParameters.ContainsKey('Auto') -or $ProvidedParameters.ContainsKey('AddRemote')){
                Write-Information $message -InformationAction Continue                
            } else {
                $title = $message
                $message = "Would you like to add this webapp as a remote named '$remoteName'?"
                $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
                    "Adds this webapp as a remote named '$remoteName'"
                $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
                    "No action is taken"
                $options = [System.Management.Automation.Host.ChoiceDescription[]]($no, $yes)
                $result = $host.ui.PromptForChoice($title, $message, $options, 0) 
            }

            if ($result -eq 1 -or $ProvidedParameters.ContainsKey('AddRemote') -or $ProvidedParameters.ContainsKey('Auto')) {
                # Add the Azure remote to a local Git respository
                $command = "git -C $GitRepositoryPath remote add $remoteName 'https://${username}:$password@$($WebApp.EnabledHostNames[0])'" + " 2> $gitOutput"
                Invoke-Expression -Command $command | Out-Null

            }            
            
            if ($gitOutPut) {
                $repoAdded = $false
            }
        }
        catch
        {
            $repoAdded = $false
        }
        finally 
        {
            $ErrorActionPreference = $OriginalErrorActionPreference
        }
        
        if ($repoAdded) {        
            $message = "Added remote '$($remoteName)'. Push your code by running the command 'git push $($remoteName) master.' "            
        } else {
            $message = "Remote '$($remoteName)' could not be added. "
        }

        Write-Information $message -InformationAction Continue
    }
}

function Get-WebAppNameGrayParam 
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [Microsoft.Azure.Commands.Websites.Experiments.CustomWebSiteManagementClient][Parameter()]$WebSitesClient
    )
    [string]$name = ""
    [bool]$nameIsAvailable = $false
    
    if ($ProvidedParameters.ContainsKey('WebAppName')) {      
        $name = $ProvidedParameters.WebAppName
        $nameIsAvailable = Test-NameAvailability $name $WebSitesClient
        if (-not $NameIsAvailable) {
            $message = "Website with given name '$name' already exists."
            $exception = New-Object -TypeName System.Exception -ArgumentList $message
            throw $exception             
        }
    } else {
        for ($i = 0; $i -le 2; $i++) {
            $defaultName ="WebApp$(Get-Random -max 1000000)"
            $nameIsAvailable = Test-NameAvailability $defaultName $WebSitesClient
            if ($NameIsAvailable) {
                 break
            }
        }

        if ($ProvidedParameters.ContainsKey('Auto')) {
            $name = $defaultName
        } else {
            $name = Get-WebAppNameFromUser $defaultName
        }
    }

    return $name          
}

function Get-WebAppNameFromUser
{
    param(
    [string][Parameter()]$DefaultName
    )
    
    $selection = Read-Host "Enter a name for you WebApp or leave blank for default($defaultName)"
    if ($selection) {
        $nameIsAvailable = Test-NameAvailability $selection $WebSitesClient
        if (-not $NameIsAvailable) {
            $message = "Website with given name '$selection' already exists."
            $exception = New-Object -TypeName System.Exception -ArgumentList $message
            throw $exception             
        } else {
            $name = $selection
        }
    } else {
        $name = $DefaultName
    }

    return $name
}


function Get-ResourceGroupInfoGrayParam
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [string][Parameter(Mandatory=$false)]$WebAppName,    
    [Microsoft.Azure.Management.ResourceManager.ResourceManagementClient][Parameter()]$ResourceManagementClient
    )
    [hashtable]$resourceGroupInfo = @{Name="";Exists=$false}
    $defaultName = $WebAppName 

    if ($ProvidedParameters.ContainsKey('ResourceGroupName')) {
        $resourceGroupInfo.Name = $ProvidedParameters.ResourceGroupName 
    } else {
        if ($ProvidedParameters.ContainsKey('Auto')) {
            $resourceGroupInfo.Name = $defaultName 
        } else {
            $resourceGroupInfo.Name = Get-ResourceGroupNameFromUser $defaultName                       
        }
        
    }

    $resourceGroupInfo.Exists = Test-ResourceGroupExistence $resourceGroupInfo.Name $ResourceManagementClient

    return $resourceGroupInfo
}

function Get-ResourceGroupNameFromUser
{
    param(
    [string][Parameter()]$DefaultName
    )
    [object[]]$resourceGroups = Get-AzureRmResourceGroup 
    Write-Host "Resource Group options: "
    Write-Host "[Default] $DefaultName"
    for ($i = 1; $i -le $resourceGroups.Count; $i++) {
        Write-Host "[$i] $($resourceGroups[$i-1].ResourceGroupName)"
    }

    $selection = Read-Host "Enter your selection or a new resource group name (leave blank for default)"
    if ($selection) {
        if ($selection -match '^\d+$' -and $selection -le $resourceGroups.Count -and $selection -gt 0) {
            $name = $resourceGroups[$selection - 1].ResourceGroupName
        } else {
             $name = $selection
        }
    } else {
        $name = $DefaultName
    }

    return $name
}

function Get-AppServicePlanInfoGrayParam
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [string][Parameter()]$WebAppName,
    [string][Parameter()]$ResourceGroupName
    )
    [hashtable]$appServicePlanInfo = @{Name="";ResourceGroup="";IsDefaultPlan=$false;Exists=$false}   
    [object[]]$appServicePlans = Get-AzureRmAppServicePlan
    $defaultName = $WebAppName 

    if ($ProvidedParameters.ContainsKey('AppServicePlan')) {  
         $regexp = '/subscriptions/[-A-Za-z0-9]+/resourceGroups/[-\w\._\(\)]+/providers/Microsoft.Web/serverfarms/[-\w\._\(\)]+$'
         $idWasProvided = $ProvidedParameters.AppServicePlan -imatch $regexp
         if ($idWasProvided) {
            $parsedId = $ProvidedParameters.AppServicePlan.split('/')            
            $appServicePlanInfo.Name = $parsedId[$parsedId.Length - 1]
            $appServicePlanInfo.ResourceGroup =  $parsedId[4] 
            $existingPlan = $appServicePlans | Where-Object {$_.Id -eq $ProvidedParameters.AppServicePlan}
            if (-not $existingPlan) {
                $message = "The app service plan with the id provided does not exist."
                $exception = New-Object -TypeName System.Exception -ArgumentList $message
                throw $exception
            }                                
        } else {
            $appServicePlanInfo.Name = $ProvidedParameters.AppServicePlan
        }
            $appServicePlanInfo.IsDefaultPlan = $false
    } else {
        if($ProvidedParameters.ContainsKey('Auto')){
            $existentDefaultPlan = Get-DefaultAppServicePlan $appServicePlans
            if ($existentDefaultPlan) {        
                $appServicePlanInfo.Name = $existentDefaultPlan.Name 
                $appServicePlanInfo.ResourceGroup = $existentDefaultPlan.ResourceGroup
                $appServicePlanInfo.IsDefaultPlan = $true   
                $appServicePlanInfo.Exists = $true
            } else {                        
                $appServicePlanInfo.Name = $defaultName
                $appServicePlanInfo.IsDefaultPlan = $false
            }
        } else {
            $appServicePlanInfo = Get-AppServicePlanInfoFromUser $appServicePlans $defaultName
        }              
    }

    if (-not $appServicePlanInfo.IsDefaultPlan) {
        [object[]]$appServicePlansWithProvidedName = $appServicePlans | Where-Object {$_.Name -eq $appServicePlanInfo.Name}
        if ($appServicePlansWithProvidedName) {
            $appServicePlanInfo.Exists = $true
            $appServicePlanWithProvidedNameAndGroup =  $appServicePlansWithProvidedName | Where-Object {$_.ResourceGroup -eq $ResourceGroupName}
            if ($appServicePlanWithProvidedNameAndGroup) {
                $appServicePlanInfo.ResourceGroup = $appServicePlanWithProvidedNameAndGroup.ResourceGroup
            } else {
                if ($appServicePlansWithProvidedName.Count -gt 1) {
                    $message = "There are various App Service Plans with that name. An existing Resource Group name should be provided."
                    $exception = New-Object -TypeName System.Exception -ArgumentList $message
                    throw $exception
                } else {                    
                    $appServicePlanInfo.ResourceGroup = $appServicePlansWithProvidedName.ResourceGroup
                }                
            }
        } else {            
            $appServicePlanInfo.Exists = $false
            $appServicePlanInfo.ResourceGroup = $ResourceGroupName
        }
    }  

    return $appServicePlanInfo
}

function Get-AppServicePlanInfoFromUser
{
    param(
    [object[]]$Plans,
    [string]$DefaultName
    )
    [hashtable]$appServicePlanInfo = @{Name="";ResourceGroup="";IsDefaultPlan=$false;Exists=$false}   
    $existentDefaultPlan = Get-DefaultAppServicePlan $Plans    

    Write-Host "Plan options:"
    if ($existentDefaultPlan) {
        Write-Host "[Default] $($existentDefaultPlan.Name) {Tier=$($existentDefaultPlan.Sku.Tier);Location=$($existentDefaultPlan.Location)}"
    } else {
        Write-Host "[Default] $($defaultName) {Tier=Free}"
    }

    for ($i = 1; $i -le $Plans.Count; $i++) {
        Write-Host "[$i] $($Plans[$i-1].Name) {Tier=$($Plans[$i-1].Sku.Tier);Location=$($Plans[$i-1].Location)}"
    }

    $selection = Read-Host "Enter your selection (leave blank for default)"
    if ($selection) {
        if ($selection -match '^\d+$' -and $selection -le $Plans.Count -and $selection -gt 0) {
            $appServicePlanInfo.Name = $Plans[$selection - 1].Name 
            $appServicePlanInfo.IsDefaultPlan = $false 
        } else {
            $appServicePlanInfo.Name = $selection
            $appServicePlanInfo.IsDefaultPlan = $false
        }        
    } else {
        if ($existentDefaultPlan) {               
            $appServicePlanInfo.Name = $existentDefaultPlan.Name 
            $appServicePlanInfo.ResourceGroup = $existentDefaultPlan.ResourceGroup
            $appServicePlanInfo.IsDefaultPlan = $true   
            $appServicePlanInfo.Exists = $true        
        } else {               
            $appServicePlanInfo.Name = $defaultName
            $appServicePlanInfo.IsDefaultPlan = $false
        }        
    }

    return $appServicePlanInfo
  }

function Get-LocationGrayParam
{
    param(
    [hashtable][Parameter()]$ProvidedParameters,
    [string][Parameter()]$ResourceGroupName,
    [bool][Parameter()]$ResourceGroupExists,
    [string[]][Parameter()]$AvailableLocations
    )
    [string]$location = ""

    if ($ProvidedParameters.ContainsKey('Location')) {
        $location = $ProvidedParameters.Location
    } else {
        if ($ResourceGroupExists) {
            $location = $(Get-AzureRmResourceGroup -Name $ResourceGroupName).Location
        } else {
            $location = Get-DefaultLocation $AvailableLocations
        }    
    }

    return $location
}

function Get-LocationFromUser 
{
    param(
    [string][Parameter()]$DefaultLocation,
    [string[]][Parameter()]$AvailableLocations
    )    
    Write-Host "WebApp Location options: "
    Write-Host "[Default] $DefaultLocation"
    for ($i = 1; $i -le $AvailableLocations.Count; $i++) {
        Write-Host "[$i] $($AvailableLocations[$i-1])"
    }

    $selection = Read-Host "Enter your selection (leave blank for default)"
    if ($selection) {
        if ($selection -match '^\d+$' -and $selection -le $AvailableLocations.Count -and $selection -gt 0) {
            $location = $AvailableLocations[$selection - 1]
        } else {
             $location = $selection
        }        
    } else {
        $location = $DefaultLocation
    }

    return $location
}


Export-ModuleMember -Cmdlet New-AzWebApp
Export-ModuleMember -Cmdlet New-AzWebAppGrayParam


# SIG # Begin signature block
# MIIkDgYJKoZIhvcNAQcCoIIj/zCCI/sCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwakmEdKQgb7DX
# 847cZQyJ4IPi7GZn3vQJ10SXJHT8TqCCDYMwggYBMIID6aADAgECAhMzAAAAxOmJ
# +HqBUOn/AAAAAADEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMTcwODExMjAyMDI0WhcNMTgwODExMjAyMDI0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCIirgkwwePmoB5FfwmYPxyiCz69KOXiJZGt6PLX4kvOjMuHpF4+nypH4IBtXrL
# GrwDykbrxZn3+wQd8oUK/yJuofJnPcUnGOUoH/UElEFj7OO6FYztE5o13jhwVG87
# 7K1FCTBJwb6PMJkMy3bJ93OVFnfRi7uUxwiFIO0eqDXxccLgdABLitLckevWeP6N
# +q1giD29uR+uYpe/xYSxkK7WryvTVPs12s1xkuYe/+xxa8t/CHZ04BBRSNTxAMhI
# TKMHNeVZDf18nMjmWuOF9daaDx+OpuSEF8HWyp8dAcf9SKcTkjOXIUgy+MIkogCy
# vlPKg24pW4HvOG6A87vsEwvrAgMBAAGjggGAMIIBfDAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUy9ZihM9gOer/Z8Jc0si7q7fDE5gw
# UgYDVR0RBEswSaRHMEUxDTALBgNVBAsTBE1PUFIxNDAyBgNVBAUTKzIzMDAxMitj
# ODA0YjVlYS00OWI0LTQyMzgtODM2Mi1kODUxZmEyMjU0ZmMwHwYDVR0jBBgwFoAU
# SG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx
# LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8y
# MDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAG
# Fh/bV8JQyCNPolF41+34/c291cDx+RtW7VPIaUcF1cTL7OL8mVuVXxE4KMAFRRPg
# mnmIvGar27vrAlUjtz0jeEFtrvjxAFqUmYoczAmV0JocRDCppRbHukdb9Ss0i5+P
# WDfDThyvIsoQzdiCEKk18K4iyI8kpoGL3ycc5GYdiT4u/1cDTcFug6Ay67SzL1BW
# XQaxFYzIHWO3cwzj1nomDyqWRacygz6WPldJdyOJ/rEQx4rlCBVRxStaMVs5apao
# pIhrlihv8cSu6r1FF8xiToG1VBpHjpilbcBuJ8b4Jx/I7SCpC7HxzgualOJqnWmD
# oTbXbSD+hdX/w7iXNgn+PRTBmBSpwIbM74LBq1UkQxi1SIV4htD50p0/GdkUieeN
# n2gkiGg7qceATibnCCFMY/2ckxVNM7VWYE/XSrk4jv8u3bFfpENryXjPsbtrj4Ns
# h3Kq6qX7n90a1jn8ZMltPgjlfIOxrbyjunvPllakeljLEkdi0iHv/DzEMQv3Lz5k
# pTdvYFA/t0SQT6ALi75+WPbHZ4dh256YxMiMy29H4cAulO2x9rAwbexqSajplnbI
# vQjE/jv1rnM3BrJWzxnUu/WUyocc8oBqAU+2G4Fzs9NbIj86WBjfiO5nxEmnL9wl
# iz1e0Ow0RJEdvJEMdoI+78TYLaEEAo5I+e/dAs8DojCCB3owggVioAMCAQICCmEO
# kNIAAAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj
# YXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcwODIxMDkw
# OVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UE
# AxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCq
# uAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOlo
# XtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3Wr
# aPPLbfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ9
# 7/vjK1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7
# La4zWMW3Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOG
# jfdf8NBSv4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I
# 4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5
# oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xbn6/83bBm
# 4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9AN0/B
# 4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY0uDW
# iIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k
# 5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD
# VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kU
# BU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3Nv
# ZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAz
# XzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAz
# XzIyLmNydDCBnwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUH
# AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9wcmltYXJ5
# Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABpAGMA
# eQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAZ/KG
# pZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79H
# qaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XU
# tR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPypr
# WEljHwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ
# 1h/DMhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiy
# WYlobm+nt3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobD
# HWM2l4bf2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1HxS+YWG18NzGGwS+
# 30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3HjXG0qKi
# n3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9azI2h15q/6/IvrC4Dq
# aTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYibV3FW
# TkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0xghXhMIIV3QIBATCBlTB+MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNy
# b3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAAxOmJ+HqBUOn/AAAAAADE
# MA0GCWCGSAFlAwQCAQUAoIHMMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCAJQD/j
# 48R+0IPP6h31JK9gBVDuvab6GNBW04KJzYMcdzBgBgorBgEEAYI3AgEMMVIwUKA2
# gDQATQBpAGMAcgBvAHMAbwBmAHQAIABBAHoAdQByAGUAIABQAG8AdwBlAHIAUwBo
# AGUAbABsoRaAFGh0dHA6Ly9Db2RlU2lnbkluZm8gMA0GCSqGSIb3DQEBAQUABIIB
# AA439wpwrFv79/TuHozKwcw5iPe9neXRlkXBVqXfXTTtan9LjxA+hgKa9wdwdvsy
# +EeXaHEepW1xYNOCpAbfiink5Fv1C4yvl0juifoxiJRFQtjLm/aXuTS3qX0hLh1x
# LavwEsH6FVGev15lZSHq70qwb9ilhse3qsDaX4//IRBUkbii4kAc51VGmBQBClyq
# 9awuq23HZQUTNXnMjmZm/I02iFUo9NLKHtgDdihka70xzWoCBmw9AMbgjfL0jMia
# h5wvawsZwlGK4FqU2SyfMGthPGmPT5O8izpMn22F70IYZXaBHY2ixixK4oq1aE0K
# v0O8p6s8HLi8Sgx8jai5qTahghNNMIITSQYKKwYBBAGCNwMDATGCEzkwghM1Bgkq
# hkiG9w0BBwKgghMmMIITIgIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPQYLKoZIhvcN
# AQkQAQSgggEsBIIBKDCCASQCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEF
# AAQg/w/ldi5UaV+oyf0ReWbaMQ50VZhCmXTOLNMIHBQ7HAsCBlm6tVNPtBgTMjAx
# NzA5MTUxOTU4MDQuNDQ2WjAHAgEBgAIB9KCBuaSBtjCBszELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMe
# bkNpcGhlciBEU0UgRVNOOkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3Nv
# ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIO0DCCBnEwggRZoAMCAQICCmEJgSoAAAAA
# AAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1
# dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIxNDY1NVowfDEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWlj
# cm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IB
# DwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD
# 5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ld
# k6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3R
# YRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEP
# nAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6
# p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB4jAQBgkrBgEE
# AYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYB
# BAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMB
# Af8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBL
# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS
# b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYB
# BAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v
# UEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBn
# AGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqG
# SIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn++ldt
# GTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbMQEBBm9xc
# F/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvk
# x872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp
# 8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4pm3S4Zz5Hfw4
# 2JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+J
# FrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x4QDf5zEHpJM6
# 92VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb
# 2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKnQqLJzxlBTeCG
# +SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M
# 5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvTX4/edIhJEjCC
# BNowggPCoAMCAQICEzMAAAChpf257qf8np0AAAAAAKEwDQYJKoZIhvcNAQELBQAw
# fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
# TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMTYwOTA3MTc1NjQ4WhcN
# MTgwOTA3MTc1NjQ4WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkJC
# RUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2
# aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm9ABeeYir3p8G3Ue
# 87dn1h3ep94ANNgS+QfqCHsfTU3KhZR6q3ZrKgdFjVEn07ZdRqUlxmIUeYtPzOYs
# 9eyfTXodNCI2KrjD4uzFUO3T/UPBLb/F8PrPzISQ66Kmsm1XoI+5YXDUSc6IL4Mu
# O4FKk7VJSsRlyZaF5C/6rOLYVx0z9r4Q58JSGxPg+RQ2qLOb9NsV8PTSa30tuFXO
# EelW/5TpIQ67kVfMnBV5cM2OrNPjgZmYww4H39tzxc8pY/U+7DcYenP2JHW1/Mk3
# lDBXB9WgQBVNCxaw5tU3XTzY06u8h5eHelVzS2FDwfMJiJK+zrjlhEo8FjecQc4g
# l4HICQIDAQABo4IBGzCCARcwHQYDVR0OBBYEFKtcuYK+cSrVj+DosinP+hvTt/pI
# MB8GA1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJ
# oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p
# Y1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB
# BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGlt
# U3RhUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYI
# KwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBACNgTLgFVOlnyb45PBUywxPIswCQ
# fxczm/12L11MErPosCSi/rL0H6iyji5OEAdc6Pc0iu40HejhRIb4HtvePRKUh8Ga
# D0Pgm/oUYau26hLjqohq12V35Qdb0FBT0cVa1CgvKkpReR95OSp3x2HlI38qBdom
# ntVAtuJf3DoTdOU6/ar7PwL8K/n4IFJbKMpdsiAo7h0e9IqEvBdS6rMScZosHRtO
# DXjR25MNJF4XiElUIfzYXCbQ6RPhbMpOvwe4O/nhnC9GDGU6nEWwCadzTCxrttcW
# Y+D8cjiZpgXNMpFBol76u9etDnuFy/MPdzt4MtNPlpEUSCPGipeXWB39pUGhggN5
# MIICYQIBATCB46GBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO
# OkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloiUKAQEwCQYFKw4DAhoFAAMVAIKuifW05j8WXCC8F+TBw0DNOetooIHC
# MIG/pIG8MIG5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0w
# CwYDVQQLEwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NERFOS0wQzVF
# LTNFMDkxKzApBgNVBAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xv
# Y2swDQYJKoZIhvcNAQEFBQACBQDdZnMGMCIYDzIwMTcwOTE1MTU0MDU0WhgPMjAx
# NzA5MTYxNTQwNTRaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAN1mcwYCAQAwCgIB
# AAICGXACAf8wBwIBAAICF0cwCgIFAN1nxIYCAQAwNgYKKwYBBAGEWQoEAjEoMCYw
# DAYKKwYBBAGEWQoDAaAKMAgCAQACAxbjYKEKMAgCAQACAwehIDANBgkqhkiG9w0B
# AQUFAAOCAQEAPygKnE52j2mWDi0S30lMHTHjSAxBNnCQojY/rdi88jXs9rh0H84G
# ae/QS3pXfq73SjiJe2xIPVkG3f9b+X4dCyV4i8OJJQYLRYgfhvIk9q367SKehRuH
# xu9xQARW8gWQsqi8IpTV9isnH4fQbFLzNvPblXYscq+ovDP0/YyApNh6EZ3qFuO2
# aFacFDoYeTUnyhLG65QJET7fziak5gk5A6rAjhSdmcCyMRH2Hcf5i6xt4fq1tJrC
# kSJ5M1AsLv+7r5hjFWYrPczIt1EuyhL4hazk/CiYEFz6YZvAhkDrmUd7JpRpjWki
# 0ppoC6eCY5wj1W7MTMfQwSmjzBGpjRBv5TGCAvUwggLxAgEBMIGTMHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAoaX9ue6n/J6dAAAAAAChMA0GCWCG
# SAFlAwQCAQUAoIIBMjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZI
# hvcNAQkEMSIEIBj5I5B+hJUOlk6+7xt+LHQqYi/yHDJPB1EOmtvWnjtgMIHiBgsq
# hkiG9w0BCRACDDGB0jCBzzCBzDCBsQQUgq6J9bTmPxZcILwX5MHDQM0562gwgZgw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAKGl/bnup/ye
# nQAAAAAAoTAWBBQMBglWDL+44/F/K5fV1xPwY8uPgDANBgkqhkiG9w0BAQsFAASC
# AQBrWdimnk9Z4Jy2Cudl+0LoA4//rNxtWh6ctIUCTwOQgmJxUyaErhpzum3ECCje
# 7plMF3aujvr9DappVKZpjRz8KAXun+omLR7Kq87fZEa0mTnLI/0hC0VN6teTVJYT
# BR4UOGuOQuXfaR5h5gRUchQcEKpqMEhQWa1ROuP5rR2InaTfSUMCnYmP01lprre1
# SsvMNaWsVyuAwlztbV/NTi0cMHWzdZVctdWgwbnU9aboRZVf8rB9xzCLHbbjOuX9
# MSPlshRHW/pPlwHOUXTgQWv8OiWyKhov+a/Inekk4TCuzYqMvrc19mDI95LjPUVq
# tZXCjz6aXDoPtRQ9izDRYQyw
# SIG # End signature block