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>
460 lines
16 KiB
PowerShell
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 ""
|
|
}
|