Files
powershell-scripts/remote-execution/Invoke-RemoteAssetCollection-HTTPS.ps1
cproudlock 62c0c7bb06 Initial commit: Organized PowerShell scripts for ShopDB asset collection
Structure:
- asset-collection/: Local PC data collection scripts
- remote-execution/: WinRM remote execution scripts
- setup-utilities/: Configuration and testing utilities
- registry-backup/: GE registry backup scripts
- winrm-https/: WinRM HTTPS certificate setup
- docs/: Complete documentation

Each folder includes a README with detailed documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 10:57:54 -05:00

560 lines
19 KiB
PowerShell

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Remotely executes asset collection script on shopfloor PCs using WinRM over HTTPS.
.DESCRIPTION
This script uses WinRM HTTPS to securely execute the Update-PC-CompleteAsset.ps1 script
on multiple shopfloor PCs. It handles:
1. Secure HTTPS connections using wildcard certificates
2. Automatic FQDN resolution from hostnames
3. Credential management for remote connections
4. Parallel execution across multiple PCs
5. Error handling and logging for remote operations
6. Collection of results from each remote PC
.PARAMETER HostnameList
Array of computer hostnames (without domain suffix).
.PARAMETER HostnameListFile
Path to a text file containing hostnames (one per line, without domain suffix).
.PARAMETER Domain
Domain suffix for FQDNs (e.g., "logon.ds.ge.com").
Will construct FQDNs as: hostname.domain
.PARAMETER Credential
PSCredential object for authenticating to remote computers.
If not provided, will prompt for credentials.
.PARAMETER MaxConcurrent
Maximum number of concurrent remote sessions (default: 5).
.PARAMETER Port
HTTPS port for WinRM (default: 5986).
.PARAMETER ProxyURL
URL for the warranty proxy server (passed to remote script).
.PARAMETER DashboardURL
URL for the dashboard API (passed to remote script).
.PARAMETER SkipWarranty
Skip warranty lookups on remote PCs (passed to remote script).
.PARAMETER LogPath
Path for log files (default: .\logs\remote-collection-https.log).
.PARAMETER TestConnections
Test remote HTTPS connections without running the full collection.
.PARAMETER ScriptPath
Path to the Update-PC-CompleteAsset.ps1 script on remote computers.
Default: C:\Scripts\Update-PC-CompleteAsset.ps1
.PARAMETER SkipCertificateCheck
Skip SSL certificate validation (not recommended for production).
.EXAMPLE
# Collect from specific hostnames
.\Invoke-RemoteAssetCollection-HTTPS.ps1 -HostnameList @("PC001", "PC002") -Domain "logon.ds.ge.com"
.EXAMPLE
# Collect from hostnames in file
.\Invoke-RemoteAssetCollection-HTTPS.ps1 -HostnameListFile ".\shopfloor-hostnames.txt" -Domain "logon.ds.ge.com"
.EXAMPLE
# Test HTTPS connections only
.\Invoke-RemoteAssetCollection-HTTPS.ps1 -HostnameList @("PC001") -Domain "logon.ds.ge.com" -TestConnections
.EXAMPLE
# Use stored credentials
$cred = Get-Credential
.\Invoke-RemoteAssetCollection-HTTPS.ps1 -HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" -Credential $cred
.NOTES
Author: System Administrator
Date: 2025-10-17
Version: 1.0
Prerequisites:
1. WinRM HTTPS must be configured on target computers (use Setup-WinRM-HTTPS.ps1)
2. Wildcard certificate installed on target computers
3. PowerShell 5.1 or later
4. Update-PC-CompleteAsset.ps1 must be present on target computers
5. Credentials with admin rights on target computers
6. Network connectivity to target computers on port 5986
Advantages over HTTP WinRM:
- Encrypted traffic (credentials and data)
- No TrustedHosts configuration required
- Better security posture for production environments
#>
param(
[Parameter(Mandatory=$false)]
[string[]]$HostnameList = @(),
[Parameter(Mandatory=$false)]
[string]$HostnameListFile,
[Parameter(Mandatory=$true)]
[string]$Domain,
[Parameter(Mandatory=$false)]
[PSCredential]$Credential,
[Parameter(Mandatory=$false)]
[int]$MaxConcurrent = 5,
[Parameter(Mandatory=$false)]
[int]$Port = 5986,
[Parameter(Mandatory=$false)]
[string]$ProxyURL = "http://10.48.130.158/vendor-api-proxy.php",
[Parameter(Mandatory=$false)]
[string]$DashboardURL = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp",
[Parameter(Mandatory=$false)]
[switch]$SkipWarranty = $true,
[Parameter(Mandatory=$false)]
[string]$LogPath = ".\logs\remote-collection-https.log",
[Parameter(Mandatory=$false)]
[switch]$TestConnections = $false,
[Parameter(Mandatory=$false)]
[string]$ScriptPath = "C:\Scripts\Update-PC-CompleteAsset.ps1",
[Parameter(Mandatory=$false)]
[switch]$SkipCertificateCheck = $false
)
# =============================================================================
# SSL/TLS Certificate Bypass for HTTPS connections
# =============================================================================
try {
if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy').Type) {
Add-Type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
}
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
} catch { }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Initialize logging
function Initialize-Logging {
param([string]$LogPath)
$logDir = Split-Path $LogPath -Parent
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $LogPath -Value "[$timestamp] Remote asset collection (HTTPS) started"
}
function Write-Log {
param([string]$Message, [string]$LogPath, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] [$Level] $Message"
Add-Content -Path $LogPath -Value $logEntry
switch ($Level) {
"ERROR" { Write-Host $logEntry -ForegroundColor Red }
"WARN" { Write-Host $logEntry -ForegroundColor Yellow }
"SUCCESS" { Write-Host $logEntry -ForegroundColor Green }
default { Write-Host $logEntry -ForegroundColor White }
}
}
function Get-ComputerTargets {
param([string[]]$HostnameList, [string]$HostnameListFile, [string]$Domain)
$hostnames = @()
# Add hostnames from direct list
if ($HostnameList.Count -gt 0) {
$hostnames += $HostnameList
}
# Add hostnames from file
if (-not [string]::IsNullOrEmpty($HostnameListFile)) {
if (Test-Path $HostnameListFile) {
$fileHostnames = Get-Content $HostnameListFile |
Where-Object { $_.Trim() -ne "" -and -not $_.StartsWith("#") } |
ForEach-Object { $_.Trim() }
$hostnames += $fileHostnames
} else {
Write-Log "Hostname list file not found: $HostnameListFile" $LogPath "ERROR"
}
}
# Remove duplicates and construct FQDNs
$fqdns = $hostnames |
Sort-Object -Unique |
ForEach-Object {
$hostname = $_.Trim()
# Remove domain if already present
if ($hostname -like "*.$Domain") {
$hostname
} else {
"$hostname.$Domain"
}
}
return $fqdns
}
function Resolve-ComputerIP {
param([string]$FQDN)
try {
$result = Resolve-DnsName -Name $FQDN -Type A -ErrorAction Stop
if ($result -and $result[0].IPAddress) {
return $result[0].IPAddress
}
return $null
}
catch {
return $null
}
}
function Test-WinRMHTTPSConnection {
param([string]$ComputerName, [PSCredential]$Credential, [int]$Port, [bool]$SkipCertCheck)
try {
$sessionOptions = New-PSSessionOption -SkipCACheck:$SkipCertCheck -SkipCNCheck:$SkipCertCheck
$session = New-PSSession -ComputerName $ComputerName `
-Credential $Credential `
-UseSSL `
-Port $Port `
-SessionOption $sessionOptions `
-ErrorAction Stop
Remove-PSSession $session
return $true
}
catch {
return $false
}
}
function Test-RemoteScriptExists {
param([string]$ComputerName, [PSCredential]$Credential, [string]$ScriptPath, [int]$Port, [bool]$SkipCertCheck)
try {
$sessionOptions = New-PSSessionOption -SkipCACheck:$SkipCertCheck -SkipCNCheck:$SkipCertCheck
$result = Invoke-Command -ComputerName $ComputerName `
-Credential $Credential `
-UseSSL `
-Port $Port `
-SessionOption $sessionOptions `
-ScriptBlock {
param($Path)
Test-Path $Path
} -ArgumentList $ScriptPath
return $result
}
catch {
return $false
}
}
function Invoke-RemoteAssetScript {
param(
[string]$ComputerName,
[PSCredential]$Credential,
[string]$ScriptPath,
[string]$ProxyURL,
[string]$DashboardURL,
[bool]$SkipWarranty,
[int]$Port,
[bool]$SkipCertCheck,
[string]$LogPath
)
try {
Write-Log "Starting asset collection on $ComputerName (HTTPS)" $LogPath "INFO"
$sessionOptions = New-PSSessionOption -SkipCACheck:$SkipCertCheck -SkipCNCheck:$SkipCertCheck
# Execute the script remotely
$result = Invoke-Command -ComputerName $ComputerName `
-Credential $Credential `
-UseSSL `
-Port $Port `
-SessionOption $sessionOptions `
-ScriptBlock {
param($ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty)
# Change to script directory
$scriptDir = Split-Path $ScriptPath -Parent
Set-Location $scriptDir
# Build parameters
$params = @{
ProxyURL = $ProxyURL
DashboardURL = $DashboardURL
}
if ($SkipWarranty) {
$params.SkipWarranty = $true
}
# Execute the script and capture output
try {
& $ScriptPath @params
return @{
Success = $true
Output = "Script completed successfully"
Error = $null
}
}
catch {
return @{
Success = $false
Output = $null
Error = $_.Exception.Message
}
}
} -ArgumentList $ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty
if ($result.Success) {
Write-Log "Asset collection completed successfully on $ComputerName" $LogPath "SUCCESS"
return @{ Success = $true; Computer = $ComputerName; Message = $result.Output }
} else {
Write-Log "Asset collection failed on $ComputerName: $($result.Error)" $LogPath "ERROR"
return @{ Success = $false; Computer = $ComputerName; Message = $result.Error }
}
}
catch {
$errorMsg = "Failed to execute on $ComputerName: $($_.Exception.Message)"
Write-Log $errorMsg $LogPath "ERROR"
return @{ Success = $false; Computer = $ComputerName; Message = $errorMsg }
}
}
function Show-SetupInstructions {
param([string]$Domain)
Write-Host "`n=== WinRM HTTPS Setup Instructions ===" -ForegroundColor Cyan
Write-Host ""
Write-Host "On each target computer, run the Setup-WinRM-HTTPS.ps1 script:" -ForegroundColor Yellow
Write-Host ""
Write-Host " # With certificate PFX file:" -ForegroundColor Gray
Write-Host " `$certPass = ConvertTo-SecureString 'Password' -AsPlainText -Force" -ForegroundColor White
Write-Host " .\Setup-WinRM-HTTPS.ps1 -CertificatePath 'C:\Certs\wildcard.pfx' ``" -ForegroundColor White
Write-Host " -CertificatePassword `$certPass -Domain '$Domain'" -ForegroundColor White
Write-Host ""
Write-Host " # Or with existing certificate:" -ForegroundColor Gray
Write-Host " .\Setup-WinRM-HTTPS.ps1 -CertificateThumbprint 'THUMBPRINT' -Domain '$Domain'" -ForegroundColor White
Write-Host ""
Write-Host "This will:" -ForegroundColor Yellow
Write-Host " 1. Install/locate the wildcard certificate" -ForegroundColor White
Write-Host " 2. Create HTTPS listener on port 5986" -ForegroundColor White
Write-Host " 3. Configure Windows Firewall" -ForegroundColor White
Write-Host " 4. Enable WinRM service" -ForegroundColor White
Write-Host ""
}
# Main execution
try {
Write-Host "=== Remote Asset Collection Script (HTTPS) ===" -ForegroundColor Cyan
Write-Host "Starting at $(Get-Date)" -ForegroundColor Gray
Write-Host ""
# Initialize logging
Initialize-Logging -LogPath $LogPath
# Get target computers
$fqdns = Get-ComputerTargets -HostnameList $HostnameList -HostnameListFile $HostnameListFile -Domain $Domain
if ($fqdns.Count -eq 0) {
Write-Log "No target computers specified. Use -HostnameList or -HostnameListFile parameter." $LogPath "ERROR"
Show-SetupInstructions -Domain $Domain
exit 1
}
Write-Log "Target computers (FQDNs): $($fqdns -join ', ')" $LogPath "INFO"
# Resolve IP addresses
Write-Host "`nResolving IP addresses..." -ForegroundColor Yellow
$resolvedComputers = @()
foreach ($fqdn in $fqdns) {
Write-Host "Resolving $fqdn..." -NoNewline
$ip = Resolve-ComputerIP -FQDN $fqdn
if ($ip) {
Write-Host " [$ip]" -ForegroundColor Green
$resolvedComputers += @{ FQDN = $fqdn; IP = $ip }
Write-Log "Resolved $fqdn to $ip" $LogPath "INFO"
} else {
Write-Host " [DNS FAILED]" -ForegroundColor Red
Write-Log "Failed to resolve $fqdn" $LogPath "WARN"
}
}
if ($resolvedComputers.Count -eq 0) {
Write-Log "No computers could be resolved via DNS" $LogPath "ERROR"
exit 1
}
# Get credentials if not provided
if (-not $Credential) {
Write-Host "`nEnter credentials for remote computer access:" -ForegroundColor Yellow
$Credential = Get-Credential
if (-not $Credential) {
Write-Log "No credentials provided" $LogPath "ERROR"
exit 1
}
}
# Test connections if requested
if ($TestConnections) {
Write-Host "`nTesting HTTPS connections only..." -ForegroundColor Yellow
foreach ($comp in $resolvedComputers) {
$fqdn = $comp.FQDN
Write-Host "Testing $fqdn..." -NoNewline
if (Test-WinRMHTTPSConnection -ComputerName $fqdn -Credential $Credential -Port $Port -SkipCertCheck $SkipCertificateCheck) {
Write-Host " [OK]" -ForegroundColor Green
Write-Log "HTTPS connection test successful for $fqdn" $LogPath "SUCCESS"
} else {
Write-Host " [FAIL]" -ForegroundColor Red
Write-Log "HTTPS connection test failed for $fqdn" $LogPath "ERROR"
}
}
exit 0
}
# Validate all connections and script existence before starting collection
Write-Host "`nValidating remote HTTPS connections and script availability..." -ForegroundColor Yellow
$validComputers = @()
foreach ($comp in $resolvedComputers) {
$fqdn = $comp.FQDN
Write-Host "Validating $fqdn..." -NoNewline
if (-not (Test-WinRMHTTPSConnection -ComputerName $fqdn -Credential $Credential -Port $Port -SkipCertCheck $SkipCertificateCheck)) {
Write-Host " [CONNECTION FAILED]" -ForegroundColor Red
Write-Log "Cannot connect to $fqdn via WinRM HTTPS" $LogPath "ERROR"
continue
}
if (-not (Test-RemoteScriptExists -ComputerName $fqdn -Credential $Credential -ScriptPath $ScriptPath -Port $Port -SkipCertCheck $SkipCertificateCheck)) {
Write-Host " [SCRIPT NOT FOUND]" -ForegroundColor Red
Write-Log "Script not found on $fqdn at $ScriptPath" $LogPath "ERROR"
continue
}
Write-Host " [OK]" -ForegroundColor Green
$validComputers += $comp
}
if ($validComputers.Count -eq 0) {
Write-Log "No valid computers found for data collection" $LogPath "ERROR"
Show-SetupInstructions -Domain $Domain
exit 1
}
Write-Log "Valid computers for collection: $($validComputers.FQDN -join ', ')" $LogPath "INFO"
# Execute asset collection
Write-Host "`nStarting asset collection on $($validComputers.Count) computers..." -ForegroundColor Cyan
Write-Host "Max concurrent sessions: $MaxConcurrent" -ForegroundColor Gray
Write-Host "Using HTTPS on port: $Port" -ForegroundColor Gray
Write-Host ""
$results = @()
$jobs = @()
$completed = 0
# Process computers in batches
for ($i = 0; $i -lt $validComputers.Count; $i += $MaxConcurrent) {
$batch = $validComputers[$i..($i + $MaxConcurrent - 1)]
Write-Host "Processing batch: $($batch.FQDN -join ', ')" -ForegroundColor Yellow
# Start jobs for current batch
foreach ($comp in $batch) {
$fqdn = $comp.FQDN
$job = Start-Job -ScriptBlock {
param($FQDN, $Credential, $ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty, $Port, $SkipCertCheck, $LogPath, $Functions)
# Import functions into job scope
Invoke-Expression $Functions
Invoke-RemoteAssetScript -ComputerName $FQDN -Credential $Credential `
-ScriptPath $ScriptPath -ProxyURL $ProxyURL -DashboardURL $DashboardURL `
-SkipWarranty $SkipWarranty -Port $Port -SkipCertCheck $SkipCertCheck -LogPath $LogPath
} -ArgumentList $fqdn, $Credential, $ScriptPath, $ProxyURL, $DashboardURL, $SkipWarranty, $Port, $SkipCertificateCheck, $LogPath, (Get-Content $PSCommandPath | Out-String)
$jobs += $job
}
# Wait for batch to complete
$jobs | Wait-Job | Out-Null
# Collect results
foreach ($job in $jobs) {
$result = Receive-Job $job
$results += $result
Remove-Job $job
$completed++
$computer = $result.Computer
if ($result.Success) {
Write-Host "[OK] $computer - Completed successfully" -ForegroundColor Green
} else {
Write-Host "[FAIL] $computer - Failed: $($result.Message)" -ForegroundColor Red
}
}
$jobs = @()
Write-Host "Batch completed. Progress: $completed/$($validComputers.Count)" -ForegroundColor Gray
Write-Host ""
}
# Summary
$successful = ($results | Where-Object { $_.Success }).Count
$failed = ($results | Where-Object { -not $_.Success }).Count
Write-Host "=== Collection Summary ===" -ForegroundColor Cyan
Write-Host "Total computers: $($validComputers.Count)" -ForegroundColor White
Write-Host "Successful: $successful" -ForegroundColor Green
Write-Host "Failed: $failed" -ForegroundColor Red
if ($failed -gt 0) {
Write-Host "`nFailed computers:" -ForegroundColor Yellow
$results | Where-Object { -not $_.Success } | ForEach-Object {
Write-Host " $($_.Computer): $($_.Message)" -ForegroundColor Red
}
}
Write-Log "Collection completed. Success: $successful, Failed: $failed" $LogPath "INFO"
} catch {
Write-Log "Fatal error: $($_.Exception.Message)" $LogPath "ERROR"
exit 1
}