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 |