View difference between Paste ID: UPd0N2Wd and L9D8Jwaf
SHOW: | | - or go back to the newest paste.
1
<#
2
Written by David Ott
3
Modified by Ed Hammond 3.9.2017 - http://edhammond.blogspot.com/
4
Ed Hammond v2.1 Change log:
5
-Added more details and graphics to the email
6
-Added a better description inside the QR Code so that your Company and Email address show up in Google Authenticator. (line 237)
7
-When the Powershell writes back to the database on line 232 I added Feitian Serial and the 30 second time out to that line. Feitian Serial seems to be required and not Google Authenticator for some reason.
8
9
This script is for mass deployment of SMS2 TOTP codes (version 2!) for use with Google/Azure Authenticator
10
*****You need to run this with an account that has modify rights to the SMS2 database!*****
11
Read through the script and note any variables you need to change for your environment - I have
12
marked them with comments ##### <-- just search for that
13
14
Take special care to read anything marked with "*****"
15
16
I wrote it because as of right now there is no tool for SMS2 to deploy multiple TOTP keys at once (you 
17
have to do them one at a time)
18
19
***** you will need qrcode.exe from https://code.google.com/p/qrencode-win32/wiki/Downloads for generating QR barcodes *****
20
21
I have to give credit to this page https://gist.github.com/JonFriesen/234c7471c3e3199f97d5 for
22
the conversion of the base32 secret to hex (which is used in the database)
23
Also, this page http://www.sharepointfire.com/MyBlog/2011/12/send-email-with-images-in-powershell/ for
24
helping me edit my sendmail function to imbed an image
25
26
I in no way shape or form provide technical support for this script.  Do not use it in production until you have 
27
tested it in a development environment!
28
#>
29
30
#requires -version 4
31
<#####***** This version requires the Active Directory module! *****#####>
32
try {
33
ipmo activedirectory
34
} catch {
35
write-host "Requires Active Directory module" -f Red
36
break
37
}
38
function Convert-Base32ToHex($base32) {
39
    $base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
40
    $bits = "";
41
    $hex = "";
42
 
43
    for ($i = 0; $i -lt $base32.Length; $i++) {
44
        $val = $base32chars.IndexOf($base32.Chars($i));
45
        $binary = [Convert]::ToString($val, 2)
46
        $staticLen = 5
47
        $padder = '0'
48
        $bits += Add-LeftPad $binary.ToString()  $staticLen  $padder
49
    }
50
 
51
 
52
    for ($i = 0; $i+4 -le $bits.Length; $i+=4) {
53
        $chunk = $bits.Substring($i, 4)
54
        $intChunk = [Convert]::ToInt32($chunk, 2)
55
        $hexChunk = Convert-IntToHex($intChunk)
56
        $hex = $hex + $hexChunk
57
    }
58
    return $hex;
59
 
60
}
61
 
62
function Convert-IntToHex([int]$num) {
63
    return ('{0:x}' -f $num)
64
}
65
 
66
function Add-LeftPad($str, $len, $pad) {
67
    if(($len + 1) -ge $str.Length) {
68
        while (($len - 1) -ge $str.Length) {
69
            $str = ($pad + $str)
70
        }
71
    }
72
    return $str;
73
}
74
function sendMail{
75
<##### Change the smtp server to yours #####>     
76
     $smtpServer = "yoursmtp.company.com"
77
     $msg = new-object Net.Mail.MailMessage
78
     $att1 = new-object Net.Mail.Attachment($img)
79
     $att1.ContentType.MediaType = “image/png”
80
     $att1.ContentId = “Attachment”
81
     $msg.Priority = [System.Net.Mail.Mailpriority]::High
82
     $msg.IsBodyHtml = $true
83
     $msg.Attachments.add($att1)
84
     $smtp = new-object Net.Mail.SmtpClient($smtpServer)
85
<##### set  your from address #####>
86
     $msg.From = "Notification@yourcompany.com"
87
     $msg.To.Add($address)
88
     $msg.subject = "Two Factor Authentication"
89
     $msg.body = $message
90
     $smtp.Send($msg)  
91
}
92
<##### csv file to store information - just make sure the folder exists #####>
93
$file = "\\server\share\logs\totplog.csv"
94
<##### Directory to store the QR .png files #####>
95
$qrdir = "\\server\share\QR\"
96
<##### Place qrcode.exe (link above) in the same directory where you are putting the QR .png files
97
you can change this if you want but you have to update the path to qrcode.exe below #####>
98
$qrcode = Join-Path $qrdir "qrcode.exe"
99
if (!(test-path $file)) {
100
101
$oldlog = @()
102
} else {
103
$oldlog = Import-Csv $file
104
}
105
106
<##### Edit the $sqlserver variable to equal your sql server name and if the database is not SMS_DB
107
update the $sqldbname variable - remember you must run this script as an AD user who has rights to edit
108
the database!! #####>
109
$SQLServer = "SQLServerName"
110
$SQLDBName = "SMS_DB"
111
$SqlQuery = "select * from SMS_Contact"
112
$SqlQuery1 = "select * from UserProviders"
113
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
114
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True; MultipleActiveResultSets = True"
115
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
116
$SqlCmd.CommandText = $SqlQuery
117
$SqlCmd.Connection = $SqlConnection
118
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
119
$SqlAdapter.SelectCommand = $SqlCmd
120
$DataSet = New-Object System.Data.DataSet
121
$SqlAdapter.Fill($DataSet)
122
123
$SqlCmd1 = New-Object System.Data.SqlClient.SqlCommand
124
$SqlCmd1.CommandText = $SqlQuery1
125
$SqlCmd1.Connection = $SqlConnection
126
$SqlAdapter1 = New-Object System.Data.SqlClient.SqlDataAdapter
127
$SqlAdapter1.SelectCommand = $SqlCmd1
128
$DataSet1 = New-Object System.Data.DataSet
129
$SqlAdapter1.Fill($DataSet1)
130
[array]$users = $dataset.tables[0] | ?{$_.userstatus -eq "1"}
131
[array]$codes = $dataset1.tables[0]
132
<#####***** Here is where this script starts to really differ from the first one I created.  This next bit will prompt you
133
for the SAMACCOUNTNAME of an AD group.  If the group does not exist, or is invalid it will start gathering information on
134
ALL users in AD.  This can take some time to process depending on how many users are in the group/domain.  It has to compare
135
2 tables in the database for each user to see if there is a TOTP value or not - in the end lets you know which users are 
136
already configured in the out-gridview *****#####>
137
$gp = Read-Host "Enter the SamAccountName of the AD Group you wish to focus on`r`nIf you do not supply a any information, or if the SamAccountName`r`nis invalid you will be returned all users"
138
$gpt = "0"
139
try {
140
$testgp = Get-ADGroup $gp
141
$gpt = "1"
142
} catch {
143
$gpt = "0"
144
}
145
$users1 = $null
146
if ($gpt -eq "0") {
147
<#####***** If the group SAMACCOUNTNAME is invalid or not supplied this will get a count of all users in the database.  
148
I filter out some based on name.  "Radiusu" is the user I use from the netscaler to monitor SMS2, so if you have something
149
like that you should filter that user out.  I also filter out some default accounts, and computer accounts *****#####>
150
Write-Host "AD group name invalid, or not entered.  Gathering information on ALL users.`r`nThis will take a long time... get some coffee" -f Red
151
$z = 0
152
<#####***** Gets the count of all users in the database (you may need to edit the filters for your environment), so the progress bar 
153
will work *****#####>
154
$adc = ($users | ?{$_.ad_username -ne "radiusu"` #####***** Radiusu is a user I use to monitor SMS2 from the Netscaler
155
 -and $_.ad_username -notlike "*$*"`
156
 -and $_.ad_username -notlike "sm_*"`
157
  -and $_.ad_username -notlike "cntd_*"`
158
   -and $_.ad_username -notlike "IUSR_*"`
159
   -and $_.ad_username -notlike "IWAM_*"`
160
   -and $_.ad_username -notlike "ILS_*"}).count
161
<#####***** Gets all the users in the database (again you may need to edit the filters for your environment).  Then for each
162
one checks to see if they already have a totp code setup (that can take a while depending on how many users it has to process).
163
Once it has gathered all the information it will spit it out in a grid and you can select which users you want to configure.
164
 *****#####>
165
$users1 = $users | ?{$_.ad_username -ne "radiusu"` #####***** Radiusu is a user I use to monitor SMS2 from the Netscaler
166
 -and $_.ad_username -notlike "*$*"`
167
 -and $_.ad_username -notlike "sm_*"`
168
  -and $_.ad_username -notlike "cntd_*"`
169
   -and $_.ad_username -notlike "IUSR_*"`
170
   -and $_.ad_username -notlike "IWAM_*"`
171
   -and $_.ad_username -notlike "ILS_*"} | select id,ad_username,display_name,email | %{
172
   $z++
173
Write-Progress -Activity "Gathering Users" -Status "User $z of $adc" -PercentComplete ($z/$adc*100)
174
   $bid = $null
175
$bid = $_.id
176
$_ | select id,ad_username,display_name,email,@{name="Configured";expression={if (($codes | ?{$_.userid -eq $bid -and $_.authproviderid -eq "2"} | select -expand config) -like "totp*"){$true}else{$false} }}
177
} | sort configured | Out-GridView -PassThru -Title "Users"
178
   } else {
179
<#####***** If the SAMACCOUNTNAME of the group is valid the script grabs every user object in the group recursively (nested 
180
groups).  Then for each user - if the user is enabled in AD - it will process like above. *****#####>
181
   $adus = $null
182
   $adus = Get-ADGroupMember $gp -Recursive | ?{$_.objectclass -eq "user"} | select -expand samaccountname | %{
183
if ((Get-ADUser $_).enabled -eq $true) {
184
$_
185
}
186
} | sort -unique
187
Write-Host "Group $gp is valid.  Please wait as information is gathered on" $adus.count "users." -f Green
188
$z = 0
189
$adc = $adus.count
190
$users1 = $users | ?{$adus -eq $_.ad_username} | select id,ad_username,display_name,email | %{
191
$z++
192
Write-Progress -Activity "Gathering Users" -Status "User $z of $adc" -PercentComplete ($z/$adc*100)
193
$bid = $null
194
$bid = $_.id
195
$_ | select id,ad_username,display_name,email,@{name="Configured";expression={if (($codes | ?{$_.userid -eq $bid -and $_.authproviderid -eq "2"} | select -expand config) -like "totp*"){$true}else{$false} }}
196
} | sort configured | Out-GridView -PassThru -Title "Users"
197
   }
198
<#####***** If you don't select any users in the gridview this will end the script *****#####>
199
if (($users1 | Measure-Object).count -lt "1") {break}
200
<#####***** Here is where the magic starts to happen!  For each user you selected it will create a random totp string, convert 
201
it to hex (which is used in the database), update the .csv log file, write the totp information to the database, create the 
202
QR .png file, email the information to the user (if they do not appear to have an email address it will display the 
203
information in the PS window to send the user on the screen), and finally remove the user from the exclusion group (see 
204
the very end of the script for more info). *****#####>
205
$arrayall = @()
206
$arrayall += $oldlog
207
$SqlConnection.open()
208
foreach ($user in $users1) {
209
$totp = (([char[]](Get-Random -Input $(50..55 + 65..90 + 97..122) -Count 16)) -join "").toupper()
210
$hex = (Convert-Base32ToHex -base32 $totp).toupper()
211
$id = $null
212
$un = $null
213
$em = $null
214
215
[string]$id = $user.ID
216
[string]$un = ($user.AD_USERNAME).toupper()
217
[string]$em = ($user.email).tolower()
218
if ($arrayall | ?{$_.username -eq "$un"}) {
219
$userupdate = $null
220
$userupdate = ($arrayall | ?{$_.username -eq "$un"})
221
$userupdate.totp = $totp
222
$userupdate.hex = $hex
223
} else {
224
$update = $null
225
$update = New-Object psobject
226
$update | Add-Member -type NoteProperty -name 'id' -Value $id
227
$update | Add-Member -type NoteProperty -name 'username' -Value $un
228
$update | Add-Member -type NoteProperty -name 'email' -Value $em
229
$update | Add-Member -type NoteProperty -name 'totp' -Value $totp
230
$update | Add-Member -type NoteProperty -name 'hex' -Value $hex
231
$arrayall += $update
232-
set config = 'TOTP,$hex,0,Default'
232+
233
234
$arrayall | Export-Csv $file
235
$SqlCmd2 = $null
236
$SqlCmd2 = New-Object System.Data.SqlClient.SqlCommand
237-
Invoke-Expression -Command "$qrcode -o $qrdir\$un.png -s 3 -l H otpauth://totp/$un`?secret=$totp"
237+
238
set config = 'TOTP,$hex,0,30,Default,Feitian Serial'
239
where userId = '$id' AND authProviderId = '2'"
240
$sqlcmd2.Connection = $SqlConnection
241
$sqlcmd2.ExecuteReader()
242
<#####***** You can replace the "Work-VPN" with your company on the end of the next line. If you add spaces you will need to escape or quote them correctly *****#####>
243
Invoke-Expression -Command "$qrcode -o $qrdir\$un.png -s 3 -l H otpauth://totp/$em`?secret=$totp""&""issuer=Work-VPN"
244
if ($em -notlike "*@*") {
245
Write-Host "User $un does not have an email address associated with their account!" -f Red
246
Write-Host "You will need to give this user their secret key `"$totp`" and/or QR code`r`n $qrdir\$un.png`r`n"
247
} else {
248
$address = $em
249
$img = "$qrdir\$un.png"
250
<#####***** Below is the message in HTML format that your users will be sent to your users.  I suggest setting up a test
251
URL for your users (you will need to edit the message to fit your environment). Also, if you have a helpdesk email address edit 
252
that line as well.  In order for the QR code and totp string to show up in the email make sure you leave the 
253
<img src="cid:Attachment"><br>
254-
<p>Your secure QR code and secret key can be found below.  Please use either the code or the secret key to setup the Azure Authenticator token on your smartphone, and be aware that it is crucial to keep this information safe.<br>
254+
255
lines alone.
256
 *****#####>
257
$message = @"
258-
<p>After you setup the account in Azure Authenticator, and <b>before you leave the office</b>, visit the <a href="https://test.2factor.com">authentication test page</a> (only available on the internal company network), and verify you can logon.<br><br><br>
258+
259
<body>
260
<style>
261
    body {
262
        background-color: White;
263
    }
264
    p,ul,li {
265
        font-family: Arial, sans-serif;
266
        font-size: 14px;
267
    }
268
</style>
269
<img src="###http://mycompanyhomepage.com/logo.png###"><br>
270
<p>We are adding a Two factor Authentication requirement for VPN access.
271
<br>You will need to install Google Authenticator from the app store on your phone and then scan the QR code with Google Authenticator app.
272
<br>Your Secure QR code and secret key can be found below.  
273
<br>Please use either the code or the secret key to setup the Google Authenticator token on your smartphone, and then delete this email.
274
<br>Be aware that it is crucial to keep this information safe from hackers by deleting this email when your done.<br>
275
<img src="cid:Attachment"><br>
276
$totp
277
</p>
278
<p>When you connect to the VPN, you will login as usual and then a second window will ask you to enter the code from Google Authenicator.<br><br><br>
279
If authentication fails, please contact the <a href="mailto:helpdesk@yourcompany.com?subject=Two Factor Authentication Help">Help Desk</a> for assistance.
280
<br><br>
281
<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">Get Google Authenticator for your iPhone</a><br><br>
282
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en">Get Google Authenticator for your Android Phone</a><br><br>
283
<img src="http://a2.mzstatic.com/us/r30/Purple49/v4/a8/3d/8c/a83d8cde-dd69-de15-3561-a118d59b4be4/screen696x696.jpeg">
284
</body>
285
</html>
286
"@
287
Write-Host "Sending email to $un"
288
sendMail
289
}
290
<#####***** In my environment I have a 2 factor exclusion group.  If you are in it you don't have to supply a token code when 
291
logging in (or can put in anything in that box).  This will remove the user from that group.  If you have something similar 
292
edit the group name below.  If not then remove or comment out the next 5 lines. *****#####>
293
try {
294
Remove-ADGroupMember 2factorexclude $un -Confirm:$false
295
} catch {
296
$null
297
}
298
}
299
$SqlConnection.close()