# Note: AosEnvironmentUtilities imports CommonRollbackUtilities. Using -Force to import a nested module, which
# exports a specific list of functions, will remove any already imported functions from being available to the caller.
Import-Module "$PSScriptRoot\AosEnvironmentUtilities.psm1" -DisableNameChecking

$global:logfile = ""
$AosWebsiteName = Get-AosWebSiteName
$BatchService = "DynamicsAxBatch"
$PrintService = "DynamicsAXPrintService"

$configEncryptor = "Microsoft.Dynamics.AX.Framework.ConfigEncryptor.exe"

$ErrorActionPreference = "Stop"

function Initialize-Log([string]$log)
{
    if (Test-Path -Path $log)
    {
        Write-Output "Removing the existing log file '$log'."
        Remove-Item -Path $log -Force | Out-Null
    }

    Write-Output "Creating the log file '$log'."
    New-Item -Path $log -ItemType File -Force | Out-Null
    $global:logfile = $log
}

function Write-Log([string]$message)
{
    $datetime = Get-Date -Format "MM-dd-yyyy:HH:mm:ss"
    Add-Content -Path $global:logfile -Value "$datetime`: $message" | Out-Null
    Write-Output "$datetime`: $message"
}

function Log-Error([string]$error, [switch]$throw)
{
    Write-Error $error
    if ($throw)
    {
        throw $error
    }
}

function Create-Backup([string]$webroot, [string]$backupdir)
{
    $orig_webconfig = Join-Path -Path $webroot -ChildPath "web.config"
    $orig_wifconfig = Join-Path -Path $webroot -ChildPath "wif.config"
    $orig_wifservicesconfig = Join-Path -Path $webroot -ChildPath "wif.services.config"

    $backup_webconfig = Join-Path -Path $backupdir -ChildPath "web.config.backup"
    $backup_wifconfig = Join-Path -Path $backupdir -ChildPath "wif.config.backup"
    $backup_wifservicesconfig = Join-Path -Path $backupdir -ChildPath "wif.services.config.backup"

    Copy-Item -Path $orig_webconfig -Destination $backup_webconfig -Force | Out-Null
    Write-Log "Copied '$orig_webconfig' to '$backup_webconfig."

    Copy-Item -Path $orig_wifconfig -Destination $backup_wifconfig -Force | Out-Null
    Write-Log "Copied '$orig_wifconfig' to '$backup_wifconfig'."

    Copy-Item -Path $orig_wifservicesconfig -Destination $backup_wifservicesconfig -Force | Out-Null
    Write-Log "Copied '$orig_wifservicesconfig' to '$backup_wifservicesconfig'."
}

# This is the main entry point to control what is upgraded.
function Upgrade-Web-Config([string]$webroot, [int]$platformVersion)
{
    $script:PlatformReleaseVersion = $platformVersion
    Decrypt-Config -webroot:$webroot
    $webconfig = Join-Path -Path $webroot -ChildPath "Web.config"
    $batchconfig = Join-Path -Path $webroot -ChildPath "bin\Batch.exe.config"
    $wifconfig = Join-Path -Path $webroot -ChildPath "Wif.config"

    Upgrade-HttpProtocol-NewNames -webconfig:$webconfig
    Upgrade-WebConfig-NewKeys -webconfig:$webconfig
    Upgrade-WebConfig-DeleteKeys -webconfig:$webconfig
    Upgrade-WebConfig-updateKeys -webconfig:$webconfig
    Upgrade-WebConfig-Add-DependentAssemblies -webconfig:$webconfig
    Upgrade-WebConfig-Update-DependentAssemblyBinding -webconfig:$webconfig
    Upgrade-WebConfig-AssemblyReferences -webconfig:$webconfig
    Upgrade-WebConfig-rootLocations -webconfig:$webconfig
    Upgrade-WebConfig-rootLocationsWithSecurity -webconfig:$webconfig
    Upgrade-WebConfig-rootLocations-PCFControls -webconfig:$webconfig
    Upgrade-WebConfig-rootLocations-ReliableCommunicationManager -webconfig:$webconfig
    Upgrade-WebConfig-rootLocations-TelemetryManager -webconfig:$webconfig
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "api"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "data"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "metadata"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "FederationMetadata"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "services"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "soap"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "FileManagement"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "Services/SessionManager.svc"    
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "apps"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "debug"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "Resources/Scripts"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "punchout"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "DynamicsAx/Services/DispatchService.svc"
    Upgrade-WebConfig-rootLocationsWithSecurity-byName -webconfig:$webconfig -locationname "DynamicsAx/Services/DispatchServiceWebHttp.svc"
    Upgrade-WebConfig-RemoveAssemblies -webConfig:$webconfig
    Upgrade-WebConfig-RemoveWcfActivityLog -webConfig:$webconfig
    Upgrade-WebConfig-Add-LcsEnvironmentId -webConfig:$webconfig
    Upgrade-WebConfig-ExcludeIdentityModelFromInheritance -webConfig:$webconfig
    Upgrade-WebConfig-debugCompilation -webConfig:$webconfig
    Upgrade-WebConfig-targetFrameworkCompilation -webConfig:$webconfig
    Upgrade-WebConfig-Add-StaticContent -webConfig:$webconfig
    Upgrade-WebConfig-Add-RequestHandlers -webConfig:$webconfig
    Upgrade-WebConfig-Add-RequestFilteringFileExtensions -webConfig:$webconfig
    Upgrade-WebConfig-Add-SecurityrequestFilteringVerbsElements -webConfig:$webconfig

    if (Test-Path -Path $batchconfig)
    {
        Upgrade-BatchConfig-Add-DependentAssemblies -batchconfig:$batchconfig
    }

    Upgrade-WifConfig-Add-CustomTokenHandler -wifconfig:$wifconfig

    Encrypt-Config -webroot:$webroot
}

function Add-DependencyAssembly([System.Xml.XmlDocument] $xmlDoc, [string] $assemblyName, [string] $publickey, [string] $oldVersion, [string] $newVersion, [string] $culture)
{
    $ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
    $ns.AddNamespace("ab", "urn:schemas-microsoft-com:asm.v1")

    $DependencyNode = $xmlDoc.SelectSingleNode("/configuration/runtime/ab:assemblyBinding", $ns)

    [System.Xml.XmlElement] $dependencyElement = $xmlDoc.CreateElement("dependentAssembly", "urn:schemas-microsoft-com:asm.v1")
    [System.Xml.XmlElement] $assemblyElement = $xmlDoc.CreateElement("assemblyIdentity", "urn:schemas-microsoft-com:asm.v1")


    $nameAttribute = $xmlDoc.CreateAttribute('name')
    $nameAttribute.Value = $assemblyName

    $publicKeyAttribute = $xmlDoc.CreateAttribute('publicKeyToken')
    $publicKeyAttribute.Value = $publickey

    $assemblyElement.Attributes.Append($nameAttribute)
    $assemblyElement.Attributes.Append($publicKeyAttribute)

    #This attribute is optional so only add the attribute if a value is passed in for culture
    if (!([String]::IsNullOrWhiteSpace($culture)))
    {
        $cultureAttribute = $xmlDoc.CreateAttribute('culture')
        $cultureAttribute.Value = $culture
        $assemblyElement.Attributes.Append($cultureAttribute)
    }

    $dependencyElement.AppendChild($assemblyElement)

    #This element is optional so only add the node if a value is passed in for old
    if (!([String]::IsNullOrWhiteSpace($oldVersion)))
    {
        [System.Xml.XmlElement] $bindingElement = $xmlDoc.CreateElement("bindingRedirect", "urn:schemas-microsoft-com:asm.v1")
        $oldVersionAttribute = $xmlDoc.CreateAttribute('oldVersion')
        $oldVersionAttribute.Value = $oldVersion

        $newVersionAttribute = $xmlDoc.CreateAttribute('newVersion')
        $newVersionAttribute.Value = $newVersion

        $bindingElement.Attributes.Append($oldVersionAttribute)
        $bindingElement.Attributes.Append($newVersionAttribute)
        $dependencyElement.AppendChild($bindingElement)
    }

    $DependencyNode.AppendChild($dependencyElement)
}

function Update-DependentAssemblyBinding([System.Xml.XmlDocument] $xmlDoc, [string] $assemblyName, [string] $oldVersion, [string] $newVersion)
{
    $ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
    $ns.AddNamespace("ab", "urn:schemas-microsoft-com:asm.v1")

    if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
    {
        $bindingNode = $xmlDoc.SelectSingleNode("/configuration/runtime/ab:assemblyBinding/ab:dependentAssembly/ab:bindingRedirect[../ab:assemblyIdentity[@name='$assemblyName']]", $ns)
        $bindingNode.oldVersion = $oldVersion
        $bindingNode.newVersion = $newVersion
    }
}

function Remove-DependentAssemblyBinding([System.Xml.XmlDocument] $xmlDoc, [string] $assemblyName)
{
    $ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
    $ns.AddNamespace("ab", "urn:schemas-microsoft-com:asm.v1")

    if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
    {
        $bindingNode = $xmlDoc.SelectSingleNode("/configuration/runtime/ab:assemblyBinding/ab:dependentAssembly/ab:bindingRedirect[../ab:assemblyIdentity[@name='$assemblyName']]", $ns)
        $NodetoRemove = $bindingNode.ParentNode
        $NodetoRemove.ParentNode.RemoveChild($NodetoRemove)
    }
}

function Upgrade-WebConfig-Update-DependentAssemblyBinding([string] $webConfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $owinVersion = '3.0.1.0'
    $owinBinding = '0.0.0.0-3.0.1.0'

    # Check if update 42 or later
    if ($PlatformReleaseVersion -ge 5968)
    {
        # Check if update 56 or later
        if ($PlatformReleaseVersion -ge 6730)
        {
           $owinVersion = '4.2.2.0'
           $owinBinding = '0.0.0.0-4.2.2.0'
        }
        elseif ($PlatformReleaseVersion -ge 6192)
        {
           $owinVersion = '4.1.1.0'
           $owinBinding = '0.0.0.0-4.1.1.0'
        }
        else
        {
          $owinVersion = '3.1.0.0'
          $owinBinding = '0.0.0.0-3.1.0.0'
        }
    }

    Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Owin' -oldVersion:$owinBinding -newVersion:$owinVersion
    Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Owin.Security' -oldVersion:$owinBinding -newVersion:$owinVersion

	# Check if update 43 or later
    if ($PlatformReleaseVersion -ge 6009)
	{
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Data.Services.Client' -oldVersion:'5.6.0.0-5.8.4.0' -newVersion:'5.8.4.0'
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Data.OData' -oldVersion:'0.0.0.0-5.8.4.0' -newVersion:'5.8.4.0'
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Data.Edm' -oldVersion:'5.6.0.0-5.8.4.0' -newVersion:'5.8.4.0'
	}

    # Check if update 48 or later
    if ($PlatformReleaseVersion -ge 6232)
	{
        # remove duplicate entry of Microsoft.OData.Edm
        $assemblyName = 'Microsoft.OData.Edm'
        if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-DependentAssemblyBinding -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }

        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-6.19.0.11114' -newVersion:'6.19.0.11114'
        }
        else
        {
		    Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:$assemblyName -oldVersion:'0.0.0.0-6.19.0.11114' -newVersion:'6.19.0.11114'
        }

		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.OData.Core' -oldVersion:'0.0.0.0-6.19.0.11114' -newVersion:'6.19.0.11114'
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Spatial' -oldVersion:'0.0.0.0-6.19.0.11114' -newVersion:'6.19.0.11114'
	}

    # Check if update 57 or later
    if ($PlatformReleaseVersion -ge 6825)
	{
        # Add redirect of Microsoft.WindowsAzure.Storage
        $assemblyName = 'Microsoft.WindowsAzure.Storage'
        if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-DependentAssemblyBinding -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }

        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-9.3.2.0' -newVersion:'9.3.2.0'
	}

    # check if update 58 or later
    if ($PlatformReleaseVersion -ge 6931)
	{
        # Add redirect of System.Security.Cryptography.Cng
        $assemblyName = 'System.Security.Cryptography.Cng'
        if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-DependentAssemblyBinding -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }

        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'b03f5f7f11d50a3a' -oldVersion:'0.0.0.0-4.3.3.0' -newVersion:'4.3.3.0'

        # Add Owin redirects
        $assemblyName = 'Microsoft.Owin'
        if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-DependentAssemblyBinding -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-4.2.2.0' -newVersion:'4.2.2.0'
        
        $assemblyName = 'Microsoft.Owin.Security'
        if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-DependentAssemblyBinding -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-4.2.2.0' -newVersion:'4.2.2.0'

        #Add redirect for System.IdentityModel.Tokens.Jwt
        $assemblyName = 'System.IdentityModel.Tokens.Jwt'
        if (Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-DependentAssemblyBinding -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }
    }

    $xmlDoc.Save($webconfig)
}

function Get-DependentAssemblyExists([string] $assemblyName, [System.Xml.XmlDocument] $xmlDoc)
{
    $dependentAssemblyExists = $false
    $ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
    $ns.AddNamespace("ab", "urn:schemas-microsoft-com:asm.v1")
    $assemblyNode = $xmlDoc.SelectSingleNode("/configuration/runtime/ab:assemblyBinding/ab:dependentAssembly/ab:assemblyIdentity[@name='$assemblyName']", $ns)
    if ($assemblyNode -ne $null)
    {
        $dependentAssemblyExists = $true
    }
    return $dependentAssemblyExists
}

function Update-AppContextSwitchOverrides([string] $keyValue, [System.Xml.XmlDocument] $xmlDoc)
{
    Write-Output "Value to be added to AppContextSwitchOverrides = '$keyValue'"
    $appContextSwitchOverrides = $xmlDoc.SelectSingleNode("/configuration/runtime/AppContextSwitchOverrides");
    if($appContextSwitchOverrides -ne $null -and $appContextSwitchOverrides.value -notmatch $keyValue)
    {
        Write-Output "Current AppContextSwitchOverrides value = '$($appContextSwitchOverrides.value)'"
        $appContextSwitchOverrides.value = $appContextSwitchOverrides.value + ";" + $keyValue
        Write-Output "Updated AppContextSwitchOverrides value = '$($appContextSwitchOverrides.value)'"
    }
}

function Upgrade-WebConfig-Add-DependentAssemblies([string] $webConfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $assemblyName = 'System.Web.http'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-5.0.0.0' -newVersion:'5.2.2.0'
    }
    $assemblyName = 'System.Web.Http.WebHost'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
    {
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-5.0.0.0' -newVersion:'5.2.2.0'
    }
    $assemblyName = 'System.Net.Http'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'b03f5f7f11d50a3a'
    }

    if ($PlatformReleaseVersion -gt 5221)
    {
        Write-Log "Upgrading ApplicationInsights binding redirect for web.config (PU25)"
        #Add app insights and dependencies binding redirects
        $assemblyName = 'Microsoft.AspNet.TelemetryCorrelation'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-1.0.5.0' -newVersion:'1.0.5.0'
        }
        $assemblyName = 'System.Diagnostics.DiagnosticSource'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'cc7b13ffcd2ddd51' -oldVersion:'0.0.0.0-4.0.3.1' -newVersion:'4.0.3.1'
        }
        $assemblyName = 'Microsoft.AI.Agent.Intercept'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.4.0.0' -newVersion:'2.4.0.0'
        }
        $assemblyName = 'Microsoft.ApplicationInsights'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.DependencyCollector'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.PerfCounterCollector'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.Web'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.WindowsServer'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.ServerTelemetryChannel'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
    }

    if ($PlatformReleaseVersion -gt 4641)
    {
        $assemblyName = 'System.Net.Http.Formatting'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-5.2.2.0' -newVersion:'5.2.3.0'
        }
    }
    $assemblyName = 'Microsoft.Dynamics.Client.InteractionService'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'6.0.0.0' -newVersion:'7.0.0.0' -culture:'neutral'
    }
    $assemblyName = 'Microsoft.Owin.Security'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
      $owinVersion = '3.0.1.0'
      $owinBinding = '0.0.0.0-3.0.1.0'

      # Check if update 42 or later
      if ($PlatformReleaseVersion -ge 5968)
      {
        # Check if update 56 or later
        if ($PlatformReleaseVersion -ge 6730)
        {
           $owinVersion = '4.2.2.0'
           $owinBinding = '0.0.0.0-4.2.2.0'
        }
        elseif ($PlatformReleaseVersion -ge 6192)
        {
           $owinVersion = '4.1.1.0'
           $owinBinding = '0.0.0.0-4.1.1.0'
        }
        else
        {
          $owinVersion = '3.1.0.0'
          $owinBinding = '0.0.0.0-3.1.0.0'
        }
      }

      Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-3.0.1.0' -newVersion:'3.0.1.0' -culture:'neutral'

    }
    $assemblyName = 'Microsoft.Diagnostics.Tracing.EventSource'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'b03f5f7f11d50a3a' -oldVersion:'1.1.17.0' -newVersion:'1.1.28.0' -culture:'neutral'
    }

    #Add check forupdate 4
    if ($PlatformReleaseVersion -ge 4425)
    {
        $assemblyName = 'Newtonsoft.Json'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'30ad4fe6b2a6aeed' -oldVersion:'0.0.0.0-9.0.0.0' -newVersion:'9.0.0.0' -culture:'neutral'
        }
        $assemblyName = 'Microsoft.IdentityModel.Clients.ActiveDirectory'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.23.0.0' -newVersion:'2.23.0.0' -culture:'neutral'
        }
        $assemblyName = 'Microsoft.Azure.ActiveDirectory.GraphClient'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.1.1.0' -newVersion:'2.1.1.0' -culture:'neutral'
        }
    }

    # PU50+
    if ($PlatformReleaseVersion -gt 6316)
    {
        $assemblyName = 'BouncyCastle.Crypto'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'0e99375e54769942' -oldVersion:'0.0.0.0-1.9.0.0' -newVersion:'1.9.0.0'
        }
    }

    if ($PlatformReleaseVersion -gt 6841)
    {
        $assemblyName = 'Microsoft.Graph'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-1.14.0.0' -newVersion:'1.7.0.0'
        }

        $assemblyName = 'Microsoft.Graph.Core'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-1.14.0.0' -newVersion:'1.7.0.0'
        }
    }

    $xmlDoc.Save($webconfig);
}

function Upgrade-BatchConfig-Add-DependentAssemblies([string] $batchConfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($batchConfig)

    #Append Switch.System.IdentityModel.DisableCngCertificates=true to AppContextSwitchOverrides if PU is 47 or above
    if ($PlatformReleaseVersion -ge 6192)
    {
        $keyValue = 'Switch.System.IdentityModel.DisableCngCertificates=true'
        Update-AppContextSwitchOverrides -keyValue: $keyValue -xmlDoc:$xmlDoc
    }

    $assemblyName = 'Microsoft.Diagnostics.Tracing.EventSource'
    if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'b03f5f7f11d50a3a' -oldVersion:'1.1.17.0' -newVersion:'1.1.28.0' -culture:'neutral'
    }

    if ($PlatformReleaseVersion -gt 5221)
    {
        Write-Log "Upgrading ApplicationInsights binding redirect for batch.config (PU25)"
        #Add app insights and dependencies binding redirects
        $assemblyName = 'Microsoft.AspNet.TelemetryCorrelation'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-1.0.5.0' -newVersion:'1.0.5.0'
        }
        $assemblyName = 'System.Diagnostics.DiagnosticSource'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'cc7b13ffcd2ddd51' -oldVersion:'0.0.0.0-4.0.3.1' -newVersion:'4.0.3.1'
        }
        $assemblyName = 'Microsoft.AI.Agent.Intercept'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.4.0.0' -newVersion:'2.4.0.0'
        }
        $assemblyName = 'Microsoft.ApplicationInsights'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.DependencyCollector'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.PerfCounterCollector'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.Web'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.WindowsServer'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
        $assemblyName = 'Microsoft.AI.ServerTelemetryChannel'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.9.0.0' -newVersion:'2.9.0.0'
        }
    }

    #Check if update 12 or later
    if ($PlatformReleaseVersion -ge 4709)
    {
        $assemblyName = 'Newtonsoft.Json'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'30ad4fe6b2a6aeed' -oldVersion:'0.0.0.0-9.0.0.0' -newVersion:'9.0.0.0' -culture:'neutral'
        }
    }

    #Add check for update 23 or later
    if ($PlatformReleaseVersion -ge 5126)
    {
        $assemblyName = 'Microsoft.IdentityModel.Clients.ActiveDirectory'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-2.23.0.0' -newVersion:'2.23.0.0' -culture:'neutral'
        }
        else
        {
            Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:$assemblyName -oldVersion:'0.0.0.0-2.23.0.0' -newVersion:'2.23.0.0'
        }
    }

	# Check if update 43 or later
    if ($PlatformReleaseVersion -ge 6009)
	{
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Data.Services.Client' -oldVersion:'5.6.0.0-5.8.4.0' -newVersion:'5.8.4.0'
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Data.OData' -oldVersion:'0.0.0.0-5.8.4.0' -newVersion:'5.8.4.0'
		Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Data.Edm' -oldVersion:'5.6.0.0-5.8.4.0' -newVersion:'5.8.4.0'
	}

    # PU50+
    if ($PlatformReleaseVersion -gt 6316)
    {
        $assemblyName = 'BouncyCastle.Crypto'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'0e99375e54769942' -oldVersion:'0.0.0.0-1.9.0.0' -newVersion:'1.9.0.0'
        }
    }

    if ($PlatformReleaseVersion -gt 6594)
    {
        $assemblyName = 'Microsoft.Graph'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-1.14.0.0' -newVersion:'1.7.0.0'
        }

        $assemblyName = 'Microsoft.Graph.Core'
        if (!(Get-DependentAssemblyExists -assemblyName:$assemblyName  -xmlDoc:$xmlDoc))
        {
            Add-DependencyAssembly -xmlDoc:$xmlDoc -assemblyName:$assemblyName -publickey:'31bf3856ad364e35' -oldVersion:'0.0.0.0-1.14.0.0' -newVersion:'1.7.0.0'
        }
    }

    $xmlDoc.Save($batchConfig);
}

function Upgrade-WebConfig-AssemblyReferences([string] $webConfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $assembliesNode = $xmlDoc.SelectSingleNode("/configuration/location/system.web/compilation/assemblies")

    $assemblyName = 'Microsoft.Dynamics.AX.Configuration.Base'
    if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
    }
    $assemblyName = 'System.Web.Http'
    if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
    }
    $assemblyName = 'System.Web.Http.WebHost'
    if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
    }
    $assemblyName = 'System.Net.Http'
    if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
    }
    if ($PlatformReleaseVersion -gt 4641)
    {
        $assemblyName = 'System.Net.Http.Formatting'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
    }
    $assemblyName = 'Microsoft.Dynamics.Client.InteractionService'
    if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
    {
        Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
    }

    #Check if update 24 or later
    if ($PlatformReleaseVersion -ge 5179)
    {
        $assemblyName = 'Microsoft.Dynamics.ServiceFramework.Xpp'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Bond'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Bond.IO'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Bond.JSON'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Microsoft.Cloud.InstrumentationFramework.Events'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Microsoft.Cloud.InstrumentationFramework.Health'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Microsoft.Cloud.InstrumentationFramework.Metrics'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Microsoft.Extensions.DependencyInjection.Abstractions'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Microsoft.Extensions.Logging'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Microsoft.Extensions.Logging.Abstractions'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
        $assemblyName = 'Newtonsoft.Json'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
    }

    #Check if update 26 or later
    if ($PlatformReleaseVersion -ge 5257)
    {
        $assemblyName = 'Microsoft.Dynamics.ApplicationPlatform.Client.Instrumentation'
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
    }

    # Check if update 40 or later
    if ($PlatformReleaseVersion -ge 5839)
    {
        $assemblyName = 'Microsoft.Dynamics.Authentication.Client'
       
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
    }

    # Check if update 43 or later
    if ($PlatformReleaseVersion -ge 5990)
    {
        $assemblyName = 'Microsoft.Dynamics.InstrumentationFramework'
        
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }

        $assemblyName = 'IfxMetricExtensionsManaged'
        
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }        
    }

    # Check if update 52 or later
    if ($PlatformReleaseVersion -ge 6401)
    {
        # If a "System.IO.Compression" entry exists, remove it so it won't conflict with the 
        # "System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        # entry that is being added below
        if(Get-AssemblyExists -assemblyName:"System.IO.Compression" -xmlDoc:$xmlDoc)
        {
            Remove-Assembly -assemblyName:"System.IO.Compression" -xmlDoc:$xmlDoc
        }

        $assemblies = @("Microsoft.Dynamics.AX.Metadata", "Microsoft.Dynamics.AX.Metadata.Core", "Microsoft.Dynamics.AX.Metadata.Storage", "System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")

        $assemblies | %{
            if (!(Get-AssemblyExists -assemblyName:$_ -xmlDoc:$xmlDoc))
            {
                Add-NewAssembly -assemblyName:$_ -parentNode:$assembliesNode -xmlDoc:$xmlDoc
            }
        }
    }

    # Check if update 56 or later
    if ($PlatformReleaseVersion -ge 6719)
    {
        $assemblyName = 'Microsoft.Dynamics.Platform.Integration.Services.Grpc'
       
        if (!(Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc))
        {
            Add-NewAssembly -assemblyName:$assemblyName -parentNode:$assembliesNode -xmlDoc:$xmlDoc
        }
    }    

    $xmlDoc.Save($webconfig)
}

function Add-NewAssembly([string] $assemblyName, [System.Xml.XmlNode] $parentNode, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlElement] $element = $xmlDoc.CreateElement("add")
    $newAttribute = $xmlDoc.CreateAttribute('assembly')
    $newAttribute.Value = $assemblyName

    $element.Attributes.Append($newAttribute)

    $parentNode.AppendChild($element)
}

function Upgrade-WebConfig-NewKeys([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $appsettings = $xmlDoc.SelectSingleNode("/configuration/appSettings")

    $key = 'Aad.AADValidAudience'
    $value = 'microsoft.erp'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'Aad.MSAIdentityProvider'
    $value = 'live.com'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'Aad.MSAOutlook'
    $value = 'outlook.com'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'BiReporting.IsSSRSEnabled'
    $value = 'true'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'CertificateHandler.HandlerType'
    $value = 'Microsoft.Dynamics.AX.Configuration.CertificateHandler.LocalStoreCertificateHandler'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'LCS.BpmAuthClient'
    $value = 'SysBpmCertClient'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'LCS.ProjectId'
    $value = ''
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'DataAccess.FlightingEnvironment'
    $value = ''
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'DataAccess.FlightingCertificateThumbprint'
    $value = ''
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'DataAccess.FlightingServiceCatalogID'
    $value = '12719367'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'DataAccess.FlightingServiceCacheFolder'
    $value = 'CarbonRuntimeBackup'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $key = 'Aos.EncryptionEngineCacheExpirationInMinutes'
    $value = '720'
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    #Check if update 7 or later
    if ($PlatformReleaseVersion -ge 4542)
    {
        $key = 'PowerBIEmbedded.AccessKey'
        $value = ''
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'PowerBIEmbedded.AccessKey2'
        $value = ''
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'PowerBIEmbedded.ApiUrl'
        $value = 'https://api.powerbi.com'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'PowerBIEmbedded.IsPowerBIEmbeddedEnabled'
        $value = 'false'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'PowerBIEmbedded.WorkspaceCollectionName'
        $value = ''
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    #Check if update 4
    if ($PlatformReleaseVersion -ge 4425)
    {
        $key = 'License.LicenseEnforced'
        $value = '1'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'License.D365EnterprisePlanId'
        $value = '95d2cd7b-1007-484b-8595-5e97e63fe189;112847d2-abbb-4b47-8b62-37af73d536c1'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'License.D365UniversalPlanId'
        $value = 'f5aa7b45-8a36-4cd1-bc37-5d06dea98645'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Infrastructure.AzureKeyVaultName'
        $value = "[Topology/Configuration/Setting[@Name='AzureKeyVaultName']/@Value]"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    #Check if update 11 or later
    if ($PlatformReleaseVersion -ge 4647)
    {
        $key = 'Aos.DeploymentName'
        $value = "initial"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Infrastructure.SDSAzureSubscriptionId'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Infrastructure.SDSAzureResourceGroup'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Infrastructure.SDSAzureDirectoryId'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Infrastructure.SDSApplicationID'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Infrastructure.SDSApplicationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Aos.ForceEnumValuesOnMetadataLoad'
        $value = "True"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    $isPPE = $false
    $isChina = $false

    #Check if update 12 or later
    if ($PlatformReleaseVersion -ge 4709)
    {
        $graphAPIkey = 'GraphApi.GraphAPIAADResource'
        if (Get-KeyExists -key:$graphAPIkey -xmlDoc:$xmlDoc)
        {
            $graphAPIValue = Get-KeyValue -key $graphAPIkey -xmlDoc $xmlDoc
            if ($graphAPIValue -eq "https://graph.ppe.windows.net")
            {
                $isPPE = $true;
            }
            elseif ($graphAPIValue -eq "https://graph.chinacloudapi.cn")
            {
                $isChina = $true;
            }
        }

        $key = 'GraphAPI.MicrosoftGraphResource'
        $value = 'https://graph.microsoft.com'

        if ($isPPE)
        {
            $value = 'https://graph.microsoft-ppe.com'
        }
        elseif ($isChina)
        {
            $value = 'https://microsoftgraph.chinacloudapi.cn'
        }

        if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
        {
            $oldMicrosoftGraphValue = Get-KeyValue -key $key -xmlDoc $xmlDoc
            if (!($oldMicrosoftGraphValue -eq $value))
            {
                Update-KeyValue -key $key -value $value -xmlDoc $xmlDoc
            }
        }
        else
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'GraphApi.MaxTries'
        $value = "5"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'GraphApi.BasedDelayTime'
        $value = "200"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'GraphApi.MaxListTaskWaitTime'
        $value = "6000000"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        else
        {
            Update-KeyValue -key $key -value $value -xmlDoc $xmlDoc
        }

        $key = 'GraphApi.MaxTaskWaitTime'
        $value = "600000"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        else
        {
            Update-KeyValue -key $key -value $value -xmlDoc $xmlDoc
        }

        $key = 'Monitoring.ActivityMetricNamespace'
        $value = "Microsoft.Dynamics.Aos"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'Monitoring.CustomMetricNamespace'
        $value = "Microsoft.Dynamics.Aos"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'Monitoring.IFXSessionName'
        $value = "Dynamics.Operations.IfxSession"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    #Check if update 22 or later
    if ($PlatformReleaseVersion -ge 5095)
    {
        $key = 'CertificateHandler.IsMSIKeyVault'
        $value = "false"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.MSIEndpoint'
        $value = "MSIEndpointDefault"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.MSIResource'
        $value = "MSIResourceDefault"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.KeyVaultUrl'
        $value = "KeyVaultUrlDefault"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.KeyVaultCacheName'
        $value = "AXKeyVaultCacheName"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.KeyVaultCacheExpirationInMilliseconds'
        $value = "86400000"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        else
        {
            Update-KeyValue -key $key -value $value -xmlDoc $xmlDoc
		}
        $key = 'CertificateHandler.KeyVaultHandlerType'
        $value = "Microsoft.Dynamics.AX.Security.KeyVaultHelper.KeyVaultCertificateHandler, Microsoft.Dynamics.AX.Security.KeyVaultHelper, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.LocalSecretHandlerType'
        $value = "Microsoft.Dynamics.ApplicationPlatform.Environment.SecretHandler.LocalSecretHandler"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'CertificateHandler.KeyVaultSecretHandlerType'
        $value = "Microsoft.Dynamics.AX.Security.KeyVaultHelper.KeyVaultSecretHandler, Microsoft.Dynamics.AX.Security.KeyVaultHelper, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        # Commerce Configuration Update
        $key = 'Commerce.DiagnosticsFilter.MinLevel'
        $value = "Informational"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.DiagnosticsFilter.ExcludedPrivacyDataTypes'
        $value = "AccessControlData;CustomerContent;EndUserIdentifiableInformation"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.SideLoadingKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.HardwareStationAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.ClientAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.CloudPosAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.RetailServerAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.AsyncClientAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.WindowsPhoneAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.AsyncServerConnectorServiceAppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        $key = 'Commerce.RetailSelfServiceInstallers.RealtimeServiceAx63AppinsightsInstrumentationKey'
        $value = ""
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    # AOS trusted issuers
    $issuerMicrosoftTenant = 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/'
    $issuerPmeTenant = 'https://sts.windows.net/975f013f-7f24-47e8-a7d3-abc4752bf346/'
    $issuerRetailTenant = 'https://sts.windows.net/57206206-ec82-4579-9724-0a098ed1b99f/'
    $issuerRetailPpeTenant = 'https://sts.windows.net/34b54b07-9b04-4369-839c-380f28f3e0fe/'

    #Check if update 30 or later
    if ($PlatformReleaseVersion -ge 5378)
    {
        # AAD Trusted Issuers - these issuers will be trusted to make call AOS (AppIds need to be allowlisted with AOS DB first)
        $key = 'Aad.TrustedIssuers'
        $aadTrustedIssuers = @()
        $aadTrustedIssuersMetadata = ""

        # Out of the box configuration only for non-Mooncake environments
        if (-not $isChina)
        {
            if ($isPPE)
            {
                # For PPE trust Retail Test Tenant and Microsoft Tenant
                $aadTrustedIssuers = $issuerMicrosoftTenant, $issuerRetailPpeTenant
            }
            else
            {
                # For PROD, trust Retail PROD Tenant and Microsoft Tenant
                $aadTrustedIssuers = $issuerMicrosoftTenant, $issuerRetailTenant
            }

            # Trusted issuer metadata is always AAD Prod
            $aadTrustedIssuersMetadata = "https://login.windows.net/common/FederationMetadata/2007-06/FederationMetadata.xml"
        }

        # validate whether required trusted issuers already set
        $node = $xmlDoc.SelectSingleNode("//add[@key='$key']")
        if ($node) {
            $oldNodeValue = $node.value
            $issuersSplit = @($oldNodeValue -split ';' | where-object { -not [String]::IsNullOrWhiteSpace($_) } | foreach-object { $_.Trim() } )
            foreach($issuer in $aadTrustedIssuers)
            {
                if($issuersSplit -notcontains $issuer)
                {
                    $issuersSplit += $issuer
                }
            }
            $node.value = $issuersSplit -join ';'

            Write-Output "Setting $key value: Old value = '$oldNodeValue' new value = '$($node.value)' when isChina=$isChina, isPPE=$isPPE"
        }
        elseif($aadTrustedIssuers.Length -gt 0) {
            $aadTrustedIssuersValue = $aadTrustedIssuers -join ';'
            Add-NewKey -key $key -value $aadTrustedIssuersValue -parentNode $appsettings -xmlDoc $xmlDoc
            Write-Output "Adding $key with value = '$aadTrustedIssuersValue' when isChina=$isChina, isPPE=$isPPE"
        }

        # Trusted issuers authority metadata endpoint
        if($aadTrustedIssuersMetadata)
        {
            $key = 'Aad.TrustedIssuersMetadataAddress'
            $node = $xmlDoc.SelectSingleNode("//add[@key='$key']")
            if (-not $node)
            {
                Add-NewKey -key $key -value $aadTrustedIssuersMetadata -parentNode $appsettings -xmlDoc $xmlDoc
                Write-Output "Adding $key with value = '$aadTrustedIssuersMetadata' when isChina=$isChina"
            }
            else
            {
                $oldNodeValue = $node.Value
                $node.Value = $aadTrustedIssuersMetadata
                Write-Output "Setting $key value: Old value = '$oldNodeValue' new value = '$($node.value)' when isChina=$isChina"
            }
        }

        # Allow event handlers and Chain Of Command subscriptions initialize in parallel
        $key = 'EventsAndCoCHandlersInitConcurrently'
        $value = "True"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'Flow.MyFlowsUrl'
        $value = 'https://go.microsoft.com/fwlink/?linkid=2122475'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'Flow.CreateAFlowUrl'
        $value = 'https://go.microsoft.com/fwlink/?linkid=2122580'
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }
    else
    {
        Write-Output "The PlatformReleaseVersion $PlatformReleaseVersion is less than 5378 (10.0.6). So, skipping configuring configs: Aad.TrustedIssuers, Aad.TrustedIssuersMetadataAddress, EventsAndCoCHandlersInitConcurrently, Flow.MyFlowsUrl, Flow.CreateAFlowUrl"
    }

    # Add PME Tenant ID to the Aad.TrustedIssuers key. Must do this regardless of the platform + application versions
    $key = "Aad.TrustedIssuers"

    Write-Log "Ensuring that key: $key contains value: $issuerPmeTenant"

    if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
    {
        $oldValue = ""
        $newValue = ""

        # get the current value
        [System.Xml.XmlNode] $node = $xmlDoc.SelectSingleNode("//add[@key='$key']")
        if ($null -ne $node)
        {
            $oldValue = $node.Value
        }

        if ($oldValue.Length -eq 0)
        {
            # The value is currently empty, so add both Microsoft and PME tenants to the list
            $newValue = "$issuerMicrosoftTenant;$issuerPmeTenant"
        }
        else
        {
            # add the PME issuer if necessary
            $oldValueSplit = $oldValue -split ";"
            if ($oldValueSplit -notcontains $issuerPmeTenant)
            {
                $newValue = "$oldValue;$issuerPmeTenant"
            }
        }

        if ($newValue.Length -gt 0)
        {
            Write-Log "Updading key $key, setting value to $newValue"
            Update-KeyValue -key $key -value $newValue -xmlDoc $xmlDoc
        }
    }
    else
    {
        # The value is currently empty, so add both Microsoft and PME tenants to the list
        $newValue = "$issuerMicrosoftTenant;$issuerPmeTenant"

        Write-Log "Adding key $key, setting value to $newValue"

        # Key Aad.TrustedIssuers does not exist, so create and populate it
        Add-NewKey -key $key -value $newValue -parentNode $appsettings -xmlDoc $xmlDoc
    }

    # Check if update 40 or later
    if ($PlatformReleaseVersion -ge 5839)
    {
        $key = 'Infrastructure.S2SCertSubjectName'
        $value = ""

        # Get S2S certificate by CertificateHandler
        $s2scert = Get-S2SCertificate

        if ($s2scert -eq $null)
        {
            # Fallback to load certificate from local store
            $s2sCertThumbprintKey = 'Infrastructure.S2SCertThumbprint'
            if (Get-KeyExists -key:$s2sCertThumbprintKey -xmlDoc:$xmlDoc)
            {
                try
                {
                    $s2sCertThumbprint = Get-KeyValue -key $s2sCertThumbprintKey -xmlDoc $xmlDoc
                    if ($s2sCertThumbprint)
                    {                    
                        $s2scert = Get-ChildItem -Path Cert:\LocalMachine\My\$s2sCertThumbprint
                    }
                }
                catch
                {
                    # no-op
                }
            }
        }

        if($s2scert -ne $null)
        {
            $value = $s2scert.Subject
        }

        # Add new s2s certificate subject name if it does not exist
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
        elseif ($value -ne "" -and (Get-KeyValue -key $key -xmlDoc $xmlDoc) -ne $value)
        {
            Update-KeyValue -key $key -value $value -xmlDoc $xmlDoc
        }

        $key = 'LCS.AppId'
        $value = "913c6de4-2a4a-4a61-a9ce-945d2b2ce2e0"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    # Check if update 43 or later
    if ($PlatformReleaseVersion -ge 5990)
    {
        #  <add key="Monitoring.BucketSize" value="250" />
        #  <add key="Monitoring.BucketCount" value="50" />
        #  <add key="Monitoring.MinimumValueInMilliseconds" value="10" />

        $key = 'Monitoring.BucketSize'
        $value = "250"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'Monitoring.BucketCount'
        $value = "50"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }

        $key = 'Monitoring.MinimumValueInMilliseconds'
        $value = "10"
        if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    # save the web.config file
    $xmlDoc.Save($webconfig);
}

function Upgrade-HttpProtocol-NewNames([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $appsettings = $xmlDoc.SelectSingleNode("/configuration/system.webServer/httpProtocol/customHeaders")

    #Check if update 11 or later
    if ($PlatformReleaseVersion -ge 4647)
    {
        $name = 'Strict-Transport-Security'
        $value = "max-age=31536000; includeSubDomains"
        if (!(Get-NameExists -name:$name -xmlDoc:$xmlDoc))
        {
            Add-NewName -name $name -value $value -parentNode $appsettings -xmlDoc $xmlDoc
        }
    }

    $xmlDoc.Save($webconfig);
}

function Upgrade-WebConfig-Add-StaticContent([string]$webconfig)
{
	[System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

	#Check if post update 26
    if ($PlatformReleaseVersion -gt 5257)
    {
		$staticContentNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/staticContent")
		$removeNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/staticContent/remove[@fileExtension='.properties']")
		$mimeMapNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/staticContent/mimeMap[@fileExtension='.properties']")

		if ($removeNode -eq $null)
		{
			Write-Log "Start adding system.webServer/staticContent/remove fileExtension node"

			[System.Xml.XmlElement] $removeElement = $xmlDoc.CreateElement("remove")
			$fileExtensionAttribute = $xmlDoc.CreateAttribute('fileExtension')
			$fileExtensionAttribute.Value = '.properties'
			$removeElement.Attributes.Append($fileExtensionAttribute)

			$staticContentNode.AppendChild($removeElement)
		}

		if ($mimeMapNode -eq $null)
		{
			Write-Log "Start adding system.webServer/staticContent/mimeMap fileExtension node"

			[System.Xml.XmlElement] $mimeMapElement = $xmlDoc.CreateElement("mimeMap")
			$fileExtensionAttribute = $xmlDoc.CreateAttribute('fileExtension')
			$fileExtensionAttribute.Value = '.properties'
			$mimeTypeAttribute = $xmlDoc.CreateAttribute('mimeType')
			$mimeTypeAttribute.Value = 'text/plain'
			$mimeMapElement.Attributes.Append($fileExtensionAttribute)
			$mimeMapElement.Attributes.Append($mimeTypeAttribute)

			$staticContentNode.AppendChild($mimeMapElement)
		}
	}

	$xmlDoc.Save($webconfig)
}

function Upgrade-WebConfig-Add-RequestHandlers([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    # Check if post update 36 (e.g. PU37+)
    if ($PlatformReleaseVersion -gt 5688)
    {
        $handlersNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/handlers")

        # Ensure DefaultClient handler exists
        $defaultClientNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/handlers/add[@name='DefaultClient']")
        if ($defaultClientNode -eq $null)
        {
            Write-Log "Start adding system.webServer/handlers/add DefaultClient node"

            [System.Xml.XmlElement] $defaultClientElement = $xmlDoc.CreateElement("add")

            $nameAttribute = $xmlDoc.CreateAttribute("name")
            $nameAttribute.Value = "DefaultClient"
            $defaultClientElement.Attributes.Append($nameAttribute)

            $pathAttribute = $xmlDoc.CreateAttribute("path")
            $pathAttribute.Value = "/"
            $defaultClientElement.Attributes.Append($pathAttribute)

            $verbAttribute = $xmlDoc.CreateAttribute("verb")
            $verbAttribute.Value = "*"
            $defaultClientElement.Attributes.Append($verbAttribute)

            $typeAttribute = $xmlDoc.CreateAttribute("type")
            $typeAttribute.Value = "System.Web.Handlers.TransferRequestHandler"
            $defaultClientElement.Attributes.Append($typeAttribute)

            $preConditionAttribute = $xmlDoc.CreateAttribute("preCondition")
            $preConditionAttribute.Value = "integratedMode,runtimeVersionv4.0"
            $defaultClientElement.Attributes.Append($preConditionAttribute)

            $handlersNode.AppendChild($defaultClientElement)
        }

        # Ensure DefaultClient-HTM handler exists
        $defaultClientHtmNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/handlers/add[@name='DefaultClient-HTM']")
        if ($defaultClientHtmNode -eq $null)
        {
            Write-Log "Start adding system.webServer/handlers/add DefaultClient-HTM node"

            [System.Xml.XmlElement] $defaultClientHtmElement = $xmlDoc.CreateElement("add")

            $nameAttribute = $xmlDoc.CreateAttribute("name")
            $nameAttribute.Value = "DefaultClient-HTM"
            $defaultClientHtmElement.Attributes.Append($nameAttribute)

            $pathAttribute = $xmlDoc.CreateAttribute("path")
            $pathAttribute.Value = "default.htm"
            $defaultClientHtmElement.Attributes.Append($pathAttribute)

            $verbAttribute = $xmlDoc.CreateAttribute("verb")
            $verbAttribute.Value = "*"
            $defaultClientHtmElement.Attributes.Append($verbAttribute)

            $typeAttribute = $xmlDoc.CreateAttribute("type")
            $typeAttribute.Value = "System.Web.Handlers.TransferRequestHandler"
            $defaultClientHtmElement.Attributes.Append($typeAttribute)

            $preConditionAttribute = $xmlDoc.CreateAttribute("preCondition")
            $preConditionAttribute.Value = "integratedMode,runtimeVersionv4.0"
            $defaultClientHtmElement.Attributes.Append($preConditionAttribute)

            $handlersNode.AppendChild($defaultClientHtmElement)
        }

        # Ensure DefaultDebugClient-HTM handler exists
        $defaultDebugClientHtmNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/handlers/add[@name='DefaultDebugClient-HTM']")
        if ($defaultDebugClientHtmNode -eq $null)
        {
            Write-Log "Start adding system.webServer/handlers/add DefaultDebugClient-HTM node"
            $defaultDebugClientHtmElement = $xmlDoc.CreateElement("add")

            $nameAttribute = $xmlDoc.CreateAttribute("name")
            $nameAttribute.Value = "DefaultDebugClient-HTM"
            $defaultDebugClientHtmElement.Attributes.Append($nameAttribute)

            $pathAttribute = $xmlDoc.CreateAttribute("path")
            $pathAttribute.Value = "defaultdebug.htm"
            $defaultDebugClientHtmElement.Attributes.Append($pathAttribute)

            $verbAttribute = $xmlDoc.CreateAttribute("verb")
            $verbAttribute.Value = "*"
            $defaultDebugClientHtmElement.Attributes.Append($verbAttribute)

            $typeAttribute = $xmlDoc.CreateAttribute("type")
            $typeAttribute.Value = "System.Web.Handlers.TransferRequestHandler"
            $defaultDebugClientHtmElement.Attributes.Append($typeAttribute)

            $preConditionAttribute = $xmlDoc.CreateAttribute("preCondition")
            $preConditionAttribute.Value = "integratedMode,runtimeVersionv4.0"
            $defaultDebugClientHtmElement.Attributes.Append($preConditionAttribute)

            $handlersNode.AppendChild($defaultDebugClientHtmElement)
        }

        # Check if post update 37 (e.g. PU38+)
        if ($PlatformReleaseVersion -gt 5746)
        {
            # Ensure ClientComputedScripts handler exists
            $clientComputedScriptsNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/handlers/add[@name='ClientComputedScripts']")
            if ($clientComputedScriptsNode -eq $null)
            {
                Write-Log "Start adding system.webServer/handlers/add ClientComputedScripts node"
                $clientComputedScriptsElement = $xmlDoc.CreateElement("add")

                $nameAttribute = $xmlDoc.CreateAttribute("name")
                $nameAttribute.Value = "ClientComputedScripts"
                $clientComputedScriptsElement.Attributes.Append($nameAttribute)

                $pathAttribute = $xmlDoc.CreateAttribute("path")
                $pathAttribute.Value = "scripts/computed"
                $clientComputedScriptsElement.Attributes.Append($pathAttribute)

                $verbAttribute = $xmlDoc.CreateAttribute("verb")
                $verbAttribute.Value = "*"
                $clientComputedScriptsElement.Attributes.Append($verbAttribute)

                $typeAttribute = $xmlDoc.CreateAttribute("type")
                $typeAttribute.Value = "System.Web.Handlers.TransferRequestHandler"
                $clientComputedScriptsElement.Attributes.Append($typeAttribute)

                $preConditionAttribute = $xmlDoc.CreateAttribute("preCondition")
                $preConditionAttribute.Value = "integratedMode,runtimeVersionv4.0"
                $clientComputedScriptsElement.Attributes.Append($preConditionAttribute)

                $handlersNode.AppendChild($clientComputedScriptsElement)
            }
        }
    }

    $xmlDoc.Save($webconfig)
}

function Upgrade-WebConfig-DeleteKeys([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $key = 'HelpWiki.AuthorizationKey'
    if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
    {
        Remove-Key -key $key -xmlDoc $xmlDoc
    }

    $key = 'HelpWiki.AuthorizationKeyValue'

    if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
    {
        Remove-Key -key $key -xmlDoc $xmlDoc
    }

    $xmlDoc.Save($webconfig);
}

function Upgrade-WebConfig-updateKeys([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    # Update the Infrastructure.InternalServiceCertificateThumbprints key to include FRServiceUser certificate thumbprint
    $allowedUserIds = "FRServiceUser"
    if (Get-AllowedUserIdsExists -allowedUserIds:$allowedUserIds -xmlDoc:$xmlDoc)
    {
        $fRServiceUserThumbPrint = Get-AllowedUserIdsName -allowedUserIds:$allowedUserIds -xmlDoc:$xmlDoc

        $key = "Infrastructure.InternalServiceCertificateThumbprints"
        if (($fRServiceUserThumbPrint.GetValue(1)) -and (Get-KeyExists -key:$key -xmlDoc:$xmlDoc))
        {
            $oldValue = Get-KeyValue -key $key -xmlDoc $xmlDoc

            if (($oldValue.GetValue(1)))
            {
                $oldValue = $oldValue.GetValue(1)
                $fRServiceUserThumbPrint = $fRServiceUserThumbPrint.GetValue(1)
                if ((!$oldValue.Contains($fRServiceUserThumbPrint)))
                {
                    $newValue = $oldValue + ";$fRServiceUserThumbPrint"
                    Update-KeyValue -key:$key -value:$newValue -xmlDoc:$xmlDoc
                }
            }
            else
            {
                Update-KeyValue -key:$key -value:$fRServiceUserThumbPrint -xmlDoc:$xmlDoc
            }
        }
    }

    # Update the AppInsightsKey if it was the old production key to the new production key. Test keys are unchanged.
    $key = "OfficeApps.AppInsightsKey"
    if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
    {
        $oldValue = Get-KeyValue -key $key -xmlDoc $xmlDoc
        if ($oldValue -eq "a8640c62-56a5-49c5-8d37-adcc48ac1523")
        {
            Write-Log "Updating OfficeApps.AppInsightsKey"
            Update-KeyValue -key $key -value "0e9ff251-74c0-4b3f-8466-c5345e5d4933" -xmlDoc $xmlDoc
        }
        else
        {
            Write-Log "Not updating OfficeApps.AppInsightsKey"
        }
    }

    # Update the HelpWiki.APIEndPoint key to the new production or test endpoint
    $key = "HelpWiki.APIEndPoint"
    if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
    {
        $oldValue = Get-KeyValue -key $key -xmlDoc $xmlDoc
        if ($oldValue -eq "http://ax.help.dynamics.com")
        {
            Write-Log "Updating HelpWiki.APIEndPoint to production endpoint"
            Update-KeyValue -key $key -value "https://lcsapi.lcs.dynamics.com" -xmlDoc $xmlDoc
        }
        elseif ($oldValue -eq "http://ax.help.int.dynamics.com")
        {
            Write-Log "Updating HelpWiki.APIEndPoint to test endpoint"
            Update-KeyValue -key $key -value "https://lcsapi.lcs.tie.dynamics.com" -xmlDoc $xmlDoc
        }
        else
        {
            Write-Log "Not updating HelpWiki.APIEndPoint endpoint"
        }
    }

    # Check for PU22+
    if ($PlatformReleaseVersion -ge 5095)
    {
        # Update the Monitoring.ETWManifests key, adding Microsoft.Dynamics.ApplicationPlatform.DatabaseSynchronize.man if it does not exist in the list
        $key = "Monitoring.ETWManifests"
        if (Get-KeyExists -key:$key -xmlDoc:$xmlDoc)
        {
            $oldValue = Get-KeyValue -key $key -xmlDoc $xmlDoc
            $oldValueSplit = $oldValue -split ";"
            if ($oldValueSplit -notcontains "Microsoft.Dynamics.ApplicationPlatform.DatabaseSynchronize.man")
            {
                Write-Log "Updating Monitoring.ETWManifests, adding Microsoft.Dynamics.ApplicationPlatform.DatabaseSynchronize.man"
                $newValue = "$oldValue;Microsoft.Dynamics.ApplicationPlatform.DatabaseSynchronize.man"
                Update-KeyValue -key $key -value $newValue -xmlDoc $xmlDoc
            }
            else
            {
                Write-Log "Not updating Monitoring.ETWManifests, Microsoft.Dynamics.ApplicationPlatform.DatabaseSynchronize.man already exists"
            }
        }      
    }

    $xmlDoc.Save($webconfig);
}

function Upgrade-WebConfig-debugCompilation([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $node = $xmlDoc.SelectSingleNode("//system.web/compilation")
    if ($node -ne $null)
    {
        Write-Log "Disabling debug compilation for assemblies"
        $node.debug = "false"
        $xmlDoc.Save($webconfig)
    }
    else
    {
        Write-Error "Cannot disable debug compilation for assemblies. No such property in the config file"
    }
}

function Upgrade-WebConfig-targetFrameworkCompilation([string]$webconfig)
{
    # perform this for PU29+
    if ($PlatformReleaseVersion -ge 5372)
    {
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)

        $node = $xmlDoc.SelectSingleNode("/configuration/location[@inheritInChildApplications='false']/system.web/compilation")
        if ($node -ne $null)
        {
            if ($node.targetFramework -ne $null)
            {
                Write-Log "Update target framework version"
                $node.targetFramework = "4.5"
                $xmlDoc.Save($webconfig)
            }
        }
    }
}

function Get-AssemblyExists([string] $assemblyName, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeToRead = $xmlDoc.SelectSingleNode("//add[@assembly='$assemblyName']")
    $keyExists = $false

    if ($nodeToRead -eq $null)
    {
        $keyExists = $false
    }
    else
    {
        $keyExists = $true
    }
    return $keyExists
}

function Get-KeyExists([string] $key, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeToRead = $xmlDoc.SelectSingleNode("//add[@key='$key']")
    $keyExists = $false

    if ($nodeToRead -ne $null)
    {
        $keyExists = $true
    }

    return $keyExists
}

function Get-NameExists([string] $name, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeToRead = $xmlDoc.SelectSingleNode("//add[@name='$name']")
    $nameExists = $false

    if ($nodeToRead -ne $null)
    {
        $nameExists = $true
    }

    return $nameExists
}

function Get-AllowedUserIdsExists([string] $allowedUserIds, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeToRead = $xmlDoc.SelectSingleNode("//add[@allowedUserIds='$allowedUserIds']")
    $allowedUserIdsExists = $false

    if ($nodeToRead -ne $null)
    {
        $allowedUserIdsExists = $true
    }

    return $allowedUserIdsExists
}

function Get-AllowedUserIdsName([string] $allowedUserIds, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeToRead = $xmlDoc.SelectSingleNode("//add[@allowedUserIds='$allowedUserIds']")

    if ($nodeToRead -eq $null)
    {
        Log-Error  "Failed to find allowedUserIds node '$allowedUserIds' for read"
    }

    Write-log "selected node '$allowedUserIds' for read"

    return $nodeToRead.name
}

function Remove-Key([string] $key, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeForDeletion = $xmlDoc.SelectSingleNode("//add[@key='$key']")

    if ($nodeForDeletion -eq $null)
    {
        Log-Error  "Failed to find key node '$key' for deletion"
    }

    Write-log "selected node '$key' for deletion"

    $nodeForDeletion.ParentNode.RemoveChild($nodeForDeletion);
}

function Remove-Assembly([string] $assemblyName, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeForDeletion = $xmlDoc.SelectSingleNode("//add[@assembly='$assemblyName']")

    if ($nodeForDeletion -eq $null)
    {
        Log-Error  "Failed to find assembly node '$assemblyName' for deletion"
    }

    Write-log "selected node ''$assemblyName' for deletion"

    $nodeForDeletion.ParentNode.RemoveChild($nodeForDeletion);
}

function Get-KeyValue([string] $key, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeToRead = $xmlDoc.SelectSingleNode("//add[@key='$key']")

    if ($nodeToRead -eq $null)
    {
        Log-Error  "Failed to find key node '$key' for read"
    }

    Write-log "selected node '$key' for read"

    return $nodeToRead.Value
}

function Update-KeyValue([string] $key, [string] $value, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlNode] $nodeForDeletion = $xmlDoc.SelectSingleNode("//add[@key='$key']")

    if ($nodeForDeletion -eq $null)
    {
        Log-Error  "Failed to find key node '$key' for update"
    }

    Write-log "selected node '$key' for update"

    $nodeForDeletion.Value = $value
}

function Add-NewKey([string] $key, [string] $value, [System.Xml.XmlNode] $parentNode, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlElement] $element = $xmlDoc.CreateElement("add")
    $newAttribute = $xmlDoc.CreateAttribute('key')
    $newAttribute.Value = $key

    $element.Attributes.Append($newAttribute)

    $newAttribute = $xmlDoc.CreateAttribute('value')
    $newAttribute.Value = $value
    $element.Attributes.Append($newAttribute)

    $parentNode.AppendChild($element)
}

function Add-NewName([string] $name, [string] $value, [System.Xml.XmlNode] $parentNode, [System.Xml.XmlDocument] $xmlDoc)
{
    [System.Xml.XmlElement] $element = $xmlDoc.CreateElement("add")
    $newAttribute = $xmlDoc.CreateAttribute('name')
    $newAttribute.Value = $name

    $element.Attributes.Append($newAttribute)

    $newAttribute = $xmlDoc.CreateAttribute('value')
    $newAttribute.Value = $value
    $element.Attributes.Append($newAttribute)

    $parentNode.AppendChild($element)
}

function Rename-File([string]$from, [string]$to)
{
    Move-Item -Path $from -Destination $to -Force | Out-Null
    Write-Log "Renamed file '$from' to '$to'."
}

function Decrypt-Config([string]$webroot)
{
    $command = Join-Path -Path "$webroot\bin" -ChildPath $configEncryptor
    if (!(Test-Path -Path $command))
    {
        Log-Error "Cannot find the Microsoft.Dynamics.AX.Framework.ConfigEncryptor.exe at '$webroot\bin\'." -throw
    }

    $webconfig = Join-Path -Path $webroot -ChildPath "Web.config"
    $commandParameter = " -decrypt `"$webconfig`""
    $logdir = [System.IO.Path]::GetDirectoryName($global:logfile)
    $stdOut = Join-Path -Path $logdir -ChildPath "config_decrypt.log"
    $stdErr = Join-Path -Path $logdir -ChildPath "config_decrypt.error.log"
    Start-Process $command $commandParameter -PassThru -Wait -RedirectStandardOutput $stdOut -RedirectStandardError $stdErr

    $decryptError = Get-Content $stdErr
    if ($decryptError -ne $null)
    {
        Log-Error $decryptError -throw
    }

    Write-Log "Finished decrypting the web.config."
}

function Encrypt-Config([string]$webroot)
{
    $command = Join-Path -Path "$webroot\bin" -ChildPath $configEncryptor
    if (!(Test-Path -Path $command))
    {
        Log-Error "Cannot find the Microsoft.Dynamics.AX.Framework.ConfigEncryptor.exe at '$webroot\bin\'." -throw
    }

    $webconfig = Join-Path -Path $webroot -ChildPath "Web.config"
    $commandParameter = " -encrypt `"$webconfig`""
    $logdir = [System.IO.Path]::GetDirectoryName($global:logfile)
    $stdOut = Join-Path -Path $logdir -ChildPath "config_encrypt.log"
    $stdErr = Join-Path -Path $logdir -ChildPath "config_encrypt.error.log"
    Start-Process $command $commandParameter -PassThru -Wait -RedirectStandardOutput $stdOut -RedirectStandardError $stdErr

    $encryptError = Get-Content $stdErr
    if ($encryptError -ne $null)
    {
        Log-Error $encryptError -throw
    }

    Write-Log "Finished encrypting the web.config."
}

function Upgrade-WebConfig-rootLocations-PCFControls([string]$webconfig)
{
    # Check if update 52 or later
    if ($PlatformReleaseVersion -ge 6401)
    {
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)

        $locationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Resources']")
        if ($locationNode -eq $null)
        {
            <#
            <!-- Enble the serving of PCF controls (see PcfControlMetadataResourceController) -->
            <location path="Resources">
                <system.webServer>
                    <security>
                        <requestFiltering>
                            <fileExtensions>
                                <remove fileExtension=".resx" />
                                <add fileExtension=".resx" allowed="true" />
                            </fileExtensions>
                        </requestFiltering>
                    </security>
                </system.webServer>
            </location>
            #>

            Write-Log "Start adding Resources location node"
            $configurationNode = $xmlDoc.SelectSingleNode("/configuration")

            [System.Xml.XmlComment] $fileExtensionsComment = $xmlDoc.CreateComment("Enable the serving of PCF controls (see PcfControlMetadataResourceController)")
            $configurationNode.AppendChild($fileExtensionsComment)

            [System.Xml.XmlElement] $locationElement = $xmlDoc.CreateElement("location")
            $pathAttribute = $xmlDoc.CreateAttribute('path')
            $pathAttribute.Value = 'Resources'
            $locationElement.Attributes.Append($pathAttribute)
            $configurationNode.AppendChild($locationElement)

            [System.Xml.XmlElement] $systemWebServerElement = $xmlDoc.CreateElement("system.webServer")
            $locationElement.AppendChild($systemWebServerElement)

            [System.Xml.XmlElement] $securityElement = $xmlDoc.CreateElement("security")
            $systemWebServerElement.AppendChild($securityElement)

            [System.Xml.XmlElement] $requestFilteringElement = $xmlDoc.CreateElement("requestFiltering")
            $securityElement.AppendChild($requestFilteringElement)

            [System.Xml.XmlElement] $fileExtensionsElement = $xmlDoc.CreateElement("fileExtensions")
            $requestFilteringElement.AppendChild($fileExtensionsElement)

            [System.Xml.XmlElement] $removeElement = $xmlDoc.CreateElement("remove")
            $pathAttribute = $xmlDoc.CreateAttribute('fileExtension')
            $pathAttribute.Value = '.resx'
            $removeElement.Attributes.Append($pathAttribute)
            $fileExtensionsElement.AppendChild($removeElement)

            [System.Xml.XmlElement] $addElement = $xmlDoc.CreateElement("add")
            $pathAttribute = $xmlDoc.CreateAttribute('fileExtension')
            $pathAttribute.Value = '.resx'
            $addElement.Attributes.Append($pathAttribute)
            $pathAttribute = $xmlDoc.CreateAttribute('allowed')
            $pathAttribute.Value = 'true'
            $addElement.Attributes.Append($pathAttribute)
            $fileExtensionsElement.AppendChild($addElement)

            $xmlDoc.Save($webconfig)
        }
    }
}

function Upgrade-WebConfig-rootLocations-ReliableCommunicationManager([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $rcmLocationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']")
    if ($rcmLocationNode -eq $null)
    {
        Write-Log "Start adding Services/ReliableCommunicationManager.svc location node"
        $configurationNode = $xmlDoc.SelectSingleNode("/configuration")

        [System.Xml.XmlElement] $locationElement = $xmlDoc.CreateElement("location")
        $pathAttribute = $xmlDoc.CreateAttribute('path')
        $pathAttribute.Value = 'Services/ReliableCommunicationManager.svc'
        $locationElement.Attributes.Append($pathAttribute)

        $configurationNode.AppendChild($locationElement)

        $xmlDoc.Save($webconfig)
    }

    #Check if platform update 25 or later
    if ($PlatformReleaseVersion -ge 5215)
    {
        # <system.web />
        $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.web")
        if ($webNode -eq $null)
        {
            Write-Log "Start adding system.web node to Services/ReliableCommunicationManager.svc location"
            $locationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']")

            [System.Xml.XmlElement] $webElement = $xmlDoc.CreateElement("system.web")

            $locationNode.AppendChild($webElement)

            $xmlDoc.Save($webconfig)
        }

        # <system.web>
        #   <compilation debug="false" />
        #   <httpRuntime enableVersionHeader="false" />
        # </system.web>

        $compilationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.web/compilation")
        if ($compilationNode -eq $null)
        {
            Write-Log "Start adding system.web/compilation node to Services/ReliableCommunicationManager.svc location"
            $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.web")

            [System.Xml.XmlElement] $compilationElement = $xmlDoc.CreateElement("compilation")
            $debugAttribute = $xmlDoc.CreateAttribute('debug')
            $debugAttribute.Value = 'false'
            $compilationElement.Attributes.Append($debugAttribute)

            $webNode.AppendChild($compilationElement)

            $xmlDoc.Save($webconfig)
        }

        $httpRuntimeNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.web/httpRuntime")
        if ($httpRuntimeNode -eq $null)
        {
            Write-Log "Start adding system.web/httpRuntime node to Services/ReliableCommunicationManager.svc location"
            $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.web")

            [System.Xml.XmlElement] $httpRuntimeElement = $xmlDoc.CreateElement("httpRuntime")
            $enableVersionHeaderAttribute = $xmlDoc.CreateAttribute('enableVersionHeader')
            $enableVersionHeaderAttribute.Value = 'false'
            $httpRuntimeElement.Attributes.Append($enableVersionHeaderAttribute)

            $webNode.AppendChild($httpRuntimeElement)

            $xmlDoc.Save($webconfig)
        }
    }

    $webServerNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer")
    if ($webServerNode -eq $null)
    {
        Write-Log "Start adding system.webServer node to Services/ReliableCommunicationManager.svc location"
        $locationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']")

        [System.Xml.XmlElement] $webServerElement = $xmlDoc.CreateElement("system.webServer")

        [System.Xml.XmlElement] $httpErrorsElement = $xmlDoc.CreateElement("httpErrors")
        $errorModeAttribute = $xmlDoc.CreateAttribute('errorMode')
        $errorModeAttribute.Value = 'Custom'
        $existingResponseAttribute = $xmlDoc.CreateAttribute('existingResponse')
        $existingResponseAttribute.Value = 'PassThrough'
        $httpErrorsElement.Attributes.Append($errorModeAttribute)
        $httpErrorsElement.Attributes.Append($existingResponseAttribute)

        $webServerElement.AppendChild($httpErrorsElement)

        $locationNode.AppendChild($webServerElement)

        $xmlDoc.Save($webconfig)
    }

    #Check if platform update 25 or later
    if ($PlatformReleaseVersion -ge 5215)
    {
        # IIS version 10 or above is supported
        $iisVersion = Get-IISVersion-Major
        if ($iisVersion -ge 10)
        {
            # <security>
            #   <requestFiltering removeServerHeader="true" />
            # </security>
            $securityNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/security")
            if ($securityNode -eq $null)
            {
                Write-Log "Start adding system.webServer/security node to Services/ReliableCommunicationManager.svc location"
                $webServerNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer")

                [System.Xml.XmlElement] $securityElement = $xmlDoc.CreateElement("security")
                [System.Xml.XmlElement] $requestFilteringElement = $xmlDoc.CreateElement("requestFiltering")
                $removeServerHeaderAttribute = $xmlDoc.CreateAttribute('removeServerHeader')
                $removeServerHeaderAttribute.Value = 'true'
                $requestFilteringElement.Attributes.Append($removeServerHeaderAttribute)

                $securityElement.AppendChild($requestFilteringElement)
                $webServerNode.AppendChild($securityElement)

                $xmlDoc.Save($webconfig)
            }
        }
    }

    $httpProtocolNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol")
    if ($httpProtocolNode -eq $null)
    {
        Write-Log "Start adding system.webServer/httpProtocol node to Services/ReliableCommunicationManager.svc location"
        $webServerNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer")

        [System.Xml.XmlElement] $httpProtocolElement = $xmlDoc.CreateElement("httpProtocol")

        $webServerNode.AppendChild($httpProtocolElement)

        $xmlDoc.Save($webconfig)
    }

    $customHeadersNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol/customHeaders")
    if ($customHeadersNode -eq $null)
    {
        Write-Log "Start adding system.webServer/httpProtocol/customHeaders node to Services/ReliableCommunicationManager.svc location"
        $httpProtocolNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol")

        [System.Xml.XmlElement] $customHeadersElement = $xmlDoc.CreateElement("customHeaders")

        $httpProtocolNode.AppendChild($customHeadersElement)

        $xmlDoc.Save($webconfig)
    }

    $removeNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol/customHeaders/remove[@name='Cache-Control']")
    if ($removeNode -eq $null)
    {
        Write-Log "Start adding system.webServer/httpProtocol/customHeaders/remove node to Services/ReliableCommunicationManager.svc location"
        $customHeadersNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol/customHeaders")

        $addNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol/customHeaders/add[@name='Cache-Control']")
        if ($addNode -ne $null)
        {
            $customHeadersNode.RemoveChild($addNode)
        }

        [System.Xml.XmlElement] $removeElement = $xmlDoc.CreateElement("remove")
        $removeNameAttribute = $xmlDoc.CreateAttribute('name')
        $removeNameAttribute.Value = 'Cache-Control'
        $removeElement.Attributes.Append($removeNameAttribute)

        $customHeadersNode.AppendChild($removeElement)

        $xmlDoc.Save($webconfig)
    }

    $addNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol/customHeaders/add[@name='Cache-Control']")
    if ($addNode -eq $null)
    {
        Write-Log "Start adding system.webServer/httpProtocol/customHeaders/add node to Services/ReliableCommunicationManager.svc location"
        $customHeadersNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/ReliableCommunicationManager.svc']/system.webServer/httpProtocol/customHeaders")

        [System.Xml.XmlElement] $addElement = $xmlDoc.CreateElement("add")
        $addNameAttribute = $xmlDoc.CreateAttribute('name')
        $addNameAttribute.Value = 'Cache-Control'
        $addValueAttribute = $xmlDoc.CreateAttribute('value')
        $addValueAttribute.Value = 'no-cache,no-store'
        $addElement.Attributes.Append($addNameAttribute)
        $addElement.Attributes.Append($addValueAttribute)

        $customHeadersNode.AppendChild($addElement)

        $xmlDoc.Save($webconfig)
    }
}

function Upgrade-WebConfig-rootLocations-TelemetryManager([string]$webconfig)
{
    #Check if platform update 25 or later
    if ($PlatformReleaseVersion -ge 5215)
    {
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)

        $tmLocationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']")
        if ($tmLocationNode -eq $null)
        {
            Write-Log "Start adding Services/TelemetryManager.svc location node"
            $configurationNode = $xmlDoc.SelectSingleNode("/configuration")

            [System.Xml.XmlElement] $locationElement = $xmlDoc.CreateElement("location")
            $pathAttribute = $xmlDoc.CreateAttribute('path')
            $pathAttribute.Value = 'Services/TelemetryManager.svc'
            $locationElement.Attributes.Append($pathAttribute)

            $configurationNode.AppendChild($locationElement)

            $xmlDoc.Save($webconfig)
        }

        # <system.web />
        $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.web")
        if ($webNode -eq $null)
        {
            Write-Log "Start adding system.web node to Services/TelemetryManager.svc location"
            $locationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']")

            [System.Xml.XmlElement] $webElement = $xmlDoc.CreateElement("system.web")

            $locationNode.AppendChild($webElement)

            $xmlDoc.Save($webconfig)
        }

        # <system.web>
        #   <compilation debug="false" />
        #   <httpRuntime enableVersionHeader="false" />
        # </system.web>

        $compilationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.web/compilation")
        if ($compilationNode -eq $null)
        {
            Write-Log "Start adding system.web/compilation node to Services/TelemetryManager.svc location"
            $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.web")

            [System.Xml.XmlElement] $compilationElement = $xmlDoc.CreateElement("compilation")
            $debugAttribute = $xmlDoc.CreateAttribute('debug')
            $debugAttribute.Value = 'false'
            $compilationElement.Attributes.Append($debugAttribute)

            $webNode.AppendChild($compilationElement)

            $xmlDoc.Save($webconfig)
        }

        $httpRuntimeNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.web/httpRuntime")
        if ($httpRuntimeNode -eq $null)
        {
            Write-Log "Start adding system.web/httpRuntime node to Services/TelemetryManager.svc location"
            $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.web")

            [System.Xml.XmlElement] $httpRuntimeElement = $xmlDoc.CreateElement("httpRuntime")
            $enableVersionHeaderAttribute = $xmlDoc.CreateAttribute('enableVersionHeader')
            $enableVersionHeaderAttribute.Value = 'false'
            $httpRuntimeElement.Attributes.Append($enableVersionHeaderAttribute)

            $webNode.AppendChild($httpRuntimeElement)

            $xmlDoc.Save($webconfig)
        }

        $webServerNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.webServer")
        if ($webServerNode -eq $null)
        {
            Write-Log "Start adding system.webServer node to Services/TelemetryManager.svc location"
            $locationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']")

            [System.Xml.XmlElement] $webServerElement = $xmlDoc.CreateElement("system.webServer")

            [System.Xml.XmlElement] $httpErrorsElement = $xmlDoc.CreateElement("httpErrors")
            $errorModeAttribute = $xmlDoc.CreateAttribute('errorMode')
            $errorModeAttribute.Value = 'Custom'
            $existingResponseAttribute = $xmlDoc.CreateAttribute('existingResponse')
            $existingResponseAttribute.Value = 'PassThrough'
            $httpErrorsElement.Attributes.Append($errorModeAttribute)
            $httpErrorsElement.Attributes.Append($existingResponseAttribute)

            $webServerElement.AppendChild($httpErrorsElement)

            $locationNode.AppendChild($webServerElement)

            $xmlDoc.Save($webconfig)
        }

        # IIS version 10 or above is supported
        $iisVersion = Get-IISVersion-Major
        if ($iisVersion -ge 10)
        {
            # <security>
            #   <requestFiltering removeServerHeader="true" />
            # </security>
            $securityNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.webServer/security")
            if ($securityNode -eq $null)
            {
                Write-Log "Start adding system.webServer/security node to Services/TelemetryManager.svc location"
                $webServerNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='Services/TelemetryManager.svc']/system.webServer")

                [System.Xml.XmlElement] $securityElement = $xmlDoc.CreateElement("security")
                [System.Xml.XmlElement] $requestFilteringElement = $xmlDoc.CreateElement("requestFiltering")
                $removeServerHeaderAttribute = $xmlDoc.CreateAttribute('removeServerHeader')
                $removeServerHeaderAttribute.Value = 'true'
                $requestFilteringElement.Attributes.Append($removeServerHeaderAttribute)

                $securityElement.AppendChild($requestFilteringElement)
                $webServerNode.AppendChild($securityElement)

                $xmlDoc.Save($webconfig)
            }
        }
    }
}

function Upgrade-WebConfig-rootLocations([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    if ($PlatformReleaseVersion -ge 4425)
    {
        $newNodeAttribute = 'punchout'
        $nodetoAdd = $xmlDoc.SelectSingleNode("/configuration/location[@path='$newNodeAttribute']")
        if ($nodetoAdd -eq $null)
        {
            Write-Log "Start adding punchout node"
            $configurationNode = $xmlDoc.SelectSingleNode("/configuration")

            [System.Xml.XmlElement] $locationElement = $xmlDoc.CreateElement("location")
            $pathAttribute = $xmlDoc.CreateAttribute('path')
            $pathAttribute.Value = $newNodeAttribute
            $locationElement.Attributes.Append($pathAttribute)

            [System.Xml.XmlElement] $webElement = $xmlDoc.CreateElement("system.web")
            [System.Xml.XmlElement] $httpRuntimeElement = $xmlDoc.CreateElement("httpRuntime")
            $requestValidationModeAttribute = $xmlDoc.CreateAttribute('requestValidationMode')
            $requestValidationModeAttribute.Value = '2.0'
            $httpRuntimeElement.Attributes.Append($requestValidationModeAttribute)

            $webElement.AppendChild($httpRuntimeElement)
            $locationElement.AppendChild($webElement)
            $configurationNode.AppendChild($locationElement)

            $xmlDoc.Save($webconfig)
        }
    }

    # Check if update 41 or later
    if ($PlatformReleaseVersion -ge 5934)
    {      
        $sameSiteNone ='None'
        $sameSiteLax ='Lax'
        $httpCookiesConfigNode = $xmlDoc.SelectSingleNode("/configuration/location/system.web/httpCookies")
        $httpCookiesConfigNodeValue = $httpCookiesConfigNode.GetAttribute("sameSite")

        # Update existing Lax value to None to support punchout scenario
         if($httpCookiesConfigNodeValue -eq $sameSiteLax)
        {
           $httpCookiesConfigNode.SetAttribute("sameSite", $sameSiteNone)
           Write-Log "Web.config http Cookie sameSite updated to '$sameSiteNone' value."
        }
        elseif($httpCookiesConfigNodeValue -eq $sameSiteNone)
        {
           Write-Log "Web.config http Cookie sameSite already has '$sameSiteNone' value."
        }
        else
        {
           $httpCookiesConfigNode.SetAttribute("sameSite", $sameSiteNone)
            Write-Log "Web.config http Cookie sameSite updated to '$sameSiteNone' value."
        }
        $xmlDoc.Save($webconfig)
    }
}

function Modify-WebConfig-removeServerHeaderSetting([System.Xml.XmlDocument]$xmlDoc, [string]$xmlNodePathHead)
{
    # <system.webServer>
    #   <security>
    #       <requestFiltering removeServerHeader="true">
    #       </requestFiltering>
    #   </security>
    # </system.webServer>
    
    # <system.webServer />
    $webServerNode = $xmlDoc.SelectSingleNode("$xmlNodePathHead/system.webServer")
    if ($webServerNode -eq $null)
    {
        Write-Log "Start adding system.webServer node to $xmlNodePathHead"
        $configurationNode = $xmlDoc.SelectSingleNode($xmlNodePathHead)

        [System.Xml.XmlElement] $webServerElement = $xmlDoc.CreateElement("system.webServer")
        $configurationNode.AppendChild($webServerElement)

        $xmlDoc.Save($webconfig)
    }

    # <security>
    $securityNode = $xmlDoc.SelectSingleNode("$xmlNodePathHead/system.webServer/security")
    if ($securityNode -eq $null)
    {
        Write-Log "Start adding security node to $xmlNodePathHead/system.webServer"
        $webServerNode = $xmlDoc.SelectSingleNode("$xmlNodePathHead/system.webServer")
        [System.Xml.XmlElement] $securityElement = $xmlDoc.CreateElement("security")               
        $webServerNode.AppendChild($securityElement)
        $xmlDoc.Save($webconfig)
    }

    # <requestFiltering removeServerHeader="true">
    $requestFilteringNode = $xmlDoc.SelectSingleNode("$xmlNodePathHead/system.webServer/security/requestFiltering")
    if ($requestFilteringNode -eq $null)
    {
        Write-Log "Start adding requestFiltering node to $xmlNodePathHead/system.webServer/security"
        $securityNode = $xmlDoc.SelectSingleNode("$xmlNodePathHead/system.webServer/security")
        [System.Xml.XmlElement] $requestFilteringElement = $xmlDoc.CreateElement("requestFiltering")
        $removeServerHeaderAttribute = $xmlDoc.CreateAttribute('removeServerHeader')
        $removeServerHeaderAttribute.Value = 'true'
        $requestFilteringElement.Attributes.Append($removeServerHeaderAttribute)

        $securityNode.AppendChild($requestFilteringElement)
        $xmlDoc.Save($webconfig)
    }
    else
    {
        Write-Log "start modifying removeServerHeader attribute of requestFiltering node under $xmlNodePathHead/system.webServer/security"
        [System.Xml.XmlElement] $requestFilteringElement = $xmlDoc.SelectSingleNode("$xmlNodePathHead/system.webServer/security/requestFiltering")
        $requestFilteringElement.SetAttribute('removeServerHeader', 'true')
        $xmlDoc.Save($webconfig)
    }
}

function Upgrade-WebConfig-rootLocationsWithSecurity([string]$webconfig)
{
    # PU44 and above.
    if ($PlatformReleaseVersion -ge 6060)
    {
        # IIS version 10 or above is supported
        $iisVersion = Get-IISVersion-Major
        if ($iisVersion -ge 10)
        {
            [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
            $xmlDoc.Load($webconfig)
        
            Modify-WebConfig-removeServerHeaderSetting -xmlDoc:$xmlDoc -xmlNodePath "/configuration"
        }
    }
}


function Upgrade-WebConfig-rootLocationsWithSecurity-byName([string]$webconfig, [string]$locationname)
{
    # PU44 and above.
    if ($PlatformReleaseVersion -ge 6060)
    {
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)

        $tmLocationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']")
        if ($tmLocationNode -eq $null)
        {
            Write-Log "$locationname location node does not exist. Return and do nothing."
            return
        }

        # <system.web />
        $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web")
        if ($webNode -eq $null)
        {
            Write-Log "Start adding system.web node to $locationname location"
            $locationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']")

            [System.Xml.XmlElement] $webElement = $xmlDoc.CreateElement("system.web")

            $locationNode.AppendChild($webElement)

            $xmlDoc.Save($webconfig)
        }

        # <system.web>
        #   <compilation debug="false" />
        #   <httpRuntime enableVersionHeader="false" />
        # </system.web>

        $compilationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web/compilation")
        if ($compilationNode -eq $null)
        {
            Write-Log "Start adding system.web/compilation node to $locationname location"
            $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web")

            [System.Xml.XmlElement] $compilationElement = $xmlDoc.CreateElement("compilation")
            $debugAttribute = $xmlDoc.CreateAttribute('debug')
            $debugAttribute.Value = 'false'
            $compilationElement.Attributes.Append($debugAttribute)

            $webNode.AppendChild($compilationElement)

            $xmlDoc.Save($webconfig)
        }
        else
        {
            Write-Log "Start modifying debug attribute of system.web/compilation node under $locationname location"
            [System.Xml.XmlElement] $compilationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web/compilation")
            $compilationNode.SetAttribute('debug', 'false')
            $xmlDoc.Save($webconfig)
        }

        $httpRuntimeNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web/httpRuntime")
        if ($httpRuntimeNode -eq $null)
        {
            Write-Log "Start adding system.web/httpRuntime node to $locationname location"
            $webNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web")

            [System.Xml.XmlElement] $httpRuntimeElement = $xmlDoc.CreateElement("httpRuntime")
            $enableVersionHeaderAttribute = $xmlDoc.CreateAttribute('enableVersionHeader')
            $enableVersionHeaderAttribute.Value = 'false'
            $httpRuntimeElement.Attributes.Append($enableVersionHeaderAttribute)

            $webNode.AppendChild($httpRuntimeElement)

            $xmlDoc.Save($webconfig)
        }
        else
        {
            Write-Log "Start modifying enableVersionHeader attribute of system.web/httpRuntime node under $locationname location"
            [System.Xml.XmlElement] $compilationNode = $xmlDoc.SelectSingleNode("/configuration/location[@path='$locationname']/system.web/httpRuntime")
            $compilationNode.SetAttribute('enableVersionHeader', 'false')
            $xmlDoc.Save($webconfig)
        }


        # <system.webServer>
        #   <security>
        #       <requestFiltering removeServerHeader="true">
        #       </requestFiltering>
        #   </security>
        # </system.webServer>
        # IIS version 10 or above is supported
        $iisVersion = Get-IISVersion-Major
        if ($iisVersion -ge 10)
        {
            Modify-WebConfig-removeServerHeaderSetting -xmlDoc:$xmlDoc -xmlNodePath "/configuration/location[@path='$locationname']"           
        }
    }
}

function Upgrade-WebConfig-RemoveAssemblies([string]$webConfig)
{
    if ($PlatformReleaseVersion -ge 4425)
    {
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)
        $assemblyName = 'Microsoft.Dynamics.IntegrationFramework.WebService.Process'
        if (Get-AssemblyExists -assemblyName:$assemblyName -xmlDoc:$xmlDoc)
        {
            Remove-Assembly -assemblyName:$assemblyName -xmlDoc:$xmlDoc
        }
    }
}

function Upgrade-WebConfig-RemoveWcfActivityLog([string]$webConfig)
{
    if ($PlatformReleaseVersion -ge 4425)
    {
        Write-Log "Start removing wcf activityLog."
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)

        $Web = $xmlDoc.SelectNodes("//*[@name='WcfActivityLog']")
        foreach ($webitem in $Web)
        {
            $webItemParent = $webitem.ParentNode
            $webItemParent.RemoveChild($webitem)
        }

        $xmlDoc.Save($webconfig)
    }
}

function Upgrade-WebConfig-Add-LcsEnvironmentId([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)
    $appsettings = $xmlDoc.SelectSingleNode("/configuration/appSettings")

    $key = 'LCS.EnvironmentId'
    Try
    {
        $value = Get-ItemProperty -Path hklm:SOFTWARE\Microsoft\Dynamics\AX\Diagnostics\MonitoringInstall -Name "LCSEnvironmentID"
        $value = $value.LCSEnvironmentId
    }
    Catch
    {
        $value = ''
    }
    if (!(Get-KeyExists -key:$key -xmlDoc:$xmlDoc) -and $value -ne '')
    {
        Add-NewKey -key $key -value $value -parentNode $appsettings -xmlDoc $xmlDoc
    }

    $xmlDoc.Save($webconfig);
}

function Upgrade-WifConfig-Add-CustomTokenHandler([string]$wifconfig)
{
    if ($PlatformReleaseVersion -ge 4977)
    {
        [System.Xml.XmlDocument] $wifXml = New-Object System.Xml.XmlDocument
        $wifXml.Load($wifconfig)

        Write-Log "Checking if the custom token handler should be added to wif.config"
        $shouldAddCustomTokenHandler = $false

        $foundNode = $wifXml.SelectSingleNode("//add[@type='MS.Dynamics.TestTools.CloudCommonTestUtilities.Authentication.PerfSdkSaml2TokenHandler, MS.Dynamics.TestTools.CloudCommonTestUtilities']")
        if ($foundNode -eq $null)
        {
            $key = 'HKLM:/SOFTWARE/Microsoft/Dynamics/AX/Diagnostics/MonitoringInstall'

            if (Test-Path -Path $key)
            {
                $lcsEnvironmentTag = (Get-ItemProperty -Path $key).LcsEnvironmentTag
                if ($lcsEnvironmentTag -eq "sandbox")
                {
                    Write-Log "Sandbox VM on PU18 or above should have the custom token handler"
                    $shouldAddCustomTokenHandler = $true
                }
            }
            elseif (Get-DevToolsInstalled)
            {
                Write-Log "Dev VM on PU18 or above should have the custom token handler"
                $shouldAddCustomTokenHandler = $true
            }
        }

        if ($shouldAddCustomTokenHandler)
        {
            Write-Log "Adding PerfSDK custom token handler"

            $securityTokenHandlerConfiguration = $wifXml.SelectSingleNode("system.identityModel/identityConfiguration/securityTokenHandlers")

            $removeNode = $wifXml.CreateElement("remove")
            $removeNode.SetAttribute("type", "System.IdentityModel.Tokens.Saml2SecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")

            $addNode = $wifXml.CreateElement("add")
            $addNode.SetAttribute("type", "MS.Dynamics.TestTools.CloudCommonTestUtilities.Authentication.PerfSdkSaml2TokenHandler, MS.Dynamics.TestTools.CloudCommonTestUtilities")

            $securityTokenHandlerConfiguration.AppendChild($removeNode)
            $securityTokenHandlerConfiguration.AppendChild($addNode)
        }
        $wifXml.Save($wifconfig)
    }
}

function Upgrade-WebConfig-ExcludeIdentityModelFromInheritance([string]$webconfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    $identityModel = $xmlDoc.SelectSingleNode("//system.identityModel")
    if ($identityModel.ParentNode.Name -ne "location")
    {
        Write-Log "Excluding identity model from inheritance"

        $inheritanceNode = $xmlDoc.CreateElement("location")
        $inheritanceNode.SetAttribute("path", ".")
        $inheritanceNode.SetAttribute("inheritInChildApplications", "false")

        $configuration = $xmlDoc.SelectSingleNode("/configuration")
        $configuration.InsertBefore($inheritanceNode, $identityModel)
        $inheritanceNode.AppendChild($identityModel)
    }
    $xmlDoc.Save($webconfig)
}

function Upgrade-WebConfig-Add-SecurityrequestFilteringVerbsElements([string] $webConfig)
{
    if (Get-DevToolsInstalled)
    {
        # Must allow DEBUG verb for development environments
        Write-Log "Development Tools are installed. Skipping Upgrade-WebConfig-Add-SecurityrequestFilteringVerbsElements"
    }
    else
    {
        # Not a development environment, so disallow DEBUG verb

        Write-Log "Upgrade-WebConfig-Add-SecurityrequestFilteringVerbsElements start"

        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)

        # /configuration/system.webServer
        [System.Xml.XmlNode] $node = $xmlDoc.SelectSingleNode("/configuration/system.webServer")
        if ($null -ne $node)
        {
            Add-SecurityrequestFilteringVerbsElement -xmlDoc $xmlDoc -webServerNode $node
        }

        $xmlDoc.Save($webconfig)

        Write-Log "Upgrade-WebConfig-Add-SecurityrequestFilteringVerbsElements end"
    }
}

function Add-SecurityrequestFilteringVerbsElement([System.Xml.XmlDocument] $xmlDoc, [System.Xml.XmlNode] $webServerNode)
{
    #  <security>
    #    <requestFiltering>
    #      <verbs>
    #        <add verb="DEBUG" allowed="false" />
    #      </verbs>
    #    </requestFiltering>
    #  </security>

    # Ensure security node exists
    [System.Xml.XmlNode] $securityNode = $webServerNode.SelectSingleNode("security")
    if ($null -eq $securityNode)
    {
        Write-Log "Add-SecurityrequestFilteringVerbsElement: Creating element 'security'"

        $securityNode = $xmlDoc.CreateNode("element", "security", $null)
        $webServerNode.AppendChild($securityNode)
    }

    # Ensure requestFiltering node exists
    [System.Xml.XmlNode] $requestFilteringNode = $securityNode.SelectSingleNode("requestFiltering")
    if ($null -eq $requestFilteringNode)
    {
        Write-Log "Add-SecurityrequestFilteringVerbsElement: Creating element 'requestFiltering'"

        $requestFilteringNode = $xmlDoc.CreateNode("element", "requestFiltering", $null)
        $securityNode.AppendChild($requestFilteringNode)
    }

    # Ensure verbs node exists
    [System.Xml.XmlNode] $verbsNode = $requestFilteringNode.SelectSingleNode("verbs")
    if ($null -eq $verbsNode)
    {
        Write-Log "Add-SecurityrequestFilteringVerbsElement: Creating element 'verbs'"

        $verbsNode = $xmlDoc.CreateNode("element", "verbs", $null)
        $requestFilteringNode.AppendChild($verbsNode)
    }

    # Add exclusion of verb DEBUG
    [System.Xml.XmlNode] $verbNode = $verbsNode.SelectSingleNode("add[@verb='DEBUG']")
    if ($null -eq $verbNode)
    {
        Write-Log "Add-SecurityrequestFilteringVerbsElement: Creating element 'add' with attribute 'verb' with value 'DEBUG'"

        Add-VerbAllowedElement -xmlDoc $xmlDoc -parentNode $verbsNode -verb "DEBUG" -allowed "false"
    }
}

function Add-VerbAllowedElement([System.Xml.XmlDocument] $xmlDoc, [System.Xml.XmlNode] $parentNode, [string] $verb, [string] $allowed)
{
    [System.Xml.XmlElement] $element = $xmlDoc.CreateElement("add");

    [System.Xml.XmlAttribute] $verbAttribute = $xmlDoc.CreateAttribute("verb")
    $verbAttribute.Value = $verb

    [System.Xml.XmlAttribute] $allowedAttribute = $xmlDoc.CreateAttribute("allowed")
    $allowedAttribute.Value = $allowed

    $element.Attributes.Append($verbAttribute)
    $element.Attributes.Append($allowedAttribute)

    $parentNode.AppendChild($element)
}

# <summary>
# Adds the fileExtensions element to requestFiltering element such that explicit
# file types are excluded from URI access
# </summary>
function Upgrade-WebConfig-Add-RequestFilteringFileExtensions([string] $webConfig)
{
    # Only run this on Platform Update 34 and later
    if ($PlatformReleaseVersion -lt 5585)
    {
        # no-op
        return
    }

    # <fileExtensions allowUnlisted="true">
    #   <add fileExtension=".dll" allowed="false" />
    #   <add fileExtension=".man" allowed="false" />
    # </fileExtensions>

    Write-Log "Upgrade-WebConfig-Add-RequestFilteringFileExtensions start"

    # Load web.config
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    # /configuration/system.webServer/security/requestFiltering
    [System.Xml.XmlNode] $requestFilteringNode = $xmlDoc.SelectSingleNode("/configuration/system.webServer/security/requestFiltering")
    if ($null -ne $requestFilteringNode)
    {
        # Does the fileExtensions element exist?
        [System.Xml.XmlNode] $fileExtensionsNode = $requestFilteringNode.SelectSingleNode("fileExtensions")

        # Create it if necessary
        if ($null -eq $fileExtensionsNode)
        {
            Write-Log "Creating element: fileExtensions"

            $fileExtensionsNode = $xmlDoc.CreateNode("element", "fileExtensions", $null)
            $requestFilteringNode.AppendChild($fileExtensionsNode)
        }

        # Does the allowUnlisted attribute exist and have the correct value?
        [System.Xml.XmlAttribute] $attribute = $fileExtensionsNode.Attributes.GetNamedItem("allowUnlisted")
        if ($null -eq $attribute -or "true" -ne $attribute.Value)
        {
            # Set allowUnlisted attribute to True
            [System.Xml.XmlNode] $allowUnlistedAttribute = $xmlDoc.CreateNode("attribute", "allowUnlisted", $null)
            $allowUnlistedAttribute.Value = "true"

            # set the element attribute
            $fileExtensionsNode.Attributes.SetNamedItem($allowUnlistedAttribute)
        }

        # Exclude file extensions in this list
        $fileExtensions = ".dll", ".man"

        # Loop through the exclusion list
        foreach ($fileExtension in $fileExtensions)
        {
            Add-FileExtensionsElement -xmlDoc $xmlDoc -parentNode $fileExtensionsNode -fileExtensionValue $fileExtension -allowedValue "false"
        }
    }

    # Save web.config
    $xmlDoc.Save($webconfig)

    Write-Log "Upgrade-WebConfig-Add-RequestFilteringFileExtensions end"
}

# <summary>
# Adds the fileExtensions element to the parent node
# </summary>
function Add-FileExtensionsElement([System.Xml.XmlDocument] $xmlDoc, [System.Xml.XmlNode] $parentNode, [string] $fileExtensionValue, [string] $allowedValue)
{
    # Do nothing if the element exists as desired
    if ($null -ne $parentNode.SelectSingleNode("/add[@fileExtension='$fileExtensionValue' and @allowed='$allowedValue']"))
    {
        # no-op
        return
    }

    Write-Log "Add-FileExtensionsElement start"

    # Does the element exist?
    [System.Xml.XmlNode] $addElement = $parentNode.SelectSingleNode("add[@fileExtension='$fileExtensionValue']")
    if ($null -eq $addElement)
    {
        Write-Log "Creating element add with attribute fileExtension having value $fileExtensionValue"

        # create the element
        $addElement = $xmlDoc.CreateNode("element", "add", $null)

        [System.Xml.XmlNode] $fileExtensionAttribute = $xmlDoc.CreateNode("attribute", "fileExtension", $null)
        $fileExtensionAttribute.Value = $fileExtensionValue

        # set the element attribute
        $addElement.Attributes.SetNamedItem($fileExtensionAttribute)

        # add the element to the parent node
        $parentNode.AppendChild($addElement)
    }

    # Does the allowed attribute exist and have the correct value?
    [System.Xml.XmlAttribute] $attribute = $addElement.Attributes.GetNamedItem("allowed")
    if ($null -eq $attribute -or $allowedValue -ne $attribute.Value)
    {
        # Set allowed attribute to the correct value
        [System.Xml.XmlNode] $allowedAttribute = $xmlDoc.CreateNode("attribute", "allowed", $null)
        $allowedAttribute.Value = $allowedValue

        # set the element attribute
        $addElement.Attributes.SetNamedItem($allowedAttribute)
    }

    Write-Log "Add-FileExtensionsElement end"
}

<#
.SYNOPSIS
    Gets the major version of IIS from registry.

.NOTES
    Used only from within this module.
#>
function Get-IISVersion-Major()
{
    $majorVersion = 0

    try
    {
        $obj = Get-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\InetStp\ -Name "MajorVersion"
        if ($null -ne $obj)
        {
            $majorVersion = $obj.MajorVersion
        }
    }
    catch
    {
        # no-op
    }

    return $majorVersion
}

Export-ModuleMember -Function Initialize-Log, Write-Log, Write-Error, Create-Backup, Upgrade-Web-Config, Encrypt-Config, Decrypt-Config -Variable $logfile
# SIG # Begin signature block
# MIInzgYJKoZIhvcNAQcCoIInvzCCJ7sCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCVnlqW2uFn/aOu
# 3L5QnH0x7tEP34HRC0dBnFtP5XJn9aCCDYUwggYDMIID66ADAgECAhMzAAACzfNk
# v/jUTF1RAAAAAALNMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NjAyWhcNMjMwNTExMjA0NjAyWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDrIzsY62MmKrzergm7Ucnu+DuSHdgzRZVCIGi9CalFrhwtiK+3FIDzlOYbs/zz
# HwuLC3hir55wVgHoaC4liQwQ60wVyR17EZPa4BQ28C5ARlxqftdp3H8RrXWbVyvQ
# aUnBQVZM73XDyGV1oUPZGHGWtgdqtBUd60VjnFPICSf8pnFiit6hvSxH5IVWI0iO
# nfqdXYoPWUtVUMmVqW1yBX0NtbQlSHIU6hlPvo9/uqKvkjFUFA2LbC9AWQbJmH+1
# uM0l4nDSKfCqccvdI5l3zjEk9yUSUmh1IQhDFn+5SL2JmnCF0jZEZ4f5HE7ykDP+
# oiA3Q+fhKCseg+0aEHi+DRPZAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU0WymH4CP7s1+yQktEwbcLQuR9Zww
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ3MDUzMDAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AE7LSuuNObCBWYuttxJAgilXJ92GpyV/fTiyXHZ/9LbzXs/MfKnPwRydlmA2ak0r
# GWLDFh89zAWHFI8t9JLwpd/VRoVE3+WyzTIskdbBnHbf1yjo/+0tpHlnroFJdcDS
# MIsH+T7z3ClY+6WnjSTetpg1Y/pLOLXZpZjYeXQiFwo9G5lzUcSd8YVQNPQAGICl
# 2JRSaCNlzAdIFCF5PNKoXbJtEqDcPZ8oDrM9KdO7TqUE5VqeBe6DggY1sZYnQD+/
# LWlz5D0wCriNgGQ/TWWexMwwnEqlIwfkIcNFxo0QND/6Ya9DTAUykk2SKGSPt0kL
# tHxNEn2GJvcNtfohVY/b0tuyF05eXE3cdtYZbeGoU1xQixPZAlTdtLmeFNly82uB
# VbybAZ4Ut18F//UrugVQ9UUdK1uYmc+2SdRQQCccKwXGOuYgZ1ULW2u5PyfWxzo4
# BR++53OB/tZXQpz4OkgBZeqs9YaYLFfKRlQHVtmQghFHzB5v/WFonxDVlvPxy2go
# a0u9Z+ZlIpvooZRvm6OtXxdAjMBcWBAsnBRr/Oj5s356EDdf2l/sLwLFYE61t+ME
# iNYdy0pXL6gN3DxTVf2qjJxXFkFfjjTisndudHsguEMk8mEtnvwo9fOSKT6oRHhM
# 9sZ4HTg/TTMjUljmN3mBYWAWI5ExdC1inuog0xrKmOWVMIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAALN82S/+NRMXVEAAAAA
# As0wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMz6
# EA2utYVRA1JMv1E59bPvBciGPd3Jb5ZncfP4/x+qMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEA4t1XaDPp3jAMwqrTWTOfJmJDGOvw08JrmWVu
# f2mQotCA1S151BqsrrC1wZmMry/6XWm6aLA16d6l0rxNLCq8SMom5cAkm6yKcZbW
# ytZH1HYc0HdkT4arsbjNqPt/x2xdsGrG5MhD0bGb9VR/uBF35hx0Z288qrCr1CSn
# 6piSkdVHX08yR72NQ/yRQzwahlS1Geo5ZY/+NVpRVSZu9n7wEd4IiZ+5j6y0LHDG
# yaLfxc9jmi3Vz7+u7Alm1jJKxU6DwKh4Sl02dMd6z27ow+/aUiUl5KyTtNAgyH3D
# fhOXBo7cyjf+qiFaQmq7dYF569H52/8iM2gEOrEnC6DjjlPqRqGCFykwghclBgor
# BgEEAYI3AwMBMYIXFTCCFxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCD1kW2ZWl+JBnUVlGwhTeJDTQOhqrkaSYra
# 7rkB7xIbNAIGZD/VVrKzGBMyMDIzMDQyNzE5NDYwMi40NTZaMASAAgH0oIHYpIHV
# MIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsT
# HVRoYWxlcyBUU1MgRVNOOjJBRDQtNEI5Mi1GQTAxMSUwIwYDVQQDExxNaWNyb3Nv
# ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAGxypBD
# 7gvwA6sAAQAAAbEwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTAwHhcNMjIwOTIwMjAyMTU5WhcNMjMxMjE0MjAyMTU5WjCB0jELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjoyQUQ0LTRCOTItRkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIai
# qz7V7BvH7IOMPEeDM2UwCpM8LxAUPeJ7Uvu9q0RiDBdBgshC/SDre3/YJBqGpn27
# a7XWOMviiBUfMNff51NxKFoSX62Gpq36YLRZk2hN1wigrCO656z5pVTjJp3Q8jdY
# AJX3ruJea3ccfTgxAgT3Uv/sP4w0+yZAYa2JZalV3MBgIFi3VwKFA4ClQcr+V4Sp
# Gzqz8faqabmYypuJ35Zn8G/201pAN2jDEOu7QaDC0rGyDdwSTVmXcHM46EFV6N2F
# 69nwfj2DZh74gnA1DB7NFcZn+4v1kqQWn7AzBJ+lmOxvKrURlV/u19Mw1YP+zVQy
# zKn5/4r/vuYSRj/thZr+FmZAUtTAacLzouBENuaSBuOY1k330eMp8nndSNUsUjj/
# nn7gcdFqzdQNudJb+XxmRwi9LwjA0/8PlOsKTZ8Xw6EEWPVLfNojSuWpZMTaMzz/
# wzSPp5J02kpYmkdl50lwyGRLO5X7iWINKmoXySdQmRdiGMTkvRStXKxIoEm/EJxC
# aI+k4S3+BWKWC07EV5T3UG7wbFb4LfvgbbaKM58HytAyjDnO9fEi0vrp8JFTtGhd
# twhEEkraMtGVt+CvnG0ZlH4mvpPRPuJbqE509e6CqmHwzTuUZPFMFWvJn4fPv0d3
# 2Ws9jv2YYmE/0WR1fULs+TxxpWgn1z0PAOsxSZRPAgMBAAGjggFJMIIBRTAdBgNV
# HQ4EFgQU9Jtnke8NrYSK9fFnoVE0pr0OOZMwHwYDVR0jBBgwFoAUn6cVXQBeYl2D
# 9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy
# MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1l
# LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQAD
# ggIBANjnN5JqpeVShIrQIaAQnNVOv1cDEmCkD6oQufX9NGOX28Jw/gdkGtMJyagA
# 0lVbumwQla5LPhBm5LjIUW/5aYhzSlZ7lxeDykw57wp2AqoMAJm7bXcXtJt/HyaR
# lN35hAhBV+DmGnBIRcE5C2bSFFY3asD50KUSCPmKl/0NFadPeoNqbj5ZUna8VAfM
# SDsdxeyxjs8r/9Vpqy8lgIVBqRrXtFt6n1+GFpJ+2AjPspfPO7Y+Y/ozv5dTEYum
# 5eDLDdD1thQmHkW8s0BBDbIOT3d+dWdPETkf50fM/nALkMEdvYo2gyiJrOSG0a9Z
# 2S/6mbJBUrgrkgPp2HjLkycR4Nhwl67ehAhWxJGKD2gRk88T2KKXLiRHAoYTZVpH
# bgkYLspBLJs9C77ZkuxXuvIOGaId7EJCBOVRMJygtx8FXpoSu3jWEdau0WBMXxhV
# AzEHTu7UKW3Dw+KGgW7RRlhrt589SK8lrPSvPM6PPnqEFf6PUsTVO0bOkzKnC3TO
# gui4JhlWliigtEtg1SlPMxcdMuc9uYdWSe1/2YWmr9ZrV1RuvpSSKvJLSYDlOf6a
# JrpnX7YKLMRoyKdzTkcvXw1JZfikJeGJjfRs2cT2JIbiNEGK4i5srQbVCvgCvdYV
# EVZXVW1Iz/LJLK9XbIkMMjmECJEsa07oadKcO4ed9vY6YYBGMIIHcTCCBVmgAwIB
# AgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0
# IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1
# WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O
# 1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZn
# hUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t
# 1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxq
# D89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmP
# frVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSW
# rAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv
# 231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zb
# r17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYcten
# IPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQc
# xWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17a
# j54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQAB
# MCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQU
# n6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEw
# QTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9E
# b2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQB
# gjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/
# MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJ
# oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p
# Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB
# BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9v
# Q2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3h
# LB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x
# 5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74p
# y27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1A
# oL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbC
# HcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB
# 9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNt
# yo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3
# rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcV
# v7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A24
# 5oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lw
# Y1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB
# 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk
# TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjoyQUQ0LTRCOTItRkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUA7WSxvqQDbA7vyy69
# Tn0wP5BGxyuggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOf035swIhgPMjAyMzA0MjcxOTQ4NDNaGA8yMDIzMDQy
# ODE5NDg0M1owdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA5/TfmwIBADAHAgEAAgIX
# TzAHAgEAAgIRKDAKAgUA5/YxGwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEE
# AYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GB
# ABU9iCWlPWqCfj/H7tsAvBj5FxH3HmN9j+t/2RuK/A2R6kwBZmqtykkbIMeuxdho
# jFDIkdOSiPlLPAyb1goMugTPjjuVuVJjw99cUej2ymHx9BPNkyAkc8FvCepzbKS+
# +ScgaSmrh8BFhnB5LHHGX6oxufpuh5QYqVjrTMCDCqKRMYIEDTCCBAkCAQEwgZMw
# fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
# TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGxypBD7gvwA6sAAQAA
# AbEwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRAB
# BDAvBgkqhkiG9w0BCQQxIgQgSbsIRAa0ZqaId4wzz03Ia+U8CSUCYiZwUFEd9g2X
# GsEwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCCD7Q2LFFvfqeDoy9gpu35t
# 6dYerrDO0cMTlOIomzTPbDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwAhMzAAABscqQQ+4L8AOrAAEAAAGxMCIEIB/aJtlKGSbw0SDwfEPhXVCi
# U/QcpYZbWxoQcNAHQoBSMA0GCSqGSIb3DQEBCwUABIICACqa3AhfQhVv8AXyfCO8
# iSKvkKCO6j2JSdtEPSO8770jlG82Uls8uw8qvVbdWkuCVO3PQaqt1Pv+ooaUvPMi
# K753fLY9GKury1hf6ArPK1fj8Hm9WXOdYX7EVm0MxZiHy6dTZOVL0cOH+TN7P2ey
# nLD2BC+Uwv6egTn6Xwe5qjkAKijeMKEPeBnSgnhArB5en6LWZ+DC34NO5g8HHV1X
# 3RIE8J/yFHxBmV0TYib5+nn5koDRymYsXxWHvJqItrlEvWOmvYj8gJf+H0X9HNXy
# RjHeInnCfCZXj1vU7Di5HSffYux8SKJIgP+dbF9XK/VDDZYvej5Vsu12XPUTFxJf
# m6YZvjE6+2OLY6BKoWzbnbXh5pLXQHuVpBRrjlv2iShV0PCpYhOlo74mPRMEhimq
# 9jkWpwqvuxNSX4ZP9SLZnNG2LdxwgkQzJMwPeO+sVdDu7EuYA2UK1OzgLQ5HYWob
# 7p8Kqa4tA4StBTL6CnP5MHKD7Dh5IH/dLxBrqynzy6MX6fvCYIqXX8izfUiPFR/o
# q9JQCvAuisg/N/2u0sJuBLxFQV2UVoRu4M/prRnVMjIJfi3laBytPIKQGTvJKVVd
# wzlqWQqKUht4Oq4cuC1AlwQvDhBleLJQqZF7o+ph4vPPHILA9io4eFeCxUuyEIEc
# kGS7fcWtKiMy3JquvIJTso55
# SIG # End signature block
