次の方法で共有


オンプレミス環境の問題を解決するスクリプト

メモ

コミュニティの関心グループが Yammer から Microsoft Viva Engage に移行されました。 Viva Engage コミュニティに参加し、最新のディスカッションに参加するには、「 Finance and Operations Viva Engage Community へのアクセスを要求する 」フォームに入力し、参加するコミュニティを選択します。

この記事は、オンプレミス環境の問題を修正するために使用できるスクリプトの中央レポジトリとして機能します。 通常、これらのスクリプトは展開前スクリプト、または展開後スクリプトとして実行する必要があります。

オンプレミス環境における問題の解決方法の詳細については、 オンプレミス配置のトラブルシューティング を参照してください。

スクリプトの実行に関する環境の準備

  1. 展開前スクリプトと展開後スクリプトの実行をコンフィギュレーションします。 詳細については、ローカル エージェントの展開前スクリプトと展開後スクリプト を参照してください。

  2. 次のコードを Predeployment.ps1 に追加します。

    # This has to be filled out
    # $agentShare = '<Agent-share path>' # E.g '\\LBDContosoShare\agent''
    
    $agentShare = '\\servername\D365FFOAgent'
    Write-Output "AgentShare is set to $agentShare"
    
    # The scripts make the assumption that the wp folder only contains one folder for the environment name.
    # If you have multiple folders in there from older deployments, then please remove those.
    # It is not recommended to use the same agent share for multiple environments.
    
    #& $agentShare\scripts\TSG_SysClassRunner.ps1 -agentShare $agentShare
    
    #& $agentShare\scripts\TSG_UpdateFRDeployerConfig.ps1 -agentShare $agentShare
    
    #& $agentShare\scripts\TSG_WindowsAzureStorage.ps1 -agentShare $agentShare
    
    
    #& $agentShare\scripts\TSG_RemoveFilesFromZip.ps1 -agentShare $agentShare -filesToRemove 'Packages\TaxEngine\bin\Microsoft.Dynamics365.ElectronicReportingMapping.dll','Packages\TaxEngine\bin\Microsoft.Dynamics365.ElectronicReportingMapping.pdb','Packages\TaxEngine\bin\Microsoft.Dynamics365.ElectronicReportingServiceContracts.dll','Packages\TaxEngine\bin\Microsoft.Dynamics365.ElectronicReportingServiceContracts.pdb','Packages\TaxEngine\bin\Microsoft.Dynamics.ElectronicReporting.Instrumentation.dll','Packages\TaxEngine\bin\Microsoft.Dynamics.ElectronicReporting.Instrumentation.pdb','Packages\TaxEngine\bin\Microsoft.Dynamics365.LocalizationFrameworkCore.dll','Packages\TaxEngine\bin\Microsoft.Dynamics365.LocalizationFrameworkCore.pdb','Packages\TaxEngine\bin\Microsoft.Dynamics365.LocalizationFrameworkForAx.dll','Packages\TaxEngine\bin\Microsoft.Dynamics365.LocalizationFrameworkForAx.pdb'
    
    #& $agentShare\scripts\TSG_EnableGMSAForAOS.ps1 -agentShare $agentShare -gmsaAccount contoso\svc-AXSF$
    
    #& $agentShare\scripts\TSG_EnableDixfService.ps1 -agentShare $agentShare -gmsaAccount contoso\svc-Dixf$ -DMFShare "\\servername\dixf-share"
    
    #& $agentShare\scripts\TSG_DisableMRDeployment.ps1 -agentShare $agentShare
    
    # The following script (when enabled) configures HTTPS for SSRS, and enables reporting services to run under a gMSA account.
    # NOTE!!! If you have used an IP address in LCS for your SSRS server, update the IP address to a Fully Qualified Domain Name (FQDN) for the reporting server. This can be changed on the Environment page. Go to Maintain > Update settings.
    #& $agentShare\scripts\TSG_SSRSEnableHTTPS.ps1 -agentShare $agentShare -ssrsSslCertificateThumbprint "<ssrshttcertthumbprint>" -principalUserAccountName contoso\svc-reportsvc$
    
    # When enabled, the following script resolves a version mismatch issue with Microsoft.Identity.Client, allowing the Reporting Services app to install successfully.
    # & $agentShare\scripts\TSG_UpdateSSRSIdentityClient.ps1 -SSRSServers BI1,BI2 # Edit list of servers as needed, separated by a comma 
    
  3. この記事の該当する部分から、問題を修正するために必要なコードをコピーし、新しいファイルに貼り付けます。 このファイルを、 Predeployment.ps1 スクリプトが格納されているフォルダと同じフォルダに保存します。 ファイル名は、コードをコピーしたセクションのタイトルと同じである必要があります。 修正する必要があるその他の問題について、この手順を繰り返します。

  4. Predeployment.ps1 スクリプトで、前に追加したコードで、使用するスクリプトを呼び出す行のコメントを解除します。

TSG_SysClassRunner.ps1

次のスクリプトは、プラットフォームの一部のバージョンで SysClassRunner が実行される場合に発生する問題を修正するために使用されます。 この問題の詳細については、 SysClassRunner が正常に実行されません を参照してください。

param (
    [Parameter(Mandatory=$true)]
    [string]
    $agentShare = ''
)

$delete = @("Microsoft.Diagnostics.Tracing.TraceEvent.dll", "Microsoft.AI.Agent.Intercept.dll", "Microsoft.AI.DependencyCollector.dll", "Microsoft.AI.DependencyCollector.xml", "Microsoft.AI.PerfCounterCollector.dll", "Microsoft.AI.ServerTelemetryChannel.dll", "Microsoft.AI.ServerTelemetryChannel.xml", "Microsoft.AI.Web.dll", "Microsoft.AI.Web.xml", "Microsoft.AI.WindowsServer.dll","Microsoft.AI.WindowsServer.xml", "Microsoft.ApplicationInsights.dll", "Microsoft.ApplicationInsights.xml")

$ErrorActionPreference = "Stop"

$basePath = Get-ChildItem $agentShare\wp\*\StandaloneSetup-*\Apps |
    Select-Object -First 1 -Expand FullName

#Some customers experience an unexpected behavior with the previous command.
if($basePath -notmatch "\AOS")
{
    $basePath = Join-Path $basePath -ChildPath "\AOS" 
}

$basePath = Join-Path $basePath -ChildPath "AXServiceApp\AXSF\Code" 

if(!(Test-Path $basePath))
{
    Write-Error "Basepath: $basePath , not found" -Exception InvalidOperation
}

foreach( $file in $delete)
{
    if(Test-Path -Path "$basePath\$file")
    {
        Remove-Item -Path "$basePath\$file"
    }
}

$axConfig = Join-Path $basePath -ChildPath "AXService.exe.config"

if(!(Test-Path $axConfig))
{
    Write-Error "Unable to find AxService.exe.config in path: $axConfig" -Exception InvalidOperation
}

Write-Output "Found config: $axConfig"

[xml]$xml = get-content $axConfig
$xml.SelectNodes("//*[@name = 'Microsoft.AI.Agent.Intercept']/..") | ForEach-Object {
    $_.bindingRedirect.oldVersion = "0.0.0.0-2.4.0.0"
    $_.bindingRedirect.newVersion = "2.4.0.0"
}

if($xml.SelectNodes("//*[@name = 'Microsoft.ApplicationInsights']").Count -eq 0)
{
    [xml]$newNode = @"
    <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
        <assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.9.0.0" newVersion="2.9.0.0" />
    </dependentAssembly>
"@
    $xml.configuration.runtime.assemblyBinding.AppendChild($xml.ImportNode($newNode.dependentAssembly, $true))
    
}

$xml.save($axConfig)

Write-Output "TSG SysClassRunner script succeeded"

TSG_UpdateFRDeployerConfig.ps1

次のスクリプトは、プラットフォームの一部のバージョンで財務諸表が配置される場合に発生する問題を修正するために使用されます。 この問題の詳細については、ファイルまたはアセンブリ EntityFramework を読み込めませんでした を参照してください。

param (
    [Parameter(Mandatory=$true)]
    [string]
    $agentShare = ''
)

$frConfig = Get-ChildItem $agentShare\wp\*\StandaloneSetup-*\Apps\FR\Deployment\FinancialReportingDeployer.exe.config |
    Select-Object -First 1 -Expand FullName

if( -not $frConfig)
{
    Write-Output "Unable to find FinancialReportingDeployer.exe.Config"
    return
}

Write-Output "Found config: $frConfig"

[xml]$xml = get-content $frConfig

$nodeList = $xml.GetElementsByTagName("loadFromRemoteSources")

if($nodeList.Count -eq 0)
{
    # Create the node 
    $newNode = $xml.CreateNode("element","loadFromRemoteSources","")
    $newNode.SetAttribute("enabled","true")
    # Find the parent
    $nodeList = $xml.GetElementsByTagName("runtime")
    $runtimeNode = $nodeList[0]
    $runtimeNode.AppendChild($newNode)
    # Save doc
    $xml.save($frConfig)
    Write-Output "Inserted new node: "$newNode.Name
}
else
{
    $node = $nodeList[0]

    $attribute = $node.Attributes.GetNamedItem("enabled")

    if($attribute.Value -eq "true")
    {
        Write-Output "Node already exists: "$node.Name
    }

    else
    {
        Write-Output "Node already exists but attribute is incorrect: " $attribute.Name "is" $attribute.Value
    }
}

TSG_WindowsAzureStorage.ps1

次のスクリプトは、一部のバージョンのプラットフォームでファイルをダウンロードまたはエクスポートできない問題を解決するために使用されます。 このスクリプトは、アプリケーションのバージョン 10.0.35 以降では使用しないでください。

param (
    [Parameter(Mandatory=$true)]
    [string]
    $agentShare = ''
)
$ErrorActionPreference = "Stop"

$delete = @("Microsoft.WindowsAzure.Storage.dll", "Microsoft.WindowsAzure.Storage.xml")

$basePath = Get-ChildItem $agentShare\wp\*\StandaloneSetup-*\Apps |
    Select-Object -First 1 -Expand FullName

#Some customers experience an unexpected behavior with the previous command.
if($basePath -notmatch "\AOS")
{
    $basePath = Join-Path $basePath -ChildPath "\AOS" 
}

$basePath = Join-Path $basePath -ChildPath "AXServiceApp\AXSF\Code" 

if(!(Test-Path $basePath))
{
    Write-Error "Basepath: $basePath , not found" -Exception InvalidOperation
}

foreach( $file in $delete)
{
    if(Test-Path -Path "$basePath\$file")
    {
        Remove-Item -Path "$basePath\$file"
    }
}

Write-Output "TSG WindowsAzureStorage script succeeded"

TSG_RemoveFilesFromZip.ps1

次のスクリプトは、以前バージョン 10.0.5 から 10.0.9 を使用していた一部の顧客に発生する問題を修正するために使用されます。 準備プロセスの仕組みにより、TaxEngine フォルダーにはいくつかの古いバージョンの DLL が 残っていますが、新しいリリースでは、別のモジュールフォルダに移動されています。 このスクリプトを使用すると、DLL が AOS ノードに配置される前に、ダウンロードした資産から削除されます。

[CmdletBinding()]
param
(
    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({ Test-Path -Path $_ })]
    [string] $agentShare,

    [ValidateNotNullOrEmpty()]
    [string[]]$filesToRemove
)

#requires -Version 5
$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'
$files = @()

[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression')

if(! (Test-Path $AgentShare))
{
    throw "The path provided for the agentshare is not valid/accessible."
}

ForEach($file in $FilesToRemove)
{
    if(!$file.StartsWith('Packages'))
    {
        throw "The path of $file does not start with Packages."
    }
    $files += $file.Replace('\', '/')
} 

$basePath = Get-ChildItem $AgentShare\wp\*\StandaloneSetup-*\Apps | Select-Object -First 1 -Expand FullName

#Some customers experience an unexpected behavior with the previous command.
if($basePath -notmatch "\AOS")
{
    $basePath = Join-Path $basePath -ChildPath "\AOS" 
}

$basePath = Join-Path $basePath -ChildPath "AXServiceApp\AXSF\Code"
$zipfile = Join-Path $basePath -ChildPath "Packages.zip"

try
{
    $stream = New-Object IO.FileStream($zipfile, [IO.FileMode]::Open)
    $mode   = [IO.Compression.ZipArchiveMode]::Update
    $zip    = New-Object IO.Compression.ZipArchive($stream, $mode)

    ($zip.Entries | ? { $files -contains $_.FullName }) | % { $_.Delete() } | Write-Output
}
catch
{
    throw 'An Error Occurred'
}
finally
{
    $zip.Dispose()
    $stream.Close()
    $stream.Dispose() 
}

TSG_EnableGMSAForAOS.ps1

次のスクリプトは、AOS が Active Directory (AD) ユーザーからグループ管理サービス アカウント (gMSA) に実行されるアカウントを変更するために使用されます。

メモ

このスクリプトは、バージョン 10.0.17 でのみ使用できます。 gMSA アカウントでは使用できないため、プリンタを各 AOS ノードに再インストールする必要があります。 詳細については、オンプレミス環境でのネットワーク プリンター デバイスのインストール を参照してください。 このスクリプトは、アプリケーション バージョン 10.0.32 で動作するように更新されていますが、過去のアプリケーション バージョンでも動作します。

param (
    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({ Test-Path -Path $_ })]
    [string] $agentShare,

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

$ErrorActionPreference = "Stop"

$basePath = Get-ChildItem $agentShare\wp\*\StandaloneSetup-*\ |
    Select-Object -First 1 -Expand FullName

if(!(Test-Path $basePath))
{
    Write-Error "Basepath: $basePath , not found" -Exception InvalidOperation
}

$configJsonPath = "$basePath\config.json"

$configJson = Get-Content $configJsonPath | ConvertFrom-Json

$updatedComponents = @()
foreach ($component in $configJson.components)
{
    if($component.name -eq "AOS")
    {
        $component.parameters.infrastructure.principalUserAccountType.value = "ManagedServiceAccount"
        $component.parameters.infrastructure.principalUserAccountName.value = $gmsaAccount
    }

    if($component.name -eq "Bootstrap" -and $component.parameters.infrastructure)
    {
        $component.parameters.infrastructure.principalUserAccountType.value = "ManagedServiceAccount"
        $component.parameters.infrastructure.principalUserAccountName.value = $gmsaAccount
    }

    if($component.name -eq "ReportingServices")
    {
        $component.parameters.accountListToAccessReports.value = $gmsaAccount
    }

    $updatedComponents += $component
}

$configJson.components = $updatedComponents

$configJson | ConvertTo-Json -Depth 100 | Out-File $configJsonPath

Write-Output "Successfully updated the configuration for AOS gMSA execution."

TSG_EnableDixfService.ps1

次のスクリプトは、データ管理フレームワーク サービスを環境で有効化する際に使用します。

メモ

このスクリプトは、バージョン 10.0.32 でのみ使用できます。 さらに、バージョン 2.18.0 以降のインフラストラクチャ スクリプトが必要です。

param (
    [Parameter(Mandatory)]
    [string]
    $AgentShare,
	[Parameter(Mandatory)]
    [string]
    $DMFShare,
	[Parameter(Mandatory)]
    [string]
    $GmsaAccount
)

$ErrorActionPreference = "Stop"

$basePath = Get-ChildItem $AgentShare\wp\*\StandaloneSetup-*\ |
    Select-Object -First 1 -Expand FullName

if(!(Test-Path $basePath))
{
    Write-Error "Basepath: $basePath , not found" -Exception InvalidOperation
}

$configJsonPath = "$basePath\config.json"

$configJson = Get-Content $configJsonPath | ConvertFrom-Json

$updatedComponents = @()
foreach ($component in $configJson.components)
{
    if($component.name -eq "AOS")
    {
        $component.parameters.services.dmfServiceFabricService.value = "True"
        $component.parameters.services.dmfFileShare.value = $DMFShare

        $gatewayComponent = $configJson.components | Where-Object { $_.name -eq "Gateway" }
        $dmfUrl = "https://$($component.parameters.infrastructure.hostName.value):$($gatewayComponent.parameters.internalServiceEndpointPort.value)/DixfApplication/DixfService/DMFService/DMFServiceHelper.svc"

        $component.parameters.services.dmfServiceUrl.value = $dmfUrl
    }
    elseif($component.name -eq "Dixf")
    {
        $component.isEnabled = "True"
        $component.parameters.infrastructure.principalUserAccountType.value = "ManagedServiceAccount"
        $component.parameters.infrastructure.principalUserAccountName.value = $GmsaAccount
    }

    $updatedComponents += $component
}

$configJson.components = $updatedComponents

$configJson | ConvertTo-Json -Depth 100 | Out-File $configJsonPath

Write-Output "Successfully updated the configuration and enabled DixfService."

TSG_DisableMRDeployment.ps1

以下のスクリプトは、財務報告サービスが展開されないようにするために使用されます。

param (
    [Parameter(Mandatory)]
    [string]
    $AgentShare
)

$ErrorActionPreference = "Stop"

$basePath = Get-ChildItem $AgentShare\wp\*\StandaloneSetup-*\ |
    Select-Object -First 1 -Expand FullName

if(!(Test-Path $basePath))
{
    Write-Error "Basepath: $basePath , not found" -Exception InvalidOperation
}

$modulesJsonPath = "$basePath\SetupModules.json"

$modulesJson = Get-Content $modulesJsonPath | ConvertFrom-Json

Write-Host "Disabling FinancialReporting component..."
$modulesJson.components = $modulesJson.components | Where-Object name -ne "financialreporting"
$modulesJson | ConvertTo-Json -Depth 20 | Out-File $modulesJsonPath
Write-Host "Finished Disabling FinancialReporting component."

TSG_SSRSEnableHTTPS.ps1

以下のスクリプトは、古い環境で SSRS を HTTPS で構成し、新しい構成で使用される gMSA アカウントを有効にするために使用できます。

メモ

SSRS サーバーで LCS に IP アドレスを使用している場合は、それをレポートサーバーの完全修飾ドメイン名 (FQDN) に変更する必要があります。 これは、環境 ページの メンテナンス>設定の更新 で変更できます。

param (
    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({ Test-Path -Path $_ })]
    [string] $agentShare,

	[Parameter(Mandatory=$true)]
    [string]
    $ssrsSslCertificateThumbprint,
	
	[Parameter(Mandatory=$true)]
    [string]
    $principalUserAccountName
)

$ErrorActionPreference = "Stop"

$basePath = Get-ChildItem $agentShare\wp\*\StandaloneSetup-*\ |
    Select-Object -First 1 -Expand FullName

if(!(Test-Path $basePath))
{
    Write-Error "Basepath: $basePath , not found" -Exception InvalidOperation
}

$configJsonPath = "$basePath\config.json"

$configJson = Get-Content $configJsonPath | ConvertFrom-Json

$updatedComponents = @()
foreach ($component in $configJson.components)
{

    if($component.name -eq "AOS")
    {
        $component.parameters.biReporting.reportingServers.value = $component.parameters.biReporting.persistentVirtualMachineIPAddressSSRS.value
        $component.parameters.biReporting.ssrsUseHttps.value = $true
		$component.parameters.biReporting.ssrsHttpsPort.value = 443
    }

    if($component.name -eq "ReportingServices")
    {
        $component.parameters.enableSecurity.value = $true
        $component.parameters.ssrsSslCertificateThumbprint.value = $ssrsSslCertificateThumbprint
		$component.parameters.ssrsHttpsPort.value = 443
		$component.parameters.reportingServers.value = $component.parameters.ssrsServerFqdn.value
		$component.parameters.infrastructure.principalUserAccountType.value = "ManagedServiceAccount"
		$component.parameters.infrastructure.principalUserAccountName.value = $principalUserAccountName
    }

    $updatedComponents += $component
}

$configJson.components = $updatedComponents

$configJson | ConvertTo-Json -Depth 100 | Out-File $configJsonPath

Write-Output "Successfully updated the configuration HTTPS (443) for Reporting Services"

TSG_UpdateSSRSIdentityClient.ps1

次のスクリプトでは、Dynamics 365 Reporting Services拡張機能がインストールできない問題を解決します。 この問題は、Dynamics 365 にバンドルされている Microsoft.Identity.Client のバージョンが原因で発生します。 インストール中に、Reporting Services 構成によって SSRS DLL と構成ファイルが古いバインディング リダイレクト バージョンに更新され、互換性の問題が発生します。

メモ

この問題は、次のバージョンで解決されます。

  • 10.0.45 - プラットフォーム バージョン - 7.0.7690.82
  • 10.0.44 - プラットフォーム バージョン - 7.0.7606.173

メモ

スクリプトは、リモート実行のために WinRM に依存しています。 PowerShell スクリプトをリモートで実行できない場合は、$scriptContent 変数から埋め込まれたスクリプトの内容を抽出し、LBDUpdateSSRSClientIdentity.ps1 として BI ノードにローカルに保存できます。 その場合は、展開中に各 BI ノードでスクリプトを手動で実行する必要があります。

#
# This source code is freeware and is provided on an "as is" basis without warranties of any kind, 
# whether express or implied, including without limitation warranties that the code is free of defect, 
# fit for a particular purpose or non-infringing.  The entire risk as to the quality and performance of 
# the code is with the end user.
# 

param (
    [Parameter(Mandatory = $true)]
    [string]$SSRSServers = ''  
)

$remoteFolder = "C:\Temp"
$remoteScriptName = "LBDUpdateSSRSClientIdentity.ps1"
$remoteScriptPath = Join-Path $remoteFolder $remoteScriptName

$scriptContent = @'
#
# This source code is freeware and is provided on an "as is" basis without warranties of any kind, 
# whether express or implied, including without limitation warranties that the code is free of defect, 
# fit for a particular purpose or non-infringing.  The entire risk as to the quality and performance of 
# the code is with the end user.
# 

$exeToWatch = "AxReportVmRoleStartupTask.exe"
$maxWaitMinutes = 120
$checkInterval = 10          
$updateInterval = 5          
$binPath = "C:\Program Files\Microsoft SQL Server Reporting Services\SSRS\ReportServer\bin"
$webConfigPath = "C:\Program Files\Microsoft SQL Server Reporting Services\SSRS\ReportServer\web.config"
$dllFile = "Microsoft.Identity.Client.dll"
$exeConfigFile = "ReportingServicesService.exe.config"
$serviceName = "SQLServerReportingServices"

$dllPath = Join-Path $binPath $dllFile
$exeConfigPath = Join-Path $binPath $exeConfigFile

function Update-BindingRedirect {
    param (
        [string]$configPath,
        [string]$assemblyName = "Microsoft.Identity.Client",
        [string]$publicKeyToken = "0a613f4dd989e8ae",
        [string]$newVersion
    )

    $updated = $false

    if (-not (Test-Path $configPath)) {
        Write-Warning "Config not found: $configPath"
        return $false
    }

    [xml]$xml = Get-Content $configPath
    $nsMgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
    $nsMgr.AddNamespace("asm", "urn:schemas-microsoft-com:asm.v1")

    $node = $xml.SelectSingleNode("//asm:assemblyIdentity[@name='$assemblyName']/..", $nsMgr)
    if (-not $node) {
        Write-Warning "Could not find dependentAssembly for $assemblyName in $configPath"
        return $false
    }

    $bindingRedirect = $node.bindingRedirect
    if ($bindingRedirect) {
        $currentVersion = $bindingRedirect.newVersion
        if ($currentVersion -ne $newVersion) {
            Write-Host "Updating $assemblyName in $configPath from $currentVersion to $newVersion"
            $bindingRedirect.oldVersion = "0.0.0.0-$newVersion"
            $bindingRedirect.newVersion = $newVersion

            $backupPath = "$configPath.bak"
            Copy-Item $configPath $backupPath -Force
            $xml.Save($configPath)
            Write-Host "Saved updated config and created backup: $backupPath"
            $updated = $true
        } else {
            Write-Host "$assemblyName in $configPath is already up to date."
        }
    } else {
        Write-Warning "bindingRedirect not found in $configPath for $assemblyName"
    }

    return $updated
}

$waitedSeconds = 0
$maxWaitSeconds = $maxWaitMinutes * 60

Write-Host "Waiting for $exeToWatch to start (timeout: $maxWaitMinutes minutes)..."

while ($waitedSeconds -lt $maxWaitSeconds) {
    $running = Get-Process | Where-Object { $_.Name -eq [System.IO.Path]::GetFileNameWithoutExtension($exeToWatch) }
    if ($running) {
        Write-Host "$exeToWatch detected. Monitoring configs..."
        break
    }
    Start-Sleep -Seconds $checkInterval
    $waitedSeconds += $checkInterval
}

if (-not $running) {
    Write-Warning "$exeToWatch did not start within $maxWaitMinutes minutes. Exiting."
    exit 0
}

while (Get-Process -Name ([System.IO.Path]::GetFileNameWithoutExtension($exeToWatch)) -ErrorAction SilentlyContinue) {
    if (-Not (Test-Path $dllPath)) {
        Write-Warning "DLL not found: $dllPath. Skipping check..."
        Start-Sleep -Seconds $updateInterval
        continue
    }
    $dllVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($dllPath).FileVersion
    $updatedExeConfig = Update-BindingRedirect -configPath $exeConfigPath -newVersion $dllVersion
    $updatedWebConfig = Update-BindingRedirect -configPath $webConfigPath -newVersion $dllVersion

    if ($updatedExeConfig -or $updatedWebConfig) {
        Write-Host "Restarting Reporting Services ($serviceName)..."
        Restart-Service -Name $serviceName -Force
        Write-Host "Service restarted."
    }
    Start-Sleep -Seconds $updateInterval
}

Write-Host "$exeToWatch has exited. Script completed."

'@

# --- Split server list ---
$servers = $SSRSServers -split ',' | ForEach-Object { $_.Trim() }

foreach ($server in $servers) {
    Write-Host "`n--- Processing server: $server ---`n"

    try {
        Invoke-Command -ComputerName $server -ScriptBlock {
            $path = "C:\Temp"
            if (-not (Test-Path $path)) {
                New-Item -Path $path -ItemType Directory -Force | Out-Null
                Write-Host "Created folder: $path"
            } else {
                Write-Host "Folder already exists: $path"
            }
        }

        Invoke-Command -ComputerName $server -ScriptBlock {
            param ($remoteScriptPath, $content)
            Set-Content -Path $remoteScriptPath -Value $content -Force -Encoding UTF8
            Write-Host "Saved script to: $remoteScriptPath"
        } -ArgumentList $remoteScriptPath, $scriptContent

        # 3. Start the script in background
        Invoke-Command -ComputerName $server -ScriptBlock {
            param ($remoteScriptPath)
            Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass -File `"$remoteScriptPath`"" -WindowStyle Hidden
            Write-Host "Started script: $remoteScriptPath"
        } -ArgumentList $remoteScriptPath

        Write-Host "Successfully deployed and started script on $server"
    }
    catch {
        Write-Warning "Failed to process ${server}: $_"
    }
}

Write-Host "`nAll deployments completed."