Install-ADFS_Demo_with_OTP.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#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

    #####################
    write-host "Single Node DEMO ADFS Deployment With SecureMFA OTP Provider Is Complete." -ForegroundColor Green
        
    }
    catch
    {
        Write-Host "$($MyInvocation.InvocationName): $_" -ForegroundColor red
    }    


}