[CmdletBinding()]
param
(
    [Parameter(Mandatory=$true)]
    [string]$servicemodelxml,

    [Parameter(Mandatory=$true)]
    [string]$log
)

#region configuration generators
function Write-LCM-Configuration($configdata)
{
    Write-Verbose "Creating the LCM configuration to set the encryption certificate thumbprint..."
    $thumbprint=$configdata.AllNodes[0].Thumbprint
    $script="`
    LocalConfigurationManager 
    {
        CertificateId=`"$thumbprint`"
        ConfigurationMode = `"ApplyOnly`"
        RefreshMode = `"Push`"
    }"
    
    write-content $script
}

function Write-Certificate-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $certificates=$xd.SelectNodes("//ServiceModel/Certificates/Certificate")
    [string]$global:lastcertidentifier=[string]::Empty

    Write-Trace-Start "Certificate installation"
    foreach($certificate in $certificates){
        $identifier=$certificate.Name
        $rawdata=$certificate.RawData
        $pwd=$certificate.Password
        $storename=$certificate.StoreName
        $storelocation=$certificate.StoreLocation
        [string]$thumbprint=$certificate.Thumbprint

        if(-not [string]::IsNullOrEmpty($thumbprint) -and -not [string]::IsNullOrEmpty($rawdata) -and -not [string]::IsNullOrEmpty($pwd)){
            Write-Log "Creating the configuration for the certificate: '$identifier'"

            if(-not [string]::IsNullOrEmpty($global:lastcertidentifier)){
                $script="`
    DynamicsCertificate $identifier `
    { `
        Ensure=`"Present`" `
        Identifier=`"$identifier`" `
        RawData=`"$rawdata`" `
        StoreName=`"$storename`" `
        StoreLocation=`"$storelocation`" `
        Password=`"$pwd`" `
        Thumbprint=`"$thumbprint`" `
        DependsOn=@(`"$global:lastcertidentifier`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
            }else{
                $script="`
    DynamicsCertificate $identifier `
    { `
        Ensure=`"Present`" `
        Identifier=`"$identifier`" `
        RawData=`"$rawdata`" `
        StoreName=`"$storename`" `
        StoreLocation=`"$storelocation`" `
        Password=`"$pwd`" `
        Thumbprint=`"$thumbprint`" `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
            }

            Write-Content $script
            $global:lastcertidentifier="[DynamicsCertificate]$identifier"
        }else{
            Write-Log "Skipping the configuration for the certificate '$identifier' as a required value is missing in the service model."
        }
    }

    Write-Trace-End "Certificate installation"
}

function Write-Dependency-Configuration
{
    $parentdir=Split-Path -parent $PSCommandPath
    $parentdir=Split-Path -parent $parentdir
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $dependencies=$xd.SelectNodes("/ServiceModel/Dependencies/Dependency")
    $global:lastdependency=$global:lastcertidentifier
    Write-Trace-Start "Install dependencies"
    foreach($dependency in $dependencies){
      $type=$dependency.getAttribute("Type")
      switch($type){
        "WindowsFeature"
        {
            $name=$dependency.getAttribute("Name")
            $script="`
    WindowsFeature $name `
    { `
        Ensure=`"Present`"
        Name=`"$name`" `
        DependsOn=@(`"$global:lastdependency`") `
    }"
            write-content $script
            write-log "Creating the configuration for the WindowsFeature $name"
            $global:lastdependency="[WindowsFeature]$name"
        }

        "Msi" 
        {
            # not supported currently as some MSI installation requires reboot
        }

        "Exe" 
        {
            # not supported currently some exe installation requires reboot
        }

        Default {}
      }
    }

    Write-Trace-End "Install dependencies"
}

function Write-Firewall-Ports-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)

    $serviceName=$xd.SelectSingleNode("//ns:ServiceModel",$ns).getAttribute("Name")
    $inputendpoints=$xd.SelectNodes("/ServiceModel/Endpoints/InputEndpoint")
    Write-Trace-start "Open firewall ports"
    [string[]]$ports=$null
    foreach($inputendpoint in $inputendpoints){
        [string]$localport=$inputendpoint.getAttribute("LocalPort")
        if($ports -notcontains $localport){
            $ports += $localport
            Write-Log "Creating the configuration for the firewall port $name"
            $name="$serviceName-$localPort"
            $displayName="$ServiceName service on port $localPort"
            $access="Allow"
            $state="Enabled"
            [string[]]$profile=@("Any")
            $direction="Inbound"
            [string[]]$remoteport=@("Any")
            $description="$ServiceName service on port $localPort"
            $protocol="TCP"
        
            $script="`
            xFirewall OpenFirewallPort-$name `
            { `
                Ensure=`"Present`" `
                Name=`"$name`" `
                DisplayName=`"$displayName`" `
                Access=`"$access`" `
                State=`"$state`" `
                Profile=`"$profile`" `
                Direction=`"$direction`" `
                RemotePort=`"$remoteport`" `
                LocalPort=`"$localPort`" `
                Description=`"$description`" `
                Protocol=`"$protocol`" `
            }"
            Write-Content $script
        }
    }

    Write-Trace-End "Open firewall ports"
}

function Write-Perf-Counters-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)

    # retrieve parameters
    $webRoot=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='WebRoot']",$ns).getAttribute("Directory")
    $codeFolder = Join-Path $webRoot "bin"

    Write-Trace-start "Perf counter initialization"
    $script="`
    DynamicsPerfCounter InitializePerfCounters `
    { `
        Ensure=`"Present`" `
        CodeFolder=`"$codeFolder`" `
        DependsOn=@(`"[DynamicsPackage]DeployPackages`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"

    Write-Content $script
    Write-Log "Creating the configuration for the Dynamics perf counters"
    Write-Trace-End "Perf counter initialization"
}

function Write-DynamicsTools-Configuration
{
    $targetPath = Join-Path $env:SystemDrive "DynamicsTools"
    $sourcePath = Join-Path $PSScriptRoot "Redist\DynamicsTools"

    Write-Trace-Start "Copy DynamicsTools"
    $script = 
@"
    File CopyDynamicsTools
    { 
        Ensure = 'Present'
        DestinationPath = '$targetPath'
        Recurse = `$true
        SourcePath = '$sourcePath'
        Type = 'Directory'
        MatchSource = `$true
        DependsOn=@("$global:lastdependency")
    }
"@

    Write-Content $script
    Write-Log "Copying supporting tools (ex. nuget and 7za) to the target machine."
    Write-Trace-End "Copy DynamicsTools"
}

function Write-AosWebsite-Configuration
{
    $parentdir=Split-Path -parent $PSCommandPath
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)

    # retrieve parameters
    $webRoot=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='WebRoot']",$ns).getAttribute("Directory")
    $source=join-path $parentdir "..\Code"

    Write-Trace-start "Copy AOS website code"

    $script="`
    File CopyAosWebsiteCode `
    { `
        Ensure=`"Present`" `
        DestinationPath=`"$webRoot`" `
        Recurse=`$true `
        SourcePath=`"$source`" `
        Type=`"Directory`" `
        MatchSource=`$true `
        DependsOn=@(`"$global:lastdependency`") `
    }"

    Write-Content $script
    Write-Log "Creating the configuration to copy the AOS website code to the target machine"
    Write-Trace-End "Copy AOS website code"
}

function Write-Packages-Configuration([string]$deploydb)
{
    $parentdir=Split-Path -parent $PSCommandPath
    $parentdir=Split-Path -parent $parentdir
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)

    # retrieve parameters
    $packageDir=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='PackagesLocalDirectory']",$ns).getAttribute("Directory")
    $clickOncePackageDir=$xd.SelectSingleNode("//ns:InstallParameter[@Name='clickOnceInstallPath']",$ns).getAttribute("Value")
    $metadataInstallPath=$xd.SelectSingleNode("//ns:InstallParameter[@Name='metadataInstallPath']",$ns).getAttribute("Value")
    $webRoot=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='WebRoot']",$ns).getAttribute("Directory")
    $frameworkInstallPath=$xd.SelectSingleNode("//ns:InstallParameter[@Name='frameworkInstallPath']",$ns).getAttribute("Value")
    $dataset=$xd.SelectSingleNode("//ns:Setting[@Name='Deploy.Dataset']",$ns).getAttribute("Value")
    $packageName=$xd.SelectSingleNode("//ns:Setting[@Name='Aos.Packages']",$ns).getAttribute("Value")
    $packageSourceDir=join-path $parentdir "Packages"
	$dataStack=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.UseManagedDatastack']",$ns).getAttribute("Value")

    Write-Trace-start "Deploy packages"

    $script = 
@"
    DynamicsPackage DeployPackages
    {
        PackageName="$packageName"
        TargetPackageDirectory="$packageDir"
        ClickOnceInstallDirectory="$clickOncePackageDir"
        PackageSourceDirectory="$packageSourceDir"
        Ensure="Present"
        MetadataDirectory="$metadataInstallPath"
        FrameworkDirectory="$frameworkInstallPath"
        DataSet="$dataset"
        WebRoot="$webRoot"
        DependsOn=@("[File]CopyAosWebsiteCode", "[File]CopyDynamicsTools")
        MonitoringAssembly="$global:telemetrydll"
        LogDir='$global:logdir'
        DeployDatabase='$deploydb'
        UseManagedDatastack='$dataStack'
    }
"@

    Write-Content $script
    Write-Log "Creating the configuration to deploy the packages"
    Write-Trace-End "Deploy packages"
}

function Write-Storage-Emulator-Configuration
{
    $emulatorinstallpath=Get-AzureStorageEmulatorInstallPath
    if(-not [string]::IsNullOrEmpty($emulatorinstallpath))
    {
        Write-Log "Creating the configuration to start the Azure Storage Emulator"
        $storageemulator=join-path $emulatorinstallpath "AzureStorageEmulator.exe"
        $initEmulatorStdOut=join-path $global:logdir "InitEmulator-output.log"
        $initEmulatorStdErr=join-path $global:logdir "InitEmulator-error.log"
        $startEmulatorStdOut=join-path $global:logdir "StartEmulator-output.log"
        $startEmulatorStdErr=join-path $global:logdir "StartEmulator-error.log"
        $clearEmulatorStdOut=join-path $global:logdir "ClearEmulator-output.log"
        $clearEmulatorStdErr=join-path $global:logdir "ClearEmulator-error.log"
        $script="`
        WindowsProcess StopEmulator
        {
            Path=`"$storageemulator`" `
            Arguments=`"start /inprocess`" `
            Ensure=`"Absent`" `
        }

        DynamicsProcess InitializeEmulator
        {
            Ensure=`"Present`" `
            Arguments=`"init -forcecreate -server $env:COMPUTERNAME`"
            File=`"$storageemulator`" `
            StandardErrorPath=`"$initEmulatorStdErr`" `
            StandardOutputPath=`"$initEmulatorStdOut`" `
            MonitoringAssembly=`"$global:telemetrydll`" `
            WaitForExit=`$true `
            DependsOn=@(`"[WindowsProcess]StopEmulator`")
        }

        DynamicsProcess StartEmulator
        {
            Ensure=`"Present`" `
            Arguments=`"start`"
            File=`"$storageemulator`" `
            StandardErrorPath=`"$startEmulatorStdErr`" `
            StandardOutputPath=`"$startEmulatorStdOut`" `
            MonitoringAssembly=`"$global:telemetrydll`" `
            WaitForExit=`$true `
            DependsOn=@(`"[DynamicsProcess]InitializeEmulator`")
        }
        
        DynamicsProcess ClearEmulator
        {
            Ensure=`"Present`" `
            Arguments=`"clear all`"
            File=`"$storageemulator`" `
            StandardErrorPath=`"$clearEmulatorStdErr`" `
            StandardOutputPath=`"$clearEmulatorStdOut`" `
            MonitoringAssembly=`"$global:telemetrydll`" `
            WaitForExit=`$true `
            DependsOn=@(`"[DynamicsProcess]InitializeEmulator`")
        }
        "
    
        Write-Content $script

        # Generate script for windows task to start the azure storage emulator (reboot scenario)
        Write-Log "Creating the configuration to register windows task to start the azure storage emulator"
        
        $script = @"
        DynamicsScheduledTask StartAzureStorageEmulator_ScheduledTask
        {
            TaskName  = "DynamicsStartAzureStorageEmulator"
            Ensure    = "Present"
            Command   = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe"
            Arguments = "START"
        }
"@
        Write-Content $script

        Write-Trace-End "start storage emulator"
    }
}

function Write-Database-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $packageDir=$xd.SelectSingleNode("//ns:Setting[@Name='Aos.PackageDirectory']",$ns).getAttribute("Value")
    $dbserver=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.DbServer']",$ns).getAttribute("Value")
    $dbname=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.Database']",$ns).getAttribute("Value")
    $sqlpwd=(Get-KeyVaultSecret -VaultUri $xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.SqlPwd']",$ns).getAttribute("Value"))
    $sqluser=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.SqlUser']",$ns).getAttribute("Value")

    Write-Trace-start "Restore database"

    $script="`
    DynamicsDatabase DeployDatabase `
    { `
        Ensure=`"Present`" `
        PackageDirectory=`"$packageDir`" `
        DatabaseName=`"$dbname`" `
        DatabaseServer=`"$dbserver`" `
        UserName=`"$sqluser`" `
        Password=`"$sqlpwd`" `
        DependsOn=@(`"[DynamicsPackage]DeployPackages`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
    Write-Log "Creating the configuration to deploy the Unified Operations database"
    Write-Content $script

    Write-Trace-End "Restore database"
}

function Write-WebSite-Configuration([switch]$DeployDb)
{
    Import-Module WebAdministration
    $parentdir=Split-Path -parent $PSCommandPath
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $websiteName=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.ApplicationName']",$ns).getAttribute("Value")
    $webAppPoolName=$websiteName
    $localPort=$xd.SelectSingleNode("//ns:InputEndpoint",$ns).getAttribute("LocalPort")
    $webRoot=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='WebRoot']",$ns).getAttribute("Directory")
    $endpoints=$xd.SelectNodes("//ServiceModel/Endpoints/InputEndpoint")
    $hostnames=@()
    [bool]$createARRhealthprobebinding=$false
    Write-Trace-start "Create AOS website"

    if($DeployDb){
    # create the aos app-pool
    $script="`
    xWebAppPool CreateAosAppPool `
    { `
        Ensure=`"Present`" `
        State=`"Started`" `
        Name=`"$webAppPoolName`" `
        DependsOn=@(`"[DynamicsDatabase]DeployDatabase`") `
    }"}else{
     # create the aos app-pool
    $script="`
    xWebAppPool CreateAosAppPool `
    { `
        Ensure=`"Present`" `
        State=`"Started`" `
        Name=`"$webAppPoolName`" `
    }"    
    }
    Write-Log "Creating the configuration to create the AOS app pool"
    Write-Content $script

    # Task 3745881: use topology data to determine app pool settings
    $optionalAppPoolSettings = 
@"        
        PingMaxResponseTime=90
        RapidFailProtection=`$false
        IdleTimeout=0
"@
    
    $script=
@"
    DynamicsApppoolManager SetApppoolIdentity
    {
        Ensure="Present"
        IdentityType=2
        ApppoolName="$webAppPoolName"
        Username=""
        Password=""
        DependsOn=@("[xWebAppPool]CreateAosAppPool")
        MonitoringAssembly="$global:telemetrydll"
$optionalAppPoolSettings
    }
"@ 
    Write-Log "Creating the configuration to set the AOS app pool identity"
    Write-Content $script

    $bindingInfo=@()
    $endpointcollection=@{}

    Write-Log "Creating ssl bindings for the AOS website"
    for($i=0;$i -lt $endpoints.Count;$i++){
        $endpoint=$endpoints[$i]
        $baseurl=New-Object System.Uri($endpoint.BaseUrl)
        $baseurlsafe=$baseurl.DnsSafeHost
        $sslCertName=$endpoint.Certificate
        if(-not [string]::IsNullOrEmpty($sslCertName)){
            $sslCertThumbprint=$xd.SelectSingleNode("//ns:Certificate[@Name='$sslCertName']",$ns).getAttribute("Thumbprint")
            $storelocation=$xd.SelectSingleNode("//ns:Certificate[@Name='$sslCertName']",$ns).getAttribute("StoreLocation")
            $storename=$xd.SelectSingleNode("//ns:Certificate[@Name='$sslCertName']",$ns).getAttribute("StoreName")
            $cert=get-childitem Cert:\$storelocation\$storeName|where {$_.Thumbprint -eq $sslCertThumbprint}
            if($cert -ne $null){
                $thumbprint=$cert.Thumbprint
            }else{
                $message="Either the SSL binding certificate is not present in the machine store or could not be retrieved."
                Write-Log $message
                throw $message
            }
        }

        $protocol = "https"
        if ([System.String]::IsNullOrWhiteSpace($endpoint.Certificate)){
            $protocol = "http"
        }

        $port=$endpoint.LocalPort
        if($port -eq 80 -and $baseurlsafe -ne "127.0.0.1")
        {
            $createARRhealthprobebinding=$true
        }

        if(IsUniqueUrlAndPort -endpointcollection:$endpointcollection -baseurl:$baseurlsafe -port:$port){
            $endpointcollection[$i]=@($baseurlsafe,$port,$protocol)
            if($baseurlsafe -ne "127.0.0.1"){
                $hostnames+=$baseurlsafe
                write-verbose "Adding url '$baseurlsafe' to the hosts file" 
            }
        }
    }

    #special binding to support the ARR health probing
    if($createARRhealthprobebinding)
    {
        $protocol='http'
        $baseurl=""
        $port=80
        $ipaddress=Get-WMIObject win32_NetworkAdapterConfiguration|Where-Object{$_.IPEnabled -eq $true}|Foreach-Object {$_.IPAddress}|Foreach-Object {[IPAddress]$_ }|Where-Object {$_.AddressFamily -eq 'Internetwork'}|Foreach-Object {$_.IPAddressToString}
        $msftwebbindinginfo="@(MSFT_xWebBindingInformation { `
                    Protocol=`"$protocol`" `
                    Port=$port `
                    HostName=`"$baseurl`" `
                    IPAddress=`"$ipaddres`" `
                    });"

        $bindingInfo+=$msftwebbindinginfo
    }

    # create the binding info collection
    for($i=0;$i -lt $endpointcollection.Keys.Count;$i++)
    {
        $bindinginfotuple=$endpointcollection[$i]
        $baseurl=$bindinginfotuple[0]
        $port=$bindinginfotuple[1]
        $protocol=$bindinginfotuple[2]
        if($i -eq $endpointcollection.Keys.Count-1){
	        $msftwebbindinginfo="@(MSFT_xWebBindingInformation { `
                    Protocol=`"$protocol`" `
                    Port=$port `
                    CertificateThumbprint=`"$thumbprint`" `
                    CertificateStoreName=`"My`" `
                    HostName=`"$baseurl`" `
                    })"
        }else{
            $msftwebbindinginfo="@(MSFT_xWebBindingInformation { `
                    Protocol=`"$protocol`" `
                    Port=$port `
                    CertificateThumbprint=`"$thumbprint`" `
                    CertificateStoreName=`"My`" `
                    HostName=`"$baseurl`" `
                    });"
        }

		$bindingInfo+=$msftwebbindinginfo
    }

    $script="`
    xWebSite CreateAosWebSite `
    { `
        Ensure=`"Present`" `
        Name=`"$websiteName`" `
        PhysicalPath=`"$webRoot`" `
        State=`"Started`" `
        ApplicationPool=`"$webAppPoolName`" `
        BindingInfo=@($bindingInfo) `
        DependsOn=@(`"[DynamicsApppoolManager]SetApppoolIdentity`") `
    }"

    Write-Log "Creating the configuration to create the AOS web site"
    Write-Content $script

    $script = "`
    DynamicsIISAdministration SetIISConfiguration `
    { `
        Ensure=`"Present`" `
        ConfigurationPath=`"MACHINE/WEBROOT/APPHOST/$websiteName/Apps`" `
        Filter=`"system.webServer/security/requestFiltering/fileExtensions/add[@fileExtension='.config']`"
        SettingName=`"allowed`" `
        SettingValue=`"True`" `
        DependsOn=@(`"[xWebSite]CreateAosWebSite`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
    Write-Log "Creating the configuration to allow the config files from being served from the IIS server"
    Write-content $script

    $hostentries=@()
    for($i=0;$i -lt $hostnames.Length;$i++)
    {
        $hostname=$hostnames[$i]
        if($i -eq $hostnames.Length-1){
            $entry="`@{Ipaddress=`"127.0.0.1`";Hostname=`"$hostname`"`}"
        }else{
            $entry="@{Ipaddress=`"127.0.0.1`";Hostname=`"$hostname`"`},"
        }

        $hostentries+=$entry
    }

    $script = "`
    DynamicsHostsFileManager SetHostsFileEntries `
    { `
        Ensure=`"Present`" `
        WebsiteName=`"$websiteName`" `
        HostEntries=$hostentries `
        DependsOn=@(`"[DynamicsIISAdministration]SetIISConfiguration`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
    Write-Log "Creating the configuration to add hosts file entries"
    Write-content $script
    
    Write-Trace-End "Create AOS website" 
}

function Write-WebConfigFile-Configuration([string]$servicemodel)
{
    Write-Trace-start "Update web config file"
    $script = "`
    DynamicsWebConfigKeyValue UpdateWebConfigSetting`
    { `
        Ensure=`"Present`" `
        ServiceModel=`"$servicemodel`" `
        DependsOn=@(`"[DynamicsHostsFileManager]SetHostsFileEntries`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"

    Write-Log "Creating the configuration to update the web.config file"
    Write-Content $script

    Write-Trace-End "Update web config file"
}

function Write-WifConfigFile-Configuration([string]$servicemodel)
{
    Write-Trace-start "Update wif and wif.services config file"
    $script = "`
    DynamicsWifConfigKeyValue UpdateWifConfigSetting `
    { `
        ServiceModel=`"$servicemodel`" `
        Ensure=`"Present`" `
        DependsOn=@(`"[DynamicsWebConfigKeyValue]UpdateWebConfigSetting`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"

    Write-Log "Creating the configuration to update the wif.config and wif.services.config files"
    write-content $script

    Write-Trace-End "Update wif and wif.services config file"
}

function Write-Aos-Http-Configuration([string]$webroot,[string]$protocol)
{
    Write-Trace-start "Configure the HTTP protocol for the AOS website"
    $script = "`
    ConfigureAosProtocol AosHttpConfiguration `
    { `
        WebRoot=`"$webroot`" `
        Ensure=`"Present`" `
        DependsOn=@(`"[DynamicsWifConfigKeyValue]UpdateWifConfigSetting`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
        Protocol=`"$protocol`" `
    }"

    Write-Log "Creating the configuration to set the AOS protocol"
    write-content $script

    Write-Trace-End "Configure the HTTP protocol for the AOS website"
}

function Write-AosUser-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)

    $dbserver=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.DbServer']",$ns).getAttribute("Value")
    $dbname=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.Database']",$ns).getAttribute("Value")
    $username=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.SqlUser']",$ns).getAttribute("Value")
    $password=(Get-KeyVaultSecret -VaultUri $xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.SqlPwd']",$ns).getAttribute("Value"))
    $hosrurl=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.HostUrl']",$ns).getAttribute("Value")

     $script = "
     DynamicsAosAdminManager UpdateAosUser `
    { `
        Ensure=`"Present`" `
        HostUrl=`"$hosrurl`" `
        DatabaseServer=`"$dbserver`" `
        DependsOn=@(`"[ConfigureAosProtocol]AosHttpConfiguration`") `
        DatabaseName=`"$dbname`" `
        UserName=`"$username`" `
        Password=`"$password`" `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"

    Write-Log "Creating the configuration to update the AOS user"
    Write-Content $script
}

function Write-DBSYNC-Configuration
{
    $parentDir=Split-Path -parent $PSCommandPath
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $bindir=$xd.SelectSingleNode("//ns:Setting[@Name='Common.BinDir']",$ns).getAttribute("Value")
    $metadataDir=$bindir
    $sqlServer=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.DbServer']",$ns).getAttribute("Value")
    $database=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.Database']",$ns).getAttribute("Value")
    $webRoot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")

    $arguments="-bindir `"$binDir`" -metadatadir `"$metadataDir`" -sqlcredentialinenvironment `"$sqlPwd`" -sqlserver `"$sqlServer`" -sqldatabase `"$database`" -setupmode `"sync`" -syncmode `"fullall`""
    $codedir=Join-Path $webRoot "bin"
    $dbsyncexe= join-path "$codedir" "Microsoft.Dynamics.AX.Deployment.Setup.exe"

    $standardout=Join-Path $global:logdir "dbsync.output"
    $standarderr=Join-Path $global:logdir "dbsync.error"

    Write-Trace-start "Perform DBSync"

    $script = "`
    DynamicsProcess DbSync `
    { `
        Ensure=`"Present`" `
        Arguments='$arguments' `
        File=`"$dbsyncexe`" `
        StandardErrorPath=`"$standarderr`" `
        StandardOutputPath=`"$standardout`" `
        MonitoringAssembly=`"$global:telemetrydll`" `
        WaitForExit=`$true `
        DependsOn=@(`"[DynamicsAosAdminManager]UpdateAosUser`") `
    }"
    
    Write-Log "Creating the configuration to execute the DBSync process"
    Write-Content $script

    Write-Trace-End "Perform DBSync"
}

function Write-InterNode-Sync-Configuration([string]$primarynode)
{
    $script="WaitForAll DbSync `
    {
        ResourceName = '[DynamicsProcess]DbSync' `
        NodeName = '$primarynode' `
        RetryIntervalSec = 60 `
        RetryCount = 50 `
    }"

    Write-Log "Creating the configuration to synchronize actions between multiple AOS instances"
    Write-Content $script
}

function Write-SymbolicLinkGeneration-Configuration([switch]$primarynode)
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $vstoolscount=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.VSToolsCount']",$ns).getAttribute("Value")
    $webRoot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")
    $packagedir=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='PackagesLocalDirectory']",$ns).getAttribute("Directory")
    if($vstoolscount -eq "0"){
        Write-Trace-start "Create symlink"

        if($primarynode){
        $script = "`
    DynamicsSymLink CreateSymLink `
    { `
        Ensure=`"Present`" `
        Webroot=`"$webRoot`" `
        PackageDir=`"$packageDir`" `
        DependsOn=@(`"[DynamicsProcess]DbSync`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
        }else{
         $script = "`
    DynamicsSymLink CreateSymLink `
    { `
        Ensure=`"Present`" `
        Webroot=`"$webRoot`" `
        PackageDir=`"$packageDir`" `
        DependsOn=@(`"[DynamicsWifConfigKeyValue]UpdateWifConfigSetting`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"
    }
        Write-Log "Creating the configuration to create sym links"
        Write-Content $script

        Write-Trace-End "Create symlink"
    }
}

function Write-NGen-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $vstoolscount=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.VSToolsCount']",$ns).getAttribute("Value")
    $webRoot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")
    $packagedir=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='PackagesLocalDirectory']",$ns).getAttribute("Directory")
    if($vstoolscount -eq "0"){
        $script = "`
    DynamicsNgen GenerateNativeImages `
    { `
        Ensure=`"Present`" `
        Webroot=`"$webRoot`" `
        PackageDir=`"$packageDir`" `
        DependsOn=@(`"[DynamicsSymLink]CreateSymLink`") `
        UpdateProbingPath=`$true `
        UseLazyTypeLoader=`$false `
        MonitoringAssembly=`"$global:telemetrydll`" `
        PerformNgen=`$true `
    }"
    }else{
        $script = "`
    DynamicsNgen GenerateNativeImages `
    { `
        Ensure=`"Present`" `
        Webroot=`"$webRoot`" `
        PackageDir=`"$packageDir`" `
        DependsOn=@(`"[DynamicsWifConfigKeyValue]UpdateWifConfigSetting`") `
        UpdateProbingPath=`$false `
        UseLazyTypeLoader=`$true `
        MonitoringAssembly=`"$global:telemetrydll`" `
        PerformNgen=`$false `
    }"
    }

    Write-Log "Creating the configuration to generate native images of the Dynamics assemblies"
    Write-Content $script
}

function Write-Resources-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $webRoot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")
    $packageDir=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='PackagesLocalDirectory']",$ns).getAttribute("Directory")

    Write-Trace-start "Deploy website resources"
    $script = "`
    DynamicsWebsiteResource AosResources `
    { `
        Ensure=`"Present`" `
        WebRoot=`"$webRoot`" `
        PackageDir=`"$packageDir`" `
        DependsOn=@(`"[DynamicsNgen]GenerateNativeImages`") `
        MonitoringAssembly=`"$global:telemetrydll`" `
    }"

    Write-Log "Creating the configuration to deploy the AOS resources"
    Write-Content $script
    
    Write-Trace-End "Deploy resources"
}

function Write-BatchService-Configuration
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $webRoot=$xd.SelectSingleNode("//ns:WorkingFolder[@Name='WebRoot']",$ns).getAttribute("Directory")
    $sqlUser=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.SqlUser']",$ns).getAttribute("Value")
    $sqlPwd=(Get-KeyVaultSecret -VaultUri $xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.SqlPwd']",$ns).getAttribute("Value"))
    $sqlServer=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.DbServer']",$ns).getAttribute("Value")
    $database=$xd.SelectSingleNode("//ns:Setting[@Name='DataAccess.Database']",$ns).getAttribute("Value")

    $webconfig=join-path $webRoot "web.config"
    $batchexe=join-path $webroot "bin\Batch.exe"
    $binarypathname="$batchexe -service $webconfig"

    Write-Trace-start "Install batch service"

    # setting the state to Running will result in the service resource timing out as the timeout is hardcoded to 2 seconds :(
    $script = "`
    Service InstallBatchService `
    { `
        Ensure=`"Present`" `
        Name=`"DynamicsAxBatch`" `
        BuiltInAccount=`"NetworkService`" `
        State=`"Stopped`" `
        DisplayName=`"Microsoft Dynamics 365 Unified Operations: Batch Management Service`" `
        Path='$binarypathname' `
        DependsOn=@(`"[DynamicsWebsiteResource]AosResources`") `
        StartupType=`"Manual`"
    }"

    Write-Log "Creating the configuration to install the Dynamics batch service"
    Write-Content $script

    if ($env:aosserviceconfiguration_skipbatchstartup -eq $true) {
        Write-Log "Skipping batch startup as aosserviceconfiguration_skipbatchstartup flag was set"
    } else {
        $sc=Join-Path $env:windir "system32\sc.exe"
        $standardout=Join-Path $global:logdir "DynamicsAXBatch-sc-start-output.log"
        $standarderr=Join-Path $global:logdir "DynamicsAXBatch-sc-start-error.log"

        $script = "`
        DynamicsProcess StartBatchService `
        { `
            Ensure=`"Present`" `
            Arguments=`"start DynamicsAXBatch`" `
            File=`"$sc`" `
            StandardErrorPath=`"$standarderr`" `
            StandardOutputPath=`"$standardout`" `
            MonitoringAssembly=`"$global:telemetrydll`" `
            DependsOn=@(`"[Service]InstallBatchService`") `
            WaitForExit=`$true `
        }"

        Write-Log "Creating the configuration to start the Dynamics batch service"
        Write-Content $script
    }

    # Generate script for windows task to start the batch service (reboot scenario)
    Write-Log "Creating the configuration to register windows task to start the batch service"
        
    $script = @"
    DynamicsScheduledTask StartBatch_ScheduledTask
    {
        TaskName  = "DynamicsStartBatch"
        Ensure    = "Present"
        Command   = "POWERSHELL.EXE"
        Arguments = "Start-Service 'MSSQLSERVER' | %{$_.WaitForStatus('Running', '00:05:00')}; Start-Service 'DynamicsAxBatch'"
        DependsOn = @("[Service]InstallBatchService")
    }
"@
    Write-Content $script

    Write-Trace-End "Install batch service"
}

function Write-Reports-Configuration
{
    $parentDir=Split-Path -parent $PSCommandPath
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $settings=$xd.SelectNodes("/ServiceModel/Configuration/Setting")
    $configuration=@{}
    foreach($setting in $settings){
        $key=$setting.getAttribute("Name")
        $value=$setting.getAttribute("Value")
        $configuration[$key]=$value
    }

    $configjson=convertto-json $configuration
    $jsontobytes=[System.Text.Encoding]::UTF8.GetBytes($configjson)
    $encodedconfiguration=[System.Convert]::ToBase64String($jsontobytes)

    $scriptPath = Join-Path $parentDir "AXDeployReports.ps1"
    $log=join-path $global:logdir "AXDeployReports.log"
    if(!(Test-path $log)){
        New-Item $log -ItemType File -Force|out-null
    }

    $expr = "$scriptPath `"$encodedconfiguration`" `"$log`""

    $dependsOn = "[DynamicsProcess]StartBatchService"
    if ($env:aosserviceconfiguration_skipbatchstartup -eq $true) {
        $dependsOn = "[Service]InstallBatchService"
    }

    Write-Trace-start "Deploy reports"

    $script = "
    Script DeployReports `
    { `
        GetScript={@{}} `
        TestScript={return `$false} `
        SetScript={& $expr} `
        DependsOn=@(`"$dependsOn`") `
    }"

    Write-Log "Creating the configuration to deploy the reports"
    Write-Content $script

    Write-Trace-End "Deploy reports"
}

function Write-RetailPerfCounters-Configuration
{
    $parentDir=Split-Path -parent $PSCommandPath
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $webRoot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")
    $instrumentedAssemblyPath=join-path $webRoot "bin\Microsoft.Dynamics.Retail.Diagnostics.dll"
    $scriptPath = Join-Path $parentDir "Register-PerfCounters.ps1"
    $log=join-path $global:logdir "RegisterRetailPerfCounters.log"
    if(!(Test-path $log)){
        New-Item $log -ItemType File -Force|out-null
    }

	$expr = "$scriptPath $instrumentedAssemblyPath $log"
	if(!(Test-Path "$log\RegisterRetailPerfCounters")){
		New-Item -ItemType directory -Path "$log\RegisterRetailPerfCounters" -Force|out-null
	}
    
    $dependsOn = "[DynamicsProcess]StartBatchService"
    if ($env:aosserviceconfiguration_skipbatchstartup -eq $true) {
        $dependsOn = "[Service]InstallBatchService"
    }

	Write-Trace-start "Create retail perf counters"
	if(Test-path $scriptPath)
	{
		$script = "`
		Script RetailPerfCounters `
		{ `
			GetScript={@{}} `
			TestScript={return `$false} `
			SetScript={& $expr} `
			DependsOn=@(`"$dependsOn`") `
		}"
	}
	else
	{
		Write-Log "Retail perf counters script not found, dummy script used for execution"
		$script = "`
		Script RetailPerfCounters `
		{ `
			GetScript={@{}} `
			TestScript={return `$false} `
			SetScript={return `$true} `
			DependsOn=@(`"$dependsOn`") `
		}"
	}

	Write-Log "Creating the configuration to execute the Retail perf counters script"
	Write-Content $script 

	Write-Trace-End "Create retail perf counters"
}

function Write-ProductConfiguration
{
    [string]$pcAppPool="ProductConfiguration"	
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $webRoot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")
    $websiteName=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.ApplicationName']",$ns).getAttribute("Value")

    $packageNames = $xd.SelectSingleNode("//ns:Setting[@Name='Aos.Packages']",$ns).getAttribute("Value")
    $packagesSourceDir = Join-Path (Split-Path -Path $PSScriptRoot -Parent) "Packages"

    # product configuration variables
	$productconfigurationdir=join-path $webRoot "productconfiguration"
    $pcwebsitepath=join-path $webRoot "productconfiguration"

    Write-Trace-start "Create product configuration web application"

    # check of production configuration package will be installed
    $productConfigurationPackageFound = $false
    $targetPackageName = 'dynamicsax-productconfiguration'
    
    Add-Type -AssemblyName "System.IO.Compression"
    Add-Type -AssemblyName "System.IO.Compression.FileSystem"

    foreach($packageName in $packageNames.Split(','))
    {
        $packageName = $packageName.Trim()
        $filter = "$packageName.*.nupkg"
        $files = Get-ChildItem -Path $packagesSourceDir -Filter $filter
        foreach($file in $files)
        {
            [System.IO.Compression.ZipArchive] $archive = [System.IO.Compression.ZipFile]::OpenRead($file.FullName)
            [System.IO.Compression.ZipArchiveEntry]$entry = $archive.GetEntry("$($packageName).nuspec")
            if ($entry -ne $null)
            {
                [xml]$xml = New-Object System.Xml.XmlDocument
                $xml.Load($entry.Open())
                $ns = @{"ns"=$xml.DocumentElement.NamespaceURI}
                $dependency = select-xml -xml $xml -Namespace $ns -XPath "//ns:dependencies/ns:dependency[@id='$targetPackageName']"
                if ($dependency -ne $null)
                {
                    $productConfigurationPackageFound = $true
                }
            }
        }
    }

    if ($productConfigurationPackageFound)
    {        
        $script = 
@"
        xWebAppPool CreateProductConfigurationWebAppPool
        {
            Ensure="Present"
            State="Started"
            Name="$pcAppPool"
            DependsOn=@("[Script]RetailPerfCounters")
        }
    	
        # create product configuration web application
        xWebApplication CreateProductConfigurationWebApplication `
        {
            Ensure="Present"
            Name="ProductConfiguration"
            Website="$websiteName"
            WebAppPool="$pcAppPool"
            PhysicalPath="$pcwebsitepath"
            DependsOn=@("[xWebAppPool]CreateProductConfigurationWebAppPool")
        }
"@

        Write-Log "Creating the configuration to create the product configuration web application"
        Write-Content $script

        
        # Configure app pool settings
        $optionalAppPoolSettings = 
@"        
        PingMaxResponseTime=90
        RapidFailProtection=`$true
        IdleTimeout=20
"@

        $script=
@"
        DynamicsApppoolManager SetProductConfigurationApppoolIdentity
        {
            Ensure="Present"
            IdentityType=2
            ApppoolName="$pcAppPool"
            Username=""
            Password=""
            DependsOn=@("[xWebAppPool]CreateProductConfigurationWebAppPool")
            MonitoringAssembly="$global:telemetrydll"
$optionalAppPoolSettings
        }
"@ 
        Write-Log "Creating the configuration to set the product configuration app pool identity"
        Write-Content $script

    }
    else
    {
        Write-Log "Skipping product configuration web site DSC because package file not found: '$targetPackageName'."
    }

    Write-Trace-End "Create product configuration web application"
}

function Write-ConfigInstallationInfo-Configuration([string]$servicemodel)
{
    Write-Trace-start "Create config installation info"
    $script = "`
    ConfigInstallationInfoManager CreateConfigInstallationInfo `
    { `
        Ensure=`"Present`" `
        MonitoringAssembly=`"$global:telemetrydll`" `
        ServiceXml=`"$servicemodel`" `
        Log=`"$global:logdir`" `
        InstallationAssembly=`"$installationdll`" `
        InstallationInfoXml=`"$installationinfoxml`" `
        DependsOn=@(`"[Script]RetailPerfCounters`") `
    } "

    Write-Content $script
    Write-Trace-End "Create config installation info"
}

function Write-EncryptionConfiguration-Configuration([string]$webroot)
{
    Write-Trace-start "Create config to encrypt web configuration"
    $script = "`
    EncryptionManager EncryptConfiguration `
    { `
        Ensure=`"Present`" `
        MonitoringAssembly=`"$global:telemetrydll`" `
        WebRoot=`"$webroot`" `
        Log=`"$global:logdir`" `
        DependsOn=@(`"[ConfigInstallationInfoManager]CreateConfigInstallationInfo`") `
    } "

    Write-Content $script
    Write-Trace-End "Create config to encrypt web configuration"
}

#endregion

#region helper functions
function Initialize-Log([string]$logdir,[string]$logfile)
{
    if(-not (Test-Path $logdir)){
        New-Item -ItemType Directory -Path $logdir|out-null
    }
    
    if(Test-Path $logfile){
        Remove-Item $logfile -Force|out-null
    }

    New-item $logfile -ItemType File -Force|out-null
}

function Write-Log([string]$message)
{
    Add-Content $global:logfile $message
    Write-Host $message
}

function Write-Header
{
    $datetime=Get-Date
    $header="`
###
# ==++==
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Generated date: $datetime
#
# This file is auto-generated by a tool. Any changes made to this file will be lost.
#
# This file will be compiled to generate a Windows DSC configuration that will deploy 
# the Dynamics AOS service on this machine.
###

#region imports
Import-Module `"`$PSScriptRoot\DeploymentHelper.psm1`" -DisableNameChecking
#endregion

#region Instrumentation helpers
StartMonitoring -monitoringdll:`"`$PSScriptRoot\Microsoft.Dynamics.AX7Deployment.Instrumentation.dll`"
#endregion

#region service configuration
Configuration AosServiceConfiguration { `

    Import-DscResource -Module xWebAdministration `
    Import-DscResource -Module xNetworking `
    Import-DscResource -Module xDynamics `
    Import-DscResource -Module xDatabase `
    Import-DscResource –ModuleName PSDesiredStateConfiguration `
    
    Node `"localhost`" { 
    "
    Write-Content $header
}

function Write-Footer([string]$outputpath)
{
    $footer="}`
    }`

`$configData=@{
    AllNodes=@(
        @{ NodeName = `"localhost`";CertificateFile=`"$global:encryptioncertpublickeyfile`";Thumbprint=`"$global:encryptioncertthumbprint`";PSDscAllowDomainUser=`$true; }
    )
}

#endregion

# generate the MOF file `    
AosServiceConfiguration -OutputPath:$outputpath -ConfigurationData:`$configData"

    Write-Content $footer
}

function Write-Content([string]$content)
{
   Add-Content $global:dscconfigfile -Value $content
}

function Write-Trace-Start([string]$message)
{
    $tracestart="Start-Trace `"$message`""
    Add-Content $global:dscconfigfile -value ([string]::Empty)
    Add-Content $global:dscconfigfile -value $tracestart
}

function Write-Trace-End([string]$message)
{
    $traceend="End-Trace `"$message`""
    Add-Content $global:dscconfigfile -value $traceend
}

function Get-MSIProperties([string]$msiFile)
{
    [hashtable]$msiprops=@{}
 
    [System.IO.FileInfo]$file=Get-Item $msiFile
    $windowsInstaller=New-Object -com WindowsInstaller.Installer
    $database=$windowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod",$null,$windowsInstaller,@($file.FullName,0))
    # product code
    $q = "SELECT Value FROM Property WHERE Property = 'ProductCode'"
    $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $database, ($q))
    $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)
    $record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)
    [string]$productCode = $record.GetType().InvokeMember("StringData", "GetProperty", $Null, $record, 1)
    $productCode=$productCode.TrimStart("{")
    $productCode=$productCode.TrimEnd("}")

    # product name
    $q = "SELECT Value FROM Property WHERE Property = 'ProductName'"
    $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $database, ($q))
    $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)
    $record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)
    [string]$productname=$record.GetType().InvokeMember("StringData", "GetProperty", $Null, $record, 1)
    $View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null)

    # product version
    $q = "SELECT Value FROM Property WHERE Property = 'ProductVersion'"
    $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $database, ($q))
    $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)
    $record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)
    [string]$productversion=$record.GetType().InvokeMember("StringData", "GetProperty", $Null, $record, 1)
    $View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null)

    $msiprops.productcode=$productCode
    $msiprops.productname=$productname
    $msiprops.productversion=$productversion
    return $msiprops
}

function IsUniqueUrlAndPort($endpointcollection,[string]$baseurl,[string]$port){
    for($i=0;$i -lt $endpointcollection.Keys.Count;$i++){
        $baseurlandport=$endpointcollection[$i]
        if($baseurlandport[0] -eq $baseurl -and $baseurlandport[1] -eq $port){
            return $false
        }
    }

    return $true
}

function Get-RegistryKeyValue([string]$registryKey,[string]$registryKeyValueName,[string]$defaultValue=[string]::Empty)
{
    $item=Get-ItemProperty "$registryKey" -ErrorAction SilentlyContinue
    $value=$item.$registryKeyValueName
    if([string]::IsNullOrEmpty($value)){
        return $defaultValue
    }

    return $value
}

function Get-AzureStorageEmulatorInstallPath
{
    $registrykey="HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Azure Storage Emulator\"
    $registrykeyvaluename="InstallPath"
    $emulatorInstallPath=Get-RegistryKeyValue -registryKey:$registrykey -registryKeyValueName:$registrykeyvaluename
    Write-Verbose "Emulator installation path: '$emulatorInstallPath'"
    return $emulatorInstallPath
}

Function Get-ProductEntry
{
    param
    (
        [string] $Name,
        [string] $IdentifyingNumber
    )
    
    $uninstallKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
    $uninstallKeyWow64 = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    
    if($IdentifyingNumber)
    {
        $keyLocation = "$uninstallKey\$identifyingNumber"
        $item = Get-Item $keyLocation -EA SilentlyContinue
        if(-not $item)
        {
            $keyLocation = "$uninstallKeyWow64\$identifyingNumber"
            $item = Get-Item $keyLocation -EA SilentlyContinue
        }

        return $item
    }
    
    foreach($item in (Get-ChildItem -EA Ignore $uninstallKey, $uninstallKeyWow64))
    {
        if($Name -eq (Get-LocalizableRegKeyValue $item "DisplayName"))
        {
            return $item
        }
    }
    
    return $null
}

function Get-LocalizableRegKeyValue
{
    param(
        [object] $RegKey,
        [string] $ValueName
    )
    
    $res = $RegKey.GetValue("{0}_Localized" -f $ValueName)
    if(-not $res)
    {
        $res = $RegKey.GetValue($ValueName)
    }
    
    return $res
}

function Copy-Files([string]$targetdir)
{
    Copy-Item "$PSScriptRoot\DeploymentHelper.psm1" $targetdir
    Copy-Item "$PSScriptRoot\..\ETWManifest\Microsoft.Dynamics.AX7Deployment.Instrumentation.dll" $targetdir
}

function CreateConfiguration($configData)
{
    [System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
    $xd.LoadXml($global:decodedservicemodelxml)
    $ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
    $ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
    $deploydb=$xd.SelectSingleNode("//ns:Setting[@Name='Deploy.DeployDatabase']",$ns).getAttribute("Value")
    $webroot=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.WebRoot']",$ns).getAttribute("Value")
    $startStorageEmulatorNode=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.StartStorageEmulator']",$ns)
    $protocol=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.HttpProtocol']",$ns).getAttribute("Value")
    $deployReportsNode=$xd.SelectSingleNode("//ns:Setting[@Name='Deploy.DeployReports']",$ns)
    
    $startStorageEmulator="false"
    if($startStorageEmulatorNode -ne $null)
    {
        $startStorageEmulator=$startStorageEmulatorNode.getAttribute("Value")
    }
    
    $deployReports="true"
    if($deployReportsNode -ne $null)
    {
        $deployReports=$deployReportsNode.getAttribute("Value")
    }

    # get the name of the primary node
    $nodes=$ConfigData.AllNodes
    foreach($node in $nodes){
        if($node.Role -eq "Primary"){
            [string]$primarynodename=$node.NodeName
            Write-Log "The primary AOS node is $primarynodename"
            break;
        }
    }

    # create the configuration file
    Write-Header 
    Write-LCM-Configuration -configdata:$ConfigData
    Write-Certificate-Configuration
    Write-Dependency-Configuration
    Write-Firewall-Ports-Configuration
    Write-Perf-Counters-Configuration
    Write-DynamicsTools-Configuration
    Write-AosWebsite-Configuration
    Write-Packages-Configuration -deploydb:$deploydb
    if($startStorageEmulator -eq "true")
    {
        Write-Storage-Emulator-Configuration
    }

    Write-WebSite-Configuration
    Write-WebConfigFile-Configuration -servicemodel:$servicemodelxml
    Write-WifConfigFile-Configuration -servicemodel:$servicemodelxml
    Write-Aos-Http-Configuration -webroot:$webroot -protocol:$protocol

    $fqdn=[System.Net.Dns]::GetHostByName(($env:computerName))

    # on a work group machine the GetHostByName() returns the netbios name
    $netbiosname=$primarynodename.Split(".")[0]
   if($primarynodename -eq $fqdn.HostName -or $netbiosname -eq $fqdn.HostName -or $primarynodename -eq $env:computerName){
        Write-AosUser-Configuration

        #TODO: refactor this code so that it uses a separate flag to determine if the DBSync should be run or not
        if($startStorageEmulator -eq "false") # if emulator is not started, we are on a non onebox deployment and run DBSync
        {
            Write-DBSYNC-Configuration
        }
               
        Write-SymbolicLinkGeneration-Configuration -primarynode
    }else{
        Write-InterNode-Sync-Configuration -primarynode:$primarynodename
        Write-SymbolicLinkGeneration-Configuration
    }
    
    Write-NGen-Configuration
    Write-Resources-Configuration
    Write-BatchService-Configuration
    if($deployReports -ne "false")
    {
        Write-Reports-Configuration
    }
    Write-RetailPerfCounters-Configuration
    Write-ProductConfiguration
    Write-ConfigInstallationInfo-Configuration -servicemodel:$servicemodelxml
    Write-EncryptionConfiguration-Configuration -webroot:$webroot
    Write-Footer -outputpath:$outputpath
}

function Copy-CustomDSCResources([string]$grandparentDir)
{
    if(Test-Path "$grandparentDir\Scripts\xDynamics"){
        Write-Log "Copying the custom DSC resources"
        Copy-Item -Path "$grandparentDir\Scripts\xDynamics" -Destination "$env:ProgramFiles\WindowsPowerShell\Modules" -Force -Recurse -Verbose
        Get-ChildItem -Path "$env:ProgramFiles\WindowsPowerShell\Modules" -Recurse | Unblock-File -Verbose  
    }else{
        Write-Log "No custom DSC resources to copy"
    }
}

function Save-EncryptionCertificate-PublicKey($certificate)
{
    Write-Log "Saving the encryption cert public key to file..."
    $global:encryptioncertpublickeyfile=join-path $global:logdir "EncryptCert.cer"
    Export-Certificate -cert:$certificate -FilePath $global:encryptioncertpublickeyfile -Force -Type CERT | out-null
}

function Get-EncryptionCertificate-Thumbprint
{
    $subject="MicrosoftDynamicsAXDSCEncryptionCert"

    #get or create a new self-signed encryption certificate to secure sensitive info in the MOF files
    $cert=Get-Make-Encryption-Cert -subject:$subject
    Save-EncryptionCertificate-PublicKey -certificate:$cert
    $cert.Thumbprint
}

function Get-Make-Encryption-Cert([string]$subject)
{
    Write-Log "Checking if a self-signed encryption cert with subject '$subject' exists..."
    $formattersubject="CN=$subject"
    $cert=Get-ChildItem Cert:\LocalMachine\My|where {$_.Subject -eq $formattersubject}
    if($cert -ne $null) # if cert existed make sure it is valid
    {
        if(!(Is-ValidCert -certificate:$cert))
        {
            Write-Log "Dynamics DSC self-signed encryption cert is expired. Generating a new self-signed encryption certificate..."
            Write-Log "Deleting the invalid self-signed encryption certificate with subject '$cert.subject'... "
            $thumbprint=$cert.Thumbprint
            Remove-Item -Path Cert:\LocalMachine\My\$thumbprint -Force -DeleteKey |out-null
            $cert=Make-Certificate -subject:$subject
        }
    }
    else
    {
        $cert=Make-Certificate -subject:$subject 
    }

    $cert
}

function Is-ValidCert($certificate)
{
    $subject=$certificate.Subject
    Write-Log "Checking if the certificate '$subject' is valid..."
    $thumbprint=$certificate.Thumbprint
    $cert=Get-ChildItem -Path Cert:\LocalMachine\My\$thumbprint
    if($cert -ne $null)
    {
        if($cert.NotAfter -lt (Get-Date)) #expired
        {
            return $false
        }
        else
        {
            return $true
        }
    }

    #if cert is not found, return false
    return $false
}

function Make-Certificate([string]$subject)
{
     Write-Log "Creating a new DSC self-signed encryption certificate with subject '$subject'..."
     return New-SelfSignedCertificate -DnsName $subject -CertStoreLocation cert:\LocalMachine\My
}

#endregion

#region Main...
$parentdir=Split-Path -parent $PSCommandPath
$grandparentdir=Split-Path -parent $parentdir

$global:decodedservicemodelxml=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($servicemodelxml))
$global:logfile=$log
$global:logdir=[System.IO.Path]::GetDirectoryName($log)
$global:dscconfigfile=join-path $global:logdir "Install.ps1"
$outputpath=join-path $global:logdir "Install"

$etwpath=join-path $grandparentdir "ETWManifest"
$global:telemetrydll = join-path $etwpath "Microsoft.Dynamics.AX7Deployment.Instrumentation.dll"
$installationdll=Join-Path $parentdir "Microsoft.Dynamics.AX.AXInstallationInfo.dll"
$installationinfoxml=join-path $parentdir "InstallationInfo.xml"
$keyVaultModule=Join-Path -Path $PSScriptRoot -ChildPath "KeyVault.psm1"

[System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
$xd.LoadXml($global:decodedservicemodelxml)
$ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
$ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
$keyVaultName=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.AzureKeyVaultName']",$ns).getAttribute("Value")
$appId=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.AzureKeyVaultAppId']",$ns).getAttribute("Value")
$thumprint=$xd.SelectSingleNode("//ns:Setting[@Name='Infrastructure.AzureKeyVaultCertThumbprint']",$ns).getAttribute("Value")

Import-Module $keyVaultModule -ArgumentList ($keyVaultName, $appId, $thumprint)

if(-not (Test-Path $global:telemetrydll)){
    throw "The deployment telemetry assembly does not exist"
}

if(Test-Path $global:dscconfigfile){
    Remove-Item $global:dscconfigfile -Force
}

Initialize-Log -logdir:$global:logdir -logfile:$log
Copy-Files -targetdir:$global:logdir
Copy-CustomDSCResources -grandparentDir:$grandparentdir

Write-Log "Enabling WinRM remote management"
WinRM quickconfig -q

# construct the config data
[System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
$xd.LoadXml($global:decodedservicemodelxml)
$ns=New-Object System.Xml.XmlNamespaceManager($xd.NameTable)
$ns.AddNamespace("ns",$xd.DocumentElement.NamespaceURI)
[string]$nodes=$xd.SelectSingleNode("//ns:Setting[@Name='Deploy.AosNodes']",$ns).getAttribute("Value")

if($env:USERDOMAIN -eq $env:COMPUTERNAME){
    $global:domain="builtin"
}else{
    $global:domain=$env:USERDOMAIN
}

$global:username=$env:USERNAME

[string[]]$aosvms=$nodes.Split(",")

if($aosvms.Count -eq 0){
    throw "Atleast one AOS node should be defined in the servicemodel.xml"
}

$global:encryptioncertthumbprint=Get-EncryptionCertificate-Thumbprint

$configData=@{
    AllNodes=@(
        @{ NodeName="*";CertificateFile=$global:encryptioncertpublickeyfile;Thumbprint=$global:encryptioncertthumbprint;PSDscAllowDomainUser=$true;}
        @{ NodeName=$aosvms[0];Role="Primary" }
    )
}

for($i=1;$i -lt $aosvms.Count;$i++){
    $configData.AllNodes += @{NodeName=$aosvms[$i];Role="Secondary" }
}

CreateConfiguration -configData:$ConfigData
#endregion

#region generate MOF and execute the configuration
try
{
    Write-Log "Generating the MOF..."
    & $global:dscconfigfile

    Set-Location $outputpath

    Write-Log "Setting up LCM to decrypt credentials..."
    Set-DscLocalConfigurationManager -path "$outputpath" -Verbose *>&1 | Tee-Object $log

    Write-Log "Applying the configuration..."
    $errorsBeforeDSCConfig=$error.Count
    Start-DscConfiguration -wait -Verbose -path "$outputpath" -Force *>&1 | Tee-Object $log
    $errorsAfterDSCConfig=$error.Count

    $configstatuslog=join-path $global:logdir "aosservice-installation-status.log"
    $ConfigStatus = Get-DscConfigurationStatus
    $ConfigStatus | Format-List -Property * | Out-File -FilePath $configstatuslog -Force
    Write-Log "Error baseline: $errorsBeforeDSCConfig, Errors after DSCConfiguration: $errorsAfterDSCConfig"

    if($ConfigStatus.Status -ieq 'Success' -and $errorsBeforeDSCConfig -eq $errorsAfterDSCConfig)
    {
        return 0
    }
    else
    {
        throw "AOS service configuration did not complete, Message: $($ConfigStatus.StatusMessage), see log for details"
    }
}
catch
{
    throw $_
}

#endregion

# SIG # Begin signature block
# MIIoKQYJKoZIhvcNAQcCoIIoGjCCKBYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC2mWX7SMVaZFe/
# 3/Li6s8tBeCcweptVrXdMwbGPseA/aCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGgkwghoFAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHU1ZNoZQtmv0cCFlmkNehBr
# bqEL11o8PE0eVJU+VEGaMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAqeUJgmclgp0AcX/LkdBzagycr/Hv7OEFVQ/xGzrqpQKqwtPbd3q+SrXT
# Z3JIhDO8bDDcaTg5IJSc7QmbRZ81P/f36uIH5atj7ho5omyWEXnwC2uZP8UHcA1y
# 2SmByKzd04/zOvYuqg+AA+IJhwptV5Js4wlQPOlOH0mrPSa4VSME04UQsm1Mjjqb
# 6/qPWc5Vyjjo/fELcKwKBotiSJWBw1o2C6HRXKQhD0oe2eqg90uIts7kJ0OjQwNr
# LJ64/s6sCCiZBpgSDh3crW4uyDpOvoejvoNkO35PtA+ZrqJAHaVDhHPE1AdLRVpS
# JFrJ3OSshL6G0ICnBcVwFy0QKgtesaGCF5MwghePBgorBgEEAYI3AwMBMYIXfzCC
# F3sGCSqGSIb3DQEHAqCCF2wwghdoAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFRBgsq
# hkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCB5TdIzKEae3AHB9BhEvStlpiHLdzqBtKMsITnZYWrSTQIGZz8x/Slw
# GBIyMDI0MTIxMjA3MjEzOS4yNlowBIACAfSggdGkgc4wgcsxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy
# aWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjpEQzAwLTA1
# RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCC
# EeowggcgMIIFCKADAgECAhMzAAAB6FCwgM8rcplNAAEAAAHoMA0GCSqGSIb3DQEB
# CwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTIzMTIwNjE4NDUy
# MloXDTI1MDMwNTE4NDUyMlowgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMx
# JzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjpEQzAwLTA1RTAtRDk0NzElMCMGA1UE
# AxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAOFBd0TRbNcbvDN4L1IZ0fp5XB0aUXpiClTvB44UGSlb
# 3K4SYlIMTUAMHH/QNO5EFqmVIPwJHVCrJCbFEMP8J28PJ5kUBxnqXb6eUyyyZnIL
# xM+3UZNYBS3cZdRTqDQXS0isU0uCh8KM6+bcbSPjzZW60pLjBlPps1WcFWwKGvYC
# OZVsYM3/pCz8SpqQvlzCkZ6XW17tQ7Xd1TBo+M7LRaLWpg2ZHyLtPWW6PYl0V19m
# Ew/KrKr80Odm441JuPwUznEoGhqvXatqALz/UJdovU4xcHAZpGglNi2SyL2eO6rj
# AORwDCK0JnkG1DY2o9nSUuJAHs7XQGb3Okdaf3HX8eZ0CfiSgxYpQPAfmx3/MO7i
# +LzzsVbGbniSRY2+TwV5DtlnRDmCoYpbiDqt69XYh0DEGhtZ2iurC/OovvRZ2yAN
# T8/lzQvy9giyRQll50IYczaEciw1HLD0QWhKiUMz7IJ52TPr3vzuzycinnmJwt8O
# grDmFqTkboxYXp3vW1w3NX36/1FAoxrpTB/Kq6t513mypdYJe/76F6TgDXlbJshz
# q59PSKVrMphm8O+hqzCCM/RJ4tbua+ZzDQUQvMXX+5ZpcSUsc5ciWJ4oXaL9jNDp
# TnQr11uGcxoNwlKwbSFctFPL8tBuw4PSHDsW4rSebOi1LH/l+j2Dt8LOgWOTe0MT
# AgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQU+jlbyj3zSQx9FVBtrpPfgfB6UtowHwYD
# VR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZO
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw
# VGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBc
# BggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0
# cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYD
# VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMC
# B4AwDQYJKoZIhvcNAQELBQADggIBAJZ9wmbavBe0+LviSbo8oCSeL34Fai2r5C0M
# aB5PkXeujs+whQ0pGRTK24aojy2EvclytvTfHoM6mlgkQD6lvqjao+MV107VMuq9
# Jetib46AIsv7v7cWZGZ+RG7yse+mtpvQ7X3ANu6SA7wjk6LVF6AwmXy3mT9S0TRZ
# vfMCU4ecKMsZLM/8Ojem3CFeiSTTYi8PtJkQFs9ugZu3DgsIexPakVqSkY4GH4hJ
# QxcF/zfkK5U1913DGbIa81LPITISTmHVWHu2nA/vPusn4eyI/ix2oGOPoC+im5/i
# vFQB+sRtXpqPy3AWztZUc2IiCsc2dr/nmTKsAb1i3X0I++RTDFgrS5m8+XZYLf2d
# 9zSM5OOK8Luz8hSjMTi1/Lck2TJDVw229l+2JfePkO354s564YOO+Em9gwgSSmRX
# rxcs0fv5kF9hr4+Z9FhqgUUcIkoShFJnc9sMk8GISFn3K7Ex12gwxDX1OTJ+2i3o
# TPReFKbCNgGrf0O8EVaGXIMYGGtTca14NaJV31gZIHZafG4UzLl9sArqjw9bPxBE
# yKm1ccrccMGvbBZYRyuuBxO4dkglOp6k2hiXH1VEW+P+8JdoAVyaRbWeTtUT6jBp
# evlEmOvIOr25uTIOrWZ2sX+B5pEAtdw1lt81ciSFfNcDprwKJqZxBb8OCYsmmJVA
# 2uJlgIcgMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG
# 9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
# BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEy
# MDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
# MTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az
# /1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V2
# 9YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oa
# ezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkN
# yjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7K
# MtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRf
# NN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SU
# HDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoY
# WmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5
# C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8
# FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TAS
# BgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1
# Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUw
# UzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNy
# b3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoG
# CCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB
# hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO
# mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
# a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr
# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3
# DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEz
# tTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJW
# AAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G
# 82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/Aye
# ixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI9
# 5ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1j
# dEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZ
# KCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xB
# Zj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuP
# Ntq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvp
# e784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCA00w
# ggI1AgEBMIH5oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScw
# JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0wNUUwLUQ5NDcxJTAjBgNVBAMT
# HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAIwk
# bi+DSO4w5WfYG4oAJS8/zQW6oIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDrBLXEMCIYDzIwMjQxMjEyMDEwNzE2
# WhgPMjAyNDEyMTMwMTA3MTZaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAOsEtcQC
# AQAwBwIBAAICBfAwBwIBAAICEi0wCgIFAOsGB0QCAQAwNgYKKwYBBAGEWQoEAjEo
# MCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG
# 9w0BAQsFAAOCAQEAO/2M38eaXkkFw1Q0R9FWT/lKIPNL9wF4EjnK04wmi/8BgaRH
# dZ8oS3AxzbYN0O1tHkkBBlxR7Tkv/Wc7yxVeRNtwixAkdKXohmkntiEutmtKGTxW
# vaWiygnE37TGtHaV5n8LA0/U8kAmiUaIeHNkmsB3MErnBwM2fS027+u0TNY3Q0U4
# SvQ73zQbOM/B/lGkfvrpHxsVmt6mhaPlYE0NCQpUiTlj09Pz3qp2kbhjmAZc1HRA
# 8QXLdYsrUZUN/itz8zSk1MxIc33VI6YiXrPh1ETxUFQmuTjLLVWiVFNeeMWPdg/7
# luGfgBhwL4WjE7sJKXyFFkwbD9XWfYUGNX6pnjGCBA0wggQJAgEBMIGTMHwxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB6FCwgM8rcplNAAEAAAHoMA0G
# CWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJ
# KoZIhvcNAQkEMSIEIHMe3fcsALxwYoCpORQ9ig50FHJLdU/5uNratyB8J+RbMIH6
# BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgKtLaxNUChCCCQdHn2k2qKB7TF8lP
# YndTxbVJzwf46x0wgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MAITMwAAAehQsIDPK3KZTQABAAAB6DAiBCCBASSLDNXcSC8WBiC3EyvF2yzw4OOm
# 8H2L7bt8WncL6TANBgkqhkiG9w0BAQsFAASCAgCYemrLtig9j4ue0E8gjUY5fWlw
# 1XqZZds/sfLTc/OTwlrV1Ha69klvPWYQCObQWQKqmCpfvq9UtpTQQQHazeSYo6Pz
# 5FOSKsxXTVPvqi8obfzS+U6qK9mG31uXMC2xUjkJELTxLQu88IyyFdkpp58vYufo
# QOA8DmkDwQ5BPVHl5lX7CZvp8O5c5GX4VDAfgEQZAOxDedFMODnXtzVUDhdIuicu
# PYIN/CV8/TXR031znd7KJjlgtDTlJIN00Wi0bSMqG8nMXUIBVwneMUD9xYOQaFsH
# G5XUC1vv4BwP9lEt5bsga2CUemsbJcpte4fnc6FqLX+OtEu/yFy6IfYEtmd8wSrU
# BrVuBkP5PnbRkM7miT/Mj3jSLMU3Cb3ef89Slb+lZCnksRz5lGzQCDk4m1brprsc
# naoF3wDd0bnU3Ui36R35mqFTyHN7xuwFHGczEx+PBJERyAWbA22ETu6anWUfuGrI
# seI+TDhVtq27pFNj+mQbK7Du4jhfvGdRwNGsqm78pt2qPPFIbIq15OFkB6eWEe6u
# gXyrhlWXE8ZY8Ac6YrNmtgbtICUjJc9xpl2dG5/ViVgBTEoP1J0UzoqDU3vehxbk
# CV5Nczh+SX5F20D7gbExCiiWVGERmMfwtIiZlRltg0w4Xn7e/52g9a2SRvXNoAnf
# aDnS7mG3NNOikTpXLA==
# SIG # End signature block
