#Requires -RunAsAdministrator <# .SYNOPSIS Sets up WinRM HTTPS configuration using a wildcard certificate. .DESCRIPTION This script configures WinRM for HTTPS connections using a wildcard certificate (e.g., *.logon.ds.ge.com). It handles: 1. Certificate installation from PFX file 2. HTTPS listener creation with proper hostname 3. Firewall rule configuration for port 5986 4. WinRM service configuration .PARAMETER CertificatePath Path to the PFX certificate file containing the wildcard certificate. .PARAMETER CertificatePassword SecureString password for the PFX certificate file. .PARAMETER Domain The domain suffix for FQDNs (e.g., "logon.ds.ge.com"). Will construct FQDN as: hostname.domain .PARAMETER CertificateThumbprint Use existing certificate by thumbprint instead of importing from PFX. .PARAMETER Port HTTPS port for WinRM (default: 5986). .PARAMETER SkipFirewall Skip firewall rule creation. .PARAMETER TestConnection Test HTTPS connection after setup. .EXAMPLE # Import certificate and setup WinRM HTTPS $certPass = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force .\Setup-WinRM-HTTPS.ps1 -CertificatePath "C:\Certs\wildcard.pfx" ` -CertificatePassword $certPass -Domain "logon.ds.ge.com" .EXAMPLE # Use existing certificate by thumbprint .\Setup-WinRM-HTTPS.ps1 -CertificateThumbprint "AB123..." -Domain "logon.ds.ge.com" .EXAMPLE # Prompt for certificate password .\Setup-WinRM-HTTPS.ps1 -CertificatePath "C:\Certs\wildcard.pfx" -Domain "logon.ds.ge.com" .NOTES Author: System Administrator Date: 2025-10-17 Version: 1.0 Prerequisites: 1. Wildcard certificate PFX file with private key 2. Administrator privileges 3. Windows with PowerShell 5.1 or later After running this script: - WinRM will listen on HTTPS (port 5986) - HTTP listener (port 5985) will remain active - Connections require -UseSSL flag in PowerShell remoting commands #> param( [Parameter(Mandatory=$false)] [string]$CertificatePath, [Parameter(Mandatory=$false)] [SecureString]$CertificatePassword, [Parameter(Mandatory=$false)] [string]$CertificateThumbprint, [Parameter(Mandatory=$true)] [string]$Domain, [Parameter(Mandatory=$false)] [int]$Port = 5986, [Parameter(Mandatory=$false)] [switch]$SkipFirewall = $false, [Parameter(Mandatory=$false)] [switch]$TestConnection = $false, [Parameter(Mandatory=$false)] [string]$LogFile ) function Write-ColorOutput { param([string]$Message, [string]$Color = "White") Write-Host $Message -ForegroundColor $Color # Also write to log file if specified if ($script:LogFile) { try { Add-Content -Path $script:LogFile -Value $Message -ErrorAction SilentlyContinue } catch { # Silently ignore logging errors to avoid breaking the script } } } function Show-WinRMStatus { Write-ColorOutput "`n=== Current WinRM Configuration ===" "Cyan" try { $winrmStatus = Get-Service WinRM $statusColor = if($winrmStatus.Status -eq 'Running') {'Green'} else {'Red'} Write-ColorOutput "WinRM Service Status: $($winrmStatus.Status)" $statusColor Write-ColorOutput "`nWinRM Listeners:" "Yellow" winrm enumerate winrm/config/listener } catch { Write-ColorOutput "Error checking WinRM status: $($_.Exception.Message)" "Red" } } function Import-WildcardCertificate { param( [string]$CertPath, [SecureString]$CertPassword ) Write-ColorOutput "`n=== Importing Certificate ===" "Cyan" if (-not (Test-Path $CertPath)) { throw "Certificate file not found: $CertPath" } try { # Prompt for password if not provided if (-not $CertPassword) { $CertPassword = Read-Host "Enter certificate password" -AsSecureString } # Import certificate to Local Computer Personal store Write-ColorOutput "Importing certificate from: $CertPath" "Yellow" $cert = Import-PfxCertificate -FilePath $CertPath ` -CertStoreLocation Cert:\LocalMachine\My ` -Password $CertPassword ` -Exportable Write-ColorOutput "[OK] Certificate imported successfully" "Green" Write-ColorOutput " Subject: $($cert.Subject)" "Gray" Write-ColorOutput " Thumbprint: $($cert.Thumbprint)" "Gray" Write-ColorOutput " Expiration: $($cert.NotAfter)" "Gray" return $cert } catch { throw "Failed to import certificate: $($_.Exception.Message)" } } function Get-ExistingCertificate { param([string]$Thumbprint) Write-ColorOutput "`n=== Locating Existing Certificate ===" "Cyan" try { $cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $Thumbprint } if (-not $cert) { throw "Certificate with thumbprint $Thumbprint not found in Local Machine store" } Write-ColorOutput "[OK] Certificate found" "Green" Write-ColorOutput " Subject: $($cert.Subject)" "Gray" Write-ColorOutput " Thumbprint: $($cert.Thumbprint)" "Gray" Write-ColorOutput " Expiration: $($cert.NotAfter)" "Gray" # Check if certificate has private key if (-not $cert.HasPrivateKey) { throw "Certificate does not have a private key. WinRM HTTPS requires a certificate with private key." } return $cert } catch { throw "Failed to locate certificate: $($_.Exception.Message)" } } function Find-WildcardCertificate { param([string]$Domain) Write-ColorOutput "`n=== Searching for Wildcard Certificate ===" "Cyan" Write-ColorOutput "Looking for certificate matching: *.$Domain" "Yellow" try { $certs = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*$Domain*" -and $_.HasPrivateKey -and $_.NotAfter -gt (Get-Date) } if ($certs.Count -eq 0) { throw "No valid wildcard certificate found for *.$Domain in Local Machine store" } if ($certs.Count -gt 1) { Write-ColorOutput "Multiple certificates found:" "Yellow" for ($i = 0; $i -lt $certs.Count; $i++) { Write-ColorOutput " [$i] Subject: $($certs[$i].Subject) | Expires: $($certs[$i].NotAfter)" "White" } $selection = Read-Host "Select certificate number (0-$($certs.Count - 1))" $cert = $certs[$selection] } else { $cert = $certs[0] } Write-ColorOutput "[OK] Certificate selected" "Green" Write-ColorOutput " Subject: $($cert.Subject)" "Gray" Write-ColorOutput " Thumbprint: $($cert.Thumbprint)" "Gray" Write-ColorOutput " Expiration: $($cert.NotAfter)" "Gray" return $cert } catch { throw "Failed to find wildcard certificate: $($_.Exception.Message)" } } function Remove-ExistingHTTPSListener { Write-ColorOutput "`n=== Checking for Existing HTTPS Listeners ===" "Cyan" try { $listeners = winrm enumerate winrm/config/listener | Select-String "Transport = HTTPS" -Context 0,10 if ($listeners) { Write-ColorOutput "Found existing HTTPS listener(s). Removing..." "Yellow" # Remove all HTTPS listeners $result = winrm delete winrm/config/Listener?Address=*+Transport=HTTPS 2>&1 if ($LASTEXITCODE -eq 0) { Write-ColorOutput "[OK] Existing HTTPS listener removed" "Green" } } else { Write-ColorOutput "[OK] No existing HTTPS listener found" "Green" } } catch { Write-ColorOutput "[WARN] Could not check/remove existing listeners: $($_.Exception.Message)" "Yellow" } } function New-WinRMHTTPSListener { param( [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, [string]$Hostname, [int]$Port ) Write-ColorOutput "`n=== Creating WinRM HTTPS Listener ===" "Cyan" Write-ColorOutput "Hostname: $Hostname" "Gray" Write-ColorOutput "Port: $Port" "Gray" try { # Remove existing HTTPS listener if present Remove-ExistingHTTPSListener # Create new HTTPS listener $thumbprint = $Certificate.Thumbprint Write-ColorOutput "Creating HTTPS listener..." "Yellow" Write-ColorOutput "Certificate Thumbprint: $thumbprint" "Gray" # Use cmd.exe to execute winrm command to avoid PowerShell quoting issues $winrmArgs = "create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=`"$Hostname`";CertificateThumbprint=`"$thumbprint`";Port=`"$Port`"}" Write-ColorOutput "Executing: winrm $winrmArgs" "Gray" $result = cmd.exe /c "winrm $winrmArgs" 2>&1 if ($LASTEXITCODE -ne 0) { Write-ColorOutput "Error output: $result" "Red" throw "Failed to create HTTPS listener. Error code: $LASTEXITCODE" } Write-ColorOutput "[OK] HTTPS listener created successfully" "Green" # Verify listener was created Write-ColorOutput "`nVerifying HTTPS listener:" "Yellow" winrm enumerate winrm/config/listener | Select-String "Transport = HTTPS" -Context 0,15 return $true } catch { throw "Failed to create HTTPS listener: $($_.Exception.Message)" } } function Enable-WinRMService { Write-ColorOutput "`n=== Configuring WinRM Service ===" "Cyan" try { # Enable PowerShell Remoting Write-ColorOutput "Enabling PowerShell Remoting..." "Yellow" Enable-PSRemoting -Force -SkipNetworkProfileCheck Write-ColorOutput "[OK] PowerShell Remoting enabled" "Green" # Start WinRM service Write-ColorOutput "Configuring WinRM service..." "Yellow" Start-Service WinRM -ErrorAction SilentlyContinue Set-Service WinRM -StartupType Automatic Write-ColorOutput "[OK] WinRM service configured" "Green" # Configure service settings Set-Item WSMan:\localhost\Service\Auth\Certificate -Value $true Write-ColorOutput "[OK] Certificate authentication enabled" "Green" } catch { throw "Failed to configure WinRM service: $($_.Exception.Message)" } } function New-FirewallRule { param([int]$Port) if ($SkipFirewall) { Write-ColorOutput "`n[SKIP] Firewall configuration skipped" "Yellow" return } Write-ColorOutput "`n=== Configuring Windows Firewall ===" "Cyan" try { $ruleName = "WinRM HTTPS-In" # Check if rule already exists $existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue if ($existingRule) { Write-ColorOutput "Removing existing firewall rule..." "Yellow" Remove-NetFirewallRule -DisplayName $ruleName } Write-ColorOutput "Creating firewall rule for port $Port..." "Yellow" New-NetFirewallRule -DisplayName $ruleName ` -Name $ruleName ` -Profile Any ` -LocalPort $Port ` -Protocol TCP ` -Direction Inbound ` -Action Allow ` -Enabled True | Out-Null Write-ColorOutput "[OK] Firewall rule created" "Green" } catch { Write-ColorOutput "[WARN] Could not configure firewall: $($_.Exception.Message)" "Yellow" } } function Test-WinRMHTTPSConnection { param([string]$Hostname, [int]$Port) Write-ColorOutput "`n=== Testing HTTPS Connection ===" "Cyan" try { Write-ColorOutput "Testing connection to https://${Hostname}:${Port}/wsman..." "Yellow" $testResult = Test-WSMan -ComputerName $Hostname -Port $Port -UseSSL -ErrorAction Stop Write-ColorOutput "[OK] HTTPS connection successful!" "Green" Write-ColorOutput "`nTest-WSMan Output:" "Gray" $testResult | Format-List return $true } catch { Write-ColorOutput "[WARN] HTTPS connection test failed: $($_.Exception.Message)" "Yellow" Write-ColorOutput "This may be normal if testing from the local machine." "Gray" Write-ColorOutput "Try testing from a remote computer using:" "Gray" Write-ColorOutput " Test-WSMan -ComputerName $Hostname -Port $Port -UseSSL" "White" return $false } } function Show-NextSteps { param([string]$Hostname, [int]$Port) Write-ColorOutput "`n=== Next Steps ===" "Cyan" Write-ColorOutput "" Write-ColorOutput "WinRM HTTPS is now configured on this computer." "Green" Write-ColorOutput "" Write-ColorOutput "To connect from a remote computer:" "Yellow" Write-ColorOutput "" Write-ColorOutput " # Test connection" "Gray" Write-ColorOutput " Test-WSMan -ComputerName $Hostname -Port $Port -UseSSL" "White" Write-ColorOutput "" Write-ColorOutput " # Create remote session" "Gray" Write-ColorOutput " `$cred = Get-Credential" "White" Write-ColorOutput " New-PSSession -ComputerName $Hostname -Credential `$cred -UseSSL -Port $Port" "White" Write-ColorOutput "" Write-ColorOutput " # Or use Enter-PSSession" "Gray" Write-ColorOutput " Enter-PSSession -ComputerName $Hostname -Credential `$cred -UseSSL -Port $Port" "White" Write-ColorOutput "" Write-ColorOutput "Notes:" "Yellow" Write-ColorOutput " - HTTP listener on port 5985 is still active" "Gray" Write-ColorOutput " - Always use -UseSSL flag for HTTPS connections" "Gray" Write-ColorOutput " - Certificate must be trusted on the client computer" "Gray" Write-ColorOutput "" } # Main execution try { # Make LogFile available to all functions $script:LogFile = $LogFile Write-ColorOutput "=== WinRM HTTPS Setup Script ===" "Cyan" Write-ColorOutput "Date: $(Get-Date)" "Gray" if ($LogFile) { Write-ColorOutput "Logging to: $LogFile" "Gray" } Write-ColorOutput "" # Construct FQDN $hostname = $env:COMPUTERNAME $fqdn = "$hostname.$Domain".ToLower() Write-ColorOutput "Computer FQDN: $fqdn" "Gray" # Show current status Show-WinRMStatus # Get or import certificate $certificate = $null if ($CertificateThumbprint) { # Use existing certificate by thumbprint $certificate = Get-ExistingCertificate -Thumbprint $CertificateThumbprint } elseif ($CertificatePath) { # Import certificate from PFX $certificate = Import-WildcardCertificate -CertPath $CertificatePath -CertPassword $CertificatePassword } else { # Try to find existing wildcard certificate $certificate = Find-WildcardCertificate -Domain $Domain } if (-not $certificate) { throw "No certificate available. Provide -CertificatePath or -CertificateThumbprint" } # Verify certificate validity if ($certificate.NotAfter -lt (Get-Date)) { throw "Certificate has expired: $($certificate.NotAfter)" } # Enable WinRM service Enable-WinRMService # Create HTTPS listener New-WinRMHTTPSListener -Certificate $certificate -Hostname $fqdn -Port $Port # Configure firewall New-FirewallRule -Port $Port # Show updated status Show-WinRMStatus # Test connection if requested if ($TestConnection) { Test-WinRMHTTPSConnection -Hostname $fqdn -Port $Port } # Show next steps Show-NextSteps -Hostname $fqdn -Port $Port Write-ColorOutput "`n[SUCCESS] WinRM HTTPS setup completed successfully!" "Green" } catch { Write-ColorOutput "`n[ERROR] Setup failed: $($_.Exception.Message)" "Red" exit 1 }