################################################################################
# Write message to log file and output stream
################################################################################
Function Write-LogOutput($message) {
    Write-Log $message
    Write-Output $message
}

################################################################################
# Install AX SSRS extension components with HTTP endpoints
################################################################################
Function Install-AxSsrsExtension {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize log file
    Initialize-Log $log
    $settings = Decode-Settings($config)
    Write-LogOutput $("")
    Write-LogOutput $settings | Format-List *

    # Get EnableSecurity value
    if ($null -eq $settings.EnableSecurity) {
        throw "Missing EnableSecurity value."
    }

    [Switch]$enableSecurity = [System.Convert]::ToBoolean($settings.EnableSecurity)

    [Switch]$isReportingClusterDeployment = $false
    [System.Boolean]::TryParse($settings.IsReportingClusterDeployment, [ref]$isReportingClusterDeployment)    

    # Check if use https
    if ((![System.String]::IsNullOrWhiteSpace($settings.SsrsSslCertificateThumbprint)) -and (![System.String]::IsNullOrWhiteSpace($settings.SsrsServerFqdn))) {
        Write-LogOutput "Install AX SSRS extension with HTTPS endpoints."
        Install-AxSsrsExtensionForHttps -config $config -log $log
        return
    }

    # Gets account list to acess reports
    [string[]] $accounts = $null
    if ($null -ne $settings.AccountListToAccessReports) {
        $accounts = $settings.AccountListToAccessReports.split(',')
    }

    # Use AxReportVmRoleStartupTask.exe to install reporting extensions
    Write-LogOutput $("Start installing AX SSRS extensions ...")

    [string] $reportServerUri = "http://localhost:80/ReportServer"
    [string] $ManagementAssemblyPath = Join-Path -Path $PSScriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
    [string] $SystemReportTimeoutConfigName = "SystemReportTimeout"
    [string] $SessionTimeoutConfigName = "SessionTimeout"
    Import-Module $ManagementAssemblyPath
    
    # Need to install SSRS extension first with windows auth to install data sources for reporting cluster mode
    if ($isReportingClusterDeployment) {
        $settings.IsReportingClusterDeployment = "False"
        $settings.EnableSecurity = "True"
        $settingsJson = ConvertTo-Json $settings
        $windowsAuthConfig = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($settingsJson))

        Setup-SSRSExtension -config $windowsAuthConfig -log $log -ErrorAction Stop

        # Set SSRS system report timeout to 1 hour
        Write-LogOutput $("Set the SSRS system report timeout to 60 minutes...")
        Set-SsrsConfigurationInfoTimeout -timeoutInSeconds 3600 -reportServerUri $reportServerUri -configName $SystemReportTimeoutConfigName

        # Set SSRS session timeout to 1 hour
        Write-LogOutput $("Set the SSRS session timeout to 60 minutes...")
        Set-SsrsConfigurationInfoTimeout -timeoutInSeconds 3600 -reportServerUri $reportServerUri -configName $SessionTimeoutConfigName

        # Set execution log days kept to 1 day
        # The records of execution log would be uploaded to Kusto every 5 minutes
        $dayskeptValue = 1
        Write-LogOutput $("Set SSRS execution log days kept value to $dayskeptValue days...")
        Set-SsrsExecutionLogDaysKept -daysKept $dayskeptValue -reportServerUri $reportServerUri

        $settings.IsReportingClusterDeployment = "True"
        $settings.EnableSecurity = $enableSecurity.ToString();

        # Install shared data sources
        Install-DataSource -reportServerUri $reportServerUri -log $log

        # Create the root report folder /Dynamics
        Install-AxReportRootFolder -ReportServerAddress "localhost" -SkipReportServerAdminCheck  -ErrorAction Stop | Tee-Object -Variable InstallLog
        Add-Content -Path $log -Value $InstallLog
    }

    # Deploy SSRS extension
    Setup-SSRSExtension -config $config -log $log -ErrorAction Stop
    Write-LogOutput $("Completed installing AX SSRS extension.")
		
    #Added this step to warmup SSRS to avoid the timeout error
    $serviceName = Get-SsrsServiceName
    Write-LogOutput ("The reporting service name is: $serviceName.")
    Restart-WindowsService -log $log -computerName $env:COMPUTERNAME -serviceName $serviceName -ErrorAction stop
    Test-ReportServer -reportServerUri $reportServerUri -log $log -ErrorAction Stop

    # Install shared data sources
    if (-not $isReportingClusterDeployment) {
        Install-DataSource -reportServerUri $reportServerUri -log $log
    }

    if ($enableSecurity) {
        # Start Creating and configuring AX reports root folder
        Write-LogOutput $("")
        Write-LogOutput $("Start creating and configuring AX reports root folder ...")			
        Install-AxReportRootFolder -Accounts $accounts  -ErrorAction Stop | Tee-Object -Variable InstallLog          
        Add-Content -Path $log -Value $InstallLog
        Write-LogOutput $("Completed creating and configuring AX reports root folder.")
        Write-LogOutput $("")
    }

    # Test the SSRS instance prior to completing this step. If this is not successful then we should fail.
    Restart-WindowsService -log $log -computerName $env:COMPUTERNAME -serviceName $serviceName -ErrorAction stop
    Test-ReportServer -reportServerUri $reportServerUri -log $log -ErrorAction Stop
    Write-LogOutput $("")
}

################################################################################
# Install AX SSRS extension components with HTTP endpoints
################################################################################
Function Install-AxSsrsExtensionForHttps {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize log file
    Initialize-Log $log
    $settings = Decode-Settings($config)
    Write-LogOutput $("")
    Write-LogOutput $settings | Format-List *    

    # Enable security
    [Switch]$enableSecurity = [System.Convert]::ToBoolean($settings.EnableSecurity)

    # IsServiceFabricDeployment (Set to true by LBD deployment)
    [Switch]$isServiceFabricDeployment = $false
    [System.Boolean]::TryParse($settings.IsServiceFabricDeployment, [ref]$isServiceFabricDeployment)

    # Gets account list to acess reports
    [string[]] $accounts = $null
    if ($null -ne $settings.AccountListToAccessReports) {
        $accounts = $settings.AccountListToAccessReports.split(',')
    }

    # Make sure HTTPS required parameters are set properly
    $sslCertThumbprint = $settings.SsrsSslCertificateThumbprint
    $serverFqdn = $settings.SsrsServerFqdn
    $httpsPort = $settings.SsrsHttpsPort
    $useHttps = (![System.String]::IsNullOrWhiteSpace($sslCertThumbprint)) -and (![System.String]::IsNullOrWhiteSpace($serverFqdn))
    if (-not $useHttps) {
        throw "Install-AxSsrsExtensionForHttps should not be called because SSLCertThumbprint: $sslCertThumbprint, Server FQDN: $serverFqdn."
    }

    $httpsPortValue = 443
    if ($httpsPort) {
        if (-not [Int32]::TryParse($settings.SsrsHttpsPort, [ref]$httpsPortValue)) {
            throw "Invalid vlaue for SsrsHttpsPort. The value is: $httpsPort"
        }
    }

    # Import management module
    [string] $ManagementAssemblyPath = Join-Path -Path $PSScriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
    Import-Module $ManagementAssemblyPath
    $lcid = [System.Globalization.CultureInfo]::GetCultureInfo('en-US').LCID

    try {
        # Use AxReportVmRoleStartupTask.exe to install reporting extensions. This will create an http endpoint for data source and ax report folder configuration
        Write-LogOutput $("Start installing AX SSRS extensions ...")
        Setup-SSRSExtension -config $config -log $log -ErrorAction Stop
        Write-LogOutput $("Completed installing AX SSRS extension.")
    
        # Install shared data sources and their parent folder if they do not exist
        Install-DataSource -reportServerUri "http://localhost/reportserver" -log $log
            
        # Start creating and configuring AX reports root folder security
        if ($enableSecurity) {
            Write-LogOutput $("")
            Write-LogOutput $("Start creating and configuring AX reports root folder ...")			
            Install-AxReportRootFolder -Accounts $accounts -DeleteExisting:$false -GrantPublishReportPermission:$isServiceFabricDeployment -ErrorAction Stop | Tee-Object -Variable InstallLog
            Add-Content -Path $log -Value $InstallLog
            Write-LogOutput $("Completed creating and configuring AX reports root folder.")
            Write-LogOutput $("")
        }
    }
    finally {
        # Make sure that http endpoint is deleted.
        $rsConfig = Get-RsConfig -ComputerName $env:COMPUTERNAME
        Remove-HttpUrl -rsConfig $rsConfig -lcid $lcid
    }

    # Ensure SSRS endpoints to Use HTTPS
    Write-LogOutput $("Start configuring SSRS HTTPS endpoint with port: $httpsPort, cert: $sslCertThumbprint, FQDN: $serverFqdn")

    $httpsReportServerUrl = "https://" + $serverFqdn + ":" + $httpsPortValue.ToString() + "/ReportServer"
    Write-LogOutput $("HTTPS Report Server URL is: $httpsReportServerUrl")

    Confirm-SsrsHttpsEndpoints -Port $httpsPortValue -Log $log -SslCertThumbprint $sslCertThumbprint -ErrorAction Stop
    Write-LogOutput $("SSRS HTTPS endpoints were configured.")
    Write-LogOutput $("")
        
    # Added this step to warmup SSRS to avoid the timeout error
    $serviceName = Get-SsrsServiceName
    Write-LogOutput ("The reporting service name is: $serviceName.")
    Restart-WindowsService -log $log -computerName $env:COMPUTERNAME -serviceName $serviceName -ErrorAction stop
    $localHttpsReportServerUrl = $("https://localhost:" + $httpsPortValue.ToString() + "/ReportServer")
    Test-ReportServer -reportServerUri $localHttpsReportServerUrl -forceTls12 $true -ignoreServerCertificate $true -log $log -ErrorAction Stop
}


################################################################################
# Install Data source (for reporting that does not need deployment)
################################################################################
Function Install-DataSource {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$reportServerUri,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Test-ReportServer -reportServerUri $reportServerUri -log $log -ErrorAction Stop
    Write-LogOutput $("")
    Write-LogOutput $("Start creating report shared data sources...")

    Install-SharedDataSource -ErrorAction Stop | Tee-Object -Variable InstallLog
    Add-Content -Path $log -Value $InstallLog
    Write-LogOutput $("Completed creating report shared data sources.")
}

################################################################################
# Setup SSRS Extension by calling exe
################################################################################
Function Setup-SSRSExtension {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    $deployExtensionExe = Join-Path -Path $PSScriptRoot -ChildPath "AxReportVmRoleStartupTask.exe"
    Write-LogOutput $deployExtensionExe
    $allArgs = @("-config", $config)
    & $deployExtensionExe $allArgs *>&1
    $exitCode = $LASTEXITCODE
    if ($exitCode -ne 0) {
        throw "Error occurred when running AxReportVmRoleStartupTask.exe to install extension. Please see details in events for AX-ReportPVMSetup."
    }
}


################################################################################
# Uninstall AX SSRS extension components
# $config parameter must have a property EnableSecurity with value "true" or "false"
################################################################################
Function Remove-AxSsrsExtension {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize log file
    Initialize-Log $log

    # Verify deployment type
    $settings = Decode-Settings($config)
    Write-LogOutput $("")
    Write-LogOutput $settings | Format-List *
    if ($settings.EnableSecurity -eq $null) {
        throw "Missing EnableSecurity value."
    }

    [Switch]$enableSecurity = [System.Convert]::ToBoolean($settings.EnableSecurity)

    # Remove Https
    $removeHttps = $false
    if ($settings.RemoveHttps) {
        if (-not [System.Boolean]::TryParse($settings.RemoveHttps, [ref]$removeHttps)) {
            throw "RemoveHttps must be 'true' or 'false'."
        }
    }

    # Verify/Add IsUninstallOnly value (true) to the $config string
    Write-LogOutput $("Verify/Add IsUninstallOnly parameter to the config string...")
    [string]$uninstallConfig = $config
    if (Get-Member -InputObject $settings -Name "IsUninstallOnly" -MemberType Properties) {
        if (![string]::Equals($settings.IsUninstallOnly, "true", [System.StringComparison]::OrdinalIgnoreCase)) {
            throw "IsUninstallOnly must be true for uninstallation."
        }

        Write-LogOutput $("Completed verifying IsUninstallOnly value in the config string.")
    }
    else {
        $settings | Add-Member -type NoteProperty -Name "IsUninstallOnly" -Value "true"
        $uninstallConfig = ConvertTo-Json $settings
        $uninstallConfig = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($uninstallConfig))
        Write-LogOutput $("Completed Adding IsUninstallOnly value in the config string.")
    }

    # Use AxReportVmRoleStartupTask.exe to uninstall reporting extensions
    Write-LogOutput("")
    Write-LogOutput $("Start uninstalling AX SSRS extensions ...")

    $deployExtensionExe = Join-Path -Path $PSScriptRoot -ChildPath "AxReportVmRoleStartupTask.exe"
    $allArgs = @("-config", $uninstallConfig)
    & $deployExtensionExe $allArgs *>&1
    $exitCode = $LASTEXITCODE
    if ($exitCode -eq 0) {
        $serviceName = Get-SsrsServiceName
        Write-LogOutput ("The reporting service name is: $serviceName.")
        Restart-WindowsService -log $log -computerName $env:COMPUTERNAME -serviceName $serviceName -ErrorAction stop
        Write-LogOutput $("Completed uninstalling AX SSRS extension.")
    }
    else {
        throw "Error occurred when running AxReportVmRoleStartupTask.exe to uninstall extension. Please see details in events for AX-ReportPVMSetup."
    }

    if ($removeHttps) {
        Write-LogOutput "Start removing all https endpoints and SSL cert bindings"
        Config-SsrsEndpoint -Port 80 -Log $log -ErrorAction Stop
        Write-LogOutput "Completed removing all https endpoints and SSL cert bindings"
    }
}

################################################################################
# Format report publishing results
################################################################################
Function Format-PublishOutput {
    param
    (
        [Parameter(ValueFromPipeline = $true, Position = 0)] 
        $result
    )

    process {
        if ($result -is [Microsoft.Dynamics.AX.Framework.Management.Reports.ReportDeploymentResult]) {
            $result | Select-Object ReportName, ReportDeploymentStatus
        }
        else {
            $result
        }
    }
}


################################################################################
# Deploy AX reports To SSRS server
################################################################################
Function Deploy-AxReport {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize the log file
    Initialize-Log $log

    # Getting values from the JSON configuration string
    $settings = Decode-Settings($config)

    [string]$reportingServers = $settings."BiReporting.ReportingServers"
    [string]$packageInstallLocation = $settings."Microsoft.Dynamics.AX.AosConfig.AzureConfig.bindir"
    [string]$useHttps = $settings."BiReporting.SsrsUseHttps"
    [string]$httpsPort = $settings."BiReporting.SsrsHttpsPort"
    [string[]]$module = $settings.Module 
    [string[]]$reportName = $settings.ReportName

    # Popuate default values 
    Write-LogOutput $('Microsoft.Dynamics.AX.AosConfig.AzureConfig.bindir: ' + $packageInstallLocation)
    Write-LogOutput $('Module: ' + $module)
    Write-LogOutput $('ReportName: ' + $reportName)

    if ($module -eq $null -or $module.Length -eq 0) {
        $module = "*"
    }

    if ($reportName -eq $null -or $reportName.Length -eq 0) {
        $reportName = "*"
    }

    Write-LogOutput ""

    # Getting Protocol and Port for ReportServer service
    if ($useHttps -eq $true) {
        $rsUri = Get-ReportServerUri -UseHttps -HttpsPort $HttpsPort
    }
    else {
        $rsUri = Get-ReportServerUri -HttpsPort $HttpsPort
    }
    $protocol = $rsUri[0]
    $port = $rsUri[1]
    Write-LogOutput $("Protocol: $protocol; Port: $port")
    Write-LogOutput ""

    # Get report servers IP address
    $rsServers = Get-ReportServers -reportingServers $reportingServers
    
    # Test each SSRS instance prior to deploying any reports. If this is not successful
    # then there is no need to deploy reports.
    $reportServerUrls = @{}
    foreach ($rsServer in $rsServers) {
        [string]$rsUrl = $($protocol + $rsServer + ":" + $port.ToString() + "/ReportServer")
        Write-LogOutput $("Testing: $rsUrl ...")
        Test-ReportServer -reportServerUri $rsUrl -log $log -ErrorAction Stop
        $reportServerUrls.Add($rsServer, $rsUrl)
    }

    Write-LogOutput $("Testing reporting services completed.")
    Write-LogOutput ""
    
    # Start deploying reports
    Write-LogOutput $("Start deploying reports ...")
    $startTime = $(Get-Date)

    if ($rsServers.Count -eq 1) {
        [string]$ManagementAssemblyPath = Join-Path -Path $PSScriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
        Import-Module $ManagementAssemblyPath

        $err = @()
        Publish-AXReport -MaxDegreeOfParallelism 1 -ErrorAction Continue -ErrorVariable err -ReportName $reportName -SkipReportServerAdminCheck -ReportServerAddress $reportServerUrls.Values[0] -BinDirectory $PackageInstallLocation -DeleteExisting -Module $module *>&1 | Format-PublishOutput | Tee-Object -Variable DeployReportsLog
        Add-Content -Path $log -Value $($DeployReportsLog | Out-String)
        Write-LogOutput $(($(Get-Date) - $startTime).TotalSeconds.ToString() + " Seconds.")

        if ($err.Count -gt 0) {
            throw "Errors occured during report deployment."
        }
    }
    else {
        foreach ($rsServer in $rsServers) {
            Start-Job -Name $("Job" + $rsServer) -ScriptBlock {
                Param
                (
                    [Parameter(Mandatory = $true)]
                    [string]$scriptRoot,
                    [Parameter(Mandatory = $true)]
                    [string]$rsUrlArg,
                    [Parameter(Mandatory = $true)]
                    [string[]]$reportNameArg,
                    [Parameter(Mandatory = $true)]
                    [string]$packageInstallLocationArg,
                    [Parameter(Mandatory = $true)]
                    [string[]]$moduleArg
                )

                Import-Module "$scriptRoot\AosCommon.psm1" -Force -DisableNameChecking -ErrorAction Stop
                Import-Module "$scriptRoot\Reporting.psm1" -Force -DisableNameChecking -ErrorAction Stop

                [string]$ManagementAssemblyPath = Join-Path -Path $scriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
                Import-Module $ManagementAssemblyPath -ErrorAction Stop

                $err = @()
                Publish-AXReport -MaxDegreeOfParallelism 1 -ErrorAction Continue -ErrorVariable err -ReportName $reportNameArg -SkipReportServerAdminCheck -ReportServerAddress $rsUrlArg -BinDirectory $packageInstallLocationArg -DeleteExisting -Module $moduleArg *>&1 | Format-PublishOutput | Out-String -OutVariable DeployReportsLog
                return @{"Error" = $err; "Log" = $DeployReportsLog }
            } -ArgumentList $PSScriptRoot, $reportServerUrls[$rsServer], $reportName, $packageInstallLocation, $module
        }

        $allErrors = @() 
        foreach ($rsServer in $rsServers) {
            $job = Get-Job $("Job" + $rsServer)
            $jobResult = Receive-Job -Job $job -Wait -AutoRemoveJob

            if ($jobResult."Error") {
                $allErrors += $jobResult."Error"
            }

            if ($jobResult."Log") {
                Write-LogOutput $($jobResult."Log" | Out-String)
            }
        }

        Write-LogOutput $(($(Get-Date) - $startTime).TotalSeconds.ToString() + " Seconds.")

        foreach ($error in $allErrors) {
            if ($error.Count -gt 0) {
                throw @("Errors occured during report deployment for server " + $rsServer)
            }
        }
    }
}


################################################################################
# Remove all AX reports from SSRS server
################################################################################
Function Remove-AllAxReports {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize the log file
    Initialize-Log $log

    # Getting values from the JSON configuration string
    $settings = Decode-Settings($config)

    [string]$reportingServers = $settings."BiReporting.ReportingServers"
    [string[]]$reportName = $settings.ReportName
    [string]$useHttps = $settings."BiReporting.SsrsUseHttps"
    [string]$httpsPort = $settings."BiReporting.SsrsHttpsPort"    

    # populate default values
    Write-LogOutput $('ReportName: ' + $reportName)
    if ($reportName -eq $null -or $reportName.Length -eq 0) {
        $reportName = "*"
    }

    # Getting Protocol and Port for ReportServer service
    $protocol = "http://"
    $port = 80

    Write-LogOutput $("Getting the protocol and port ...")
    if (-not [String]::IsNullOrWhiteSpace($useHttps)) {
        $useHttpsValue = $false
        if (-not [Boolean]::TryParse($useHttps, [ref]$useHttpsValue)) {
            throw "Invalid value for BiReporting.SsrsUseHttps. The value is : $($useHttps)"
        }
    
        if ($useHttpsValue) {
            $protocol = "https://"
            $port = 443
            if (-not [String]::IsNullOrWhiteSpace($httpsPort)) {
                if (-not [Int32]::TryParse($httpsPort, [ref]$port)) {
                    throw "Invalid vlaue for BiReporting.SsrsHttpsPort. The value is: $httpsPort"
                }
            }
        }
    }
    
    Write-LogOutput $("Protocol: $protocol; Port: $port")
    Write-LogOutput ""    

    # split report servers string
    Write-LogOutput $('Reporting Servers: ' + $reportingServers)
    $rsServers = $reportingServers.Split(",", [System.StringSplitOptions]::RemoveEmptyEntries) | % { $_.trim() }
    $rsServers = $rsServers.Where({ -not [System.String]::IsNullOrWhiteSpace($_) })
    
    # Test each SSRS instance prior to deploying any reports. If this is not successful
    # then there is no need to deploy reports.
    $reportServerUrls = @{}
    foreach ($rsServer in $rsServers) {
        [string]$rsUrl = $($protocol + $rsServer + ":" + $port.ToString() + "/ReportServer")
        Write-LogOutput $("Testing: $rsUrl ...")
        Test-ReportServer -reportServerUri $rsUrl -log $log -ErrorAction Stop
        $reportServerUrls.Add($rsServer, $rsUrl)
    }
    Write-LogOutput $("Testing reporting services completed.")
    Write-LogOutput ""

    # Start clearing reports
    Write-LogOutput $("Start removing reports ....")
    $startTime = $(Get-Date)

    if ($rsServers.Count -eq 1) {
        [string]$ManagementAssemblyPath = Join-Path -Path $PSScriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
        Import-Module $ManagementAssemblyPath

        Remove-AxReport -ErrorAction Continue -MaxDegreeOfParallelism 1 -ReportName $reportName -ReportServerAddress $reportServerUrls.Values[0] *>&1 |  Format-PublishOutput | Tee-Object -Variable ReportRemovalLog
        Add-Content -Path $log -Value $ReportRemovalLog
        Write-LogOutput $(($(Get-Date) - $startTime).TotalSeconds.ToString() + " Seconds.")
    }
    else {
        foreach ($rsServer in $rsServers) {
            Start-Job -Name $("RmJob" + $rsServer) -ScriptBlock {
                Param
                (
                    [Parameter(Mandatory = $true)]
                    [string]$scriptRoot,
                    [Parameter(Mandatory = $true)]
                    [string]$rsUrlArg,
                    [Parameter(Mandatory = $true)]
                    [string[]]$reportNameArg
                )

                Import-Module "$scriptRoot\AosCommon.psm1" -Force -DisableNameChecking -ErrorAction Stop
                Import-Module "$scriptRoot\Reporting.psm1" -Force -DisableNameChecking -ErrorAction Stop

                [string]$ManagementAssemblyPath = Join-Path -Path $scriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
                Import-Module $ManagementAssemblyPath -ErrorAction Stop

                Remove-AxReport -ErrorAction Continue -MaxDegreeOfParallelism 1 -ReportName $reportNameArg -ReportServerAddress $rsUrlArg *>&1 | Format-PublishOutput | Out-String -OutVariable ReportRemovalLog
            } -ArgumentList $PSScriptRoot, $reportServerUrls[$rsServer], $reportName
        }

        foreach ($rsServer in $rsServers) {
            $job = Get-Job $("RmJob" + $rsServer)
            $jobResultLog = Receive-Job -Job $job -Wait -AutoRemoveJob

            if ($jobResultLog) {
                Write-LogOutput $($jobResultLog | Out-String)
            }
        }

        Write-LogOutput $(($(Get-Date) - $startTime).TotalSeconds.ToString() + " Seconds.")
    }
}

################################################################################
# Install report fonts to SSRS server
################################################################################
Function Install-Font {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$config,
        [Parameter(Mandatory = $true)]
        [string]$log,
        [Parameter(Mandatory = $true)]
        [string]$fontFilePath
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize the log file
    Initialize-Log $log

    # Start installing fonts
    Write-Output("")
    Write-LogOutput "Start installing fonts to SSRS ..."
    Write-LogOutput $("Font file path: " + $fontFilePath)
    $MangementAssemblyPath = Join-Path -Path $PSScriptRoot -ChildPath "Microsoft.Dynamics.AX.Framework.Management.dll"
    Import-Module $MangementAssemblyPath

    (Get-ChildItem -Path $fontFilePath -Filter "*.ttf").FullName | Install-ReportFont *>&1 | Tee-Object -Variable ReportFontLog
    Add-Content -Path $log -Value $ReportFontLog

    # Restart SSRS
    $serviceName = Get-SsrsServiceName
    Write-LogOutput ("The reporting service name is: $serviceName.")
    Get-Service -Name $serviceName | Restart-Service *>&1 | Tee-Object -Variable RestartSsrsLog
    Add-Content -Path $log -Value $RestartSsrsLog

    Write-LogOutput $("Completed installing fonts to SSRS.")
}

#####################################################
# Configure SSRS web service and web app protocols
#####################################################
function Config-SsrsEndpoint {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [int]$Port,
        [Parameter(Mandatory = $true)]
        [string]$log,
        [Parameter(Mandatory = $false)]
        [string]$SslCertThumbprint
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    $rsConfig = Get-RsConfig -ComputerName $env:COMPUTERNAME
    if (!$rsConfig) {
        throw "Could not find reporting service WMI object MSReportServer_ConfigurationSetting"
    }

    $lcid = [System.Globalization.CultureInfo]::GetCultureInfo('en-US').LCID
    $rsWebServiceAppName = 'ReportServerWebService'
    $reportManagerAppName = 'ReportServerWebApp'

    # Remove all existing ResvervedUrls
    $result = $rsConfig.ListReservedUrls()
    Verify-SsrsWmiCall -resultObject $result -methodName 'ListReservedurls'
    if ($result.Application) {
        for ($i = 0; $i -lt $result.Application.length; $i++) {
            $removeResult = $rsConfig.RemoveURL($result.Application[$i], $result.UrlString[$i], $lcid)
            Verify-SsrsWmiCall -resultObject $removeResult -methodName 'RemoveURL'
            Write-LogOutput $([string]::Format("Removed URL Application={0} UrlString= {1}", $result.Application[$i], $result.UrlString[$i]))
        }
    }

    $removedIpPorts = New-Object -TypeName System.Collections.Generic.HashSet[string]

    # Remove all SSL Certficate Bindings from Reporting Service
    $result = $rsConfig.ListSSLCertificateBindings($lcid)
    if ($result.Application) {
        for ($i = 0; $i -lt $result.Application.length; $i++) {
            $removeResult = $rsConfig.RemoveSSLCertificateBindings($result.Application[$i], $result.CertificateHash[$i], $result.IPAddress[$i], $result.Port[$i], $lcid)
            Verify-SsrsWmiCall -resultObject $removeResult -methodName 'RemoveSSLCertificateBindings'
            Write-LogOutput $([string]::Format("Removed SSL Binding Application={0} Certificate={1} IPAddress={2} Port={3}", $result.Application[$i], $result.CertificateHash[$i], $result.IPAddress[$i], $result.Port[$i]))

            Remove-SSLBindings -ipAddress $($result.IPAddress[$i]).ToString() -port $($result.Port[$i]).ToString() -removedIpPorts $removedIpPorts
        }
    }

    # Remove all SSL Certficate Bindings and ssl bindings from OS
    if ($SslCertThumbprint) {
        Remove-SSLBindings -ipAddress "0.0.0.0" -port $Port.ToString() -removedIpPorts $removedIpPorts
        Remove-SSLBindings -ipAddress "[::]" -port $Port.ToString() -removedIpPorts $removedIpPorts
        Remove-ReservedUrl -url "https://+:$($Port.ToString())/ReportServer/"
        Remove-ReservedUrl -url "https://+:$($Port.ToString())/Reports/"
    }

    # Reserve URL for web service and web app
    $urlString = $([string]::Format("http://+:{0}", $Port))
    if ($SslCertThumbprint) {
        $urlString = [string]::Format("https://+:{0}", $Port)
    }

    $rsConfig.SetVirtualDirectory($rsWebServiceAppName, "ReportServer", $lcid)
    $result = $rsConfig.ReserveURL($rsWebServiceAppName, $urlString, $lcid)
    Verify-SsrsWmiCall -resultObject $result -methodName 'ReserveURL'
    Write-LogOutput $([string]::Format("Reserved URL string {0} for {1}", $urlString, $rsWebServiceAppName))

    $rsConfig.SetVirtualDirectory($reportManagerAppName, "Reports", $lcid)
    $result = $rsConfig.ReserveURL($reportManagerAppName, $urlString, $lcid)
    Verify-SsrsWmiCall -resultObject $result -methodName 'ReserveURL'
    Write-LogOutput $([string]::Format("Reserved URL string {0} for {1}", $urlString, $reportManagerAppName))

    # Create SSL Certificate Bindings for web service and web app
    if ($SslCertThumbprint) {
        $ipV4Address = "0.0.0.0";
        $ipV6Address = "::";

        $result = $rsConfig.CreateSSLCertificateBinding($rsWebServiceAppName, $SslCertThumbprint, $ipV4Address, $Port, $lcid);
        Verify-SsrsWmiCall -resultObject $result -methodName 'CreateSSLCertificateBinding'
        Write-LogOutput $([string]::Format("Created SSL Certificate Binding Application={0} SslCertThumbPrint={1} IPAddress={2} Port={3}", $rsWebServiceAppName, $SslCertThumbprint, $ipV4Address, $Port))

        $result = $rsConfig.CreateSSLCertificateBinding($rsWebServiceAppName, $SslCertThumbprint, $ipV6Address, $Port, $lcid);
        Verify-SsrsWmiCall -resultObject $result -methodName 'CreateSSLCertificateBinding'
        Write-LogOutput $([string]::Format("Created SSL Certificate Binding Application={0} SslCertThumbPrint={1} IPAddress={2} Port={3}", $rsWebServiceAppName, $SslCertThumbprint, $ipV6Address, $Port))

        $result = $rsConfig.CreateSSLCertificateBinding($reportManagerAppName, $SslCertThumbprint, $ipV4Address, $Port, $lcid);
        Verify-SsrsWmiCall -resultObject $result -methodName 'CreateSSLCertificateBinding'
        Write-LogOutput $([string]::Format("Created SSL Certificate Binding Application={0} SslCertThumbPrint={1} IPAddress={2} Port={3}", $reportManagerAppName, $SslCertThumbprint, $ipV4Address, $Port))

        $result = $rsConfig.CreateSSLCertificateBinding($reportManagerAppName, $SslCertThumbprint, $ipV6Address, $Port, $lcid);
        Verify-SsrsWmiCall -resultObject $result -methodName 'CreateSSLCertificateBinding'
        Write-LogOutput $([string]::Format("Created SSL Certificate Binding Application={0} SslCertThumbPrint={1} IPAddress={2} Port={3}", $reportManagerAppName, $SslCertThumbprint, $ipV6Address, $Port))
    }

    #Restart reporting services service
    $serviceName = Get-SsrsServiceName
    Write-LogOutput ("The reporting service name is: $serviceName.")
    Restart-WindowsService -computerName $env:COMPUTERNAME -serviceName $serviceName -log $log
}

#####################################################
# Confirm SSRS web service and web app HTTPS endpoints
#####################################################
function Confirm-SsrsHttpsEndpoints {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)]
        [string]$SslCertThumbprint,
        [Parameter(Mandatory = $true)]
        [int]$Port,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    $rsConfig = Get-RsConfig -ComputerName $env:COMPUTERNAME
    if (!$rsConfig) {
        throw "Could not find reporting service WMI object MSReportServer_ConfigurationSetting"
    }

    $lcid = [System.Globalization.CultureInfo]::GetCultureInfo('en-US').LCID
    $rsWebServiceAppName = 'ReportServerWebService'
    $reportManagerAppName = 'ReportServerWebApp'

    # Test whether local HTTPS URL is configured
    $serviceName = Get-SsrsServiceName
    Write-LogOutput ("The reporting service name is: $serviceName.")
    Restart-WindowsService -log $log -computerName $env:COMPUTERNAME -serviceName $serviceName -ErrorAction stop
    $localHttpsReportServerUrl = $("https://localhost:" + $httpsPortValue.ToString() + "/ReportServer")

    $reconfigEndoints = $false
    $httpsEndpointExists = $true
    try {
        Test-ReportServer -reportServerUri $localHttpsReportServerUrl -forceTls12 $true -ignoreServerCertificate $true -log $log -ErrorAction Stop
    }
    catch {
        $httpsEndpointExists = $false
    }

    if (-not $httpsEndpointExists) {
        # No https endpoint, so there is no need to validate.
        $reconfigEndoints = $true
    }
    else {
        # Validate whether SSL Bindings need to be updated
        $expectedSslBindings = @{}
        $expectedSslBindings.Add($("$rsWebServiceAppName-0.0.0.0-$Port-$SslCertThumbprint"), 1)
        $expectedSslBindings.Add($("$rsWebServiceAppName-::-$Port-$SslCertThumbprint"), 1)
        $expectedSslBindings.Add($("$reportManagerAppName-0.0.0.0-$Port-$SslCertThumbprint"), 1)
        $expectedSslBindings.Add($("$reportManagerAppName-::-$Port-$SslCertThumbprint"), 1)
        
        $result = $rsConfig.ListSSLCertificateBindings($lcid)
        $foundBindingCount = 0
        if ($result.Application -and $result.Application.Length -eq $expectedSslBindings.Count) {
            for ($i = 0; $i -lt $result.Application.length; $i++) {
                $bindingString = $("$($result.Application[$i])-$($result.IPAddress[$i])-$($result.Port[$i])-$($result.CertificateHash[$i])")
                if ($expectedSslBindings.ContainsKey($bindingString)) {
                    Write-LogOutput "Found $bindingString"
                    $foundBindingCount++
                }
            }
        }

        if ($foundBindingCount -ne $expectedSslBindings.Count) {
            # SSL bindings do not have expected values, needs reconfig
            $reconfigEndoints = $true
        }
        else {
            # Remove all Http based URLs
            Write-LogOutput "SSRS reserved URLs and SSL bindings are already configured. Skip configuring."
            Remove-HttpUrl -rsConfig $rsConfig -lcid $lcid
        }
    }

    if ($reconfigEndoints) {
        Write-LogOutput "SSRS reserved URLs and SSL bindings need to be reconfigured."
        Config-SsrsEndpoint -Port $Port -log $log -SslCertThumbprint $SslCertThumbprint    
    }
}

####################################################################
# Remove SSL bindings from network configuration
####################################################################
function Remove-SSLBindings {
    param 
    (
        [Parameter(Mandatory = $true)]
        [string]$ipAddress,
        [Parameter(Mandatory = $true)]
        [string]$port,
        [Parameter(Mandatory = $true)]
        [AllowEmptyCollection()]
        [System.Collections.Generic.HashSet[string]]$removedIpPorts
    )

    if ($ipAddress -eq "::") {
        $ipAddress = "[::]"
    }

    $ipAddressWithPort = "$($ipAddress):$($port)"
    if (!$removedIpPorts.Contains($ipAddressWithPort)) {
        $showCommand = $("netsh http show sslcert ipport=$ipAddressWithPort")
        Write-LogOutput ""
        Write-LogOutput("Check SSL cert bindings from computer network config with '$showCommand")
        $netshResult = Invoke-Expression $showCommand -ErrorAction Stop
        Write-LogOutput $netshResult

        if ($netshResult.Count -ge 10) {
            $removeCommand = $("netsh http delete sslcert ipport=$ipAddressWithPort")
            Write-LogOutput("Remove SSL cert bindings from computer network config with '$removeCommand")
            $netshResult = Invoke-Expression $removeCommand -ErrorAction Stop
            $removedIpPorts.Add($ipAddressWithPort) | Out-Null
            Write-LogOutput $netshResult
        }
    }
}

####################################################################
# Remove reserved URL
####################################################################
function Remove-ReservedUrl {
    param 
    (
        [Parameter(Mandatory = $true)]
        [string]$url
    )

    $showCommand = $("netsh http show urlacl url=$url")
    Write-LogOutput ""
    Write-LogOutput $("Check reserved URL with $showCommand")
    $netshResult = Invoke-Expression $showCommand -ErrorAction Stop
    Write-LogOutput $netshResult

    if ($netshResult.Count -ge 6) {
        $removeCommand = $("netsh http delete urlacl url=$url")
        Write-LogOutput $("Remove reserved URL with $removeCommand")
        $netshResult = Invoke-Expression $removeCommand -ErrorAction Stop
        Write-LogOutput $netshResult
    }
}

####################################################################
# Remove all HTTP based URLs
####################################################################
function Remove-HttpUrl {
    param 
    (
        [Parameter(Mandatory = $true)]
        $rsConfig,
        [Parameter(Mandatory = $true)]
        $lcid
    )

    Write-LogOutput "Remove all HTTP URLs..."
    $result = $rsConfig.ListReservedUrls()
    Verify-SsrsWmiCall -resultObject $result -methodName 'ListReservedurls'
    if ($result.Application) {
        for ($i = 0; $i -lt $result.Application.length; $i++) {
            if ($result.UrlString[$i] -and $result.UrlString[$i].StartsWith("http://", [System.StringComparison]::OrdinalIgnoreCase)) {
                $removeResult = $rsConfig.RemoveURL($result.Application[$i], $result.UrlString[$i], $lcid)
                Verify-SsrsWmiCall -resultObject $removeResult -methodName 'RemoveURL'
                Write-LogOutput $([string]::Format("Removed URL Application={0} UrlString= {1}", $result.Application[$i], $result.UrlString[$i]))
            }
        }
    }    
}

####################################################################
# Verify SSRS WMI calls
####################################################################
function Verify-SsrsWmiCall {
    param
    (
        $resultObject,
        $methodName
    )

    if (!$resultObject) {
        throw $("Returned Null object when calling $methodName")
    }

    if ($resultObject.HRESULT -ne 0) {
        throw $("Error occured when calling {0}. HResult = {1} Error={2}" -f $methodName, $resultObject.HRESULT, $resultObject.Error)
    }
}


################################################################################
# Test the SSRS Server
################################################################################
Function Test-ReportServer {
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$reportServerUri,
        [Parameter(Mandatory = $false)]
        [bool]$ignoreServerCertificate = $false,
        [Parameter(Mandatory = $false)]
        [bool]$forceTls12 = $false,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    # Initialize the log file
    Initialize-Log $log

    Write-LogOutput $('Checking status of Report Server URL: ' + $reportServerUri)

    # Assume a 400 response code
    $statusCode = 400

    $MaxNumberOfRetries = 5
    $retry = 1
    while ($retry -le $MaxNumberOfRetries) {
        try {
            try {
                # Invoke the web request and get the status
                $uri = New-Object -TypeName System.Uri($reportServerUri)
                [System.Net.HttpWebRequest]$request = [System.Net.HttpWebRequest]::CreateHttp($uri)
        
                $request.Timeout = 5 * 60 * 1000 
                $request.UseDefaultCredentials = $true
                $request.KeepAlive = $false
                if ($ignoreServerCertificate) {
                    $request.ServerCertificateValidationCallback = { $true }
                }

                if ($forceTls12) {
                    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
                }

                [System.Net.WebResponse]$response = $request.GetResponse()
                $statusCode = $response.StatusCode
            }
            catch [System.Net.WebException] {
                Write-LogOutput $('Failure! Status check of Report Server URL: ' + $reportServerUri)
                Write-LogOutput $($_.Exception.ToString())

                throw "An exception of type System.Net.WebException occurred when making an http request to: " + $reportServerUri + ". Refer to the log file for more details."
            }
    
            # check the status code is 200 OK 
            if ($statusCode -eq 200) {
                Write-LogOutput $('Success! Status check of Report Server URL: ' + $reportServerUri)
            }
            else {
                Write-LogOutput $('Failure! Status check of Report Server URL: ' + $reportServerUri)
                Write-LogOutput $('StatusCode value: ' + $statusCode)

                throw "Http response contains StatusCode of " + $statusCode + ". Unable to communicate with the SQL Server ReportServer service."
            }
	
            break
        }
        catch {
            if ($retry -lt $MaxNumberOfRetries) {
                $retry++
                [int]$waitSeconds = 10
                Write-LogOutput $('Wait ' + $waitSeconds + ' seconds and retry count ' + $retry + '(' + $MaxNumberOfRetries + ')')
                Start-Sleep -Seconds $waitSeconds
            }
            else {
                throw
            }
        }
    }
}

################################################################################
# Restart a windows service
################################################################################
function Restart-WindowsService {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$log,
        [Parameter(Mandatory = $true)]
        [string]$computerName,
        [Parameter(Mandatory = $true)]
        [string]$serviceName
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    # Initialize log file
    Initialize-Log $log

    $retryCount = 3;

    while ($retryCount -gt 0) {
        Write-LogOutput $("Restart " + $serviceName + " service on " + $computerName + "...")
        
        try {
            Get-Service -ErrorAction Stop -Name $serviceName -ComputerName $computerName | Restart-Service -ErrorAction Stop -Force *>&1 | Tee-Object -Variable RestartServiceLog
            Add-Content -Path $log -Value $RestartServiceLog
            break
        }
        catch {
            $retryCount -= 1;
            if ($retryCount -le 0) {
                throw
            }

            Start-Sleep -Seconds 30
        }
    }

    Write-LogOutput $($serviceName + " service restarted on " + $computerName)
}

################################################################################
# Get reporting service config WMI object
################################################################################
function Get-RsConfig {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName
    )

    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    try {
        # SSRS 2022
        Write-Log "Searching SSRS 2022 WMI object"
        $rsconfig = Get-WmiObject -ErrorAction SilentlyContinue -namespace "root\Microsoft\SqlServer\ReportServer\RS_SSRS\v16\Admin" -class MSReportServer_ConfigurationSetting -ComputerName $ComputerName -Filter "InstanceName='SSRS'"
        
        if ($rsconfig) {
            Write-Log "Found SSRS 2022 WMI object"
            return $rsconfig
        }
 
        # SSRS 2019
        Write-Log "Searching SSRS 2019 WMI object"
        $rsconfig = Get-WmiObject -ErrorAction SilentlyContinue -namespace "root\Microsoft\SqlServer\ReportServer\RS_SSRS\v15\Admin" -class MSReportServer_ConfigurationSetting -ComputerName $ComputerName -Filter "InstanceName='SSRS'"
        
        if ($rsconfig) {
            Write-Log "Found SSRS 2019 WMI object"
            return $rsconfig
        }

        # SSRS 2016
        Write-Log "Searching SSRS 2016 WMI object"
        $rsconfig = Get-WmiObject -ErrorAction SilentlyContinue -namespace "root\Microsoft\SqlServer\ReportServer\RS_MSSQLSERVER\v13\Admin" -class MSReportServer_ConfigurationSetting -ComputerName $ComputerName -Filter "InstanceName='MSSQLSERVER'"
        
        if ($rsconfig) {
            Write-Log "Found SSRS 2019 WMI object"
            return $rsconfig
        }
    }
    catch {
        Write-Log $($_.Exception.ToString())
        throw "SSRS WMI object was not found."
    }

    return $rsconfig
}

################################################################################
# Set SSRS system report timeout limit
################################################################################
function Set-SsrsConfigurationInfoTimeout {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateRange(1800, 7200)]
        [int]$timeoutInSeconds,
        [Parameter(Mandatory = $true)]
        [string]$reportServerUri,
        [Parameter(Mandatory = $true)]
        [string]$configName
    )

    Set-SsrsConfigurationSystemProperty -reportServerUri $reportServerUri -propertyName $configName -propertyValue $timeoutInSeconds
}

################################################################################
# Set SSRS execution log days kept property value
################################################################################
function Set-SsrsExecutionLogDaysKept {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateRange(0, 60)]
        [int]$daysKept,
        [Parameter(Mandatory = $true)]
        [string]$reportServerUri
    )

    $propertyName = "ExecutionLogDaysKept"
    Set-SsrsConfigurationSystemProperty -reportServerUri $reportServerUri -propertyName $propertyName -propertyValue $daysKept
}

################################################################################
# Set SSRS system report timeout limit
################################################################################
function Set-SsrsConfigurationSystemProperty {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$reportServerUri,
        [Parameter(Mandatory = $true)]
        [string]$propertyName,
        [Parameter(Mandatory = $true)]
        [string]$propertyValue
    )

    $proxyUri = [System.IO.Path]::Combine($reportServerUri, "ReportService2005.asmx")
    $rsProxy = New-WebServiceProxy -Uri $proxyUri -UseDefaultCredential
    $systemProperties = $rsProxy.GetSystemProperties($null)
    if (-not $systemProperties) {
        throw "Could not get SSRS system properties"
    }

    $systemProperty = $systemProperties | Where-Object { $_.Name -eq $propertyName }
    if (-not $systemProperty) {
        throw "Could not find SSRS system property $propertyName"
    }

    # set system report timeout 
    $systemProperty.Value = $propertyValue
    $changeProperties = @($systemProperty)
    $rsProxy.SetSystemProperties($changeProperties)
}

################################################################################
# Get SSRS service name
################################################################################
function Get-SsrsServiceName {
    Import-Module "$PSScriptRoot\AosCommon.psm1" -DisableNameChecking

    $productNamePath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
    $productNameItem = 'ProductName'
    $ssrsPost2019ServiceName = 'SQLServerReportingServices'
    $ssrs2016ServiceName = 'ReportServer'
    $ssrsService = Get-Service -Name $ssrsPost2019ServiceName -ErrorAction SilentlyContinue
        
    if ($ssrsService) {
        Write-Log "The SSRS 2019 reporting services name is set to $ssrsService"
        $ssrsServiceName = $ssrsService.Name;
    }
    else {
        $ssrsService = Get-Service -Name $ssrs2016ServiceName -ErrorAction SilentlyContinue

        if ($ssrsService) {
            Write-Log "The SSRS 2016 reporting services name is set to $ssrsService"
            $ssrsServiceName = $ssrsService.Name;
        }
    }

    if ($ssrsServiceName) {
        Write-Log "The SSRS service name from reporting services is $ssrsServiceName."
        return $ssrsServiceName
    }
    else {
        Write-Log 'Looking up reporting services name from OS.'
        if (Test-Path -Path $productNamePath -PathType Container) {
            $productNamePathItem = Get-ItemProperty -Path $productNamePath 
            $productNameMember = Get-Member -InputObject $productNamePathItem -Name $productNameItem

            if ($productNameMember) {
                $productName = (Get-ItemProperty -Path $productNamePath -Name $productNameItem).$productNameItem
                Write-Log 'The product name is: ' + $productName
            }
        }

        if (($productName -eq 'Windows Server 2016 Datacenter') -or ($productName -eq 'Windows Server 2016 Standard')) {
            return $ssrs2016ServiceName
        }

        if (($productName -eq 'Windows Server 2019 Datacenter') -or ($productName -eq 'Windows Server 2019 Standard')) {
            return $ssrsPost2019ServiceName
        }

        if (($productName -eq 'Windows Server 2022 Datacenter') -or ($productName -eq 'Windows Server 2022 Standard')) {
            return $ssrsPost2019ServiceName
        }

        throw "The product name $productName is not a supported product"
    }
}

################################################################################
# Get Protocol and ports
################################################################################
Function Get-ReportServerUri {
    param 
    (
        [Parameter(Mandatory = $false)]
        [switch]$useHttps = $false,
        [Parameter(Mandatory = $false)]
        [int]$HttpsPort = 443
    )

    # Getting Protocol and Port for ReportServer service
    $protocol = "http://"
    $port = 80

    Write-Log $("Getting the protocol and port ...")
    if (-not [String]::IsNullOrWhiteSpace($useHttps)) {
        $useHttpsValue = $false
        if (-not [Boolean]::TryParse($useHttps, [ref]$useHttpsValue)) {
            throw "Invalid value for BiReporting.SsrsUseHttps. The value is : $($useHttps)"
        }
    
        if ($useHttpsValue) {
            $protocol = "https://"
            $port = 443
            if (-not [String]::IsNullOrWhiteSpace($httpsPort)) {
                if (-not [Int32]::TryParse($httpsPort, [ref]$port)) {
                    throw "Invalid vlaue for BiReporting.SsrsHttpsPort. The value is: $httpsPort"
                }
            }
        }
    }
    $reportServerUrls = @()
    $reportServerUrls += ($protocol, $port)

    return $reportServerUrls
}

################################################################################
# Get Reporting servers IP
################################################################################
Function Get-ReportServers {
    param
    (
        [Parameter(Mandatory = $false)]
        [string[]]$reportingServers = "127.0.0.1"
    )

    # Split report servers string
    Write-Log $('Reporting Servers: ' + $reportingServers)
    $rsServers = $reportingServers.Split(",", [System.StringSplitOptions]::RemoveEmptyEntries) | % { $_.trim() }
    $rsServers = $rsServers.Where({ -not [System.String]::IsNullOrWhiteSpace($_) })

    return $rsServers
}

################################################################################
# Validate the SSRS Service is up, if not start the service
################################################################################
Function Validate-ReportService {
    param
    (
        [Parameter(Mandatory = $false)]
        [string[]]$reportServerIP = "127.0.0.1",
        [Parameter(Mandatory = $false)]
        [switch]$UseHttps = $false,
        [Parameter(Mandatory = $false)]
        [int]$HttpsPort = 443,
        [Parameter(Mandatory = $true)]
        [string]$log
    )

    # Initialize the log file
    Initialize-Log $log

    # Getting Protocol and Port for ReportServer service
    if ($UseHttps) {
        $rsUri = Get-ReportServerUri -UseHttps -HttpsPort $HttpsPort
    }
    else {
        $rsUri = Get-ReportServerUri -HttpsPort $HttpsPort
    }

    $protocol = $rsUri[0]
    $port = $rsUri[1]
    Write-Log $("Protocol: $protocol; Port: $port")
    
    # Get report servers IP address
    $rsServers = Get-ReportServers -reportingServers $reportServerIP
    
    # Validate ReportServers 
    foreach ($rsServer in $rsServers) {
        [string]$rsUrl = $($protocol + $rsServer + ":" + $port.ToString() + "/ReportServer")
        Write-Log $("Testing: $rsUrl ...")

        #$computerName = [System.Net.Dns]::GetHostEntry($rsServer).HostName.Split('.')[0]
        $computerName = $computerName = Get-WmiObject win32_networkadapterconfiguration -filter "ipenabled = 'True'" -ComputerName $rsServer | foreach { $_.PSComputername }
        Write-Log ("Report Server Hostname: $computerName")

        # Check if the reporting service is not running, then restart it
        $serviceName = Get-SsrsServiceName
        $service = Get-Service -Name $serviceName -ComputerName $computerName
        $serviceStatus = $service.Status
        Write-Log ("The reporting service Status is: $serviceStatus.")

        if ($serviceStatus -ne 'Running') {
            $service | Set-Service -Status Running
            Write-Log 'Starting SSRS service'
            Start-Sleep -seconds 60
            if ($service.Status -eq 'Running') {
                Write-Log 'SSRS service is now Running'
            }

            # Test the SSRS report Server
            Test-ReportServer -reportServerUri $rsUrl -log $log -ErrorAction Stop
        }
    }   
}
# SIG # Begin signature block
# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCfa748biK4fIL5
# dcslLjS8bMtIXDYxXU+h/PbZEpTBuaCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIORbaQ/yhbrMNg1oDsoCwCsp
# XaRvWRJrqk8Sn2iZZKxyMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEATxEk9fehyFgfMm2Efh2508TfdR32QA1uIhoQWyjUkXsl2cmuZzvPAwjS
# K07wvv4TdRuA/nP3l4U9AOvy6JlLE01PKwfjWa570v0VAeb1+Di6BBHW+6cmk5u6
# Ce3FM9klPb5nD/uQ207aYvMB9iZeMMNM7eiGFbJOHpB3BYS0TNmag7amHdJ4krRr
# 5ns9Zdo5jPVEFgs8iyUKCCMkg/6vMdJyW2oUsXnZIqOAY59g/BPK0sTIIOJYQe1G
# g+mY2dYYPKq+7/ajRAeIFHkjJcIzstuMAcfivYY1Zoszru+K2KBgwjsJF2Y64z0/
# v3YVLO/Fd70h0tXAq5I0EFsya/3xiqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCAD0sqNu5JtE5R6vqcC6d6fbNvxJH5gkhjrTRMHJFrDjwIGaBjB1T1E
# GBMyMDI1MDUxMjE5MjY0NS4yNzZaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHtMIIHIDCCBQigAwIBAgITMwAAAgkIB+D5XIzmVQABAAACCTANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNTAxMzAxOTQy
# NTVaFw0yNjA0MjIxOTQyNTVaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDClEow9y4M3f1S9z1xtNEETwWL1vEiiw0oD7SXEdv4
# sdP0xsVyidv6I2rmEl8PYs9LcZjzsWOHI7dQkRL28GP3CXcvY0Zq6nWsHY2QamCZ
# FLF2IlRH6BHx2RkN7ZRDKms7BOo4IGBRlCMkUv9N9/twOzAkpWNsM3b/BQxcwhVg
# sQqtQ8NEPUuiR+GV5rdQHUT4pjihZTkJwraliz0ZbYpUTH5Oki3d3Bpx9qiPriB6
# hhNfGPjl0PIp23D579rpW6ZmPqPT8j12KX7ySZwNuxs3PYvF/w13GsRXkzIbIyLK
# EPzj9lzmmrF2wjvvUrx9AZw7GLSXk28Dn1XSf62hbkFuUGwPFLp3EbRqIVmBZ42w
# cz5mSIICy3Qs/hwhEYhUndnABgNpD5avALOV7sUfJrHDZXX6f9ggbjIA6j2nhSAS
# Iql8F5LsKBw0RPtDuy3j2CPxtTmZozbLK8TMtxDiMCgxTpfg5iYUvyhV4aqaDLwR
# BsoBRhO/+hwybKnYwXxKeeOrsOwQLnaOE5BmFJYWBOFz3d88LBK9QRBgdEH5CLVh
# 7wkgMIeh96cH5+H0xEvmg6t7uztlXX2SV7xdUYPxA3vjjV3EkV7abSHD5HHQZTrd
# 3FqsD/VOYACUVBPrxF+kUrZGXxYInZTprYMYEq6UIG1DT4pCVP9DcaCLGIOYEJ1g
# 0wIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFEmL6NHEXTjlvfAvQM21dzMWk8rSMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBcXnxvODwk4h/jbUBsnFlFtrSuBBZb7wSZ
# fa5lKRMTNfNlmaAC4bd7Wo0I5hMxsEJUyupHwh4kD5qkRZczIc0jIABQQ1xDUBa+
# WTxrp/UAqC17ijFCePZKYVjNrHf/Bmjz7FaOI41kxueRhwLNIcQ2gmBqDR5W4TS2
# htRJYyZAs7jfJmbDtTcUOMhEl1OWlx/FnvcQbot5VPzaUwiT6Nie8l6PZjoQsuxi
# asuSAmxKIQdsHnJ5QokqwdyqXi1FZDtETVvbXfDsofzTta4en2qf48hzEZwUvbkz
# 5smt890nVAK7kz2crrzN3hpnfFuftp/rXLWTvxPQcfWXiEuIUd2Gg7eR8QtyKtJD
# U8+PDwECkzoaJjbGCKqx9ESgFJzzrXNwhhX6Rc8g2EU/+63mmqWeCF/kJOFg2eJw
# 7au/abESgq3EazyD1VlL+HaX+MBHGzQmHtvOm3Ql4wVTN3Wq8X8bCR68qiF5rFas
# m4RxF6zajZeSHC/qS5336/4aMDqsV6O86RlPPCYGJOPtf2MbKO7XJJeL/UQN0c3u
# ix5RMTo66dbATxPUFEG5Ph4PHzGjUbEO7D35LuEBiiG8YrlMROkGl3fBQl9bWbgw
# 9CIUQbwq5cTaExlfEpMdSoydJolUTQD5ELKGz1TJahTidd20wlwi5Bk36XImzsH4
# Ys15iXRfAjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ
# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQB8
# 762rPTQd7InDCQdb1kgFKQkCRKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA68x6UDAiGA8yMDI1MDUxMjEzNDY1
# NloYDzIwMjUwNTEzMTM0NjU2WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDrzHpQ
# AgEAMAoCAQACAgmvAgH/MAcCAQACAhNvMAoCBQDrzcvQAgEAMDYGCisGAQQBhFkK
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
# KoZIhvcNAQELBQADggEBAE0kK8PhFWpqgeaSpdRTa901fB66CQCeSap/a0vFAIur
# utAdC37hqsieQlKrhtvdiI63qiQbq89gWNMdb5/Ahr3E5SqPwcIhEVm7ePqosUP8
# K3yW36Zxdl2bEAK0qtMf7YsXiC6mhazzDDIuOXKjVCup/C7dYJALAjyj2AbkCCsO
# C63RZA11iwXYSz7ARBswglUfuOBtAUwbWDXOSxDI64NstVjZHATAh9SpTzSzNiWx
# MwjerqLaZpE9hshpplF+xphS4o0FyGFnoqqqVz68GvvMnmlpGZUYpX6wVA9e8Awa
# IbfGUeIq6zwoqdh54f9y+OeSES+asV2eMhtt78C/50AxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAgkIB+D5XIzmVQABAAAC
# CTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCD69joUrRFJVwrHGu1xpnkLQVQXI3ztrkOEtVPyXY2h
# YzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIGgbLB7IvfQCLmUOUZhjdUqK
# 8bikfB6ZVVdoTjNwhRM+MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAIJCAfg+VyM5lUAAQAAAgkwIgQgK561J16PtcZ9PUIMTcSxQOOO
# iKDQoDF8ITLPNUTfA80wDQYJKoZIhvcNAQELBQAEggIAiZNRnAt0EjLc8WPSkZHO
# Ukvb/uR5IVWGAPNshLgGdFo5P2+Kwe+dxldARQxEPgA+1PTZ6GfBdTThcSwEfhO9
# LimY1TYuFukPFleCPGwGaR5wFEyuKh9VFvA/kyhj6tAYa+3DanzTrtolodoAKUU1
# mPpPSVMEizVs+cnHyow1BLrnoICCY2BpitS4tPkYjA+e82pQDL519o3ZHNdx6bIz
# JILW+4/wNkDr6uv+yBcn996B0PrvAgMjRxm9DTnBU2lwalR2TUjUwttwAyNRGXDJ
# 9JpiSs2NEE81VdT822eQrc8uquXvTsTlEwQpXIq1U8filBFlaCHHER29z+p8whr6
# ZbykbSuBOmi+DZGXi/sL4W/fdTkVjGn0OEaAE50nTp7fbvlAr+E9S1Ummia72zXN
# 31dx5A3TohL8gTrAgEggHmsxey5FPwDsja6C43SDTxb+rJG0FGygGqxaWvnx/5oB
# xx7hwjca7tJoHdldHKo23hUz6I8va5Utiu2V3XVm9TToFn/wx7zZchqO1cj+HtbQ
# NDHAMkkelEPUl3xZDYUOcaGMKz1JBjgirrknIiPZZg2lIpcAN5LSZ8A22axNyxCR
# Lz+XNyM6rh67//Kg9j5jjm7Ad0QMsMBZf09AAbyl9o6vQMQQXDj/VbPdwFEhencX
# b58Yb2nqT9xo/SzjYs10b2Y=
# SIG # End signature block
