param( [string]$RscUrl = "https://zf-prod.my.rubrik.com/", [string]$ServiceAccountFile = "C:\Rubrik\scripts\local.xml", [string]$LogPath = ".\RscSanityCheck-{0}.log" -f (Get-Date -Format "yyyyMMdd-HHmmss") ) #-----------------------------# # Helper: logging #-----------------------------# function Write-Log { param( [AllowEmptyString()][string]$Message = "" ) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff" $line = "[{0}] {1}" -f $timestamp, $Message $line | Out-File -FilePath $LogPath -Encoding UTF8 -Append Write-Host $line } #-----------------------------# # Helper: dump exception + inners #-----------------------------# function Write-ExceptionDetails { param( [Parameter(Mandatory=$true)][System.Exception]$Exception, [string]$Prefix = "EX" ) Write-Log "${Prefix}: Type = $($Exception.GetType().FullName)" Write-Log "${Prefix}: Message = $($Exception.Message)" Write-Log "${Prefix}: HResult = $($Exception.HResult)" if ($Exception.StackTrace) { Write-Log "${Prefix}: StackTrace:" $Exception.StackTrace -split "`r?`n" | ForEach-Object { Write-Log " $_" } } # Aggregate inner exceptions if ($Exception -is [System.AggregateException] -and $Exception.InnerExceptions) { $i = 0 foreach ($inner in $Exception.InnerExceptions) { Write-Log "${Prefix}: Inner[$i] ------------------------------" Write-ExceptionDetails -Exception $inner -Prefix "$Prefix.Inner[$i]" $i++ } } elseif ($Exception.InnerException) { Write-Log "${Prefix}: Inner ------------------------------" Write-ExceptionDetails -Exception $Exception.InnerException -Prefix "$Prefix.Inner" } } #-----------------------------# # Start log #-----------------------------# Write-Log "=== Rubrik RSC PowerShell Sanity Check ===" Write-Log "Log file: $LogPath" Write-Log "RSC URL: $RscUrl" Write-Log "SA file: $ServiceAccountFile" Write-Log "" if (-not (Test-Path -LiteralPath $ServiceAccountFile -PathType Leaf)) { Write-Log "WARNING: Service account file does not exist: $ServiceAccountFile" } else { Write-Log "Service account file found." } Write-Log "" #-----------------------------# # Environment info #-----------------------------# Write-Log "=== Environment ===" Write-Log "--- PowerShell Version Table ---" $PSVersionTable.GetEnumerator() | Sort-Object Name | ForEach-Object { Write-Log ("PSVersionTable.{0} = {1}" -f $_.Name, $_.Value) } try { $framework = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription Write-Log "RuntimeInformation.FrameworkDescription = $framework" } catch { Write-Log "Failed to get RuntimeInformation.FrameworkDescription" Write-ExceptionDetails $_.Exception "RuntimeInfo" } Write-Log "" Write-Log "--- TLS Settings ---" try { Write-Log ("Current SecurityProtocol = {0}" -f [Net.ServicePointManager]::SecurityProtocol) # Force TLS 1.2 for tests [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Write-Log ("Updated SecurityProtocol = {0}" -f [Net.ServicePointManager]::SecurityProtocol) } catch { Write-Log "Failed to read/set SecurityProtocol" Write-ExceptionDetails $_.Exception "TLS" } Write-Log "" Write-Log "--- TLS 1.2 Cipher Suites (if available) ---" try { if (Get-Command Get-TlsCipherSuite -ErrorAction SilentlyContinue) { $ciphers = Get-TlsCipherSuite | Where-Object { $_.Name -match 'TLS' } foreach ($c in $ciphers) { Write-Log ("Cipher: {0} | Protocols: {1} | Kex: {2} | CipherAlgo: {3} | HashAlgo: {4}" -f ` $c.Name, ($c.Protocols -join ","), $c.KeyExchangeAlgorithm, $c.CipherAlgorithm, $c.HashAlgorithm) } if (-not $ciphers) { Write-Log "No cipher suites returned by Get-TlsCipherSuite." } } else { Write-Log "Get-TlsCipherSuite not available on this OS/PowerShell." } } catch { Write-Log "Failed to enumerate cipher suites." Write-ExceptionDetails $_.Exception "Ciphers" } Write-Log "" Write-Log "--- Rubrik RSC PowerShell Module ---" try { $modules = @(Get-Module RubrikSecurityCloud -ListAvailable) if ($modules) { $modules | Sort-Object Version -Descending | ForEach-Object { Write-Log ("Module: {0} | Version: {1} | Path: {2}" -f $_.Name, $_.Version, $_.ModuleBase) } } else { Write-Log "RubrikSecurityCloud module not found in module path." } } catch { Write-Log "Failed to query Rubrik RSC module." Write-ExceptionDetails $_.Exception "Module" } #-----------------------------# # Test Invoke-WebRequest #-----------------------------# Write-Log "" Write-Log "=== Invoke-WebRequest Tests ===" function Test-Http { param( [Parameter(Mandatory=$true)][string]$Url, [string]$Name ) Write-Log "--- $Name : $Url ---" try { $resp = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 15 -ErrorAction Stop Write-Log "StatusCode: $($resp.StatusCode) $($resp.StatusDescription)" Write-Log "Headers:" foreach ($k in $resp.Headers.Keys) { Write-Log (" {0}: {1}" -f $k, $resp.Headers[$k]) } } catch { $isExpectedGraphQl405 = $Url.TrimEnd('/').EndsWith('/api/graphql') -and $_.Exception.Message -match '\(405\)' if ($isExpectedGraphQl405) { Write-Log "Invoke-WebRequest to $Url returned 405 Method Not Allowed (expected for GET on GraphQL endpoint)." } else { Write-Log "Invoke-WebRequest to $Url failed." Write-ExceptionDetails $_.Exception "InvokeWebRequest" } } } Test-Http -Url $RscUrl -Name "RSC Root" Test-Http -Url ($RscUrl.TrimEnd('/') + "/api/graphql") -Name "RSC GraphQL" #-----------------------------# # Test Connect-Rsc #-----------------------------# Write-Log "" Write-Log "=== Connect-Rsc Test ===" try { Import-Module RubrikSecurityCloud -ErrorAction Stop Write-Log "RubrikSecurityCloud imported successfully." } catch { Write-Log "Failed to import Rubrik RSC module." Write-ExceptionDetails $_.Exception "ImportModule" } try { if (-not (Test-Path -LiteralPath $ServiceAccountFile -PathType Leaf)) { throw "Service account file not found: $ServiceAccountFile" } Write-Log "Attempting Connect-Rsc with service account file..." $VerbosePreference = "Continue" Connect-Rsc -ServiceAccountFile $ServiceAccountFile -Verbose -ErrorAction Stop | Out-Null Write-Log "Connect-Rsc succeeded." } catch { Write-Log "Connect-Rsc failed." Write-ExceptionDetails $_.Exception "ConnectRsc" } Write-Log "" Write-Log "=== Sanity Check Complete ==="