Install-SecureMFA_WEPAPI_Portal.ps1
#Requires -RunAsAdministrator #Requires -Version 5.0 <# .SYNOPSIS Installs SecureMFA WEB API Portal on empty Windows Server. .DESCRIPTION Installs SecureMFA WEB API Portal on empty Windows Server and applies connection configuration to the backend database. Dependencies: * Server has Microsoft Framework 4.6.1 and above installed. * Deployment account must be member or local computer administrators� group to install successfully. * Server is Windows 2019 or 2016. * If you access SQL service with windows integrated authentication , you must have service account created and configured on IIS Application pool which runs your API WEB portal. .NOTES Version: 2.0.0.1 Author: SecureMfa.com Creation Date: 28/08/2020 Purpose/Change: Release .EXAMPLE C:\PS> Install-SecureMFA_WEPAPI_Portal -sqlserver "asqlaol1.adatum.labnet" -sqldbname "SecureMfaOTP" -sqlintegratedsecurity $true This command will Installs IIS and configures SecureMFA WEB API Portal on empty Windows Server. #> #Static Parameters $WebAPIPortalSource = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFAWebAPI.zip) $serverFQDN = (([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname).tolower() $webapi_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("Secure MFA WEBAPI") -eq $False) {New-EventLog -LogName "Application" -Source "Secure MFA WEBAPI" ; Write-Host "Secure MFA WEBAPI Log Source Created."} Function Install-SecureMFA_WEPAPI_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 = "SecureMFAWebAPI", [Parameter(Mandatory=$false)][string]$WebAPIPortalPath = "C:\inetpub\SecureMFAWebAPI\", [Parameter(Mandatory=$false)][string]$IISAppPoolName = "SecureMFAWebAPI", [Parameter(Mandatory=$false)][Switch]$Force ) #Check if .Net 4.5 Framework existi on the system $FrameworkVersion = "NET-Framework-45-Features"; $Framework = Get-WindowsFeature -Name $FrameworkVersion if(!$Framework.Installed) {Write-Host "WindowsFeature $FrameworkVersion could not be found installed on the server. Please Install WindowsFeature $FrameworkVersion and start again. Exiting!" -ForegroundColor Red}; if (!$Force) { $message = "Please confirm if you want to install WEB API Portal [Old deployment for web site $siteName will be deleted and reinstalled into $WebAPIPortalPath ]" $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 $WebAPIPortalSource -Type Leaf) ) { throw "$WebAPIPortalSource 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 #Site Configuration Import-Module WebAdministration #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 $webapi_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 $webapi_service_account :"} else {Write-Host "[Retry: $inputfailures] Passwords didn't match for $webapi_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 $webapi_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 if(!(Test-Path ($WebAPIPortalPath))){New-Item -ItemType Directory -Force -Path $WebAPIPortalPath} else {write-host "Folder already exist: $WebAPIPortalPath , cleaning up!." -ForegroundColor Yellow ; Remove-Item $WebAPIPortalPath -Recurse -Force ; New-Item -ItemType Directory -Force -Path $WebAPIPortalPath} Expand-Archive -LiteralPath $WebAPIPortalSource -DestinationPath $WebAPIPortalPath -Force New-WebSite -Name $siteName -Port 80 -HostHeader $serverFQDN -PhysicalPath $WebAPIPortalPath -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 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 web.config #Connection string update and replace $test = (Get-Content -path ($WebAPIPortalPath + "Web.Config") -Raw) $newtest = "<connectionStrings>CONNECTIONPLACEHOLDER</connectionStrings>" $pattern = "(?s)<connectionStrings>(.*?)</connectionStrings>" $result0 = [regex]::match($test, $pattern).Groups[1].Value $result1 = [regex]::match($newtest, $pattern).Groups[1].Value $test -replace [regex]::Escape($result0), $result1.Replace('$', '$$') | Set-Content -Path ($WebAPIPortalPath + "Web.Config") if ($sqlintegratedsecurity) {$ConnectionString = "<add name=`"SecureMfaOTPEntities`" connectionString=`"metadata=res://*/Models.SecureMFAOTP.csdl|res://*/Models.SecureMFAOTP.ssdl|res://*/Models.SecureMFAOTP.msl;provider=System.Data.SqlClient;provider connection string="data source=" + $sqlserver + ";initial catalog=" + $sqldbname + ";integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"`" providerName=`"System.Data.EntityClient`" />"} else {$ConnectionString = "<add name=`"SecureMfaOTPEntities`" connectionString=`"metadata=res://*/Models.SecureMFAOTP.csdl|res://*/Models.SecureMFAOTP.ssdl|res://*/Models.SecureMFAOTP.msl;provider=System.Data.SqlClient;provider connection string="data source=" + $sqlserver + ";initial catalog=" + $sqldbname + ";persist security info=True;user id=" + $sqluseraccount + ";password=" + $sqluserpassword + ";MultipleActiveResultSets=True;App=EntityFramework"`" providerName=`"System.Data.EntityClient`" />"} (Get-Content -path ($WebAPIPortalPath + "Web.Config") -Raw) -replace 'CONNECTIONPLACEHOLDER',$ConnectionString| Set-Content -Path ($WebAPIPortalPath + "Web.Config") #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."} #Get <appSettings> values write-host "IIS Site: $siteName ($WebAPIPortalPath) application settings. To update values use Update-SecureMFA_WEPAPI_Portal PS command." -ForegroundColor Green Get-WebConfigurationProperty -pspath "iis:\Sites\$siteName" -filter "/appSettings/add" -name * | select key,value | FT # Verify your WEB API service by navigating into following URLs using your browser: write-host "Installation of Site: $siteName ($WebAPIPortalPath) complete. App Pool Service account: $webapi_service_account" -ForegroundColor Green write-host "WEB API service endpoints for access:" -ForegroundColor Green write-host "http://$serverFQDN/api/securemfaotp" -ForegroundColor Cyan write-host "http://$serverFQDN/" -ForegroundColor Cyan } catch { Write-Host "$($MyInvocation.InvocationName): $_" -ForegroundColor red } } |