FILE: C:\Program Files\IIS\Microsoft Web Deploy V3\Scripts\SetupSiteForPublish.ps1
--
param(
[parameter(Mandatory = $false)]
$siteName,
[parameter(Mandatory = $false)]
$sitePhysicalPath,
[parameter(Mandatory = $false)]
$siteAppPoolName,
[parameter(Mandatory = $false)]
[int]$sitePort,
[parameter(Mandatory = $false)]
$deploymentUserName,
[parameter(Mandatory = $false)]
$deploymentUserPassword,
[parameter(Mandatory = $false)]
$managedRunTimeVersion,
[parameter(Mandatory = $false)]
$publishSettingSavePath,
[parameter(Mandatory = $false)]
$publishSettingFileName
)
Import-LocalizedData -BindingVariable Resources -FileName Resources.psd1
# ==================================
#constants
$SCRIPTERROR = 0
$WARNING = 1
$INFO = 2
$logfile = ".\HostingLog-$(get-date -format MMddyyHHmmss).log"
$template = @"
"@
#the order is important. Check for apppool name first. Its possible that
#the user gave just a sitename to set permissions. In this case leave apppool emtpy.
#else give a default name to the apppool.
if(!$siteAppPoolName)
{
if(!$siteName)
{
$siteAppPoolName = "WDeployAppPool"
}
}
else
{
$siteAppPoolName = $siteAppPoolName.Trim()
}
#now the sitename check. If its empty give it a default name
if(!$siteName)
{
$siteName = "WDeploySite"
}
else
{
$siteName = $siteName.Trim()
}
if(!$sitePhysicalPath)
{
$sitePhysicalPath = $env:SystemDrive + "\inetpub\WDeploySite"
}
else
{
$sitePhysicalPath = $sitePhysicalPath.Trim()
}
#global variable. Because we need to return two values from MWA from one function. [REF] has bugs. Hence global
$global:sitePath = $sitePhysicalPath
$global:publishURL = $null
# this function does logging
function write-log([int]$type, [string]$info){
$message = $info -f $args
$logMessage = get-date -format HH:mm:ss
Switch($type)
{
$SCRIPTERROR
{
$logMessage = $logMessage + "`t" + $Resources.Error + "`t" + $message
write-host -foregroundcolor white -backgroundcolor red $logMessage
}
$WARNING
{
$logMessage = $logMessage + "`t" + $Resources.Warning + "`t" + $message
write-host -foregroundcolor black -backgroundcolor yellow $logMessage
}
default
{
$logMessage = $logMessage + "`t" + $Resources.Info + "`t" + $message
write-host -foregroundcolor black -backgroundcolor green $logMessage
}
}
$logMessage >> $logfile
}
function GetPublishSettingSavePath()
{
if(!$publishSettingFileName)
{
$publishSettingFileName = "WDeploy.PublishSettings"
}
if(!$publishSettingSavePath)
{
$publishSettingSavePath = [System.Environment]::GetFolderPath("Desktop")
}
if((test-path $publishSettingSavePath) -eq $false)
{
write-log $SCRIPTERROR $Resources.FailedToAccessScriptsFolder $publishSettingSavePath
return $null
}
return Join-Path $publishSettingSavePath $publishSettingFileName
}
# returns false if OS is not server SKU
function NotServerOS
{
$sku = $((gwmi win32_operatingsystem).OperatingSystemSKU)
$server_skus = @(7,8,9,10,12,13,14,15,17,18,19,20,21,22,23,24,25)
return ($server_skus -notcontains $sku)
}
# gives a user access to an IIS site's scope
function GrantAccessToSiteScope($username, $websiteName)
{
trap [Exception]
{
write-log $SCRIPTERROR $Resources.FailedToGrantUserAccessToSite $username $websiteName
return $false
}
foreach($mInfo in [Microsoft.Web.Management.Server.ManagementAuthorization]::GetAuthorizedUsers($websiteName, $false, 0,[int]::MaxValue))
{
if($mInfo.Name -eq $username)
{
write-log $INFO $Resources.UserHasAccessToSite $username $websiteName
return $true
}
}
[Microsoft.Web.Management.Server.ManagementAuthorization]::Grant($username, $websiteName, $FALSE) | out-null
write-log $INFO $Resources.GrantedUserAccessToSite $username $websiteName
return $true
}
# gives a user permissions to a file on disk
function GrantPermissionsOnDisk($username, $type, $options)
{
trap [Exception]
{
write-log $SCRIPTERROR $Resources.NotGrantedPermissions $type $username $global:sitePath
}
$acl = (Get-Item $global:sitePath).GetAccessControl("Access")
$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule($username, $type, $options, "None", "Allow")
$acl.AddAccessRule($accessrule)
set-acl -aclobject $acl $global:sitePath
write-log $INFO $Resources.GrantedPermissions $type $username $global:sitePath
}
function AddUser($username, $password)
{
if(-not (CheckLocalUserExists($username) -eq $true))
{
$comp = [adsi] "WinNT://$env:computername,computer"
$user = $comp.Create("User", $username)
$user.SetPassword($password)
$user.SetInfo()
write-log $INFO $Resources.CreatedUser $username
}
}
function CheckLocalUserExists($username)
{
$objComputer = [ADSI]("WinNT://$env:computername")
$colUsers = ($objComputer.psbase.children | Where-Object {$_.psBase.schemaClassName -eq "User"} | Select-Object -expand Name)
$blnFound = $colUsers -contains $username
if ($blnFound)
{
return $true
}
else
{
return $false
}
}
function CheckIfUserIsAdmin($username)
{
$computer = [ADSI]("WinNT://$env:computername,computer")
$group = $computer.psbase.children.find("Administrators")
$colMembers = $group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name",'GetProperty',$null,$_,$null)}
$bIsMember = $colMembers -contains $username
if($bIsMember)
{
return $true
}
else
{
return $false
}
}
function CreateLocalUser($username, $password, $isAdmin)
{
AddUser $username $password
if($isAdmin)
{
if(-not(CheckIfUserIsAdmin($username) -eq $true))
{
$group = [ADSI]"WinNT://$env:computername/Administrators,group"
$group.add("WinNT://$env:computername/$username")
write-log $INFO $Resources.AddedUserAsAdmin $username
}
else
{
write-log $INFO $Resources.IsAdmin $username
}
}
return $true
}
function Initialize
{
trap [Exception]
{
write-log $SCRIPTERROR $Resources.CheckIIS7Installed
break
}
$inetsrvPath = ${env:windir} + "\system32\inetsrv\"
[System.Reflection.Assembly]::LoadFrom( $inetsrvPath + "Microsoft.Web.Administration.dll" ) > $null
[System.Reflection.Assembly]::LoadFrom( $inetsrvPath + "Microsoft.Web.Management.dll" ) > $null
}
function GetPublicHostname()
{
$ipProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
if($ipProperties.DomainName -eq "")
{
return $ipProperties.HostName
}
else
{
return "{0}.{1}" -f $ipProperties.HostName, $ipProperties.DomainName
}
}
function GenerateStrongPassword()
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Web") > $null
return [System.Web.Security.Membership]::GeneratePassword(12,4)
}
function GetPublishURLFromBindingInfo($bindingInfo, $protocol, $hostname)
{
$port = 80
trap [Exception]
{
#return defaults
return "http://$hostname"
}
if(($bindingInfo -match "(.*):(\d*):([^:]*)$") -and
($Matches.Count -eq 4 ))
{
$port = $Matches[2]
$header = $Matches[3]
$ipaddress = $Matches[1]
if($header)
{
$hostname = $header
}
elseif(($ipaddress) -AND (-not($ipaddress -eq "*")))
{
$bracketsArray = @('[',']')
$hostname = $ipaddress.Trim($bracketsArray)
}
if(-not($port -eq 80))
{
$hostname = $hostname + ":" + $port
}
}
return $protocol + "://" + $hostname
}
function GetUnusedPortForSiteBinding()
{
[int[]] $portArray = $null
$serverManager = (New-Object Microsoft.Web.Administration.ServerManager)
foreach($site in $serverManager.Sites)
{
foreach($binding in $site.Bindings)
{
if($binding.IsIPPortHostBinding)
{
if($binding.Protocol -match "https?")
{
if(($binding.BindingInformation -match "(.*):(\d*):([^:]*)$") -and
($Matches.Count -eq 4 ))
{
$portArray = $portArray + $Matches[2]
}
}
}
}
}
if(-not($portArray -eq $null))
{
$testPortArray = 8080..8200
foreach($port in $testPortArray)
{
if($portArray -notcontains $port)
{
return $port
}
}
}
return 8081 #default
}
function CreateSite($name, $appPoolName, $port, $dotnetVersion)
{
trap [Exception]
{
write-log $SCRIPTERROR $Resources.SiteCreationFailed
return $false
}
$hostname = GetPublicHostName
$global:publishURL = "http://$hostname"
if(-not($port -eq 80))
{
$global:publishURL = $global:publishURL + ":" + $port
}
$configHasChanges = $false
$serverManager = (New-Object Microsoft.Web.Administration.ServerManager)
#appPool might be empty. WHen the user gave just a site name to
#set the permissions on. As long as the sitename is not empty
if($appPoolName)
{
$appPool = $serverManager.ApplicationPools[$appPoolName]
if ($appPool -eq $null)
{
$appPool = $serverManager.ApplicationPools.Add($appPoolName)
$appPool.Enable32BitAppOnWin64 = $true
if( ($dotnetVersion) -and
(CheckVersionWithinAllowedRange $dotnetVersion) )
{
$appPool.ManagedRuntimeVersion = $dotnetVersion
}
$configHasChanges = $true
write-log $INFO $Resources.AppPoolCreated $appPoolName
}
else
{
write-log $WARNING $Resources.AppPoolExists $appPoolName
}
}
$newSite = $serverManager.Sites[$name]
if ($newSite -eq $null)
{
$newSite = $serverManager.Sites.Add($name,$global:sitePath, $port)
if($appPool)
{
$newSite.Applications[0].ApplicationPoolName = $appPool.Name
}
if((test-path $global:sitePath) -eq $false)
{
[System.IO.Directory]::CreateDirectory($global:sitePath)
}
else
{
write-log $WARNING $Resources.SiteVirtualDirectoryExists $global:sitePath
}
$newSite.ServerAutoStart = $true
$configHasChanges = $true
write-log $INFO $Resources.SiteCreated $name
}
else
{
#get virtual directory and siteport
$global:sitePath = [System.Environment]::ExpandEnvironmentVariables($newSite.Applications["/"].VirtualDirectories["/"].PhysicalPath)
foreach($binding in $newSite.Bindings)
{
if($binding.IsIPPortHostBinding)
{
if($binding.Protocol -match "https?")
{
$global:publishURL = GetPublishURLFromBindingInfo $binding.BindingInformation $binding.Protocol $hostname
}
}
}
if($appPoolName)
{
if (-not($newSite.Applications[0].ApplicationPoolName -eq $appPool.Name ))
{
$newSite.Applications[0].ApplicationPoolName = $appPool.Name
$configHasChanges = $true
write-log $INFO $Resources.SiteAppPoolUpdated $name $appPoolName
}
else
{
write-log $INFO $Resources.SiteExists $name $appPoolName
}
}
else
{
write-log $INFO $Resources.SiteExists $name $newSite.Applications[0].ApplicationPoolName
}
}
if ($configHasChanges)
{
$serverManager.CommitChanges()
}
return $true
}
function CheckUserViaLogon($username, $password)
{
$signature = @'
[DllImport("advapi32.dll")]
public static extern int LogonUser(
string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
'@
$type = Add-Type -MemberDefinition $signature -Name Win32Utils -Namespace LogOnUser -PassThru
[IntPtr]$token = [IntPtr]::Zero
$value = $type::LogOnUser($username, $env:computername, $password, 2, 0, [ref] $token)
if($value -eq 0)
{
return $false
}
return $true
}
function CheckUsernamePasswordCombination($user, $password)
{
if(($user) -AND ($password))
{
if(CheckLocalUserExists($user) -eq $true)
{
if(CheckUserViaLogon $user $password)
{
return $true
}
else
{
write-Log $SCRIPTERROR $Resources.FailedToValidateUserWithSpecifiedPassword $user
return $false
}
}
}
return $true
}
function CreateProfileXml([string]$nameofSite, [string]$username, $password, [string]$hostname, $pathToSaveFile)
{
trap [Exception]
{
write-log $SCRIPTERROR $Resources.FailedToWritePublishSettingsFile $pathToSaveFile
return
}
$xml = New-Object xml
if(Test-Path $pathToSaveFile)
{
$xml.Load($pathToSaveFile)
}
else
{
$xml.LoadXml($template)
}
$newProfile = (@($xml.publishData.publishProfile)[0])
$newProfile.publishUrl = $hostname
$newProfile.msdeploySite = $nameofSite
$newProfile.destinationAppUrl = $global:publishURL.ToString()
$newProfile.userName = $username
if(-not ($password -eq $null))
{
$newProfile.userPWD = $password.ToString()
}
else
{
write-log $WARNING $Resources.NoPasswordForExistingUserForPublish
}
$xml.Save($pathToSaveFile)
write-log $INFO $Resources.SavingPublishXmlToPath $pathToSaveFile
}
function CheckVersionWithinAllowedRange($managedVersion)
{
trap [Exception]
{
return $false
}
$KeyPath = "HKLM:\Software\Microsoft\.NETFramework"
$key = Get-ItemProperty -path $KeyPath
$path = $key.InstallRoot
$files = [System.IO.Directory]::GetFiles($path, "mscorlib.dll", [System.IO.SearchOption]::AllDirectories)
foreach($file in $files)
{
if($file -match "\\(v\d\.\d).\d*\\")
{
if($Matches[1] -eq $managedVersion)
{
return $true
}
}
}
return $false
}
#================= Main Script =================
if(NotServerOS)
{
write-log $SCRIPTERROR $Resources.NotServerOS
break
}
Initialize
if(CheckUsernamePasswordCombination $deploymentUserName $deploymentUserPassword)
{
if(!$sitePort)
{
$sitePort = GetUnusedPortForSiteBinding
}
if(CreateSite $siteName $siteAppPoolName $sitePort $managedRunTimeVersion)
{
if(!$deploymentUserName)
{
$idx = $siteName.IndexOf(' ')
if( ($idx -gt 0) -or ($siteName.Length -gt 16))
{
$deploymentUserName = "WDeployuser"
}
else
{
$deploymentUserName = $siteName + "user"
}
}
if( (CheckLocalUserExists($deploymentUserName) -eq $true))
{
$deploymentUserPassword = $null
}
else
{
if(!$deploymentUserPassword)
{
$deploymentUserPassword = GenerateStrongPassword
}
}
if(CreateLocalUser $deploymentUserName $deploymentUserPassword $false)
{
GrantPermissionsOnDisk $deploymentUserName "FullControl" "ContainerInherit,ObjectInherit"
if(GrantAccessToSiteScope ($env:computername + "\" + $deploymentUserName) $siteName)
{
$hostname = GetPublicHostName
$savePath = GetPublishSettingSavePath
if($savePath)
{
CreateProfileXml $siteName $deploymentUserName $deploymentUserPassword $hostname $savePath
}
}
}
}
}
# SIG # Begin signature block
# MIIdhQYJKoZIhvcNAQcCoIIddjCCHXICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4AUtviQq7SNFU/aDJqnC6g+j
# QX6gghhlMIIEwzCCA6ugAwIBAgITMwAAAMp9MhZ8fv0FAwAAAAAAyjANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODU1
# WhcNMTgwOTA3MTc1ODU1WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO
# OjcyOEQtQzQ1Ri1GOUVCMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj3CeDl2ll7S4
# 96ityzOt4bkPI1FucwjpTvklJZLOYljFyIGs/LLi6HyH+Czg8Xd/oDQYFzmJTWac
# A0flGdvk8Yj5OLMEH4yPFFgQsZA5Wfnz/Cg5WYR2gmsFRUFELCyCbO58DvzOQQt1
# k/tsTJ5Ns5DfgCb5e31m95yiI44v23FVpKnTY9CUJbIr8j28O3biAhrvrVxI57GZ
# nzkUM8GPQ03o0NGCY1UEpe7UjY22XL2Uq816r0jnKtErcNqIgglXIurJF9QFJrvw
# uvMbRjeTBTCt5o12D4b7a7oFmQEDgg+koAY5TX+ZcLVksdgPNwbidprgEfPykXiG
# ATSQlFCEXwIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFGb30hxaE8ox6QInbJZnmt6n
# G7LKMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw
# SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz
# L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG
# AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv
# c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
# hvcNAQEFBQADggEBAGyg/1zQebvX564G4LsdYjFr9ptnqO4KaD0lnYBECEjMqdBM
# 4t+rNhN38qGgERoc+ns5QEGrrtcIW30dvMvtGaeQww5sFcAonUCOs3OHR05QII6R
# XYbxtAMyniTUPwacJiiCSeA06tLg1bebsrIY569mRQHSOgqzaO52EzJlOtdLrGDk
# Ot1/eu8E2zN9/xetZm16wLJVCJMb3MKosVFjFZ7OlClFTPk6rGyN9jfbKKDsDtNr
# jfAiZGVhxrEqMiYkj4S4OyvJ2uhw/ap7dbotTCfZu1yO57SU8rE06K6j8zWB5L9u
# DmtgcqXg3ckGvdmWVWBrcWgnmqNMYgX50XSzffQwggYHMIID76ADAgECAgphFmg0
# AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX
# BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290
# IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx
# MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf
# BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB
# BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn
# 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0
# Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n
# rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR
# JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54
# QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G
# A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG
# A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg
# QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG
# CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg
# Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ
# MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1
# Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB
# BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
# b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB
# BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i
# uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r
# kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct
# xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F
# NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo
# nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0
# NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp
# K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J
# oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0
# eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng
# 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TCCBhEwggP5
# oAMCAQICEzMAAACOh5GkVxpfyj4AAAAAAI4wDQYJKoZIhvcNAQELBQAwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTAeFw0xNjExMTcyMjA5MjFaFw0xODAy
# MTcyMjA5MjFaMIGDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MQ0wCwYDVQQLEwRNT1BSMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24w
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQh9RCK36d2cZ61KLD4xWS
# 0lOdlRfJUjb6VL+rEK/pyefMJlPDwnO/bdYA5QDc6WpnNDD2Fhe0AaWVfIu5pCzm
# izt59iMMeY/zUt9AARzCxgOd61nPc+nYcTmb8M4lWS3SyVsK737WMg5ddBIE7J4E
# U6ZrAmf4TVmLd+ArIeDvwKRFEs8DewPGOcPUItxVXHdC/5yy5VVnaLotdmp/ZlNH
# 1UcKzDjejXuXGX2C0Cb4pY7lofBeZBDk+esnxvLgCNAN8mfA2PIv+4naFfmuDz4A
# lwfRCz5w1HercnhBmAe4F8yisV/svfNQZ6PXlPDSi1WPU6aVk+ayZs/JN2jkY8fP
# AgMBAAGjggGAMIIBfDAfBgNVHSUEGDAWBgorBgEEAYI3TAgBBggrBgEFBQcDAzAd
# BgNVHQ4EFgQUq8jW7bIV0qqO8cztbDj3RUrQirswUgYDVR0RBEswSaRHMEUxDTAL
# BgNVBAsTBE1PUFIxNDAyBgNVBAUTKzIzMDAxMitiMDUwYzZlNy03NjQxLTQ0MWYt
# YmM0YS00MzQ4MWU0MTVkMDgwHwYDVR0jBBgwFoAUSG5k5VAF04KqFzc3IrVtqMp1
# ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br
# aW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNybDBhBggrBgEF
# BQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNydDAMBgNV
# HRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBEiQKsaVPzxLa71IxgU+fKbKhJ
# aWa+pZpBmTrYndJXAlFq+r+bltumJn0JVujc7SV1eqVHUqgeSxZT8+4PmsMElSnB
# goSkVjH8oIqRlbW/Ws6pAR9kRqHmyvHXdHu/kghRXnwzAl5RO5vl2C5fAkwJnBpD
# 2nHt5Nnnotp0LBet5Qy1GPVUCdS+HHPNIHuk+sjb2Ns6rvqQxaO9lWWuRi1XKVjW
# kvBs2mPxjzOifjh2Xt3zNe2smjtigdBOGXxIfLALjzjMLbzVOWWplcED4pLJuavS
# Vwqq3FILLlYno+KYl1eOvKlZbiSSjoLiCXOC2TWDzJ9/0QSOiLjimoNYsNSa5jH6
# lEeOfabiTnnz2NNqMxZQcPFCu5gJ6f/MlVVbCL+SUqgIxPHo8f9A1/maNp39upCF
# 0lU+UK1GH+8lDLieOkgEY+94mKJdAw0C2Nwgq+ZWtd7vFmbD11WCHk+CeMmeVBoQ
# YLcXq0ATka6wGcGaM53uMnLNZcxPRpgtD1FgHnz7/tvoB3kH96EzOP4JmtuPe7Y6
# vYWGuMy8fQEwt3sdqV0bvcxNF/duRzPVQN9qyi5RuLW5z8ME0zvl4+kQjOunut6k
# LjNqKS8USuoewSI4NQWF78IEAA1rwdiWFEgVr35SsLhgxFK1SoK3hSoASSomgyda
# Qd691WZJvAuceHAJvDCCB3owggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcN
# AQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAw
# BgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEx
# MB4XDTExMDcwODIwNTkwOVoXDTI2MDcwODIxMDkwOVowfjELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUg
# U2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCquAY4GgRJun/DDB7dN2vGEtgL
# 8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/X
# llnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3WraPPLbfM6XKEW9Ea64DhkrG5k
# NXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJ
# Xtjt7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7La4zWMW3Pv4y07MDPbGyr5I4
# ftKdgCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgo
# tswnKDglmDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I4iVd0yFLPlLEtVc/JAPw0Xpb
# L9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy
# 5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xbn6/83bBm4sGXgXvt1u1L50kppxMopqd9
# Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9AN0/B4YVEicQJTMXUpUMvdJX3bvh4
# IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB6TAQ
# BgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k5VAF04KqFzc3IrVtqMp1ApUw
# GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB
# /wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0f
# BFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJv
# ZHVjdHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcB
# AQRSMFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kv
# Y2VydHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDCBnwYDVR0gBIGX
# MIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggrBgEFBQcC
# AjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABpAGMAeQBfAHMAdABhAHQAZQBtAGUA
# bgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuWEeFj
# kplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79HqaPzadtjvyI1pZddZYSQfYtG
# UFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32m
# kHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPyprWEljHwlpblqYluSD9MCP80Yr
# 3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYK
# wsatruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiyWYlobm+nt3TDQAUGpgEqKD6C
# PxNNZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX
# 0O5dY0HjWwechz4GdwbRBrF1HxS+YWG18NzGGwS+30HHDiju3mUv7Jf2oVyW2ADW
# oUa9WfOXpQlLSBCZgB/QACnFsZulP0V3HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9r
# t0uX4ut1eBrs6jeZeRhL/9azI2h15q/6/IvrC4DqaTuv/DDtBEyO3991bWORPdGd
# Vk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYibV3FWTkhFwELJm3ZbCoBIa/15n8G9
# bW1qyVJzEw16UM0xggSKMIIEhgIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5n
# IFBDQSAyMDExAhMzAAAAjoeRpFcaX8o+AAAAAACOMAkGBSsOAwIaBQCggZ4wGQYJ
# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
# gjcCARUwIwYJKoZIhvcNAQkEMRYEFEUMF5prSvgmieyveBSCFo1x/0mtMD4GCisG
# AQQBgjcCAQwxMDAuoBaAFABXAGUAYgAgAEQAZQBwAGwAbwB5oRSAEmh0dHA6Ly93
# d3cuaWlzLm5ldDANBgkqhkiG9w0BAQEFAASCAQBHzXW4UeIl+Gfj4qA43uINE+cA
# zald/rSVSTUm3T56kBMDGyiOjQsnqKvJoSNXhfEOmqzj6fCaaU2kKzsuBTU3lhoZ
# V9ZVkei+ibIhHCFKgIAMSK+IfPAGSCgHTNdY79p7lx7Z9m/tRBbo2BUv5sVPIrpB
# 684Vw3Z2BcGRDIk8ui4trIIulxAkH+19Ot0kM/lAOpDZYNTe7GtfOe5ZnqqQY/+I
# efhUgrFZHTup59x9gNWRdCYkOVBqGl3O+14BYFvWj1TZgLclEbVQBZ/ooDQqn9yy
# faFozhnSJj2P+MEgulLs94gtacYBR1RuhEMGmOP35TXplM+m6h42wVrZsN91oYIC
# KDCCAiQGCSqGSIb3DQEJBjGCAhUwggIRAgEBMIGOMHcxCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFBDQQITMwAAAMp9MhZ8fv0FAwAAAAAAyjAJBgUrDgMCGgUAoF0wGAYJKoZI
# hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTcwNDI4MDExNTAz
# WjAjBgkqhkiG9w0BCQQxFgQUrofrE8/aSiUlnEdo40IAKhNkk30wDQYJKoZIhvcN
# AQEFBQAEggEAIYnJGVZHpdoXO17y3H+PxB1o7C4PmffFqPD5qRnuTzw6RiyFMzBr
# sSnZhI7YTHkMmk7MlchegKohZ8Ipfcncq1k1h9AlyLJqpxI7qs8yobqaAAn5IIns
# XgsWVe47ZWluX57B4bFgJWOTuHsuXMqSYDHv6Dr4YaEwY7zntUww9AyIhwl+1lKS
# WzlZk2hbcri7xB+qHmx7l2wbuerQRqvhrWB0pZx71NFGdx0M/2xyCg42iYmJEbrM
# k7EsJb8Mt77k2K8FrlZ99TFJN6KIxZl672NeFRmboo+kTjlzwdJ5GVR3kjcmUKfs
# cNo23hNAGeSwhAW0kJeq0YSZcpAXFZLWpA==
# SIG # End signature block
--