custom/New-AzPostgreSqlFlexibleServer.ps1
# ---------------------------------------------------------------------------------- # # 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. # ---------------------------------------------------------------------------------- <# .Synopsis Creates a new PostgreSQL flexible server. .Description Creates a new PostgreSQL flexible server. #> $DELEGATION_SERVICE_NAME = "Microsoft.DBforPostgreSQL/flexibleServers" $DEFAULT_VNET_PREFIX = '10.0.0.0/16' $DEFAULT_SUBNET_PREFIX = '10.0.0.0/24' $AZURE_ARMNAME = '^[^<>%&:\\?/]{1,260}$' function New-AzPostgreSqlFlexibleServer { [OutputType([Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Models.Api20210601.IServerAutoGenerated])] [CmdletBinding(DefaultParameterSetName='CreateExpanded', PositionalBinding=$false, SupportsShouldProcess, ConfirmImpact='Medium')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Description('Creates a new server.')] param( [Parameter(HelpMessage = 'The name of the server.')] [Alias('ServerName')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Path')] [System.String] ${Name}, [Parameter(HelpMessage = 'The name of the resource group that contains the resource, You can obtain this value from the Azure Resource Manager API or the portal.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Path')] [System.String] ${ResourceGroupName}, [Parameter(HelpMessage='The subscription ID that identifies an Azure subscription.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Path')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Runtime.DefaultInfo(Script='(Get-AzContext).Subscription.Id')] [System.String] ${SubscriptionId}, [Parameter(HelpMessage = 'Availability zone into which to provision the resource.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.String] ${Zone}, [Parameter(HelpMessage = 'The location the resource resides in.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.String] ${Location}, [Parameter(HelpMessage = 'Administrator username for the server. Once set, it cannot be changed.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.String] ${AdministratorUserName}, [Parameter(HelpMessage = 'The password of the administrator. Minimum 8 characters and maximum 128 characters. Password must contain characters from three of the following categories: English uppercase letters, English lowercase letters, numbers, and non-alphanumeric characters.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.Security.SecureString] [ValidateNotNullOrEmpty()] ${AdministratorLoginPassword}, [Parameter(HelpMessage = 'The name of the sku, typically, tier + family + cores, e.g. Standard_B1ms, Standard_D2s_v3.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.String] ${Sku}, [Parameter(HelpMessage = 'Compute tier of the server. Accepted values: Burstable, GeneralPurpose, Memory Optimized. Default: Burstable.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.String] ${SkuTier}, [Parameter(HelpMessage = "Backup retention days for the server. Day count is between 7 and 35.")] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.Int32] ${BackupRetentionDay}, [Parameter(HelpMessage = 'Max storage allowed for a server. ')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [System.Int32] [ValidateSet(32768, 65536, 131072, 262144, 524288, 1048576 , 2097152, 4194304, 8388608, 16777216)] ${StorageInMb}, [Parameter(HelpMessage='Enable or disable high availability feature. Allowed values: Enabled, Disabled')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [Validateset('Enabled', 'Disabled')] [System.String] ${HaEnabled}, [Parameter(HelpMessage = 'Application-specific metadata in the form of key-value pairs.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Runtime.Info(PossibleTypes=([Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Models.Api20171201.IServerForCreateTags]))] [System.Collections.Hashtable] ${Tag}, [Parameter(HelpMessage = 'Server version.')] [ArgumentCompleter([Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Support.ServerVersion])] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Body')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Support.ServerVersion] ${Version}, [Parameter(HelpMessage = 'The id of an existing private dns zone. You can use the private dns zone from same resource group, different resource group, or different subscription. The suffix of dns zone has to be same as that of fully qualified domain of the server. ')] [System.String] ${PrivateDnsZone}, # [Parameter(ParameterSetName='CreateWithPrivateAccess')] [Parameter(HelpMessage = 'The subnet IP address prefix to use when creating a new vnet in CIDR format. Default value is 10.0.0.0/24.')] [System.String] ${SubnetPrefix}, # [Parameter(ParameterSetName='CreateWithPrivateAccess')] [Parameter(HelpMessage = 'The Name or Id of an existing Subnet or name of a new one to create. Please note that the subnet will be delegated to Microsoft.DBforPostgreSQL/flexibleServers. After delegation, this subnet cannot be used for any other type of Azure resources.')] [System.String] ${Subnet}, # [Parameter(ParameterSetName='CreateWithPrivateAccess')] [Parameter(HelpMessage = 'The IP address prefix to use when creating a new vnet in CIDR format. Default value is 10.0.0.0/16.')] [System.String] ${VnetPrefix}, # [Parameter(ParameterSetName='CreateWithPrivateAccess')] [Parameter(HelpMessage = 'The Name or Id of an existing virtual network or name of a new one to create. The name must be between 2 to 64 characters. The name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens.')] [System.String] ${Vnet}, [Parameter(HelpMessage = " Determines the public access. Enter single or range of IP addresses to be included in the allowed list of IPs. IP address ranges must be dash- separated and not contain any spaces. Specifying 0.0.0.0 allows public access from any resources deployed within Azure to access your server. Specifying no IP address sets the server in public access mode but does not create a firewall rule.")] [System.String] ${PublicAccess}, [Parameter(HelpMessage = 'The credentials, account, tenant, and subscription used for communication with Azure.')] [Alias('AzureRMContext', 'AzureCredential')] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Azure')] [System.Management.Automation.PSObject] ${DefaultProfile}, [Parameter(HelpMessage = 'Run the command as a job.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [System.Management.Automation.SwitchParameter] ${AsJob}, [Parameter(DontShow, HelpMessage = 'Wait for .NET debugger to attach.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [System.Management.Automation.SwitchParameter] ${Break}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Runtime.SendAsyncStep[]] # SendAsync Pipeline Steps to be appended to the front of the pipeline. ${HttpPipelineAppend}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Runtime.SendAsyncStep[]] # SendAsync Pipeline Steps to be prepended to the front of the pipeline. ${HttpPipelinePrepend}, [Parameter(HelpMessage = 'Run the command asynchronously.')] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [System.Management.Automation.SwitchParameter] ${NoWait}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [System.Uri] # The URI for the proxy server to use. ${Proxy}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [System.Management.Automation.PSCredential] # Credentials for a proxy server to use for the remote call. ${ProxyCredential}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Use the default credentials for the proxy. ${ProxyUseDefaultCredentials} ) process { try { if (!$PSBoundParameters.ContainsKey('Location')) { $PSBoundParameters.Location = 'eastus' } if ($PSBoundParameters.ContainsKey('Zone')) { $PSBoundParameters.AvailabilityZone = $PSBoundParameters.Zone $null = $PSBoundParameters.Remove('Zone') } if (!$PSBoundParameters.ContainsKey('AdministratorLoginPassword')) { $Password = Get-GeneratePassword $PSBoundParameters.AdministratorLoginPassword = $Password | ConvertTo-SecureString -AsPlainText -Force } Import-Module -Name Az.Resources if(!$PSBoundParameters.ContainsKey('ResourceGroupName')) { $PSBoundParameters.ResourceGroupName = Get-RandomNumbers -Prefix 'group' -Length 10 $Msg = "Creating Resource Group {0}..." -f $PSBoundParameters.ResourceGroupName Write-Host $Msg if($PSCmdlet.ShouldProcess($PSBoundParameters.ResourceGroupName)) { $null = New-AzResourceGroup -Name $PSBoundParameters.ResourceGroupName -Location $PSBoundParameters.Location -Force } } else { $Msg = 'Checking the existence of the resource group {0} ...' -f $PSBoundParameters.ResourceGroupName Write-Host $Msg try { $null = Get-AzResourceGroup -Name $PSBoundParameters.ResourceGroupName -ErrorAction Stop $Msg = 'Resource group {0} exists ? : True' -f $PSBoundParameters.ResourceGroupName Write-Host $Msg } catch { $Msg = 'Resource group {0} exists ? : False' -f $PSBoundParameters.ResourceGroupName Write-Host $Msg $Msg = "Creating Resource Group {0}..." -f $PSBoundParameters.ResourceGroupName Write-Host $Msg if($PSCmdlet.ShouldProcess($PSBoundParameters.ResourceGroupName)) { $null = New-AzResourceGroup -Name $PSBoundParameters.ResourceGroupName -Location $PSBoundParameters.Location -Force } } } if (!$PSBoundParameters.ContainsKey('Name')) { $PSBoundParameters.Name = Get-RandomNumbers -Prefix 'server' -Length 10 } if ($PSBoundParameters.ContainsKey('Sku')) { $PSBoundParameters.SkuName = $PSBoundParameters['Sku'] $null = $PSBoundParameters.Remove('Sku') } else { $PSBoundParameters.SkuName = 'Standard_D2s_v3' } if (!$PSBoundParameters.ContainsKey('SkuTier')) { $PSBoundParameters.SkuTier = 'GeneralPurpose' } if (!$PSBoundParameters.ContainsKey('BackupRetentionDay')) { $PSBoundParameters.BackupRetentionDay = 7 } if ($PSBoundParameters.ContainsKey('StorageInMb')) { $PSBoundParameters.StorageSizeGb = [Math]::floor($PSBoundParameters['StorageInMb'] / 1024) $null = $PSBoundParameters.Remove('StorageInMb') } else { $PSBoundParameters.StorageSizeGb = 128 } if (!$PSBoundParameters.ContainsKey('Version')) { $PSBoundParameters.Version = '12' } if ($PSBoundParameters.ContainsKey('AdministratorUserName')) { $PSBoundParameters.AdministratorLogin = $PSBoundParameters['AdministratorUserName'] $null = $PSBoundParameters.Remove('AdministratorUserName') } else { $PSBoundParameters.AdministratorLogin = Get-RandomName } if ($PSBoundParameters.ContainsKey('HaEnabled')){ if ($PSBoundParameters["HaEnabled"] -eq "Enabled"){ $PSBoundParameters.HighAvailabilityMode = [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Support.HighAvailabilityMode]::ZoneRedundant } else { $PSBoundParameters.HighAvailabilityMode = [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Support.HighAvailabilityMode]::Disabled } $null = $PSBoundParameters.Remove('HaEnabled') } $PSBoundParameters.CreateMode = [Microsoft.Azure.PowerShell.Cmdlets.PostgreSql.Support.CreateMode]::Default # Handling Vnet & Subnet $NetworkKeys = 'PublicAccess', 'Subnet', 'Vnet', 'SubnetPrefix', 'VnetPrefix', 'PrivateDnsZone' $NetworkParameters = @{} foreach($Key in $NetworkKeys){ if ($PSBoundParameters.ContainsKey($Key)){ $NetworkParameters[$Key] = $PSBoundParameters[$Key] $null = $PSBoundParameters.Remove($Key) } } $RequiredKeys = 'SubscriptionId', 'ResourceGroupName', 'Name', 'Location' foreach($Key in $RequiredKeys){ $NetworkParameters[$Key] = $PSBoundParameters[$Key] } if ($NetworkParameters.ContainsKey('Vnet') -Or $NetworkParameters.ContainsKey('Subnet')){ $VnetSubnetParameters = CreateNetworkResource $NetworkParameters $SubnetId = GetSubnetId $VnetSubnetParameters.ResourceGroupName $VnetSubnetParameters.VnetName $VnetSubnetParameters.SubnetName $VnetId = [string]::Join("/",$SubnetId.split("/")[0..8]) $PSBoundParameters.NetworkDelegatedSubnetResourceId = $SubnetId if ([string]::IsNullOrEmpty($PSBoundParameters.NetworkDelegatedSubnetResourceId)) { $null = $PSBoundParameters.Remove('NetworkDelegatedSubnetResourceId') } if ($NetworkParameters.ContainsKey('PrivateDnsZone')){ if (!(Get-Module -ListAvailable -Name Az.PrivateDns)) { throw 'Please install Az.Network module by entering "Install-Module -Name Az.PrivateDns"' } else { Import-Module -Name Az.PrivateDns } $ZoneName = $NetworkParameters["PrivateDnsZone"].split("/")[-1] $DnsResourceGroup = $NetworkParameters["PrivateDnsZone"].split("/")[4] $Links = Get-AzPrivateDnsVirtualNetworkLink -ZoneName $ZoneName -ResourceGroupName $DnsResourceGroup $LinkedFlag = $false foreach($Link in $Links){ if ($Link.VirtualNetworkId -eq $VnetId){ $LinkedFlag = $true break } } if (!$LinkedFlag){ Write-Host "Adding virtual network link to the DNS zone..." New-AzPrivateDnsVirtualNetworkLink -ZoneName $ZoneName -ResourceGroupName $DnsResourceGroup -Name $PSBoundParameters["Name"] -VirtualNetworkId $VnetId } $PSBoundParameters.NetworkPrivateDnsZoneArmResourceId = $NetworkParameters["PrivateDnsZone"] $null = $PSBoundParameters.Remove('PrivateDnsZone') } else{ throw "To provision a server with private access, you need to provide private DNS zone." } } else{ $RuleName, $StartIp, $EndIp = ParseFirewallRule $NetworkParameters.PublicAccess } $Msg = 'Creating PostgreSQL server {0} in group {1}...' -f $PSBoundParameters.Name, $PSBoundParameters.resourceGroupName Write-Host $Msg $Msg = 'Your server {0} is using sku {1} (Paid Tier). Please refer to https://aka.ms/postgresql-pricing for pricing details' -f $PSBoundParameters.Name, $PSBoundParameters.SkuName Write-Host $Msg $Server = Az.PostgreSql.internal\New-AzPostgreSqlFlexibleServer @PSBoundParameters # Create Firewallrules if (![string]::IsNullOrEmpty($RuleName)) { $FirewallRuleName = CreateFirewallRule $RuleName $StartIp $EndIp $PSBoundParameters.ResourceGroupName $PSBoundParameters.Name $Server.FirewallRuleName = $FirewallRuleName } $Server.SecuredPassword = $PSBoundParameters.AdministratorLoginPassword return $Server } catch { throw } } } function CreateNetworkResource($NetworkParameters) { [OutputType([hashtable])] $WarningPreference = 'silentlycontinue' if (!(Get-Module -ListAvailable -Name Az.Network)) { throw 'Please install Az.Network module by entering "Install-Module -Name Az.Network"' } else { Import-Module -Name Az.Network } # 1. Error Handling # Raise error when user passes values for both parameters if ($NetworkParameters.Containskey('Subnet') -And $NetworkParameters.ContainsKey('PublicAccess')) { throw "Incorrect usage : A combination of the parameters -Subnet and -PublicAccess is invalid. Use either one of them." } # # When address space parameters are passed, the only valid combination is : -Vnet -Subnet -VnetPrefix -SubnetPrefix if ($NetworkParameters.ContainsKey('Vnet') -Or $NetworkParameters.ContainsKey('Subnet')) { if (($NetworkParameters.ContainsKey('VnetPrefix') -And !$NetworkParameters.ContainsKey('SubnetPrefix')) -Or (!$NetworkParameters.ContainsKey('VnetPrefix') -And $NetworkParameters.ContainsKey('SubnetPrefix')) -Or ($NetworkParameters.ContainsKey('VnetPrefix') -And $NetworkParameters.ContainsKey('SubnetPrefix') -And (!$NetworkParameters.ContainsKey('Vnet') -Or !$NetworkParameters.ContainsKey('Subnet')))){ throw "Incorrect usage : -Vnet -Subnet -VnetPrefix -SubnetPrefix must be supplied together." } } #Handle Vnet, Subnet scenario # Only the Subnet ID provided.. if (!$NetworkParameters.ContainsKey('Vnet') -And $NetworkParameters.ContainsKey('Subnet')) { if (IsValidSubnetId $NetworkParameters.Subnet) { Write-Host "You have supplied a subnet Id. Verifying its existence..." $ParsedResult = ParseResourceId $NetworkParameters.Subnet $NetworkParameters.VnetName = $ParsedResult.VnetName $NetworkParameters.SubnetName = $ParsedResult.SubnetName $NetworkParameters.ResourceGroupName = $ParsedResult.ResourceGroupName $SubnetFlag = $true try { # Valid Subnet ID is provided $Subnet = Get-AzVirtualNetworkSubnetConfig -ResourceId $NetworkParameters.Subnet -ErrorAction Stop } catch { # Invalid subnet ID is provided, creating a new one. $SubnetFlag = $false Write-Host "The subnet doesn't exist. Creating the subnet" $Subnet = CreateVnetSubnet $NetworkParameters } if ($SubnetFlag){ $Delegations = Get-AzDelegation -Subnet $Subnet if ($null -ne $Delegations){ # Valid but incorrect delegation $Delegations | ForEach-Object {if ($PSItem.ServiceName -ne $DELEGATION_SERVICE_NAME) { $Msg = "Can not use subnet with existing delegations other than {0}" -f $DELEGATION_SERVICE_NAME throw $Msg }} } else { # Valid but no delegation $Vnet = Get-AzVirtualNetwork -ResourceGroupName $NetworkParameters.ResourceGroupName -Name $NetworkParameters.VnetName $Subnet = Get-AzVirtualNetworkSubnetConfig -Name $NetworkParameters.SubnetName -VirtualNetwork $Vnet $Subnet = Add-AzDelegation -Name $DELEGATION_SERVICE_NAME -ServiceName $DELEGATION_SERVICE_NAME -Subnet $Subnet $Vnet | Set-AzVirtualNetwork } } } else { throw "The Subnet ID is not a valid form of resource id." } } elseif ($NetworkParameters.ContainsKey('Vnet') -And !$NetworkParameters.ContainsKey('Subnet')) { if (IsValidVnetId $NetworkParameters.Vnet){ Write-Host "You have supplied a vnet Id. Verifying its existence..." IsValidRgLocation $NetworkParameters.Vnet $NetworkParameters $ParsedResult = ParseResourceId $NetworkParameters.Vnet $NetworkParameters.VnetName = $ParsedResult.VnetName $NetworkParameters.SubnetName = 'Subnet' + $NetworkParameters.Name $Subnet = CreateVnetSubnet $NetworkParameters } elseif ($NetworkParameters.Vnet -Match $AZURE_ARMNAME) { Write-Host "You have supplied a vnet Name. Verifying its existence..." $NetworkParameters.VnetName = $NetworkParameters.Vnet $NetworkParameters.SubnetName = 'Subnet' + $NetworkParameters.Name $Subnet = CreateVnetSubnet $NetworkParameters IsValidRgLocation $Subnet.Id $NetworkParameters } else { throw "Incorrectly formed Vnet id or Vnet name" } } else { # Both Vnet and Subnet provided if ($NetworkParameters.Vnet -Match $AZURE_ARMNAME -And $NetworkParameters.Subnet -Match $AZURE_ARMNAME) { $NetworkParameters.VnetName = $NetworkParameters.Vnet $NetworkParameters.SubnetName = $NetworkParameters.Subnet $Subnet = CreateVnetSubnet $NetworkParameters } else { if ($NetworkParameters.ContainsKey('SubnetPrefix') -And $NetworkParameters.ContainsKey('VnetPrefix')) { $Msg = "If you pass an address prefix, please consider passing a name (instead of Id) for a subnet or vnet." } else { $Msg = "If you pass both --vnet and --subnet, consider passing names instead of ids." } throw $Msg } } return $NetworkParameters } function GetSubnetId($ResourceGroupName, $VnetName, $SubnetName){ if (!($ResourceGroupName -is [String])){ $ResourceGroupName = $ResourceGroupName[0]} $Vnet = Get-AzVirtualNetwork -Name $VnetName -ResourceGroupName $ResourceGroupName $Subnet = Get-AzVirtualNetworkSubnetConfig -Name $SubnetName -VirtualNetwork $Vnet return $Subnet.Id } function CreateVnetSubnet($Parameters){ if (!$Parameters.ContainsKey('SubnetPrefix')){$Parameters.SubnetPrefix = $DEFAULT_SUBNET_PREFIX} if (!$Parameters.ContainsKey('VnetPrefix')){$Parameters.VnetPrefix = $DEFAULT_VNET_PREFIX} try { $Vnet = Get-AzVirtualNetwork -Name $Parameters.VnetName -ResourceGroupName $Parameters.ResourceGroupName -ErrorAction Stop $Msg = "The provided vnet does exist." Write-Host $Msg $prefixes = $Vnet.AddressSpace.AddressPrefixes if (!($prefixes -Contains $Parameters.VnetPrefix)){ $prefixes.Add($Parameters.VnetPrefix) $Vnet.AddressSpace.AddressPrefixes = $prefixes $Vnet | Set-AzVirtualNetwork } } catch { $Msg = "Creating new vnet {0} in resource group {1}" -f $Parameters.VnetName, $Parameters.ResourceGroupName Write-Host $Msg if($PSCmdlet.ShouldProcess($Parameters.VnetName)) { New-AzVirtualNetwork -Name $Parameters.VnetName -ResourceGroupName $Parameters.ResourceGroupName -Location $Parameters.Location -AddressPrefix $Parameters.VnetPrefix -Force } } $Subnet = CreateAndDelegateSubnet $Parameters return $Subnet } function CreateAndDelegateSubnet($Parameters) { $SubnetFlag = $true $Vnet = Get-AzVirtualNetwork -Name $Parameters.VnetName -ResourceGroupName $Parameters.ResourceGroupName -ErrorAction Stop try { $Subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $Vnet -Name $Parameters.SubnetName -ErrorAction Stop $Msg = "The provided subnet does exist." Write-Host $Msg } catch { $SubnetFlag = $false $Msg = 'Creating new subnet {0} in resource group {1} and delegating it to {2}' -f $Parameters.SubnetName, $Parameters.ResourceGroupName, $DELEGATION_SERVICE_NAME Write-Host $Msg } if (!$SubnetFlag) { $Delegation = New-AzDelegation -Name $DELEGATION_SERVICE_NAME -ServiceName $DELEGATION_SERVICE_NAME Add-AzVirtualNetworkSubnetConfig -Name $Parameters.SubnetName -VirtualNetwork $Vnet -AddressPrefix $Parameters.SubnetPrefix -Delegation $Delegation | Set-AzVirtualNetwork } else { # check if existing subnet is delegated $Delegations = Get-AzDelegation -Subnet $Subnet if ($null -ne $Delegations){ $Delegations | ForEach-Object {If ($PSItem.ServiceName -ne $DELEGATION_SERVICE_NAME) { $Msg = "Can not use subnet with existing delegations other than {0}" -f $DELEGATION_SERVICE_NAME throw $Msg }} } else { # Valid but no delegation $Subnet = Add-AzDelegation -Name $DELEGATION_SERVICE_NAME -ServiceName $DELEGATION_SERVICE_NAME -Subnet $Subnet $Vnet | Set-AzVirtualNetwork } } return $Subnet } function CreateFirewallRule($RuleName, $StartIp, $EndIp, $ResourceGroupName, $ServerName) { $FirewallRule = New-AzPostgreSqlFlexibleServerFirewallRule -Name $RuleName -ResourceGroupName $ResourceGroupName -ServerName $ServerName -EndIPAddress $EndIp -StartIPAddress $StartIp return $FirewallRule.Name } function ParseFirewallRule($PublicAccess){ $PublicAccess = [string]$PublicAccess if ([string]::IsNullOrEmpty($PublicAccess)) { $PublicAccess = 'none' } if ($PublicAccess.ToLower() -ne 'none') { $Date = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" if ($PublicAccess.ToLower() -eq 'all'){ $StartIp = '0.0.0.0' $EndIp = '255.255.255.255' $RuleName = "AllowAll_" + $Date } else { $Parsed = $PublicAccess -split "-" if ($Parsed.length -eq 1) { $StartIp = $Parsed[0] $EndIp = $Parsed[0] } elseif ($Parsed.length -eq 2) { $StartIp = $Parsed[0] $EndIp = $Parsed[1] } else { throw "Incorrect usage: --public-access. Acceptable values are \'all\', \'none\',\'<startIP>\' and \'<startIP>-<destinationIP>\' where startIP and destinationIP ranges from 0.0.0.0 to 255.255.255.255" } if ($StartIp -eq '0.0.0.0' -And $EndIp -eq '0.0.0.0') { $RuleName = "AllowAllAzureServicesAndResourcesWithinAzureIps_" + $Date $Msg = 'Configuring server firewall rule to accept connections from all Azure resources...' } elseif ($StartIP -eq $EndIP) { $Msg = 'Configuring server firewall rule to accept connections from ' + $StartIP $RuleName = "FirewallIPAddress_" + $Date } else { $Msg = 'Configuring server firewall rule to accept connections from {0} to {1}' -f $StartIP, $EndIp $RuleName = "FirewallIPAddress_" + $Date } Write-Host $Msg } } else{ $StartIp = $null $EndIp = $null $RuleName = $null } return $RuleName, $StartIp, $EndIp } function IsValidVnetId($Rid){ $VnetFormat = "\/subscriptions\/[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}\/resourceGroups\/[-\w\._\(\)]+\/providers\/Microsoft.Network\/virtualNetworks\/[^<>%&:\\?/]{1,260}$" if ( $Rid -match $VnetFormat ) { return $True } return $False } function IsValidSubnetId($Rid){ $SubnetFormat = "\/subscriptions\/[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}\/resourceGroups/[-\w\._\(\)]+\/providers\/Microsoft.Network\/virtualNetworks\/[^<>%&:\\?/]{1,260}\/subnets\/[^<>%&:\\?/]{1,260}$" if ( $Rid -match $SubnetFormat ) { return $True } return $False } function ParseResourceId($Rid){ $Splits = $Rid -split "/" $ParsedResults = @{} if ($Splits.length -gt 1){ $ParsedResults["SubscriptionId"] = $Splits[2] $ParsedResults["ResourceGroupName"] = $Splits[4] $ParsedResults["VnetName"] = $Splits[8] if ($Splits.length -eq 11) { $ParsedResults["SubnetName"] = $Splits[10] } } return $ParsedResults } function IsValidRgLocation($ResourceId, $Parameters){ $ParsedResults = ParseResourceId $ResourceId $Group = Get-AzResourceGroup -Name $ParsedResults["ResourceGroupName"] $ParsedResults["Location"] = $Group.Location if ($Parameters.SubscriptionId -eq $ParsedResults.SubscriptionId -And $Parameters.Location -eq $ParsedResults.Location) { return $True } throw "Incorrect Usage : The location and subscription of the server, Vnet and Subnet should be same." } function Get-RandomNumbers($Prefix, $Length) { $Generated = "" for($i = 0; $i -lt $Length; $i++){ $Generated += Get-Random -Maximum 10 } return $Prefix + $Generated } function Get-RandomName() { $Noun = Get-Content -Path (Join-Path $PSScriptRoot ".\nouns.txt") | Get-Random $Adjective = Get-Content -Path (Join-Path $PSScriptRoot ".\adjectives.txt") | Get-Random $Number = Get-Random -Maximum 10 $RandomName = $Adjective + $Noun + $Number return $RandomName } function Get-GeneratePassword() { $Password = '' $Chars = 'abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890' for ($i = 0; $i -lt 16; $i++ ) { $Password += $Chars[(Get-Random -Minimum 0 -Maximum $Chars.Length)] } $Password = ($Password -split '' | Sort-Object {Get-Random}) -join '' return $Password } # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDp4R5YAQ5W4cXg # fM9F8W8Gg6Xg0WpiQx58Iz+gkuD52KCCDYEwggX/MIID56ADAgECAhMzAAAB32vw # LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn # s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw # PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS # yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG # 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh # EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH # tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS # 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp # TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok # t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4 # b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao # mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD # Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt # VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G # CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+ # Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82 # oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQglJ85dm9S # 0fA/eoQOXLouPGqOF4h8hpMnq64Qio7R2/8wQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQARDQuoH7O2i+jNqAm60U5IynejDr6XAg0CDTEmtWea # qYz7xlyhVUsu4sLKqg96o9gYC14GSORRQ47N+3ebTnQXyDy4WmKMvtFqXjnkJAdM # XSBYng1qqy4iO7TvyrUqs37JB2Orl9I/ljm+HP1zIN/IwhzmLItBLjuHEH7XTxFP # 9TuK8VVQJeITB/4aux2l2GyMtXToHF5nbMPDDW21QYDTGpX8tI+bnAXsLxy+Il3y # TBwKICNm+XH3hQRLejp7wfC5WI9spz7cAD6gyYw1PrPQ3C8f5tnJVc/TTX8oUGgR # GQOAneDQhRUOqvyck0byqGRAUhGlIMS9lmANVUKbF101oYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIBJspW/fletz2Namy5dNF9mov9OebVcgmlciAUCQ # bjGuAgZheck9RRYYEzIwMjExMDI5MDc1NTI1Ljk3OFowBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo4OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABYAcg8JJI2r7rAAAA # AAFgMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTIxMDExNDE5MDIyMFoXDTIyMDQxMTE5MDIyMFowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4OTdB # LUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALQxgBzQ8sbM8VIzPgmN # 9TWkWs2k8S1CZUyN58SLzLW3158rZZ/O4l68Gb4SkE/iBUSk4yVesBKOVonbLL3n # AwIXmsYhn5BR0RXPI9XLizzVqslUgHPzTPRtMtZu+jtygCph5MfquBia/Sp0Hjj7 # lEDrNHR/C/1/xCaZyQpgIR7lARJIaRVh5y+WqbqmbUsFIoF5UHwwzJFJpOXxYtA0 # uWuraKpdCUzAzh6wkKZgMkRrXP3E/w5cs4U7cYiYjZRGlm3Gr+vJgkyTsKW3OBiT # tkaW/ejtpW+S6pu/NezXFqAzwuSDjeIImOOeEavlA9O9VpCexo1z6VCpp0Eb3904 # uhMCAwEAAaOCARswggEXMB0GA1UdDgQWBBSnmx74t6gX4JrhdTIJdXzQ674fYzAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAVNJgImzoFpolQdU8nYdcX7ySlKEP3 # 59qg5Q/SFp7e4j5dA1V+ksvmKdZB1jhjsBs2ImNOItyNY2u2Nwja4JNxFf+2y/KT # cP8GE97H8ogr9c+Vb1uHF7gtne3F8vnv0Cb79K4mbhi6S0QIhqPcI8c36UqePwKM # Lm32J+vO9wuKW2NK8v9bA5gQu92Aj6uf7AGAuNYHIh0iZok+bcuJpYPKKY1nvS2E # a1gzmF8J/irPY4MW8v4/gaekJosfkc6gboma4w1gFDGKgKhv7tlT1+53uHGR2TS1 # qMYeNwGfGtjiTxQ1BlQEZibk7PFfCe+KRhTRTCRtzWnSg3NEE0y2qLnUMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4 # OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUA+zKSdHWLRNzIlyqHVxXZgKnZ0KKggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOUlmTgwIhgPMjAyMTEwMjkwMTQ4NDBaGA8yMDIxMTAzMDAxNDg0MFowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA5SWZOAIBADAKAgEAAgIj8AIB/zAHAgEAAgIR7DAK # AgUA5SbquAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAGSa7vbtIavOK4d6 # FY1bnw8ECnXBcEkg2CYUIuLfhHEi+gIH0spszatnuvkPikk+2tV+SgSTcjMHO1ta # Avy//BqVsM6rMp60/DndwgFwLko95p0QpSl35oGDiEcTIvhQ6YV/2yGQiQPxbQBZ # BF7abIA0US9UzXLWf5gHV0pD2POhMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAFgByDwkkjavusAAAAAAWAwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgmVBRMnyex/CDCw5Pc2xdtbWw1axr9pxI3ZxQ4y+pJAMwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCACEqO9o3Mul1tFp68e6ivKfpXS/49cFwniXkj3 # qLBluTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # YAcg8JJI2r7rAAAAAAFgMCIEIA8P46fm22auovZDF9bJidEU6dLBgLvZmsMDNON0 # vaZVMA0GCSqGSIb3DQEBCwUABIIBAA7eaVRfSmXEbv7VvV4FL0Ay3XBxj6dix/nh # pMAaUmei9FSN71VJLrsdD7UnWoNDCXDAt4Intvw0jwzuwXo/IGcjnneWZEuqK2AN # emo8bKBvl4hc2nGfOxAU4IVn5y50K3PYZ4iQBJEPYCExwDli0Ak7sT5akZvcjwOW # h8iL3ruIjdr3fcLYkr2M5gA3mPJl7E06wfFCBuwThL209WXKwBOwSi2GcHwH+sDp # seTyrItDbX03xZLp7ZrOrKiOvuduDgi+orBEZ85nM89TQHw2JMPWvi/mDJ/uXsBG # fjPT+wb5IwpArNF4LUu+zoKCGmAWdDC54gtYhZk7hYLV5H6CljQ= # SIG # End signature block |