#Requires -Version 7.0 <# .SYNOPSIS Creates or updates a configuration file for IIS setup of TailorWell.Admin. .DESCRIPTION This script helps create a personalized configuration file by gathering system information and prompting for required values. It validates the configuration against the JSON schema and saves it to a specified file. .PARAMETER OutputFile Name of the output configuration file. Defaults to "[username]-config.json". .PARAMETER Template Path to a template configuration file to use as a starting point. .PARAMETER Interactive Prompt for all values, even if defaults are available. .EXAMPLE .\create-a-config-for-environment.ps1 .\create-a-config-for-environment.ps1 -OutputFile "my-config.json" .\create-a-config-for-environment.ps1 -Template "team-config.json" -Interactive #> param( [string]$OutputFile = "$($env:USERNAME.ToLower())-config.json", [string]$Template, [switch]$Interactive ) $ErrorActionPreference = "Stop" # Helper functions function Write-Step { param([string]$Message) Write-Host "`n═══ $Message ═══" -ForegroundColor Cyan } function Write-Prompt { param([string]$Message) Write-Host "→ $Message" -ForegroundColor Yellow -NoNewline } function Write-Success { param([string]$Message) Write-Host "✓ $Message" -ForegroundColor Green } function Write-Info { param([string]$Message) Write-Host "ℹ $Message" -ForegroundColor Blue } function Read-Value { param( [string]$Prompt, [string]$Default, [switch]$Required, [switch]$Secure ) $displayDefault = if ($Default) { " [$Default]" } else { "" } Write-Prompt "$Prompt$displayDefault`: " if ($Secure) { $secureValue = Read-Host -AsSecureString if ($secureValue.Length -eq 0 -and $Default) { return $Default } $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureValue) $value = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } else { $value = Read-Host } if ([string]::IsNullOrWhiteSpace($value)) { if ($Default) { return $Default } elseif ($Required) { Write-Host "This value is required." -ForegroundColor Red return Read-Value -Prompt $Prompt -Default $Default -Required:$Required -Secure:$Secure } } return $value } function Test-CertificateFile { param([string]$Path) if (-not (Test-Path $Path)) { return $false } try { $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cert.Import($Path) return $true } catch { return $false } } function Get-DefaultProjectPath { # Try to find the project path relative to this script $scriptDir = Split-Path -Parent $PSScriptRoot $projectPath = Join-Path $scriptDir "Company.Admin" if (Test-Path $projectPath) { return $projectPath } return $null } function Get-SystemInfo { $info = @{ Username = $env:USERNAME MachineName = $env:COMPUTERNAME WindowsVersion = (Get-CimInstance Win32_OperatingSystem).Caption PowerShellVersion = $PSVersionTable.PSVersion.ToString() IISInstalled = Test-Path "$env:SystemRoot\System32\inetsrv\appcmd.exe" } return $info } function Validate-Configuration { param([hashtable]$Config) $errors = @() # Validate required fields $requiredFields = @( 'certificatePath', 'projectPath', 'siteName', 'appPoolName', 'projectUrl', 'databaseEnvironmentVariable', 'databaseServer', 'databaseName', 'databaseUserId', 'databasePassword' ) foreach ($field in $requiredFields) { if (-not $Config.ContainsKey($field) -or [string]::IsNullOrWhiteSpace($Config[$field])) { $errors += "Missing required field: $field" } } # Validate certificate path if ($Config.certificatePath -and -not (Test-Path $Config.certificatePath)) { $errors += "Certificate file not found: $($Config.certificatePath)" } # Validate project path if ($Config.projectPath -and -not (Test-Path $Config.projectPath)) { $errors += "Project path not found: $($Config.projectPath)" } # Validate URL format if ($Config.projectUrl -and $Config.projectUrl -notmatch '^https://') { $errors += "Project URL must start with https://" } # Validate ports if ($Config.iisSettings) { if ($Config.iisSettings.httpPort -and ($Config.iisSettings.httpPort -lt 1 -or $Config.iisSettings.httpPort -gt 65535)) { $errors += "HTTP port must be between 1 and 65535" } if ($Config.iisSettings.httpsPort -and ($Config.iisSettings.httpsPort -lt 1 -or $Config.iisSettings.httpsPort -gt 65535)) { $errors += "HTTPS port must be between 1 and 65535" } } return $errors } # Main script try { Write-Host "`n╔════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan Write-Host "║ IIS Configuration Creator for TailorWell.Admin ║" -ForegroundColor Cyan Write-Host "╚════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan # Display system information Write-Step "System Information" $sysInfo = Get-SystemInfo foreach ($key in $sysInfo.Keys) { Write-Info "$key`: $($sysInfo[$key])" } if (-not $sysInfo.IISInstalled) { Write-Host "`n⚠ WARNING: IIS does not appear to be installed!" -ForegroundColor Yellow Write-Host " You will need to install IIS before running the setup script." -ForegroundColor Yellow } # Load template or create new configuration $config = @{} if ($Template -and (Test-Path $Template)) { Write-Step "Loading template configuration" $templateContent = Get-Content $Template -Raw | ConvertFrom-Json foreach ($prop in $templateContent.PSObject.Properties) { $config[$prop.Name] = $prop.Value } Write-Success "Template loaded from $Template" } else { # Load defaults from schema $schemaPath = Join-Path $PSScriptRoot "config-schema.json" if (Test-Path $schemaPath) { Write-Step "Loading configuration schema" $schema = Get-Content $schemaPath -Raw | ConvertFrom-Json # Extract defaults from schema foreach ($prop in $schema.properties.PSObject.Properties) { if ($prop.Value.default) { $config[$prop.Name] = $prop.Value.default } } # Handle nested IIS settings defaults if ($schema.properties.iisSettings) { $config['iisSettings'] = @{} foreach ($iisProp in $schema.properties.iisSettings.properties.PSObject.Properties) { if ($iisProp.Value.default) { $config.iisSettings[$iisProp.Name] = $iisProp.Value.default } } } # Handle nested features defaults if ($schema.properties.features) { $config['features'] = @{} foreach ($featureProp in $schema.properties.features.properties.PSObject.Properties) { if ($featureProp.Value.default) { $config.features[$featureProp.Name] = $featureProp.Value.default } } } Write-Success "Schema defaults loaded" } } # Gather configuration values Write-Step "Configuration Settings" Write-Host "Please provide the following configuration values:" -ForegroundColor White # Certificate configuration Write-Host "`n[Certificate Configuration]" -ForegroundColor Magenta $defaultCertPath = if ($config.certificatePath) { $config.certificatePath } else { "" } $certPath = Read-Value -Prompt "Certificate path (.pfx file)" -Default $defaultCertPath -Required # Expand environment variables and tilde $certPath = $certPath.Replace('~', $HOME) $certPath = [System.Environment]::ExpandEnvironmentVariables($certPath) $config['certificatePath'] = $certPath # Check if certificate needs a password $needsPassword = $false try { $testCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $testCert.Import($certPath) } catch { $needsPassword = $true } if ($needsPassword -or $Interactive) { $certPassword = Read-Value -Prompt "Certificate password (leave empty if none)" -Secure $config['certificatePassword'] = if ($certPassword) { $certPassword } else { $null } } else { $config['certificatePassword'] = $null } # Project configuration Write-Host "`n[Project Configuration]" -ForegroundColor Magenta $defaultProjectPath = if ($config.projectPath) { $config.projectPath } else { Get-DefaultProjectPath } $projectPath = Read-Value -Prompt "Project path (Company.Admin directory)" -Default $defaultProjectPath -Required $projectPath = $projectPath.Replace('~', $HOME) $projectPath = [System.Environment]::ExpandEnvironmentVariables($projectPath) $config['projectPath'] = $projectPath # Site configuration Write-Host "`n[IIS Site Configuration]" -ForegroundColor Magenta $config['siteName'] = Read-Value -Prompt "Site name" -Default $config.siteName -Required $config['appPoolName'] = Read-Value -Prompt "Application pool name" -Default $config.appPoolName -Required $config['projectUrl'] = Read-Value -Prompt "Project URL" -Default $config.projectUrl -Required # Database configuration Write-Host "`n[Database Configuration]" -ForegroundColor Magenta $config['databaseEnvironmentVariable'] = Read-Value -Prompt "Database environment variable name" -Default $config.databaseEnvironmentVariable -Required $config['databaseServer'] = Read-Value -Prompt "Database server" -Default $config.databaseServer -Required $config['databaseName'] = Read-Value -Prompt "Database name" -Default $config.databaseName -Required $config['databaseUserId'] = Read-Value -Prompt "Database user ID" -Default $config.databaseUserId -Required $config['databasePassword'] = Read-Value -Prompt "Database password" -Required -Secure # IIS Settings (optional) if ($Interactive -or -not $config.iisSettings) { Write-Host "`n[IIS Settings (Optional)]" -ForegroundColor Magenta if (-not $config.iisSettings) { $config['iisSettings'] = @{} } $config.iisSettings['httpPort'] = [int](Read-Value -Prompt "HTTP port" -Default ($config.iisSettings.httpPort ?? 8080)) $config.iisSettings['httpsPort'] = [int](Read-Value -Prompt "HTTPS port" -Default ($config.iisSettings.httpsPort ?? 443)) $config.iisSettings['runtimeVersion'] = Read-Value -Prompt "Runtime version (v4.0 or v2.0)" -Default ($config.iisSettings.runtimeVersion ?? "v4.0") $config.iisSettings['pipelineMode'] = Read-Value -Prompt "Pipeline mode (Integrated or Classic)" -Default ($config.iisSettings.pipelineMode ?? "Integrated") $enable32Bit = Read-Value -Prompt "Enable 32-bit apps (true/false)" -Default ($config.iisSettings.enable32BitApps ?? "false") $config.iisSettings['enable32BitApps'] = $enable32Bit -eq "true" $config.iisSettings['identityType'] = Read-Value -Prompt "Identity type" -Default ($config.iisSettings.identityType ?? "LocalSystem") } # Features (optional) if ($Interactive -or -not $config.features) { Write-Host "`n[Features (Optional)]" -ForegroundColor Magenta if (-not $config.features) { $config['features'] = @{} } $enableDebug = Read-Value -Prompt "Enable debug mode (true/false)" -Default ($config.features.enableDebugMode ?? "true") $config.features['enableDebugMode'] = $enableDebug -eq "true" $enableErrors = Read-Value -Prompt "Enable detailed errors (true/false)" -Default ($config.features.enableDetailedErrors ?? "true") $config.features['enableDetailedErrors'] = $enableErrors -eq "true" $addHosts = Read-Value -Prompt "Add hosts file entry (true/false)" -Default ($config.features.addHostsEntry ?? "true") $config.features['addHostsEntry'] = $addHosts -eq "true" $validateCert = Read-Value -Prompt "Validate certificate (true/false)" -Default ($config.features.validateCertificate ?? "true") $config.features['validateCertificate'] = $validateCert -eq "true" } # Validate configuration Write-Step "Validating Configuration" $errors = Validate-Configuration -Config $config if ($errors.Count -gt 0) { Write-Host "Configuration validation failed:" -ForegroundColor Red foreach ($err in $errors) { Write-Host " ✗ $err" -ForegroundColor Red } throw "Configuration validation failed" } Write-Success "Configuration is valid" # Save configuration Write-Step "Saving Configuration" $outputPath = Join-Path $PSScriptRoot $OutputFile # Convert to JSON with proper formatting $jsonConfig = $config | ConvertTo-Json -Depth 10 Set-Content -Path $outputPath -Value $jsonConfig -Encoding UTF8 Write-Success "Configuration saved to $outputPath" # Display summary Write-Host "`n╔════════════════════════════════════════════════════════════╗" -ForegroundColor Green Write-Host "║ Configuration Created Successfully ║" -ForegroundColor Green Write-Host "╚════════════════════════════════════════════════════════════╝" -ForegroundColor Green Write-Host "`nConfiguration Summary:" -ForegroundColor Cyan Write-Host " • Site Name: $($config.siteName)" -ForegroundColor White Write-Host " • Project URL: $($config.projectUrl)" -ForegroundColor White Write-Host " • Database: $($config.databaseServer)\$($config.databaseName)" -ForegroundColor White Write-Host " • Output File: $outputPath" -ForegroundColor White Write-Host "`nNext Steps:" -ForegroundColor Yellow Write-Host " 1. Review the configuration file: $OutputFile" -ForegroundColor White Write-Host " 2. Run as Administrator: .\setup-iis-for-admin-portal-locally.ps1 -ConfigFile $OutputFile" -ForegroundColor White Write-Host " 3. Open Visual Studio and start debugging" -ForegroundColor White } catch { Write-Host "`n✗ Error: $_" -ForegroundColor Red exit 1 }