Install-ADFS_Demo_with_OTP.ps1
#Requires -RunAsAdministrator #Requires -Version 4.0 <# .SYNOPSIS Installs ADFS Service with SecureMFA OTP Provider Web Portal on empty Windows Server. .DESCRIPTION Installs ADFS Service with SecureMFA OTP Provider on a single ADFS server using MS SQLEXPRESS database. Dependencies: * Deployment must be done with service account which will be used for ADFS service. Account must be member or local computer administrators� group and Domain Administrators groups for ADFS service to register successfully. * Server is Windows 2019 or 2016 serve and joined to domain. * Server has a valid certificate for ADFS Service communication. It must have CN name which is different to computer's FQDN * Server has "SqlServer" PS Module installed. * Server has a free edition of MSSQL SQLEXPRESS service installed using default Instance Name and Instance ID - SQLEXPRESS * Server has Microsoft Framework 4.6.1 and above installed. .NOTES Version: 2.0.0.1 Author: SecureMfa.com Creation Date: 27/07/2020 Purpose/Change: Release .EXAMPLE C:\PS> Install-ADFS_Demo_with_OTP -adfs_certificate_thumbprint "285394eae4e52bf64d3b6f6c304584f30c3f004f" This command will install ADFS service with SecureMFA OTP Provider and Web Portal which allows to enrol users on a single ADFS server using MSSQL SQLEXPRESS database. #> $zipfilepath = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFA_OTP_SPA.zip) $sqlservermodulepath = "C:\Program Files\WindowsPowerShell\Modules\SqlServer" #Check if windows events source for application log exist, if not create one. if ([System.Diagnostics.EventLog]::SourceExists("Secure MFA OTP") -eq $False) {New-EventLog -LogName "Application" -Source "Secure MFA OTP" ; Write-Host "Secure MFA OTP Log Source Created."} Function Install-ADFS_Demo_with_OTP { Param ( [Parameter(Mandatory=$true)][string]$adfs_certificate_thumbprint, [Parameter(Mandatory=$false)][Switch]$Force ) #Check if ADFS service existi on the system if(((Get-Service adfssrv -ErrorAction SilentlyContinue).Status -ne $null) -and (!($Force))) {write-host "ADFS Service is allready running on this server $env:COMPUTERNAME . Please use new Windows build or unistall ADFS deployment before retry." -ForegroundColor Yellow; break} #Check if ADFS service existi on the system if(((Get-Service 'MSSQL$SQLEXPRESS' -ErrorAction SilentlyContinue).Status -ne 'Running') -and (!($Force))) {write-host "MSSQL SQLEXPRESS Edition is not running on a server $env:COMPUTERNAME . Please make sure you deploy a Free editon of MSSQL SQLEXPRESS Edition using default Instance Name and Instance ID: SQLEXPRESS on this computer to continue." -ForegroundColor Yellow; break} try { $Error.Clear() if (!(Test-Path $zipfilepath -Type Leaf) ) { throw "$zipfilepath does not exist" ; break} if (!(Test-Path $sqlservermodulepath) ) { throw "SqServer module $sqlservermodulepath does not exist please install it from PSGalery: Install-Module sqlserver" ; break} if(!(Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -eq $adfs_certificate_thumbprint})) {throw "Certificate $adfs_certificate_thumbprint does not exist in Cert:\LocalMachine\My. Please install certificate first before retry." ; break} #Start deployment #SQL Procedure for WID Database creation $SQLProcedureTemplate = @( "USE [master]", "GO", "/****** Object: Database [SecureMfaOTP] Script Date: 27/01/2020 21:52:40 ******/", "CREATE DATABASE [SecureMfaOTP]", "CONTAINMENT = NONE", "ON PRIMARY", "( NAME = N'SecureMfaOTP', FILENAME = N'C:\SecureMFA\SecureMfaOTP.mdf' , SIZE = 6144KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )", "LOG ON", "( NAME = N'SecureMfaOTP_log', FILENAME = N'C:\SecureMFA\SecureMfaOTP_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)", "GO", "ALTER DATABASE [SecureMfaOTP] SET COMPATIBILITY_LEVEL = 120", "GO", "IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))", "begin", "EXEC [SecureMfaOTP].[dbo].[sp_fulltext_database] @action = 'enable'", "end", "GO", "ALTER DATABASE [SecureMfaOTP] SET ANSI_NULL_DEFAULT OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET ANSI_NULLS OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET ANSI_PADDING OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET ANSI_WARNINGS OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET ARITHABORT OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET AUTO_CLOSE OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET AUTO_SHRINK OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET AUTO_UPDATE_STATISTICS ON", "GO", "ALTER DATABASE [SecureMfaOTP] SET CURSOR_CLOSE_ON_COMMIT OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET CURSOR_DEFAULT GLOBAL", "GO", "ALTER DATABASE [SecureMfaOTP] SET CONCAT_NULL_YIELDS_NULL OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET NUMERIC_ROUNDABORT OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET QUOTED_IDENTIFIER OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET RECURSIVE_TRIGGERS OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET DISABLE_BROKER", "GO", "ALTER DATABASE [SecureMfaOTP] SET AUTO_UPDATE_STATISTICS_ASYNC OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET DATE_CORRELATION_OPTIMIZATION OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET TRUSTWORTHY OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET ALLOW_SNAPSHOT_ISOLATION OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET PARAMETERIZATION SIMPLE", "GO", "ALTER DATABASE [SecureMfaOTP] SET READ_COMMITTED_SNAPSHOT OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET HONOR_BROKER_PRIORITY OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET RECOVERY SIMPLE", "GO", "ALTER DATABASE [SecureMfaOTP] SET MULTI_USER", "GO", "ALTER DATABASE [SecureMfaOTP] SET PAGE_VERIFY CHECKSUM", "GO", "ALTER DATABASE [SecureMfaOTP] SET DB_CHAINING OFF", "GO", "ALTER DATABASE [SecureMfaOTP] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )", "GO", "ALTER DATABASE [SecureMfaOTP] SET TARGET_RECOVERY_TIME = 0 SECONDS", "GO", "ALTER DATABASE [SecureMfaOTP] SET DELAYED_DURABILITY = DISABLED", "GO", "EXEC sys.sp_db_vardecimal_storage_format N'SecureMfaOTP', N'ON'", "GO", "ALTER DATABASE [SecureMfaOTP] SET READ_WRITE", "GO", "USE [SecureMfaOTP]", "GO", "CREATE TABLE [dbo].[Secrets](", "[upn] [varchar](255) NOT NULL,", "[secret] [char](48) NOT NULL,", "[logon] [tinyint] NULL,", "[lastlogon] [datetime] NULL,", "[logoncount] [int] DEFAULT 0 NOT NULL,", "[failedlogoncount] [int] DEFAULT 0 NOT NULL,", "[failedlastlogon] [datetime] NULL,", "[failedcode] [char](6),", "[logonip] [varchar](39),", "[useragent] [varchar](128),", " CONSTRAINT [PK_Secrets] PRIMARY KEY CLUSTERED", "(", "[upn] ASC", ")WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]", ") ON [PRIMARY]", "GO", "CREATE TABLE [dbo].[UsedCodes](", "[upn] [varchar](255) NOT NULL,", "[interval] [bigint] NOT NULL", ") ON [PRIMARY]", "GO" ) #Start $adfs_service_account = "$env:USERDOMAIN\$env:USERNAME" do { if($inputfailures -eq 0) {Write-Host "Please enter a password for ADFS Service Account $adfs_service_account :"} else {Write-Host "[Retry: $inputfailures] Passwords didn't match for $adfs_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 $adfs_service_account,$pwd1 #Create Temp folder and export SQL procedure New-Item -Path "c:\" -Name "SecureMFA" -ItemType "directory" -Force $SQLProcedureTemplate | Out-File C:\SecureMFA\sql_Create_WID_Database_SecureMfaOTP.sql -Force #write-host "Adding $adfs_service_account account into the Local Administrators group" -ForegroundColor Green #Add-LocalGroupMember -Group "Administrators" -Member $adfs_service_account -ErrorAction SilentlyContinue #Select CN from certificate to use with ADFS FederationServiceName $adfs_service_name = Get-ChildItem -Path cert:\LocalMachine\My\$adfs_certificate_thumbprint | select subject | Select-String -Pattern 'CN\=([^},\r\n]+)' | %{$_.Matches.Groups[1].value} write-host "ADFS service name $adfs_service_name has been selected using CN value from $adfs_certificate_thumbprint certificate" -ForegroundColor Green #ADFS Install write-host "Installing ADFS service on a single server" -ForegroundColor Green Add-WindowsFeature ADFS-Federation -Includemanagementtools $install = Install-AdfsFarm -CertificateThumbprint $adfs_certificate_thumbprint -FederationServiceName $adfs_service_name -ServiceAccountCredential $fscredential sleep -Seconds 10 write-host "Installation status:" $install.Status write-host $install.Message -ForegroundColor Yellow #Enable AUdit AUDITPOL /SET /SUBCATEGORY:"Application Generated" /FAILURE:ENABLE /SUCCESS:ENABLE #Enable SAML 2.0 IdP-initiated sign-on set-adfsproperties -EnableIdpInitiatedSignon $True #Test ADFS Service installation Test-AdfsFarmInstallation -FederationServiceName $adfs_service_name -ServiceAccountCredential $fscredential write-host "Starting SecureMFA OTP Install" -ForegroundColor Green #SQL WID Configuration on localhost Invoke-Sqlcmd -ServerInstance "localhost\SQLEXPRESS" -InputFile "C:\SecureMFA\sql_Create_WID_Database_SecureMfaOTP.sql" #Deploy SecureMFA OTP Provider Install-SecureMfaOtpProvider # Verify your ADFS service by navigating into following URLs using your browser: write-host "Installation of ADFS service $adfs_service_name complete." -ForegroundColor Green write-host "ADFS service endpoints for FederationMetadata:" -ForegroundColor Green write-host "https://$adfs_service_name/FederationMetadata/2007-06/FederationMetadata.xml" -ForegroundColor Cyan write-host "https://$adfs_service_name/adfs/ls/IdpInitiatedSignon.aspx" -ForegroundColor Cyan #ADFS Application Group configuration New-AdfsApplicationGroup -Name "SecureMFA" -ApplicationGroupIdentifier "498a47d9-bbae-439e-9ffd-4f1afc609350" -Description "SecureMFA OTP WebPortal" Add-AdfsNativeClientApplication -ApplicationGroupIdentifier "498a47d9-bbae-439e-9ffd-4f1afc609350" -Name "SecureMFA - Native application" -Identifier "498a47d9-bbae-439e-9ffd-4f1afc609350" -RedirectUri (("http://" + (([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname).tolower() + "/"),("http://" + ($env:computerName).tolower() + "/")) -Description "SecureMFA OTP WebPortal" Get-AdfsApplicationGroup -Name SecureMFa | Add-AdfsWebApiApplication -Name "SecureMFA - Web application" -Identifier "498a47d9-bbae-439e-9ffd-4f1afc609350" -AccessControlPolicyName "Permit everyone and require MFA" -IssueOAuthRefreshTokensTo "AllDevices" -AllowedClientTypes "Public, Confidential" Grant-AdfsApplicationPermission -ClientRoleIdentifier "498a47d9-bbae-439e-9ffd-4f1afc609350" -ServerRoleIdentifier "498a47d9-bbae-439e-9ffd-4f1afc609350" -ScopeNames "openid" -Description "SecureMFA WebPortal" #Deploy IIS Service with SecureMFA OTP WebPortal Install-WindowsFeature -Name Web-Server Remove-Item C:\inetpub\wwwroot\*.* -Force Expand-Archive -LiteralPath $zipfilepath -DestinationPath C:\inetpub\wwwroot -Force $ServiceHost = (([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname).tolower() (Get-Content -path C:\inetpub\wwwroot\App\Scripts\app.js -Raw) -replace 'adfs.mydomain.com',$adfs_service_name| Set-Content -Path C:\inetpub\wwwroot\App\Scripts\app.js ##################### $RPURL = "http://" + (([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname).tolower() + "/" write-host "To access SecureMFA OTP Web Portal go to: $RPURL" -ForegroundColor Cyan write-host "Single Node DEMO ADFS Deployment With SecureMFA OTP Provider Is Complete." -ForegroundColor Green } catch { Write-Host "$($MyInvocation.InvocationName): $_" -ForegroundColor red } } |