Install-SecureMFA_SSPR_Portal.ps1
#Requires -RunAsAdministrator #Requires -Version 5.0 <# .SYNOPSIS Installs SecureMFA SSPR Portal on empty Windows Server. .DESCRIPTION Installs SecureMFA SSPR Portal on empty Windows Server and applies connection configuration to the backend database amd IIS application pool. Dependencies: * Server is Microsoft Windows x64 architecture and can run IIS 10 and above. * Deployment account must be member or local computer administrators� group to install successfully. * Windows Hosting Bundle Installer deployed for ASP.NET Core 3.1 Runtime (minimum v3.1.10). The installer URL can be obtained from: https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-aspnetcore-3.1.10-windows-hosting-bundle-installer Specify a path to downloaded file using -dotnet_hosting_bundle_path parameter to install it during SSPR Portal installation. * If you access SQL service with windows integrated authentication , you must have service account created and configured on IIS Application pool which runs your SSPR Portal. .NOTES Version: 2.0.0.1 Author: SecureMfa.com Creation Date: 06/01/2021 Purpose/Change: New Release .EXAMPLE C:\PS> Install-SecureMFA_SSPR_Portal -sqlserver "asqlaol1.adatum.labnet,1433" -sqldbname "SecureMfaOTP" -sqlintegratedsecurity $true This command will Installs IIS applies configuration to IIS Application Pools and configures SecureMFA SSPR Portal on empty Windows Server. #> #Static Parameters $WebSSPRPortalSource = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFASSPR.zip) $serverFQDN = (([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname).tolower() $websspr_service_account = "NA"; $ConnectionString = "" $sqluseraccount = "sa" $sqluserpassword = "" #Check if windows events source for application log exist, if not create one. if ([System.Diagnostics.EventLog]::SourceExists("SecureMFA SSPR") -eq $False) {New-EventLog -LogName "Application" -Source "SecureMFA SSPR" ; Write-Host "SecureMFA SSPR Log Source Created."} Function Install-SecureMFA_SSPR_Portal { Param ( [Parameter(Mandatory=$false)][string]$sqlserver = "asqlaol1.adatum.labnet", [Parameter(Mandatory=$false)][string]$sqldbname = "SecureMfaOTP", [Parameter(Mandatory=$false)][bool]$sqlintegratedsecurity = $true, [Parameter(Mandatory=$false)][string]$siteName = "SecureMFASSPR", [Parameter(Mandatory=$false)][string]$WebSSPRPortalPath = "C:\inetpub\SecureMFASSPR\", [Parameter(Mandatory=$false)][string]$IISAppPoolName = "SecureMFASSPR", [Parameter(Mandatory=$false)][string]$dotnet_hosting_bundle_path = "c:\temp\dotnet-hosting-3.1.10-win.exe", [Parameter(Mandatory=$false)][Switch]$Force ) #START if (!$Force) { $message = "Please confirm if you want to install SecureMFA SSPR Portal [Old deployment for web site $siteName will be deleted and reinstalled into $WebSSPRPortalPath ]" $question = 'Please confirm?' $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision_Validation = $Host.UI.PromptForChoice($message, $question, $choices, 0) if ($decision_Validation -eq 1 ) {Write-Host "Deployment has been cancelled, exiting!" -ForegroundColor Yellow ; break} } #Read sql usrname and password if windows integrated security is not used if(!$sqlintegratedsecurity) { write-host "Please enter sql account details for web.config file configuration." -ForegroundColor Cyan $sqluseraccount = $( Read-Host "Enter sqluseraccount") $sqluserpasswordsec = $( Read-Host "Enter sqluserpassword" -AsSecureString ) $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sqluserpasswordsec) $sqluserpassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) } try { $Error.Clear() if (!(Test-Path $WebSSPRPortalSource -Type Leaf) ) { throw "$WebSSPRPortalSource does not exist" ; break} #Start deployment #Deploy IIS Service with SecureMFA Web API Portal #Install-WindowsFeature Web-Server,Web-Net-Ext45,Web-Asp-Net45,Web-Mgmt-Console,Web-Scripting-Tools,Web-Http-Logging Install-WindowsFeature Web-Server,Web-Filtering,Web-Mgmt-Console,Web-Scripting-Tools,Web-Stat-Compression,Web-Http-Logging,Web-Static-Content,Web-Http-Errors,Web-Dir-Browsing,Web-Default-Doc #Site Configuration Import-Module WebAdministration $dotnet_core_hosting_module = Get-WebGlobalModule | where-object { $_.name.ToLower() -like "aspnetcoremodule*" } if (!$dotnet_core_hosting_module) { if (!(Test-Path $dotnet_hosting_bundle_path -Type Leaf) ) { throw "Cannot deploy dotnet hosting bundle because file does not exist in $dotnet_hosting_bundle_path" ; break} Write-Host "Installing dotnet hosting bundle $dotnet_hosting_bundle_path ..." Start-Process $dotnet_hosting_bundle_path -ArgumentList "/install /passive /quiet /norestart" -Wait -NoNewWindow -PassThru } else { Write-Host "dotnet hosting bundle has been allready installed." } #Create a new IIS app pool if (Test-Path ("IIS:\AppPools\" + $IISAppPoolName)) { Write-Host "$IISAppPoolName IIS AppPool exists. Skipping configuration ..." -ForegroundColor Yellow } else { Write-Host "Creating $IISAppPoolName IIS AppPool ..." $appPool = New-WebAppPool -Name $IISAppPoolName $message = "Do you want to set up App pool account on IIS server for website $siteName (Recommended)" $question = 'Please confirm?' $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision_Validation2 = $Host.UI.PromptForChoice($message, $question, $choices, 0) #Select YES if ($decision_Validation2 -eq 0) { #Configure service account for IIS apppool $websspr_service_account = Read-Host "Enter service account username for IIS application pool $IISAppPoolName in following format DOMAIN\USERNAME " do { if($inputfailures -eq 0) {Write-Host "Please enter a password for $IISAppPoolName AppPool Service Account $websspr_service_account :"} else {Write-Host "[Retry: $inputfailures] Passwords didn't match for $websspr_service_account Try again:"} $pwd1 = Read-Host "Password" -AsSecureString $pwd2 = Read-Host "Re-enter Password" -AsSecureString $pwd1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd1)) $pwd2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd2)) $inputfailures++ } while ($pwd1_text -ne $pwd2_text ) $fscredential = new-object -typename System.Management.Automation.PSCredential -argumentlist $websspr_service_account,$pwd1 Set-ItemProperty "IIS:\AppPools\$IISAppPoolName" -name processModel.identityType -Value SpecificUser Set-ItemProperty "IIS:\AppPools\$IISAppPoolName" -name processModel.userName -Value $fscredential.UserName Set-ItemProperty "IIS:\AppPools\$IISAppPoolName" -name processModel.password -Value $pwd1_text } else {Write-Host "App pool account creation has been cancelled, skipping ..." -ForegroundColor Yellow ;} } #Create WEB API portal #Stop IIS website and app pool if it was deployed before. $waitunload = $false if ((Get-IISAppPool -Name $IISAppPoolName).state -eq "Started") {Stop-WebAppPool -Name $IISAppPoolName ; write-host "$IISAppPoolName app pool has been stoped."; $waitunload = $true} else {write-host "$IISAppPoolName app pool is not running."} if ((Get-Website -Name $siteName).state -eq "Started") {Stop-Website $siteName ; write-host "$siteName website has been stoped."; $waitunload = $true} else {write-host "$siteName website is not running."} if($waitunload) {write-host "Waiting for $siteName to unlaod ..." ; Start-Sleep -s 10} if(!(Test-Path ($WebSSPRPortalPath))){New-Item -ItemType Directory -Force -Path $WebSSPRPortalPath} else {write-host "Folder already exist: $WebSSPRPortalPath , cleaning up!." -ForegroundColor Yellow ; Remove-Item $WebSSPRPortalPath -Recurse -Force ; New-Item -ItemType Directory -Force -Path $WebSSPRPortalPath} Expand-Archive -LiteralPath $WebSSPRPortalSource -DestinationPath $WebSSPRPortalPath -Force New-WebSite -Name $siteName -Port 80 -HostHeader $serverFQDN -PhysicalPath $WebSSPRPortalPath -ApplicationPool $IISAppPoolName -Force # IIS Website performance tunning. #Ensure Application Initialization is available $webAppInit = Get-WindowsFeature -Name "Web-AppInit" if(!$webAppInit.Installed) { Write-Host "$($webAppInit.DisplayName) not present, installing" Install-WindowsFeature $webAppInit -ErrorAction Stop Write-Host "`nInstalled $($webAppInit.DisplayName)`n" -ForegroundColor Green } else {Write-Host "$($webAppInit.DisplayName) was already installed" -ForegroundColor Yellow} #Fetch the site $site = Get-Website -Name $siteName if(!$site) { Write-Host "Site $siteName could not be found, exiting!" -ForegroundColor Yellow Break } #Fetch the application pool $appPool = Get-ChildItem IIS:\AppPools\ | Where-Object { $_.Name -eq $site.applicationPool } #Set up .NET CLR version to be No Managed Code if($appPool.startMode -ne "AlwaysRunning") { Write-Host ".NET CLR version is set to $($appPool.managedRuntimeVersion), changing to 'No Managed Code'" $appPool | Set-ItemProperty -name "managedRuntimeVersion" -Value "" $appPool = Get-ChildItem IIS:\AppPools\ | Where-Object { $_.Name -eq $site.applicationPool } Write-Host ".NET CLR version is now set to 'No Managed Code'`n" -ForegroundColor Green } else {Write-Host ".NET CLR version was already set to 'No Managed Code' for the application pool $($site.applicationPool)" -ForegroundColor Yellow} #Set up AlwaysRunning if($appPool.startMode -ne "AlwaysRunning") { Write-Host "startMode is set to $($appPool.startMode ), activating AlwaysRunning" $appPool | Set-ItemProperty -name "startMode" -Value "AlwaysRunning" $appPool = Get-ChildItem IIS:\AppPools\ | Where-Object { $_.Name -eq $site.applicationPool } Write-Host "startMode is now set to $($appPool.startMode)`n" -ForegroundColor Green } else {Write-Host "startMode was already set to $($appPool.startMode) for the application pool $($site.applicationPool)" -ForegroundColor Yellow} #Enable preloadEnabled on the IIS Site instance if(!(Get-ItemProperty "IIS:\Sites\$siteName" -Name applicationDefaults.preloadEnabled).Value) { Write-Host "preloadEnabled is inactive, activating" Set-ItemProperty "IIS:\Sites\$siteName" -Name applicationDefaults.preloadEnabled -Value True Write-Host "preloadEnabled is now set to $((Get-ItemProperty "IIS:\Sites\$siteName" -Name applicationDefaults.preloadEnabled).Value)" -ForegroundColor Green } else {Write-Host "preloadEnabled already active" -ForegroundColor Yellow} #Apply connection string into appsettings.json $AppConfig = (Get-Content -path ($WebSSPRPortalPath + "appsettings.json")) -replace '^\s*//.*' | Out-String | ConvertFrom-Json if($sqlintegratedsecurity) {$AppConfig.ConnectionStrings.DefaultConnection = "Server=" + $sqlserver + ";Database=" + $sqldbname + ";integrated security=true;user id=;password="} else {$AppConfig.ConnectionStrings.DefaultConnection = "Server=" + $sqlserver + ";Database=" + $sqldbname + ";integrated security=false;user id=" + $sqluseraccount + ";password=" + $sqluserpassword} $AppConfig | ConvertTo-Json | set-content -Path ($WebSSPRPortalPath + "appsettings.json") #Start IIS website and app pool. if ((Get-IISAppPool -Name $IISAppPoolName).state -eq "Started") {write-host "$IISAppPoolName app pool is running."} else {Start-WebAppPool -Name $IISAppPoolName ; write-host "$IISAppPoolName app pool has been started."} if ((Get-Website -Name $siteName).state -eq "Started") {write-host "$siteName website is running."} else {Start-Website $siteName ; write-host "$siteName website has been started."} # Verify your SSPR Portal service by navigating into following URLs using your browser: write-host "Installation of Site: $siteName ($WebSSPRPortalPath) complete. App Pool Service account: $websspr_service_account" -ForegroundColor Green write-host "SSPR Portal service endpoints for access:" -ForegroundColor Green write-host "http://$serverFQDN/" -ForegroundColor Cyan } catch { Write-Host "$($MyInvocation.InvocationName): $_" -ForegroundColor red } } |