Public/Install-sqmSsrsReportServer.ps1
|
<# .SYNOPSIS Installs SQL Server Reporting Services 2022 from a network share and automatically configures the instance afterwards. .DESCRIPTION Executes the following steps in sequence: [1] Check prerequisites - Administrator rights on the target computer - Installer (.exe or .msi) found in the configured share (SsrsInstallerPath) - SSRS not yet installed (skippable with -Force) [2] Installation - Copies the installer to a local temp directory (UNC paths are not directly supported as process start) - Runs the installer silently: SQLServerReportingServices.exe /quiet /IAcceptLicenseTerms /Edition=<Edition> /IAcceptLicenseTerms - Evaluates the exit code (0 = OK, 3010 = restart recommended) - Waits up to 60 seconds for the SSRS WMI namespace (service startup) [3] Configuration - Calls Set-sqmSsrsConfiguration with all passed configuration parameters (splatting). Parameters not passed use the defaults of Set-sqmSsrsConfiguration. The installer path is read preferably from the -InstallerPath parameter. If missing, Get-sqmConfig -Key 'SsrsInstallerPath' is used. If that is also not set, an error is thrown. .PARAMETER ComputerName Target computer. Default: $env:COMPUTERNAME (local). Remote installation via WinRM / PsRemoting is supported. .PARAMETER InstallerPath Full UNC or local path to the installation file (SQLServerReportingServices.exe or .msi). Overrides Get-sqmConfig -Key 'SsrsInstallerPath'. .PARAMETER Edition License edition for the silent parameter /Edition. Valid values: Eval, Developer, Expr, Web, Standard, Enterprise. Default: 'Developer'. .PARAMETER ProductKey Product key (25 characters). If specified, instead of -Edition the parameter /IAcceptLicenseTerms /PID:<Key> is used. .PARAMETER Force Perform installation even if SSRS is already installed. .PARAMETER SkipConfiguration Install only; do not call Set-sqmSsrsConfiguration. .PARAMETER InstanceName SSRS instance name. Passed to Set-sqmSsrsConfiguration. Default: 'MSSQLSERVER'. .PARAMETER DatabaseServer SQL Server for the ReportServer database. Passed to Set-sqmSsrsConfiguration. .PARAMETER DatabaseName Name of the ReportServer database. Default: 'ReportServer'. .PARAMETER ReportServerUrl URL for the ReportServer web service. Default: 'http://+:80/ReportServer'. .PARAMETER ReportsUrl URL for the reports portal. Default: 'http://+:80/Reports'. .PARAMETER ServiceAccount Windows service account for SSRS. .PARAMETER ServiceAccountPassword Password for -ServiceAccount (SecureString). .PARAMETER DatabaseAuthType Authentication for the DB connection: 'Windows' or 'SQL'. .PARAMETER DatabaseCredential PSCredential for SQL authentication (only with -DatabaseAuthType SQL). .PARAMETER EncryptionKeyFile Path for the encryption key backup (.snk). .PARAMETER EncryptionKeyPassword Password for the key backup (SecureString). .PARAMETER SkipDatabase Skip database configuration in Set-sqmSsrsConfiguration. .PARAMETER SkipUrls Skip URL configuration in Set-sqmSsrsConfiguration. .PARAMETER SkipServiceAccount Skip service account configuration in Set-sqmSsrsConfiguration. .PARAMETER SkipEncryptionKeyBackup Skip the key backup in Set-sqmSsrsConfiguration. .PARAMETER Credential PSCredential for the WinRM connection to the target computer (remote operation). .PARAMETER OutputPath Output directory for the configuration report. .PARAMETER WmiWaitSeconds Maximum wait time in seconds for the SSRS WMI namespace after installation. Default: 60. .PARAMETER ContinueOnError Do not treat configuration errors as terminating. .PARAMETER EnableException Throw exceptions immediately. .OUTPUTS [PSCustomObject] with the following fields: ComputerName, InstallerUsed, Edition, InstallExitCode, RebootRequired, InstallResult, ConfigResult, OverallStatus, Message .EXAMPLE Install-sqmSsrsReportServer Installs SSRS using the installer path stored in sqmConfig, Edition Developer, followed by full configuration with default values. .EXAMPLE Install-sqmSsrsReportServer ` -InstallerPath '\\srv-share\Software\SSRS2022\SQLServerReportingServices.exe' ` -Edition Standard ` -DatabaseServer 'SQL-AG-Listener' ` -ServiceAccount 'DOMAIN\svc_ssrs' ` -EncryptionKeyPassword (Read-Host -AsSecureString 'Key-Passwort') .EXAMPLE Install-sqmSsrsReportServer -SkipConfiguration -WhatIf Shows what would be installed without making any changes. .NOTES Prerequisites : Invoke-sqmLogging, Set-sqmSsrsConfiguration, local administrator rights Supported versions: SSRS 2022 (SQLServerReportingServices.exe) Exit Codes : 0 = Success, 3010 = Restart recommended, other = Error WMI Namespace : root\Microsoft\SqlServer\ReportServer #> function Install-sqmSsrsReportServer { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [OutputType([PSCustomObject])] param ( # ?? Zielrechner ???????????????????????????????????????????????????? [Parameter(Mandatory = $false)] [string]$ComputerName = $env:COMPUTERNAME, # ?? Installer-Quelle ??????????????????????????????????????????????? [Parameter(Mandatory = $false)] [string]$InstallerPath, [Parameter(Mandatory = $false)] [ValidateSet('Eval', 'Developer', 'Expr', 'Web', 'Standard', 'Enterprise')] [string]$Edition = 'Developer', [Parameter(Mandatory = $false)] [string]$ProductKey, # ?? Installations-Steuerung ???????????????????????????????????????? [Parameter(Mandatory = $false)] [switch]$Force, [Parameter(Mandatory = $false)] [switch]$SkipConfiguration, [Parameter(Mandatory = $false)] [int]$WmiWaitSeconds = 60, # ?? Konfigurations-Parameter (Durchreich an Set-sqmSsrsConfiguration) ?? [Parameter(Mandatory = $false)] [string]$InstanceName = 'MSSQLSERVER', [Parameter(Mandatory = $false)] [string]$DatabaseServer, [Parameter(Mandatory = $false)] [string]$DatabaseName = 'ReportServer', [Parameter(Mandatory = $false)] [string]$ReportServerUrl = 'http://+:80/ReportServer', [Parameter(Mandatory = $false)] [string]$ReportsUrl = 'http://+:80/Reports', [Parameter(Mandatory = $false)] [string]$ServiceAccount, [Parameter(Mandatory = $false)] [System.Security.SecureString]$ServiceAccountPassword, [Parameter(Mandatory = $false)] [ValidateSet('Windows', 'SQL')] [string]$DatabaseAuthType = 'Windows', [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential]$DatabaseCredential, [Parameter(Mandatory = $false)] [string]$EncryptionKeyFile, [Parameter(Mandatory = $false)] [System.Security.SecureString]$EncryptionKeyPassword, [Parameter(Mandatory = $false)] [switch]$SkipDatabase, [Parameter(Mandatory = $false)] [switch]$SkipUrls, [Parameter(Mandatory = $false)] [switch]$SkipServiceAccount, [Parameter(Mandatory = $false)] [switch]$SkipEncryptionKeyBackup, # ?? Verbindung / Ausgabe ??????????????????????????????????????????? [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false)] [string]$OutputPath = (Get-sqmConfig -Key 'OutputPath'), # ?? Fehlerbehandlung ??????????????????????????????????????????????? [Parameter(Mandatory = $false)] [switch]$ContinueOnError, [Parameter(Mandatory = $false)] [switch]$EnableException ) begin { $functionName = $MyInvocation.MyCommand.Name $isLocal = $ComputerName -in @($env:COMPUTERNAME, 'localhost', '127.0.0.1', '.') # ?? Rueckgabe-Objekt vorab befuellen ????????????????????????????????? $result = [PSCustomObject]@{ ComputerName = $ComputerName InstallerUsed = $null Edition = $Edition InstallExitCode = $null RebootRequired = $false InstallResult = 'NotStarted' ConfigResult = 'Skipped' OverallStatus = 'Unknown' Message = $null } # ?? Hilfsfunktion: einheitliches Fehlerhandling ??????????????????? function _Fail ([string]$msg) { Invoke-sqmLogging -Message $msg -FunctionName $functionName -Level 'ERROR' $result.OverallStatus = 'Failed' $result.Message = $msg if ($EnableException) { throw $msg } Write-Error $msg return $result } Invoke-sqmLogging -Message "Starte $functionName auf '$ComputerName'" ` -FunctionName $functionName -Level 'INFO' } process { # ?????????????????????????????????????????????????????????????????? # [1] Installer-Pfad aufloesen # ?????????????????????????????????????????????????????????????????? $effectiveInstaller = $InstallerPath if (-not $effectiveInstaller) { $effectiveInstaller = Get-sqmConfig -Key 'SsrsInstallerPath' } if ([string]::IsNullOrWhiteSpace($effectiveInstaller)) { return (_Fail ("Kein Installer-Pfad angegeben und 'SsrsInstallerPath' ist nicht in der " + "Modulkonfiguration gesetzt. Bitte -InstallerPath angeben oder " + "Set-sqmConfig -SsrsInstallerPath '<UNC-Pfad>' ausfuehren.")) } $installerExtension = [System.IO.Path]::GetExtension($effectiveInstaller).ToLower() if ($installerExtension -notin @('.exe', '.msi')) { return (_Fail "Installer-Datei '$effectiveInstaller' hat keine unterstuetzte Erweiterung (.exe / .msi).") } if (-not (Test-Path -LiteralPath $effectiveInstaller)) { return (_Fail "Installer-Datei nicht erreichbar: '$effectiveInstaller'") } $result.InstallerUsed = $effectiveInstaller Invoke-sqmLogging -Message "Installer: '$effectiveInstaller'" ` -FunctionName $functionName -Level 'INFO' # ?????????????????????????????????????????????????????????????????? # [2] Voraussetzungen pruefen # ?????????????????????????????????????????????????????????????????? # 2a. Administratorrechte (nur bei lokaler Ausfuehrung direkt pruefbar) if ($isLocal) { $principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { return (_Fail "Keine lokalen Administratorrechte - Installation nicht moeglich. PowerShell als Administrator starten.") } } # 2b. Ist SSRS bereits installiert? $ssrsAlreadyInstalled = $false try { $cimParams = @{ Namespace = 'root\Microsoft\SqlServer\ReportServer'; ErrorAction = 'Stop' } if (-not $isLocal) { $sessionOpts = New-CimSessionOption -Protocol Wsman $cimConnParams = @{ ComputerName = $ComputerName; SessionOption = $sessionOpts } if ($Credential) { $cimConnParams['Credential'] = $Credential } $checkSession = New-CimSession @cimConnParams -ErrorAction Stop $cimParams['CimSession'] = $checkSession } $nsCheck = Get-CimInstance @cimParams -ClassName '__NAMESPACE' -ErrorAction SilentlyContinue $ssrsAlreadyInstalled = $null -ne $nsCheck if (isset $checkSession) { Remove-CimSession $checkSession -ErrorAction SilentlyContinue } } catch { # Namespace nicht vorhanden = SSRS nicht installiert, das ist der Normalfall $ssrsAlreadyInstalled = $false } if ($ssrsAlreadyInstalled -and -not $Force) { $msg = "SSRS ist auf '$ComputerName' bereits installiert. " + "Verwenden Sie -Force um die Installation trotzdem durchzufuehren." Invoke-sqmLogging -Message $msg -FunctionName $functionName -Level 'WARNING' Write-Warning $msg $result.InstallResult = 'AlreadyInstalled' $result.OverallStatus = 'AlreadyInstalled' $result.Message = $msg # Wenn SSRS schon da und SkipConfiguration nicht gesetzt: # direkt zur Konfiguration springen if (-not $SkipConfiguration) { Invoke-sqmLogging -Message "ueberspringe Installation - fahre direkt mit Konfiguration fort." ` -FunctionName $functionName -Level 'INFO' # Goto Konfigurations-Block (ueber $skipInstall-Flag) } else { return $result } } # ?????????????????????????????????????????????????????????????????? # [3] Installation # ?????????????????????????????????????????????????????????????????? $skipInstall = ($ssrsAlreadyInstalled -and -not $Force) if (-not $skipInstall) { if ($PSCmdlet.ShouldProcess($ComputerName, "SSRS 2022 installieren von '$effectiveInstaller'")) { try { # Installer in lokales Temp kopieren # (UNC-Pfade koennen nicht direkt als Prozess gestartet werden) $tempDir = Join-Path $env:TEMP "SsrsInstall_$(Get-Random)" $tempInstaller = Join-Path $tempDir ([System.IO.Path]::GetFileName($effectiveInstaller)) New-Item -ItemType Directory -Path $tempDir -Force | Out-Null Invoke-sqmLogging -Message "Kopiere Installer nach '$tempInstaller'..." ` -FunctionName $functionName -Level 'INFO' Write-Host " [1/2] Installer kopieren von Share..." -ForegroundColor Gray Copy-Item -LiteralPath $effectiveInstaller -Destination $tempInstaller -Force -ErrorAction Stop # Silent-Parameter zusammenstellen # SSRS 2022 EXE: /quiet /IAcceptLicenseTerms /Edition=<X> # SSRS 2022 MSI: /quiet /norestart IACCEPTLICENSETERMS=YES $isMsi = $installerExtension -eq '.msi' if ($isMsi) { $installArgs = @( '/i', "`"$tempInstaller`"", '/quiet', '/norestart', 'IACCEPTLICENSETERMS=YES' ) if ($ProductKey) { $installArgs += "PIDKEY=$ProductKey" } else { $installArgs += "EDITION=$Edition" } $installExe = 'msiexec.exe' } else { $installArgs = @( '/quiet', '/IAcceptLicenseTerms', '/norestart' ) if ($ProductKey) { $installArgs += "/PID=$ProductKey" } else { $installArgs += "/Edition=$Edition" } $installExe = $tempInstaller } $argString = $installArgs -join ' ' Invoke-sqmLogging -Message "Starte Installation: $installExe $argString" ` -FunctionName $functionName -Level 'INFO' Write-Host " [2/2] SSRS installieren (silent)..." -ForegroundColor Gray $proc = Start-Process -FilePath $installExe ` -ArgumentList $installArgs ` -Wait -PassThru -NoNewWindow ` -ErrorAction Stop $result.InstallExitCode = $proc.ExitCode switch ($proc.ExitCode) { 0 { $result.InstallResult = 'OK' $result.RebootRequired = $false Write-Host " ? SSRS installiert (ExitCode 0)." -ForegroundColor Green Invoke-sqmLogging -Message "Installation erfolgreich (ExitCode 0)." ` -FunctionName $functionName -Level 'INFO' } 3010 { $result.InstallResult = 'OK' $result.RebootRequired = $true Write-Host " ? SSRS installiert (ExitCode 3010 - Neustart empfohlen)." -ForegroundColor Yellow Write-Warning "SSRS wurde installiert, ein Neustart wird empfohlen." Invoke-sqmLogging -Message "Installation erfolgreich, Neustart empfohlen (ExitCode 3010)." ` -FunctionName $functionName -Level 'WARNING' } default { throw "Installer beendet mit ExitCode $($proc.ExitCode). Installation fehlgeschlagen." } } } catch { $errMsg = "Installations-Fehler: $($_.Exception.Message)" Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level 'ERROR' $result.InstallResult = 'Failed' $result.OverallStatus = 'Failed' $result.Message = $errMsg if ($EnableException) { throw } Write-Error $errMsg return $result } finally { if (Test-Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } } # ?? WMI-Namespace abwarten ???????????????????????????????? # SSRS-Dienst braucht nach dem Installer-Start einige Sekunden # bis der WMI-Provider registriert ist. Invoke-sqmLogging -Message "Warte auf SSRS-WMI-Namespace (max. $WmiWaitSeconds s)..." ` -FunctionName $functionName -Level 'INFO' Write-Host " Warte auf SSRS-WMI-Namespace..." -ForegroundColor Gray $wmiReady = $false $waitStart = [datetime]::UtcNow $wmiNs = 'root\Microsoft\SqlServer\ReportServer' $wmiPollBase = @{ Namespace = $wmiNs; ClassName = '__NAMESPACE'; ErrorAction = 'SilentlyContinue' } if (-not $isLocal -and $checkSession) { $wmiPollBase['CimSession'] = $checkSession } while (-not $wmiReady -and ([datetime]::UtcNow - $waitStart).TotalSeconds -lt $WmiWaitSeconds) { Start-Sleep -Seconds 3 try { $ns = Get-CimInstance @wmiPollBase $wmiReady = $null -ne $ns } catch { $wmiReady = $false } } if (-not $wmiReady) { $warnMsg = "SSRS-WMI-Namespace nach $WmiWaitSeconds s noch nicht vorhanden. " + "Konfiguration wird trotzdem versucht." Invoke-sqmLogging -Message $warnMsg -FunctionName $functionName -Level 'WARNING' Write-Warning $warnMsg } else { Invoke-sqmLogging -Message "SSRS-WMI-Namespace verfuegbar nach $([int]([datetime]::UtcNow - $waitStart).TotalSeconds) s." ` -FunctionName $functionName -Level 'INFO' Write-Host " ? SSRS-WMI-Namespace bereit." -ForegroundColor Green } } else { # WhatIf-Pfad $result.InstallResult = 'WhatIf' Invoke-sqmLogging -Message "WhatIf: Installation wuerde gestartet werden." ` -FunctionName $functionName -Level 'INFO' } } # ?????????????????????????????????????????????????????????????????? # [4] Konfiguration via Set-sqmSsrsConfiguration # ?????????????????????????????????????????????????????????????????? if ($SkipConfiguration) { $result.ConfigResult = 'Skipped' $result.OverallStatus = $result.InstallResult $result.Message = "Installation: $($result.InstallResult) | Konfiguration: uebersprungen (-SkipConfiguration)." Invoke-sqmLogging -Message $result.Message -FunctionName $functionName -Level 'INFO' return $result } if ($result.InstallResult -eq 'WhatIf') { $result.ConfigResult = 'WhatIf' $result.OverallStatus = 'WhatIf' $result.Message = 'WhatIf: Installation und Konfiguration wuerden durchgefuehrt.' return $result } Write-Host "" Write-Host "[$ComputerName] Starte SSRS-Konfiguration..." -ForegroundColor Cyan Invoke-sqmLogging -Message "Starte Set-sqmSsrsConfiguration auf '$ComputerName'." ` -FunctionName $functionName -Level 'INFO' # Nur explizit uebergebene Parameter weiterreichen (kein ueberschreiben der # Standardwerte in Set-sqmSsrsConfiguration durch leere Werte) $configSplat = @{ ComputerName = $ComputerName InstanceName = $InstanceName DatabaseName = $DatabaseName ReportServerUrl = $ReportServerUrl ReportsUrl = $ReportsUrl DatabaseAuthType = $DatabaseAuthType } if ($PSBoundParameters.ContainsKey('DatabaseServer') -and $DatabaseServer) { $configSplat['DatabaseServer'] = $DatabaseServer } if ($PSBoundParameters.ContainsKey('ServiceAccount') -and $ServiceAccount) { $configSplat['ServiceAccount'] = $ServiceAccount } if ($PSBoundParameters.ContainsKey('ServiceAccountPassword') -and $ServiceAccountPassword) { $configSplat['ServiceAccountPassword'] = $ServiceAccountPassword } if ($PSBoundParameters.ContainsKey('DatabaseCredential') -and $DatabaseCredential) { $configSplat['DatabaseCredential'] = $DatabaseCredential } if ($PSBoundParameters.ContainsKey('EncryptionKeyFile') -and $EncryptionKeyFile) { $configSplat['EncryptionKeyFile'] = $EncryptionKeyFile } if ($PSBoundParameters.ContainsKey('EncryptionKeyPassword') -and $EncryptionKeyPassword) { $configSplat['EncryptionKeyPassword'] = $EncryptionKeyPassword } if ($PSBoundParameters.ContainsKey('Credential') -and $Credential) { $configSplat['Credential'] = $Credential } if ($PSBoundParameters.ContainsKey('OutputPath') -and $OutputPath) { $configSplat['OutputPath'] = $OutputPath } # Skip-Schalter if ($SkipDatabase) { $configSplat['SkipDatabase'] = $true } if ($SkipUrls) { $configSplat['SkipUrls'] = $true } if ($SkipServiceAccount) { $configSplat['SkipServiceAccount'] = $true } if ($SkipEncryptionKeyBackup) { $configSplat['SkipEncryptionKeyBackup'] = $true } if ($ContinueOnError) { $configSplat['ContinueOnError'] = $true } if ($EnableException) { $configSplat['EnableException'] = $true } try { $configResult = Set-sqmSsrsConfiguration @configSplat $result.ConfigResult = $configResult.OverallStatus $result.OverallStatus = switch ($true) { ($result.InstallResult -in @('OK', 'AlreadyInstalled') -and $configResult.OverallStatus -eq 'Success') { 'Success' } ($configResult.OverallStatus -eq 'PartialSuccess') { 'PartialSuccess' } default { 'Failed' } } $result.Message = "Installation: $($result.InstallResult) | " + "Konfiguration: $($configResult.OverallStatus) | " + $configResult.Message } catch { $errMsg = "Konfigurationsfehler: $($_.Exception.Message)" Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level 'ERROR' $result.ConfigResult = 'Failed' $result.OverallStatus = 'PartialSuccess' # Installiert, aber Konfig fehlgeschlagen $result.Message = "Installation: $($result.InstallResult) | Konfiguration: Failed | $errMsg" if ($EnableException) { throw } Write-Error $errMsg } Write-Host "" Write-Host "[$ComputerName] $functionName : $($result.OverallStatus)" ` -ForegroundColor $(if ($result.OverallStatus -eq 'Success') { 'Green' } else { 'Yellow' }) return $result } } |