335 lines
11 KiB
PowerShell
335 lines
11 KiB
PowerShell
param(
|
|
[string[]]$SqlInstance = @(),
|
|
[string]$OutputDirectory = (Join-Path $env:USERPROFILE "Desktop"),
|
|
[switch]$ScanProgramFiles
|
|
)
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
function Add-Section {
|
|
param([string]$Title)
|
|
|
|
$script:reportLines += ""
|
|
$script:reportLines += "============================================================"
|
|
$script:reportLines += $Title
|
|
$script:reportLines += "============================================================"
|
|
}
|
|
|
|
function Add-Line {
|
|
param([string]$Text = "")
|
|
|
|
$script:reportLines += $Text
|
|
}
|
|
|
|
function Read-RegistryValues {
|
|
param([string]$Path)
|
|
|
|
try {
|
|
if (Test-Path $Path) {
|
|
return Get-ItemProperty -Path $Path -ErrorAction Stop
|
|
}
|
|
}
|
|
catch {
|
|
Add-Line "Registry read failed: $Path :: $($_.Exception.Message)"
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Get-SageUninstallEntries {
|
|
$paths = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
|
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
|
)
|
|
|
|
foreach ($path in $paths) {
|
|
Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
|
|
Where-Object {
|
|
$_.DisplayName -and (
|
|
$_.DisplayName -match "Sage" -or
|
|
$_.Publisher -match "Sage"
|
|
)
|
|
} |
|
|
Select-Object DisplayName, DisplayVersion, Publisher, InstallDate, InstallLocation, UninstallString, PSPath
|
|
}
|
|
}
|
|
|
|
function Get-SageFileVersions {
|
|
$roots = @(
|
|
$env:ProgramFiles,
|
|
${env:ProgramFiles(x86)},
|
|
$env:ProgramData
|
|
) | Where-Object { $_ -and (Test-Path $_) }
|
|
|
|
foreach ($root in $roots) {
|
|
Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Name -match "Sage" } |
|
|
ForEach-Object {
|
|
Get-ChildItem -LiteralPath $_.FullName -Recurse -File -Include *.exe,*.dll -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.VersionInfo.ProductName -match "Sage" -or $_.VersionInfo.CompanyName -match "Sage" -or $_.Name -match "Sage" } |
|
|
Select-Object FullName,
|
|
@{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}},
|
|
@{Name="ProductVersion"; Expression={$_.VersionInfo.ProductVersion}},
|
|
@{Name="ProductName"; Expression={$_.VersionInfo.ProductName}},
|
|
@{Name="CompanyName"; Expression={$_.VersionInfo.CompanyName}}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-SqlInstancesFromRegistry {
|
|
$instanceRoots = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL",
|
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Microsoft SQL Server\Instance Names\SQL"
|
|
)
|
|
|
|
$instances = New-Object System.Collections.Generic.List[object]
|
|
|
|
foreach ($root in $instanceRoots) {
|
|
$values = Read-RegistryValues $root
|
|
if (-not $values) {
|
|
continue
|
|
}
|
|
|
|
$values.PSObject.Properties |
|
|
Where-Object { $_.Name -notlike "PS*" } |
|
|
ForEach-Object {
|
|
$instanceName = $_.Name
|
|
$instanceId = [string]$_.Value
|
|
$setupPaths = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceId\Setup",
|
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Microsoft SQL Server\$instanceId\Setup"
|
|
)
|
|
|
|
foreach ($setupPath in $setupPaths) {
|
|
$setup = Read-RegistryValues $setupPath
|
|
if ($setup) {
|
|
$instances.Add([pscustomobject]@{
|
|
InstanceName = $instanceName
|
|
InstanceId = $instanceId
|
|
Edition = $setup.Edition
|
|
Version = $setup.Version
|
|
PatchLevel = $setup.PatchLevel
|
|
ProductCode = $setup.ProductCode
|
|
SQLPath = $setup.SQLPath
|
|
SetupPath = $setupPath
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $instances
|
|
}
|
|
|
|
function Get-SqlServices {
|
|
Get-CimInstance Win32_Service -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Name -like "MSSQL*" -or $_.Name -like "SQLAgent*" -or $_.DisplayName -like "*SQL Server*" } |
|
|
Select-Object Name, DisplayName, State, StartMode, PathName
|
|
}
|
|
|
|
function Resolve-Sqlcmd {
|
|
$cmd = Get-Command sqlcmd.exe -ErrorAction SilentlyContinue
|
|
if ($cmd) {
|
|
return $cmd.Source
|
|
}
|
|
|
|
$candidates = @(
|
|
"$env:ProgramFiles\Microsoft SQL Server\Client SDK\ODBC\*\Tools\Binn\sqlcmd.exe",
|
|
"${env:ProgramFiles(x86)}\Microsoft SQL Server\Client SDK\ODBC\*\Tools\Binn\sqlcmd.exe",
|
|
"$env:ProgramFiles\Microsoft SQL Server\*\Tools\Binn\sqlcmd.exe",
|
|
"${env:ProgramFiles(x86)}\Microsoft SQL Server\*\Tools\Binn\sqlcmd.exe"
|
|
)
|
|
|
|
foreach ($candidate in $candidates) {
|
|
$match = Get-ChildItem -Path $candidate -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if ($match) {
|
|
return $match.FullName
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Invoke-SqlVersionQuery {
|
|
param(
|
|
[string]$SqlcmdPath,
|
|
[string]$Instance
|
|
)
|
|
|
|
$query = @"
|
|
SET NOCOUNT ON;
|
|
SELECT
|
|
@@VERSION AS FullVersion,
|
|
SERVERPROPERTY('ProductVersion') AS ProductVersion,
|
|
SERVERPROPERTY('ProductLevel') AS ProductLevel,
|
|
SERVERPROPERTY('Edition') AS Edition,
|
|
SERVERPROPERTY('EngineEdition') AS EngineEdition,
|
|
SERVERPROPERTY('MachineName') AS MachineName,
|
|
SERVERPROPERTY('ServerName') AS ServerName,
|
|
SERVERPROPERTY('InstanceName') AS InstanceName,
|
|
SERVERPROPERTY('Collation') AS Collation;
|
|
"@
|
|
|
|
$tempQuery = Join-Path $env:TEMP ("sql_version_query_{0}.sql" -f ([guid]::NewGuid().ToString("N")))
|
|
Set-Content -LiteralPath $tempQuery -Value $query -Encoding UTF8
|
|
|
|
try {
|
|
$output = & $SqlcmdPath -S $Instance -E -i $tempQuery -W -s "|" -b 2>&1
|
|
[pscustomobject]@{
|
|
Instance = $Instance
|
|
Success = $LASTEXITCODE -eq 0
|
|
Output = ($output -join [Environment]::NewLine)
|
|
}
|
|
}
|
|
catch {
|
|
[pscustomobject]@{
|
|
Instance = $Instance
|
|
Success = $false
|
|
Output = $_.Exception.Message
|
|
}
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $tempQuery -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
|
New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
|
|
|
|
$reportPath = Join-Path $OutputDirectory "Sage_SQL_Environment_$timestamp.txt"
|
|
$jsonPath = Join-Path $OutputDirectory "Sage_SQL_Environment_$timestamp.json"
|
|
$reportLines = New-Object System.Collections.Generic.List[string]
|
|
|
|
$computer = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue
|
|
$os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
|
|
$sageEntries = @(Get-SageUninstallEntries)
|
|
$sqlRegistryInstances = @(Get-SqlInstancesFromRegistry)
|
|
$sqlServices = @(Get-SqlServices)
|
|
$sageFileVersions = @()
|
|
|
|
if ($ScanProgramFiles) {
|
|
$sageFileVersions = @(Get-SageFileVersions)
|
|
}
|
|
|
|
$sqlcmdPath = Resolve-Sqlcmd
|
|
$queryInstances = New-Object System.Collections.Generic.List[string]
|
|
|
|
foreach ($instance in $SqlInstance) {
|
|
if (-not [string]::IsNullOrWhiteSpace($instance)) {
|
|
$queryInstances.Add($instance)
|
|
}
|
|
}
|
|
|
|
if ($queryInstances.Count -eq 0) {
|
|
$machineName = $env:COMPUTERNAME
|
|
foreach ($instance in $sqlRegistryInstances) {
|
|
if ($instance.InstanceName -eq "MSSQLSERVER") {
|
|
$queryInstances.Add("localhost")
|
|
}
|
|
else {
|
|
$queryInstances.Add("localhost\$($instance.InstanceName)")
|
|
}
|
|
}
|
|
}
|
|
|
|
$queryInstances = @($queryInstances | Select-Object -Unique)
|
|
$sqlQueryResults = @()
|
|
if ($sqlcmdPath -and $queryInstances.Count -gt 0) {
|
|
foreach ($instance in $queryInstances) {
|
|
$sqlQueryResults += Invoke-SqlVersionQuery -SqlcmdPath $sqlcmdPath -Instance $instance
|
|
}
|
|
}
|
|
|
|
Add-Section "Capture metadata"
|
|
Add-Line "Timestamp: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")"
|
|
Add-Line "Computer: $env:COMPUTERNAME"
|
|
Add-Line "User: $env:USERDOMAIN\$env:USERNAME"
|
|
Add-Line "Output text: $reportPath"
|
|
Add-Line "Output json: $jsonPath"
|
|
|
|
Add-Section "Windows / machine"
|
|
Add-Line "Manufacturer: $($computer.Manufacturer)"
|
|
Add-Line "Model: $($computer.Model)"
|
|
Add-Line "OS: $($os.Caption)"
|
|
Add-Line "OS Version: $($os.Version)"
|
|
Add-Line "OS Build: $($os.BuildNumber)"
|
|
Add-Line "Install date: $($os.InstallDate)"
|
|
|
|
Add-Section "Sage entries from installed programs"
|
|
if ($sageEntries.Count -eq 0) {
|
|
Add-Line "No Sage uninstall entries found."
|
|
}
|
|
else {
|
|
$sageEntries | Format-List | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
|
}
|
|
|
|
Add-Section "Sage file versions"
|
|
if (-not $ScanProgramFiles) {
|
|
Add-Line "Skipped. Re-run with -ScanProgramFiles for file version scan."
|
|
}
|
|
elseif ($sageFileVersions.Count -eq 0) {
|
|
Add-Line "No Sage file versions found under Program Files / ProgramData."
|
|
}
|
|
else {
|
|
$sageFileVersions | Sort-Object ProductName, ProductVersion, FullName | Format-Table -AutoSize | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
|
}
|
|
|
|
Add-Section "SQL Server instances from registry"
|
|
if ($sqlRegistryInstances.Count -eq 0) {
|
|
Add-Line "No SQL Server registry instances found."
|
|
}
|
|
else {
|
|
$sqlRegistryInstances | Format-List | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
|
}
|
|
|
|
Add-Section "SQL Server services"
|
|
if ($sqlServices.Count -eq 0) {
|
|
Add-Line "No SQL Server services found."
|
|
}
|
|
else {
|
|
$sqlServices | Format-Table -AutoSize | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
|
}
|
|
|
|
Add-Section "SQL Server live query"
|
|
Add-Line "sqlcmd path: $(if ($sqlcmdPath) { $sqlcmdPath } else { "not found" })"
|
|
if (-not $sqlcmdPath) {
|
|
Add-Line "Cannot query SQL Server live because sqlcmd.exe was not found."
|
|
}
|
|
elseif ($queryInstances.Count -eq 0) {
|
|
Add-Line "No SQL instances to query. Pass -SqlInstance server\instance if needed."
|
|
}
|
|
else {
|
|
foreach ($result in $sqlQueryResults) {
|
|
Add-Line ""
|
|
Add-Line "Instance: $($result.Instance)"
|
|
Add-Line "Success: $($result.Success)"
|
|
Add-Line $result.Output
|
|
}
|
|
}
|
|
|
|
$data = [pscustomobject]@{
|
|
CapturedAt = (Get-Date).ToString("o")
|
|
ComputerName = $env:COMPUTERNAME
|
|
UserName = "$env:USERDOMAIN\$env:USERNAME"
|
|
Windows = [pscustomobject]@{
|
|
Caption = $os.Caption
|
|
Version = $os.Version
|
|
BuildNumber = $os.BuildNumber
|
|
InstallDate = $os.InstallDate
|
|
}
|
|
SageUninstallEntries = $sageEntries
|
|
SageFileVersions = $sageFileVersions
|
|
SqlRegistryInstances = $sqlRegistryInstances
|
|
SqlServices = $sqlServices
|
|
SqlcmdPath = $sqlcmdPath
|
|
SqlQueryResults = $sqlQueryResults
|
|
}
|
|
|
|
$reportLines | Set-Content -LiteralPath $reportPath -Encoding UTF8
|
|
$data | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $jsonPath -Encoding UTF8
|
|
|
|
Write-Host "Created:"
|
|
Write-Host " $reportPath"
|
|
Write-Host " $jsonPath"
|