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>
228 lines
7.2 KiB
PowerShell
228 lines
7.2 KiB
PowerShell
#Requires -RunAsAdministrator
|
|
|
|
param(
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$HostnameFile = "shopfloor-hostnames.txt",
|
|
|
|
[Parameter(Mandatory=$false)]
|
|
[string]$CAPfxPath,
|
|
|
|
[string]$Domain = "logon.ds.ge.com",
|
|
[string]$OutputPath = ".\pc-certificates",
|
|
[int]$ValidityYears = 2,
|
|
[SecureString]$CAPassword,
|
|
[SecureString]$CertificatePassword
|
|
)
|
|
|
|
Write-Host ""
|
|
Write-Host "=== Bulk PC Certificate Signing ===" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# Check hostname file
|
|
if (-not (Test-Path $HostnameFile)) {
|
|
Write-Host "[ERROR] Hostname file not found: $HostnameFile" -ForegroundColor Red
|
|
Write-Host "Looking for: $HostnameFile" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
|
|
$hostnames = Get-Content $HostnameFile | Where-Object {$_ -match '\S'} | ForEach-Object {$_.Trim()}
|
|
Write-Host "Found $($hostnames.Count) hostnames to process"
|
|
Write-Host ""
|
|
|
|
# Auto-detect CA file if not specified
|
|
if (-not $CAPfxPath) {
|
|
Write-Host "Looking for CA certificate file..." -ForegroundColor Yellow
|
|
$caFiles = Get-ChildItem -Filter "*CA*.pfx" | Sort-Object LastWriteTime -Descending
|
|
|
|
if ($caFiles.Count -eq 0) {
|
|
Write-Host "[ERROR] No CA PFX file found in current directory" -ForegroundColor Red
|
|
Write-Host "Please specify -CAPfxPath parameter or ensure CA PFX file is in current directory" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
|
|
if ($caFiles.Count -gt 1) {
|
|
Write-Host "Multiple CA files found:" -ForegroundColor Yellow
|
|
for ($i = 0; $i -lt $caFiles.Count; $i++) {
|
|
Write-Host " [$i] $($caFiles[$i].Name) (Modified: $($caFiles[$i].LastWriteTime))"
|
|
}
|
|
$selection = Read-Host "Select CA file number (0-$($caFiles.Count - 1))"
|
|
$CAPfxPath = $caFiles[$selection].FullName
|
|
} else {
|
|
$CAPfxPath = $caFiles[0].FullName
|
|
Write-Host "[OK] Found CA file: $($caFiles[0].Name)" -ForegroundColor Green
|
|
}
|
|
Write-Host ""
|
|
}
|
|
|
|
# Check CA file
|
|
if (-not (Test-Path $CAPfxPath)) {
|
|
Write-Host "[ERROR] CA PFX file not found: $CAPfxPath" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Get passwords
|
|
if (-not $CAPassword) {
|
|
$CAPassword = Read-Host "Enter CA certificate password" -AsSecureString
|
|
}
|
|
|
|
if (-not $CertificatePassword) {
|
|
$CertificatePassword = Read-Host "Enter password for PC certificates (same for all)" -AsSecureString
|
|
}
|
|
|
|
# Load CA certificate
|
|
Write-Host "Loading CA certificate..."
|
|
try {
|
|
$caCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CAPfxPath, $CAPassword, 'Exportable')
|
|
Write-Host "[OK] CA loaded: $($caCert.Subject)"
|
|
Write-Host " Thumbprint: $($caCert.Thumbprint)"
|
|
Write-Host ""
|
|
} catch {
|
|
Write-Host "[ERROR] Failed to load CA: $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
if (-not $caCert.HasPrivateKey) {
|
|
Write-Host "[ERROR] CA certificate does not have private key" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Create output directory
|
|
$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"
|
|
Write-Host ""
|
|
Write-Host "Processing certificates..."
|
|
Write-Host ""
|
|
|
|
$results = @()
|
|
$successCount = 0
|
|
$failCount = 0
|
|
$counter = 0
|
|
|
|
foreach ($hostname in $hostnames) {
|
|
$counter++
|
|
$hostname = $hostname.Trim() -replace "\.$Domain$", ""
|
|
$fqdn = "$hostname.$Domain".ToLower()
|
|
|
|
Write-Host "[$counter/$($hostnames.Count)] $hostname ... " -NoNewline
|
|
|
|
try {
|
|
$notAfter = (Get-Date).AddYears($ValidityYears)
|
|
|
|
$pcCert = New-SelfSignedCertificate `
|
|
-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
|
|
|
|
# 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 "OK" -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++
|
|
}
|
|
}
|
|
|
|
# Export results
|
|
$csvPath = Join-Path $batchPath "certificate-list.csv"
|
|
$results | Export-Csv -Path $csvPath -NoTypeInformation
|
|
|
|
$summaryPath = Join-Path $batchPath "SUMMARY.txt"
|
|
$summaryContent = @"
|
|
Certificate Signing Summary
|
|
===========================
|
|
|
|
Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
|
Batch: $timestamp
|
|
|
|
Statistics:
|
|
Total: $($hostnames.Count)
|
|
Successful: $successCount
|
|
Failed: $failCount
|
|
|
|
CA Certificate:
|
|
Subject: $($caCert.Subject)
|
|
Thumbprint: $($caCert.Thumbprint)
|
|
|
|
Output Directory: $batchPath
|
|
|
|
Files:
|
|
- $successCount PFX files (certificates with private keys)
|
|
- $successCount CER files (public certificates)
|
|
- certificate-list.csv (spreadsheet)
|
|
|
|
Next Steps:
|
|
1. Install CA certificate on management computers:
|
|
Import-Certificate -FilePath 'CA.cer' -CertStoreLocation Cert:\LocalMachine\Root
|
|
|
|
2. Deploy certificates to PCs (each PC gets its own):
|
|
- Copy PFX file to PC
|
|
- Import: Import-PfxCertificate -FilePath 'HOSTNAME.pfx' -CertStoreLocation Cert:\LocalMachine\My -Password `$pass
|
|
- Configure WinRM: .\Setup-WinRM-HTTPS.ps1 -CertificateThumbprint THUMBPRINT -Domain logon.ds.ge.com
|
|
|
|
3. Connect from management computer:
|
|
Enter-PSSession -ComputerName HOSTNAME.logon.ds.ge.com -Credential `$cred -UseSSL -Port 5986
|
|
(No -SessionOption needed!)
|
|
"@
|
|
|
|
$summaryContent | Out-File -FilePath $summaryPath -Encoding UTF8
|
|
|
|
Write-Host ""
|
|
Write-Host "=== CERTIFICATE SIGNING COMPLETE ===" -ForegroundColor Green
|
|
Write-Host ""
|
|
Write-Host "Summary:"
|
|
Write-Host " Total: $($hostnames.Count)"
|
|
Write-Host " Successful: $successCount" -ForegroundColor Green
|
|
Write-Host " Failed: $failCount" -ForegroundColor $(if($failCount -gt 0){'Red'}else{'Green'})
|
|
Write-Host ""
|
|
Write-Host "Output: $batchPath"
|
|
Write-Host ""
|
|
Write-Host "Files:"
|
|
Write-Host " - certificate-list.csv (list of all certificates)"
|
|
Write-Host " - SUMMARY.txt (detailed summary)"
|
|
Write-Host " - $successCount PFX files (one per PC)"
|
|
Write-Host ""
|