# 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-rootLocations-ReliableCommunicationManager -webconfig:$webconfig
    Upgrade-WebConfig-rootLocations-TelemetryManager -webconfig:$webconfig
    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 Upgrade-WebConfig-Update-DependentAssemblyBinding([string] $webConfig)
{
    [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
    $xmlDoc.Load($webconfig)

    Update-DependentAssemblyBinding -xmlDoc:$xmlDoc -assemblyName:'Microsoft.Owin' -oldVersion:'0.0.0.0-3.0.1.0' -newVersion:'3.0.1.0'

    $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 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))
    {
        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'
        }
    }
    $xmlDoc.Save($webconfig);
}

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

    $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'
        }
    }

    $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
        }
    }

    $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 whitelisted 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
        }
    }

    # 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-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)
{
    if ($PlatformReleaseVersion -ge 4425)
    {
        [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
        $xmlDoc.Load($webconfig)
        $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)
        }
    }
}

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
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDRvk2fpWJCk6VN
# 4jMg2e49qIevCcA4azPL8CvKHSDubqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgUoqL/tYu
# a3PHGcgOSeI/6dITHVYE+02RwbmWMPMB0yEwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQAUWCBMldOlKWvZUb6HqS14/wGledarK+TwTnfDG/Gy
# f5VEsEtRIHASrjeUhm+5wRs2bG/MfyVFMOqFXgXWqn5zYDogc0PhgW2gDurPa7tj
# bvnwizb6zny81zDiskECGpLYWEmOdoykvsGX7FjHpMH0wAke3U7J7p364HI5zbyu
# rhhcLqDSTClRWaiOnMYGOPLGzXiFssRfC7bXuoSRdoDPUriRsKfdq5KhlPZHNSbW
# CWo44/S4o/7KRT/abGNAM8rVsi63gGMYTaqpbsIQdtObKb0yrAviffQXNFpSOUUi
# YCBdTn5o62iIEFuh5OCP/NbYL9mlP//ECJFcQNdOZJShoYIS8TCCEu0GCisGAQQB
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEINc6aobBUoN0Gc9xFebI6K3A0fFkrvE0OSLlTXL9
# 2onBAgZfrcT1a20YEzIwMjAxMTE5MjAzNjQ2LjM4MVowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjozMkJELUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABLqjSGQeT9GvoAAAA
# AAEuMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE5MTIxOTAxMTUwNVoXDTIxMDMxNzAxMTUwNVowgc4xCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozMkJE
# LUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7TTKJRU196LFIjMQ9q
# /UjpPhz43m5RnHgHAVp2YGni74+ltsYoO1nZ58rTbJhCQ8GYHy8B4devgbqqYPQN
# U3i+drpEtEcNLbsMr4MEq3SM+vO3a6QMFd1lDRy7IQLPJNLKvcM69Nt7ku1YyM5N
# nPNDcRJsnUb/8Yx/zcW5cWjnoj8s9fQ93BPf/J74qM1ql2CdzQV74PBisMP/tppA
# nSuNwo8I7+uWr6vfpBynSWDvJeMDrcsa62Xsm7DbB1NnSsPGAGt3RzlBV9KViciz
# e4U3fo4chdoB2+QLu17PaEmj07qq700CG5XJkpEYOjedNFiByApF7YRvQrOZQ07Q
# YiMCAwEAAaOCARswggEXMB0GA1UdDgQWBBSGmokmTguJN7uqSTQ1UhLwt1RObDAf
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCN4ARqpzCuutNqY2nWJDDXj35iaidl
# gtJ/bspYsAX8atJl19IfUKIzTuuSVU3caXZ6/YvMMYMcbsNa/4J28us23K6PWZAl
# jIj0G8QtwDMlQHjrKnrcr4FBAz6ZqvB6SrN3/Wbb0QSK/OlxsU0mfD7z87R2JM4g
# wKJvH6EILuAEtjwUGSB1NKm3Twrm51fCD0jxvWxzaUS2etvMPrh8DNrrHLJBR3UH
# vg/NXS2IzdQn20xjjsW0BUAiTf+NCRpxUvu/j80Nb1++vnejibfpQJ2IlXiJdIi+
# Hb+OL3XOr8MaDDSYOaRFAIfcoq3VPi4BkvSC8QGrvhjAZafkE7R6L5FJMIIGcTCC
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoz
# MkJELUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUA+1/CN6BILeU1yDGo+b6WkpLoQpuggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AONg1KUwIhgPMjAyMDExMTkxNTI1MjVaGA8yMDIwMTEyMDE1MjUyNVowdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA42DUpQIBADAKAgEAAgIfRAIB/zAHAgEAAgIRpTAK
# AgUA42ImJQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAHr9v84/G25Atx+1
# O5ObiJnUr6cFxuox1betL9SHiZ1lX8I8fQFIj5LzbFmT8WKKRIxUiVJd85X0oq70
# DTQ3KcpcA/6M5c55x0LPOsnQK4yKRBjX6YUHeGJJnT1RKjFbwRnENbmSrXwakUdb
# wDAQTEt8UPiOP6xvGG8d7PCCDYdoMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEuqNIZB5P0a+gAAAAAAS4wDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgEaSpNwuyUhVus6K4yyv/7rfn22y6dCrjIYvZcPOop7owgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCDa/s3O8YhWiqpVN0kTeK+x2m0RAh17JpR6DiFo
# TILJKTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# LqjSGQeT9GvoAAAAAAEuMCIEIBuAtS9lULQPGJOtT20ZqRxCDqv1aV3i3RdN4of9
# 7NQIMA0GCSqGSIb3DQEBCwUABIIBAKoyqF0NKnQyF+plwuibsaHREWp+y+ViDHdd
# 2HQ6risVRwlap0zvz0xJ15ZF+xyqIQwAswUOwdXvZ7ibLmC8Lh1KQAm1yuin4QE2
# BnXOo1A01OMPTVbCRcRrWcsYSPpz3A/ijShsXbmeQ3y/74a9bR81Jkt4DZ6NhYsz
# AbVHnnnLE/7KtaeIuMbKIot8wt/U5I3/cq1OwkLjMSa3xPTkUGUNY6pgFsnF0ou/
# Xg7lgwy9x+ZH5YNIhEt3IrGt13wmU6u4OyDGoFZ82uDzbfJKBsrrvU0fb8nHe/hP
# r27XmEFP4wtA4qY19Q2rrLOkX1MKmKOtM5F3Rs7lDVQww2Afrzk=
# SIG # End signature block
