|
#Requires -RunAsAdministrator |
|
#Requires -Version 7.0 |
|
|
|
<# |
|
.SYNOPSIS |
|
Sets up IIS for local TailorWell.Admin development with HTTPS support. |
|
|
|
.DESCRIPTION |
|
This script configures IIS with an application pool, site, and HTTPS binding |
|
for the TailorWell.Admin ASP.NET application. It reads configuration from a |
|
JSON file and sets up the database connection through an environment variable. |
|
|
|
.PARAMETER ConfigFile |
|
Path to the JSON configuration file. Defaults to "bobby-config.json" in the same directory. |
|
|
|
.PARAMETER SkipValidation |
|
Skip configuration validation steps. |
|
|
|
.EXAMPLE |
|
.\setup-iis-for-admin-portal-locally.ps1 |
|
.\setup-iis-for-admin-portal-locally.ps1 -ConfigFile "team-config.json" |
|
#> |
|
|
|
param( |
|
[string]$ConfigFile = "$PSScriptRoot\me-config.json", |
|
[switch]$SkipValidation |
|
) |
|
|
|
$ErrorActionPreference = "Stop" |
|
$ProgressPreference = "Continue" |
|
|
|
# Helper functions |
|
function Write-Step { |
|
param([string]$Message) |
|
Write-Host "`n[$([datetime]::Now.ToString('HH:mm:ss'))] $Message" -ForegroundColor Cyan |
|
} |
|
|
|
function Write-Success { |
|
param([string]$Message) |
|
Write-Host "✓ $Message" -ForegroundColor Green |
|
} |
|
|
|
function Write-Warning { |
|
param([string]$Message) |
|
Write-Host "⚠ $Message" -ForegroundColor Yellow |
|
} |
|
|
|
function Write-Error { |
|
param([string]$Message) |
|
Write-Host "✗ $Message" -ForegroundColor Red |
|
} |
|
|
|
function Test-Administrator { |
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() |
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser) |
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) |
|
} |
|
|
|
function Test-IISInstalled { |
|
return Test-Path "$env:SystemRoot\System32\inetsrv\appcmd.exe" |
|
} |
|
|
|
function Invoke-AppCmd { |
|
param( |
|
[string]$Arguments, |
|
[switch]$IgnoreError |
|
) |
|
|
|
$appcmd = "$env:SystemRoot\System32\inetsrv\appcmd.exe" |
|
$result = & $appcmd $Arguments.Split(' ') 2>&1 |
|
|
|
if ($LASTEXITCODE -ne 0 -and -not $IgnoreError) { |
|
throw "appcmd failed: $result" |
|
} |
|
|
|
return $result |
|
} |
|
|
|
# Main script |
|
try { |
|
Write-Step "Starting IIS Setup for TailorWell.Admin" |
|
|
|
if (-not (Test-Administrator)) { |
|
throw "This script must be run as Administrator. Please restart PowerShell as Administrator." |
|
} |
|
|
|
if (-not (Test-IISInstalled)) { |
|
throw "IIS is not installed. Please install IIS with ASP.NET support first." |
|
} |
|
|
|
Write-Step "Loading configuration from $ConfigFile" |
|
if (-not (Test-Path $ConfigFile)) { |
|
throw "Configuration file not found: $ConfigFile" |
|
} |
|
|
|
$config = Get-Content $ConfigFile -Raw | ConvertFrom-Json |
|
Write-Success "Configuration loaded successfully" |
|
|
|
if (-not $SkipValidation) { |
|
Write-Step "Validating configuration" |
|
|
|
if (-not (Test-Path $config.certificatePath)) { |
|
throw "Certificate file not found: $($config.certificatePath)" |
|
} |
|
Write-Success "Certificate file found" |
|
|
|
if (-not (Test-Path $config.projectPath)) { |
|
throw "Project path not found: $($config.projectPath)" |
|
} |
|
Write-Success "Project path found" |
|
|
|
if ([string]::IsNullOrWhiteSpace($config.databaseServer) -or $config.databaseServer -eq "YOUR_DATABASE_SERVER") { |
|
throw "Database server not configured. Please update the configuration file with actual database credentials." |
|
} |
|
if ([string]::IsNullOrWhiteSpace($config.databaseUserId) -or $config.databaseUserId -eq "YOUR_USER_ID") { |
|
throw "Database user ID not configured. Please update the configuration file with actual database credentials." |
|
} |
|
if ([string]::IsNullOrWhiteSpace($config.databasePassword) -or $config.databasePassword -eq "YOUR_PASSWORD") { |
|
throw "Database password not configured. Please update the configuration file with actual database credentials." |
|
} |
|
Write-Success "Database configuration validated" |
|
} |
|
|
|
Write-Step "Setting up database environment variable" |
|
$connectionString = "Server=$($config.databaseServer);Database=$($config.databaseName);User Id=$($config.databaseUserId);Password=$($config.databasePassword);Encrypt=true;TrustServerCertificate=true;Connection Timeout=30;" |
|
|
|
$env:ADMIN_PORTAL_V8_PRIMARY_DB_CONNECTION = $connectionString |
|
|
|
[System.Environment]::SetEnvironmentVariable( |
|
$config.databaseEnvironmentVariable, |
|
$connectionString, |
|
[System.EnvironmentVariableTarget]::User |
|
) |
|
Write-Success "Environment variable '$($config.databaseEnvironmentVariable)' set" |
|
|
|
Write-Step "Enabling required IIS features" |
|
$features = @( |
|
"IIS-WebServerRole", |
|
"IIS-WebServer", |
|
"IIS-CommonHttpFeatures", |
|
"IIS-HttpErrors", |
|
"IIS-HttpRedirect", |
|
"IIS-ApplicationDevelopment", |
|
"IIS-NetFxExtensibility45", |
|
"IIS-HealthAndDiagnostics", |
|
"IIS-HttpLogging", |
|
"IIS-Security", |
|
"IIS-RequestFiltering", |
|
"IIS-Performance", |
|
"IIS-WebServerManagementTools", |
|
"IIS-ManagementConsole", |
|
"IIS-IIS6ManagementCompatibility", |
|
"IIS-Metabase", |
|
"IIS-ASPNET45", |
|
"IIS-NetFxExtensibility", |
|
"IIS-ISAPIExtensions", |
|
"IIS-ISAPIFilter" |
|
) |
|
|
|
foreach ($feature in $features) { |
|
Write-Host " Enabling $feature..." -NoNewline |
|
Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart -WarningAction SilentlyContinue | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
} |
|
Write-Success "IIS features enabled" |
|
|
|
Write-Step "Registering ASP.NET with IIS" |
|
Write-Host " Registering ASP.NET 4.0..." -NoNewline |
|
$aspnetRegPath = "$env:WINDIR\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe" |
|
if (Test-Path $aspnetRegPath) { |
|
& $aspnetRegPath -iru | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
|
|
Write-Host " Configuring handler mappings..." -NoNewline |
|
Invoke-AppCmd "set config -section:system.webServer/handlers /accessPolicy:Read,Script,Execute" -IgnoreError | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
} else { |
|
Write-Warning "ASP.NET registration tool not found" |
|
} |
|
Write-Success "ASP.NET registration complete" |
|
|
|
Write-Step "Clearing ASP.NET temporary files" |
|
$tempPaths = @( |
|
"$env:WINDIR\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files", |
|
"$env:WINDIR\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files", |
|
"$env:TEMP\Temporary ASP.NET Files", |
|
"$env:LOCALAPPDATA\Temp\Temporary ASP.NET Files" |
|
) |
|
|
|
foreach ($tempPath in $tempPaths) { |
|
if (Test-Path $tempPath) { |
|
Write-Host " Clearing $([System.IO.Path]::GetFileName($tempPath))..." -NoNewline |
|
try { |
|
Get-ChildItem $tempPath -Recurse -ErrorAction SilentlyContinue | |
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue |
|
Write-Host " Done" -ForegroundColor Green |
|
} catch { |
|
Write-Host " Skipped (in use)" -ForegroundColor Yellow |
|
} |
|
} |
|
} |
|
Write-Success "Temporary files cleared" |
|
|
|
Write-Step "Resetting IIS" |
|
Write-Host " Stopping IIS..." -NoNewline |
|
iisreset /stop /noforce | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
Write-Host " Starting IIS..." -NoNewline |
|
iisreset /start | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
Write-Success "IIS reset complete" |
|
|
|
Write-Step "Configuring Application Pool" |
|
$existingPool = Invoke-AppCmd "list apppool /name:$($config.appPoolName)" -IgnoreError |
|
if ($existingPool -and $existingPool -notlike "*ERROR*") { |
|
Write-Warning "Removing existing application pool" |
|
Invoke-AppCmd "stop apppool /apppool.name:$($config.appPoolName)" -IgnoreError | Out-Null |
|
Invoke-AppCmd "delete apppool /apppool.name:$($config.appPoolName)" |
|
} |
|
|
|
Write-Host " Creating application pool..." -NoNewline |
|
Invoke-AppCmd "add apppool /name:$($config.appPoolName)" | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
|
|
Write-Host " Configuring pool settings..." -NoNewline |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /managedRuntimeVersion:$($config.iisSettings.runtimeVersion)" | Out-Null |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /managedPipelineMode:$($config.iisSettings.pipelineMode)" | Out-Null |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /enable32BitAppOnWin64:$($config.iisSettings.enable32BitApps.ToString().ToLower())" | Out-Null |
|
|
|
if ($config.iisSettings.identityType -eq "LocalSystem") { |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /processModel.identityType:LocalSystem" | Out-Null |
|
} elseif ($config.iisSettings.identityType -eq "NetworkService") { |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /processModel.identityType:NetworkService" | Out-Null |
|
} elseif ($config.iisSettings.identityType -eq "LocalService") { |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /processModel.identityType:LocalService" | Out-Null |
|
} else { |
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /processModel.identityType:ApplicationPoolIdentity" | Out-Null |
|
} |
|
|
|
Invoke-AppCmd "set apppool /apppool.name:$($config.appPoolName) /recycling.periodicRestart.time:00:00:00" | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
|
|
Write-Success "Application pool '$($config.appPoolName)' created" |
|
|
|
Write-Step "Configuring IIS Site" |
|
$existingSite = Invoke-AppCmd "list site /name:$($config.siteName)" -IgnoreError |
|
if ($existingSite -and $existingSite -notlike "*ERROR*") { |
|
Write-Warning "Removing existing site" |
|
Invoke-AppCmd "stop site /site.name:$($config.siteName)" -IgnoreError | Out-Null |
|
Invoke-AppCmd "delete site /site.name:$($config.siteName)" -IgnoreError | Out-Null |
|
} |
|
|
|
$cleanPath = $config.projectPath.Replace('/', '\').TrimEnd('\') |
|
if (-not (Test-Path $cleanPath)) { |
|
throw "Physical path does not exist: $cleanPath" |
|
} |
|
|
|
Write-Host " Finding available site ID..." -NoNewline |
|
$allSites = Invoke-AppCmd "list sites" -IgnoreError |
|
$usedIds = @(1) |
|
foreach ($line in $allSites) { |
|
if ($line -match 'id:(\d+)') { |
|
$usedIds += [int]$matches[1] |
|
} |
|
} |
|
$siteId = 2 |
|
while ($usedIds -contains $siteId) { |
|
$siteId++ |
|
} |
|
Write-Host " Using ID: $siteId" -ForegroundColor Green |
|
|
|
Write-Host " Creating site with application..." -NoNewline |
|
$createCmd = "add site /name:$($config.siteName) /id:$siteId /physicalPath:$cleanPath /bindings:http/*:$($config.iisSettings.httpPort):" |
|
Invoke-AppCmd $createCmd | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
|
|
Write-Host " Setting application pool..." -NoNewline |
|
Invoke-AppCmd "set site $($config.siteName) /applicationDefaults.applicationPool:$($config.appPoolName)" | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
|
|
Write-Host " Configuring ASP.NET modules..." -NoNewline |
|
Invoke-AppCmd "set config $($config.siteName) /section:system.webServer/modules /runAllManagedModulesForAllRequests:true" -IgnoreError | Out-Null |
|
Invoke-AppCmd "set config $($config.siteName) /section:system.webServer/handlers /accessPolicy:Read,Script" -IgnoreError | Out-Null |
|
Write-Host " Done" -ForegroundColor Green |
|
|
|
Write-Success "Site '$($config.siteName)' created with ID $siteId" |
|
|
|
Write-Step "Configuring HTTPS certificate" |
|
|
|
$certPassword = if ($config.certificatePassword) { |
|
ConvertTo-SecureString -String $config.certificatePassword -AsPlainText -Force |
|
} else { |
|
$null |
|
} |
|
|
|
$cert = Import-PfxCertificate -FilePath $config.certificatePath -CertStoreLocation Cert:\LocalMachine\My -Password $certPassword |
|
Write-Success "Certificate imported (Thumbprint: $($cert.Thumbprint))" |
|
|
|
$uri = [System.Uri]$config.projectUrl |
|
$hostname = $uri.Host |
|
|
|
Invoke-AppCmd "set site /site.name:$($config.siteName) /+bindings.[protocol='https',bindingInformation='*:$($config.iisSettings.httpsPort):$hostname']" | Out-Null |
|
|
|
$bindingInfo = netsh http show sslcert hostnameport="${hostname}:$($config.iisSettings.httpsPort)" 2>$null |
|
if ($bindingInfo -notlike "*error*" -and $bindingInfo -notlike "*not found*") { |
|
Write-Warning "Removing existing SSL certificate binding" |
|
netsh http delete sslcert hostnameport="${hostname}:$($config.iisSettings.httpsPort)" | Out-Null |
|
} |
|
|
|
$appId = "{$([guid]::NewGuid().ToString().ToUpper())}" |
|
netsh http add sslcert hostnameport="${hostname}:$($config.iisSettings.httpsPort)" certhash=$($cert.Thumbprint) appid="$appId" certstorename=MY | Out-Null |
|
Write-Success "HTTPS binding configured for $hostname" |
|
|
|
if ($config.features.addHostsEntry) { |
|
Write-Step "Updating hosts file" |
|
$hostsFile = "$env:SystemRoot\System32\drivers\etc\hosts" |
|
$hostsContent = Get-Content $hostsFile -Raw |
|
$hostEntry = "127.0.0.1`t$hostname" |
|
|
|
if ($hostsContent -notmatch [regex]::Escape($hostname)) { |
|
Add-Content -Path $hostsFile -Value "`n$hostEntry" -Encoding ASCII |
|
Write-Success "Added hosts file entry for $hostname" |
|
} else { |
|
Write-Warning "Hosts file entry already exists for $hostname" |
|
} |
|
} |
|
|
|
Write-Step "Setting permissions for debugging" |
|
$projectDir = $config.projectPath |
|
$acl = Get-Acl $projectDir |
|
|
|
$permission = "IIS_IUSRS", "Modify", "ContainerInherit,ObjectInherit", "None", "Allow" |
|
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission |
|
$acl.SetAccessRule($accessRule) |
|
|
|
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name |
|
$permission = $currentUser, "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" |
|
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission |
|
$acl.SetAccessRule($accessRule) |
|
|
|
Set-Acl -Path $projectDir -AclObject $acl |
|
Write-Success "Permissions configured for debugging" |
|
|
|
Write-Step "Starting IIS components" |
|
|
|
Write-Host " Starting application pool..." -NoNewline |
|
try { |
|
$poolStatus = Invoke-AppCmd "list apppool /name:$($config.appPoolName) /state" -IgnoreError |
|
if ($poolStatus -notlike "*Started*") { |
|
Invoke-AppCmd "start apppool /apppool.name:$($config.appPoolName)" | Out-Null |
|
Write-Host " Started" -ForegroundColor Green |
|
} else { |
|
Write-Host " Already running" -ForegroundColor Green |
|
} |
|
} catch { |
|
Write-Host " Failed" -ForegroundColor Red |
|
Write-Warning "Could not start application pool: $_" |
|
} |
|
|
|
Write-Host " Starting site..." -NoNewline |
|
try { |
|
$siteStatus = Invoke-AppCmd "list site /name:$($config.siteName) /state" -IgnoreError |
|
if ($siteStatus -notlike "*Started*") { |
|
Invoke-AppCmd "start site /site.name:$($config.siteName)" | Out-Null |
|
Write-Host " Started" -ForegroundColor Green |
|
} else { |
|
Write-Host " Already running" -ForegroundColor Green |
|
} |
|
} catch { |
|
Write-Host " Failed" -ForegroundColor Red |
|
Write-Warning "Could not start site: $_" |
|
} |
|
|
|
Start-Sleep -Seconds 2 |
|
$finalPoolStatus = Invoke-AppCmd "list apppool /name:$($config.appPoolName)" -IgnoreError |
|
if ($finalPoolStatus -like "*Started*") { |
|
Write-Success "Application pool and site started successfully" |
|
} else { |
|
Write-Warning "Application pool may not be running properly. Check Event Viewer for errors." |
|
} |
|
|
|
Write-Step "Testing site accessibility" |
|
Write-Host " Testing HTTP response..." -NoNewline |
|
Start-Sleep -Seconds 2 |
|
try { |
|
$testUrl = "http://localhost:$($config.iisSettings.httpPort)/" |
|
$response = Invoke-WebRequest -Uri $testUrl -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop |
|
Write-Host " Success (Status: $($response.StatusCode))" -ForegroundColor Green |
|
Write-Success "Site is accessible at $testUrl" |
|
} catch { |
|
if ($_.Exception.Response.StatusCode -eq 500) { |
|
Write-Host " Site responds but has errors (500)" -ForegroundColor Yellow |
|
Write-Warning "Site is returning 500 errors. Check:" |
|
Write-Warning " - Application has been built (DLLs in bin folder)" |
|
Write-Warning " - Web.config is valid" |
|
Write-Warning " - Database connection is configured" |
|
Write-Warning " - Check Event Viewer for detailed errors" |
|
} else { |
|
Write-Host " Failed" -ForegroundColor Red |
|
Write-Warning "Could not access site: $_" |
|
} |
|
} |
|
|
|
Write-Host "`n" -NoNewline |
|
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green |
|
Write-Host " IIS Setup Complete!" -ForegroundColor Green |
|
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green |
|
Write-Host "`n Site URL: $($config.projectUrl)" -ForegroundColor Cyan |
|
Write-Host " App Pool: $($config.appPoolName)" -ForegroundColor Cyan |
|
Write-Host " Site Name: $($config.siteName)" -ForegroundColor Cyan |
|
Write-Host " Project: $($config.projectPath)" -ForegroundColor Cyan |
|
Write-Host "`n Next Steps:" -ForegroundColor Yellow |
|
Write-Host " 1. Open Visual Studio and load the solution" -ForegroundColor White |
|
Write-Host " 2. Set Company.Admin as the startup project" -ForegroundColor White |
|
Write-Host " 3. Press F5 to debug or Ctrl+F5 to run" -ForegroundColor White |
|
Write-Host " 4. Navigate to $($config.projectUrl)" -ForegroundColor White |
|
Write-Host "`n═══════════════════════════════════════════════════════════" -ForegroundColor Green |
|
|
|
} catch { |
|
Write-Error "Setup failed: $_" |
|
exit 1 |
|
} |