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>
This commit is contained in:
459
winrm-https/Sign-BulkPCCertificates.ps1
Normal file
459
winrm-https/Sign-BulkPCCertificates.ps1
Normal file
@@ -0,0 +1,459 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Signs certificates for multiple PCs using the Certificate Authority
|
||||
|
||||
.DESCRIPTION
|
||||
Reads a list of hostnames and creates individual signed certificates for each PC.
|
||||
This is the proper way to deploy WinRM HTTPS to 175 shopfloor PCs.
|
||||
|
||||
.PARAMETER HostnameFile
|
||||
Path to text file containing PC hostnames (one per line)
|
||||
|
||||
.PARAMETER Hostnames
|
||||
Array of hostnames to process
|
||||
|
||||
.PARAMETER Domain
|
||||
The domain suffix (default: logon.ds.ge.com)
|
||||
|
||||
.PARAMETER CAThumbprint
|
||||
Thumbprint of the CA certificate used to sign certificates
|
||||
|
||||
.PARAMETER CAPfxPath
|
||||
Path to the CA PFX file
|
||||
|
||||
.PARAMETER CAPassword
|
||||
Password for the CA PFX file
|
||||
|
||||
.PARAMETER OutputPath
|
||||
Directory to save signed certificates (default: ./pc-certificates)
|
||||
|
||||
.PARAMETER ValidityYears
|
||||
How many years certificates should be valid (default: 2)
|
||||
|
||||
.PARAMETER CertificatePassword
|
||||
Password for all exported PC certificates (use same password for simplicity)
|
||||
|
||||
.EXAMPLE
|
||||
# Sign certificates for all PCs in file
|
||||
$caPass = ConvertTo-SecureString "CAPassword" -AsPlainText -Force
|
||||
$certPass = ConvertTo-SecureString "PCCertPass" -AsPlainText -Force
|
||||
.\Sign-BulkPCCertificates.ps1 -HostnameFile shopfloor-hostnames.txt `
|
||||
-CAPfxPath "CA.pfx" -CAPassword $caPass -CertificatePassword $certPass
|
||||
|
||||
.EXAMPLE
|
||||
# Sign certificates using CA from local store
|
||||
$certPass = ConvertTo-SecureString "PCCertPass" -AsPlainText -Force
|
||||
.\Sign-BulkPCCertificates.ps1 -HostnameFile shopfloor-hostnames.txt `
|
||||
-CAThumbprint "ABC123..." -CertificatePassword $certPass
|
||||
|
||||
.NOTES
|
||||
Author: System Administrator
|
||||
Date: 2025-10-17
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$HostnameFile,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string[]]$Hostnames,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Domain = "logon.ds.ge.com",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$CAThumbprint,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$CAPfxPath,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[SecureString]$CAPassword,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$OutputPath = "./pc-certificates",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[int]$ValidityYears = 2,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[SecureString]$CertificatePassword
|
||||
)
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host "║ Bulk PC Certificate Signing with CA ║" -ForegroundColor Cyan
|
||||
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Get hostnames
|
||||
$hostnameList = @()
|
||||
|
||||
if ($HostnameFile) {
|
||||
if (-not (Test-Path $HostnameFile)) {
|
||||
Write-Host "✗ Hostname file not found: $HostnameFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Reading hostnames from file: $HostnameFile" -ForegroundColor Yellow
|
||||
$hostnameList = Get-Content $HostnameFile | Where-Object {$_ -match '\S'} | ForEach-Object {$_.Trim()}
|
||||
Write-Host "✓ Found $($hostnameList.Count) hostnames" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
} elseif ($Hostnames) {
|
||||
$hostnameList = $Hostnames
|
||||
Write-Host "Processing $($hostnameList.Count) hostnames from parameter" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
} else {
|
||||
Write-Host "✗ Must specify either -HostnameFile or -Hostnames" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($hostnameList.Count -eq 0) {
|
||||
Write-Host "✗ No hostnames to process" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get CA certificate
|
||||
$caCert = $null
|
||||
|
||||
if ($CAPfxPath) {
|
||||
Write-Host "Loading CA certificate from file..." -ForegroundColor Yellow
|
||||
Write-Host " File: $CAPfxPath" -ForegroundColor Gray
|
||||
|
||||
if (-not (Test-Path $CAPfxPath)) {
|
||||
Write-Host "✗ CA PFX file not found: $CAPfxPath" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not $CAPassword) {
|
||||
$CAPassword = Read-Host "Enter CA certificate password" -AsSecureString
|
||||
}
|
||||
|
||||
try {
|
||||
$caCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CAPfxPath, $CAPassword, 'Exportable')
|
||||
Write-Host "✓ CA certificate loaded" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "✗ Failed to load CA certificate: $($_.Exception.Message)" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
} elseif ($CAThumbprint) {
|
||||
Write-Host "Loading CA certificate from local store..." -ForegroundColor Yellow
|
||||
Write-Host " Thumbprint: $CAThumbprint" -ForegroundColor Gray
|
||||
|
||||
try {
|
||||
$caCert = Get-ChildItem Cert:\LocalMachine\My\$CAThumbprint -ErrorAction Stop
|
||||
Write-Host "✓ CA certificate found" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "✗ CA certificate not found in local store" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
} else {
|
||||
Write-Host "✗ Must specify either -CAThumbprint or -CAPfxPath" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not $caCert.HasPrivateKey) {
|
||||
Write-Host "✗ CA certificate does not have private key" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "CA Certificate:" -ForegroundColor Cyan
|
||||
Write-Host " Subject: $($caCert.Subject)" -ForegroundColor White
|
||||
Write-Host " Thumbprint: $($caCert.Thumbprint)" -ForegroundColor White
|
||||
Write-Host " Valid Until: $($caCert.NotAfter)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
# Get certificate password
|
||||
if (-not $CertificatePassword) {
|
||||
Write-Host "Enter password for PC certificates (same password for all):" -ForegroundColor Yellow
|
||||
$CertificatePassword = Read-Host "Certificate Password" -AsSecureString
|
||||
}
|
||||
|
||||
# Create output directory
|
||||
if (-not (Test-Path $OutputPath)) {
|
||||
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
||||
$batchPath = Join-Path $OutputPath "batch-$timestamp"
|
||||
New-Item -ItemType Directory -Path $batchPath -Force | Out-Null
|
||||
|
||||
Write-Host "Output Directory: $batchPath" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Process each hostname
|
||||
$results = @()
|
||||
$successCount = 0
|
||||
$failCount = 0
|
||||
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Gray
|
||||
Write-Host "Processing Certificates..." -ForegroundColor Yellow
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
$counter = 0
|
||||
foreach ($hostname in $hostnameList) {
|
||||
$counter++
|
||||
$hostname = $hostname.Trim() -replace "\.$Domain$", ""
|
||||
$fqdn = "$hostname.$Domain".ToLower()
|
||||
|
||||
Write-Host "[$counter/$($hostnameList.Count)] Processing: $hostname" -ForegroundColor Cyan
|
||||
|
||||
try {
|
||||
# Create certificate
|
||||
$notAfter = (Get-Date).AddYears($ValidityYears)
|
||||
|
||||
$certParams = @{
|
||||
Subject = "CN=$fqdn"
|
||||
DnsName = @($fqdn, $hostname)
|
||||
KeyExportPolicy = 'Exportable'
|
||||
KeyUsage = 'DigitalSignature', 'KeyEncipherment'
|
||||
KeyLength = 2048
|
||||
KeyAlgorithm = 'RSA'
|
||||
HashAlgorithm = 'SHA256'
|
||||
CertStoreLocation = 'Cert:\LocalMachine\My'
|
||||
NotAfter = $notAfter
|
||||
TextExtension = @('2.5.29.37={text}1.3.6.1.5.5.7.3.1')
|
||||
Signer = $caCert
|
||||
}
|
||||
|
||||
$pcCert = New-SelfSignedCertificate @certParams
|
||||
|
||||
# Export PFX
|
||||
$pfxPath = Join-Path $batchPath "$hostname-$Domain-$timestamp.pfx"
|
||||
Export-PfxCertificate -Cert $pcCert -FilePath $pfxPath -Password $CertificatePassword | Out-Null
|
||||
|
||||
# Export CER
|
||||
$cerPath = Join-Path $batchPath "$hostname-$Domain-$timestamp.cer"
|
||||
Export-Certificate -Cert $pcCert -FilePath $cerPath | Out-Null
|
||||
|
||||
# Remove from store
|
||||
Remove-Item "Cert:\LocalMachine\My\$($pcCert.Thumbprint)" -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host " ✓ Certificate created: $pfxPath" -ForegroundColor Green
|
||||
|
||||
$results += [PSCustomObject]@{
|
||||
Hostname = $hostname
|
||||
FQDN = $fqdn
|
||||
Thumbprint = $pcCert.Thumbprint
|
||||
ValidUntil = $pcCert.NotAfter
|
||||
PFXFile = Split-Path $pfxPath -Leaf
|
||||
Status = "Success"
|
||||
Error = $null
|
||||
}
|
||||
|
||||
$successCount++
|
||||
|
||||
} catch {
|
||||
Write-Host " ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
|
||||
$results += [PSCustomObject]@{
|
||||
Hostname = $hostname
|
||||
FQDN = $fqdn
|
||||
Thumbprint = $null
|
||||
ValidUntil = $null
|
||||
PFXFile = $null
|
||||
Status = "Failed"
|
||||
Error = $_.Exception.Message
|
||||
}
|
||||
|
||||
$failCount++
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Create summary report
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Gray
|
||||
Write-Host "Creating Summary Report..." -ForegroundColor Yellow
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
$summaryPath = Join-Path $batchPath "CERTIFICATE-SUMMARY.txt"
|
||||
$csvPath = Join-Path $batchPath "certificate-list.csv"
|
||||
|
||||
$summaryContent = @"
|
||||
================================================================================
|
||||
BULK CERTIFICATE SIGNING SUMMARY
|
||||
================================================================================
|
||||
|
||||
Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||
Batch: $timestamp
|
||||
|
||||
Statistics:
|
||||
-----------
|
||||
Total Hostnames: $($hostnameList.Count)
|
||||
Successful: $successCount
|
||||
Failed: $failCount
|
||||
Success Rate: $([math]::Round($successCount / $hostnameList.Count * 100, 2))%
|
||||
|
||||
CA Certificate:
|
||||
---------------
|
||||
Subject: $($caCert.Subject)
|
||||
Thumbprint: $($caCert.Thumbprint)
|
||||
Valid Until: $($caCert.NotAfter)
|
||||
|
||||
Certificate Settings:
|
||||
---------------------
|
||||
Domain: $Domain
|
||||
Validity: $ValidityYears years
|
||||
Key Size: 2048-bit RSA
|
||||
Hash: SHA256
|
||||
Password: (Same for all certificates)
|
||||
|
||||
Output Directory:
|
||||
-----------------
|
||||
$batchPath
|
||||
|
||||
================================================================================
|
||||
CERTIFICATE LIST
|
||||
================================================================================
|
||||
|
||||
$($results | Where-Object {$_.Status -eq "Success"} | ForEach-Object {
|
||||
"Hostname: $($_.Hostname)
|
||||
FQDN: $($_.FQDN)
|
||||
Thumbprint: $($_.Thumbprint)
|
||||
Valid Until: $($_.ValidUntil)
|
||||
PFX File: $($_.PFXFile)
|
||||
" + ("-" * 80)
|
||||
} | Out-String)
|
||||
|
||||
$(if ($failCount -gt 0) {
|
||||
"================================================================================
|
||||
FAILED CERTIFICATES
|
||||
================================================================================
|
||||
|
||||
$($results | Where-Object {$_.Status -eq "Failed"} | ForEach-Object {
|
||||
"Hostname: $($_.Hostname)
|
||||
Error: $($_.Error)
|
||||
" + ("-" * 80)
|
||||
} | Out-String)
|
||||
"})
|
||||
|
||||
================================================================================
|
||||
DEPLOYMENT INSTRUCTIONS
|
||||
================================================================================
|
||||
|
||||
1. INSTALL CA CERTIFICATE ON MANAGEMENT COMPUTERS
|
||||
- Install the CA public certificate (*.cer from CA creation)
|
||||
- Import to Trusted Root Certification Authorities
|
||||
- This makes all signed certificates automatically trusted
|
||||
|
||||
Command:
|
||||
Import-Certificate -FilePath "CA.cer" ``
|
||||
-CertStoreLocation Cert:\LocalMachine\Root
|
||||
|
||||
2. DISTRIBUTE PC CERTIFICATES
|
||||
- Each PC needs its own PFX file
|
||||
- Copy the appropriate certificate to each PC
|
||||
- Password is the same for all certificates
|
||||
|
||||
3. DEPLOY TO EACH PC
|
||||
Option A - Manual deployment:
|
||||
a. Copy PFX file to PC
|
||||
b. Import certificate:
|
||||
`$pass = ConvertTo-SecureString "PASSWORD" -AsPlainText -Force
|
||||
Import-PfxCertificate -FilePath "HOSTNAME.pfx" ``
|
||||
-CertStoreLocation Cert:\LocalMachine\My ``
|
||||
-Password `$pass
|
||||
c. Configure WinRM HTTPS with the certificate
|
||||
|
||||
Option B - Automated deployment (recommended):
|
||||
- Update deployment package with individual certificates
|
||||
- Use deployment script for each PC
|
||||
- See: Deploy-IndividualCertificates.ps1
|
||||
|
||||
4. VERIFY DEPLOYMENT
|
||||
From management computer:
|
||||
Test-WSMan -ComputerName HOSTNAME.$Domain -UseSSL -Port 5986
|
||||
# Should work without -SessionOption if CA is trusted
|
||||
|
||||
================================================================================
|
||||
FILES GENERATED
|
||||
================================================================================
|
||||
|
||||
Summary Reports:
|
||||
- CERTIFICATE-SUMMARY.txt (this file)
|
||||
- certificate-list.csv (spreadsheet format)
|
||||
|
||||
Certificate Files:
|
||||
$successCount PFX files (one per PC with private key)
|
||||
$successCount CER files (public certificates)
|
||||
|
||||
================================================================================
|
||||
SECURITY NOTES
|
||||
================================================================================
|
||||
|
||||
1. All PC certificates use the same password for simplicity
|
||||
2. Store the certificate password securely (password manager)
|
||||
3. CA private key is NOT distributed to PCs (only signing cert)
|
||||
4. Each PC only receives its own certificate
|
||||
5. If a certificate is compromised, only one PC is affected
|
||||
6. Certificates expire after $ValidityYears years - plan renewal
|
||||
|
||||
================================================================================
|
||||
NEXT STEPS
|
||||
================================================================================
|
||||
|
||||
[ ] 1. Install CA certificate on all management computers
|
||||
[ ] 2. Test: Import one certificate to one PC manually
|
||||
[ ] 3. Configure WinRM HTTPS on test PC
|
||||
[ ] 4. Verify remote connection works
|
||||
[ ] 5. Deploy to remaining PCs in batches
|
||||
[ ] 6. Track deployment progress
|
||||
[ ] 7. Verify all deployments successful
|
||||
[ ] 8. Document certificate expiration dates
|
||||
[ ] 9. Set calendar reminder for renewal
|
||||
|
||||
================================================================================
|
||||
"@
|
||||
|
||||
$summaryContent | Out-File -FilePath $summaryPath -Encoding UTF8
|
||||
Write-Host "✓ Summary created: $summaryPath" -ForegroundColor Green
|
||||
|
||||
# Export CSV
|
||||
$results | Export-Csv -Path $csvPath -NoTypeInformation
|
||||
Write-Host "✓ CSV created: $csvPath" -ForegroundColor Green
|
||||
|
||||
Write-Host ""
|
||||
|
||||
# Final summary
|
||||
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Green
|
||||
Write-Host "║ BULK CERTIFICATE SIGNING COMPLETE ║" -ForegroundColor Green
|
||||
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Summary:" -ForegroundColor Cyan
|
||||
Write-Host " Total Processed: $($hostnameList.Count)" -ForegroundColor White
|
||||
Write-Host " Successful: $successCount" -ForegroundColor Green
|
||||
Write-Host " Failed: $failCount" -ForegroundColor $(if($failCount -gt 0){'Red'}else{'Green'})
|
||||
Write-Host ""
|
||||
Write-Host "Output Directory:" -ForegroundColor Cyan
|
||||
Write-Host " $batchPath" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "Files Created:" -ForegroundColor Cyan
|
||||
Write-Host " - $successCount PC certificates (PFX)" -ForegroundColor White
|
||||
Write-Host " - $successCount public certificates (CER)" -ForegroundColor White
|
||||
Write-Host " - Summary report (TXT)" -ForegroundColor White
|
||||
Write-Host " - Certificate list (CSV)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
if ($successCount -gt 0) {
|
||||
Write-Host "Next Steps:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Install CA certificate on management computers" -ForegroundColor White
|
||||
Write-Host " 2. Deploy individual certificates to each PC" -ForegroundColor White
|
||||
Write-Host " 3. Configure WinRM HTTPS on each PC" -ForegroundColor White
|
||||
Write-Host " 4. Verify connections from management computer" -ForegroundColor White
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
if ($failCount -gt 0) {
|
||||
Write-Host "⚠ WARNING: $failCount certificate(s) failed" -ForegroundColor Yellow
|
||||
Write-Host " Review the summary report for details" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
}
|
||||
Reference in New Issue
Block a user