Files
powershell-scripts/winrm-https/Sign-BulkPCCertificates.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

460 lines
16 KiB
PowerShell

#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 ""
}