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=&quot;data source=" + $sqlserver + ";initial catalog=" + $sqldbname + ";integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;`" 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=&quot;data source=" + $sqlserver + ";initial catalog=" + $sqldbname + ";persist security info=True;user id=" + $sqluseraccount + ";password=" + $sqluserpassword + ";MultipleActiveResultSets=True;App=EntityFramework&quot;`" 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
    }    


}