Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <#
- Written by David Ott
- This script is for mass deployment of SMS2 TOTP codes (version 2!) for use with Google/Azure Authenticator
- *****You need to run this with an account that has modify rights to the SMS2 database!*****
- Read through the script and note any variables you need to change for your environment - I have
- marked them with comments ##### <-- just search for that
- Take special care to read anything marked with "*****"
- I wrote it because as of right now there is no tool for SMS2 to deploy multiple TOTP keys at once (you
- have to do them one at a time)
- ***** you will need qrcode.exe from https://code.google.com/p/qrencode-win32/wiki/Downloads for generating QR barcodes *****
- I have to give credit to this page https://gist.github.com/JonFriesen/234c7471c3e3199f97d5 for
- the conversion of the base32 secret to hex (which is used in the database)
- Also, this page http://www.sharepointfire.com/MyBlog/2011/12/send-email-with-images-in-powershell/ for
- helping me edit my sendmail function to imbed an image
- I in no way shape or form provide technical support for this script. Do not use it in production until you have
- tested it in a development environment!
- #>
- #requires -version 4
- <#####***** This version requires the Active Directory module! *****#####>
- try {
- ipmo activedirectory
- } catch {
- write-host "Requires Active Directory module" -f Red
- break
- }
- function Convert-Base32ToHex($base32) {
- $base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
- $bits = "";
- $hex = "";
- for ($i = 0; $i -lt $base32.Length; $i++) {
- $val = $base32chars.IndexOf($base32.Chars($i));
- $binary = [Convert]::ToString($val, 2)
- $staticLen = 5
- $padder = '0'
- $bits += Add-LeftPad $binary.ToString() $staticLen $padder
- }
- for ($i = 0; $i+4 -le $bits.Length; $i+=4) {
- $chunk = $bits.Substring($i, 4)
- $intChunk = [Convert]::ToInt32($chunk, 2)
- $hexChunk = Convert-IntToHex($intChunk)
- $hex = $hex + $hexChunk
- }
- return $hex;
- }
- function Convert-IntToHex([int]$num) {
- return ('{0:x}' -f $num)
- }
- function Add-LeftPad($str, $len, $pad) {
- if(($len + 1) -ge $str.Length) {
- while (($len - 1) -ge $str.Length) {
- $str = ($pad + $str)
- }
- }
- return $str;
- }
- function sendMail{
- <##### Change the smtp server to yours #####>
- $smtpServer = "yoursmtp.company.com"
- $msg = new-object Net.Mail.MailMessage
- $att1 = new-object Net.Mail.Attachment($img)
- $att1.ContentType.MediaType = “image/png”
- $att1.ContentId = “Attachment”
- $msg.Priority = [System.Net.Mail.Mailpriority]::High
- $msg.IsBodyHtml = $true
- $msg.Attachments.add($att1)
- $smtp = new-object Net.Mail.SmtpClient($smtpServer)
- <##### set your from address #####>
- $msg.From = "Notification@yourcompany.com"
- $msg.To.Add($address)
- $msg.subject = "Two Factor Authentication"
- $msg.body = $message
- $smtp.Send($msg)
- }
- <##### csv file to store information - just make sure the folder exists #####>
- $file = "\\server\share\logs\totplog.csv"
- <##### Directory to store the QR .png files #####>
- $qrdir = "\\server\share\QR\"
- <##### Place qrcode.exe (link above) in the same directory where you are putting the QR .png files
- you can change this if you want but you have to update the path to qrcode.exe below #####>
- $qrcode = Join-Path $qrdir "qrcode.exe"
- if (!(test-path $file)) {
- $oldlog = @()
- } else {
- $oldlog = Import-Csv $file
- }
- <##### Edit the $sqlserver variable to equal your sql server name and if the database is not SMS_DB
- update the $sqldbname variable - remember you must run this script as an AD user who has rights to edit
- the database!! #####>
- $SQLServer = "SQLServerName"
- $SQLDBName = "SMS_DB"
- $SqlQuery = "select * from SMS_Contact"
- $SqlQuery1 = "select * from UserProviders"
- $SqlConnection = New-Object System.Data.SqlClient.SqlConnection
- $SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True; MultipleActiveResultSets = True"
- $SqlCmd = New-Object System.Data.SqlClient.SqlCommand
- $SqlCmd.CommandText = $SqlQuery
- $SqlCmd.Connection = $SqlConnection
- $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
- $SqlAdapter.SelectCommand = $SqlCmd
- $DataSet = New-Object System.Data.DataSet
- $SqlAdapter.Fill($DataSet)
- $SqlCmd1 = New-Object System.Data.SqlClient.SqlCommand
- $SqlCmd1.CommandText = $SqlQuery1
- $SqlCmd1.Connection = $SqlConnection
- $SqlAdapter1 = New-Object System.Data.SqlClient.SqlDataAdapter
- $SqlAdapter1.SelectCommand = $SqlCmd1
- $DataSet1 = New-Object System.Data.DataSet
- $SqlAdapter1.Fill($DataSet1)
- [array]$users = $dataset.tables[0] | ?{$_.userstatus -eq "1"}
- [array]$codes = $dataset1.tables[0]
- <#####***** Here is where this script starts to really differ from the first one I created. This next bit will prompt you
- for the SAMACCOUNTNAME of an AD group. If the group does not exist, or is invalid it will start gathering information on
- 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
- 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
- already configured in the out-gridview *****#####>
- $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"
- $gpt = "0"
- try {
- $testgp = Get-ADGroup $gp
- $gpt = "1"
- } catch {
- $gpt = "0"
- }
- $users1 = $null
- if ($gpt -eq "0") {
- <#####***** If the group SAMACCOUNTNAME is invalid or not supplied this will get a count of all users in the database.
- I filter out some based on name. "Radiusu" is the user I use from the netscaler to monitor SMS2, so if you have something
- like that you should filter that user out. I also filter out some default accounts, and computer accounts *****#####>
- 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
- $z = 0
- <#####***** Gets the count of all users in the database (you may need to edit the filters for your environment), so the progress bar
- will work *****#####>
- $adc = ($users | ?{$_.ad_username -ne "radiusu"` #####***** Radiusu is a user I use to monitor SMS2 from the Netscaler
- -and $_.ad_username -notlike "*$*"`
- -and $_.ad_username -notlike "sm_*"`
- -and $_.ad_username -notlike "cntd_*"`
- -and $_.ad_username -notlike "IUSR_*"`
- -and $_.ad_username -notlike "IWAM_*"`
- -and $_.ad_username -notlike "ILS_*"}).count
- <#####***** Gets all the users in the database (again you may need to edit the filters for your environment). Then for each
- 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).
- 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.
- *****#####>
- $users1 = $users | ?{$_.ad_username -ne "radiusu"` #####***** Radiusu is a user I use to monitor SMS2 from the Netscaler
- -and $_.ad_username -notlike "*$*"`
- -and $_.ad_username -notlike "sm_*"`
- -and $_.ad_username -notlike "cntd_*"`
- -and $_.ad_username -notlike "IUSR_*"`
- -and $_.ad_username -notlike "IWAM_*"`
- -and $_.ad_username -notlike "ILS_*"} | select id,ad_username,display_name,email | %{
- $z++
- Write-Progress -Activity "Gathering Users" -Status "User $z of $adc" -PercentComplete ($z/$adc*100)
- $bid = $null
- $bid = $_.id
- $_ | 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} }}
- } | sort configured | Out-GridView -PassThru -Title "Users"
- } else {
- <#####***** If the SAMACCOUNTNAME of the group is valid the script grabs every user object in the group recursively (nested
- groups). Then for each user - if the user is enabled in AD - it will process like above. *****#####>
- $adus = $null
- $adus = Get-ADGroupMember $gp -Recursive | ?{$_.objectclass -eq "user"} | select -expand samaccountname | %{
- if ((Get-ADUser $_).enabled -eq $true) {
- $_
- }
- } | sort -unique
- Write-Host "Group $gp is valid. Please wait as information is gathered on" $adus.count "users." -f Green
- $z = 0
- $adc = $adus.count
- $users1 = $users | ?{$adus -eq $_.ad_username} | select id,ad_username,display_name,email | %{
- $z++
- Write-Progress -Activity "Gathering Users" -Status "User $z of $adc" -PercentComplete ($z/$adc*100)
- $bid = $null
- $bid = $_.id
- $_ | 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} }}
- } | sort configured | Out-GridView -PassThru -Title "Users"
- }
- <#####***** If you don't select any users in the gridview this will end the script *****#####>
- if (($users1 | Measure-Object).count -lt "1") {break}
- <#####***** Here is where the magic starts to happen! For each user you selected it will create a random totp string, convert
- it to hex (which is used in the database), update the .csv log file, write the totp information to the database, create the
- QR .png file, email the information to the user (if they do not appear to have an email address it will display the
- information in the PS window to send the user on the screen), and finally remove the user from the exclusion group (see
- the very end of the script for more info). *****#####>
- $arrayall = @()
- $arrayall += $oldlog
- $SqlConnection.open()
- foreach ($user in $users1) {
- $totp = (([char[]](Get-Random -Input $(50..55 + 65..90 + 97..122) -Count 16)) -join "").toupper()
- $hex = (Convert-Base32ToHex -base32 $totp).toupper()
- $id = $null
- $un = $null
- $em = $null
- [string]$id = $user.ID
- [string]$un = ($user.AD_USERNAME).toupper()
- [string]$em = ($user.email).tolower()
- if ($arrayall | ?{$_.username -eq "$un"}) {
- $userupdate = $null
- $userupdate = ($arrayall | ?{$_.username -eq "$un"})
- $userupdate.totp = $totp
- $userupdate.hex = $hex
- } else {
- $update = $null
- $update = New-Object psobject
- $update | Add-Member -type NoteProperty -name 'id' -Value $id
- $update | Add-Member -type NoteProperty -name 'username' -Value $un
- $update | Add-Member -type NoteProperty -name 'email' -Value $em
- $update | Add-Member -type NoteProperty -name 'totp' -Value $totp
- $update | Add-Member -type NoteProperty -name 'hex' -Value $hex
- $arrayall += $update
- }
- $arrayall | Export-Csv $file
- $SqlCmd2 = $null
- $SqlCmd2 = New-Object System.Data.SqlClient.SqlCommand
- $sqlcmd2.commandtext = "update userProviders
- set config = 'TOTP,$hex,0,Default'
- where userId = '$id' AND authProviderId = '2'"
- $sqlcmd2.Connection = $SqlConnection
- $sqlcmd2.ExecuteReader()
- Invoke-Expression -Command "$qrcode -o $qrdir\$un.png -s 3 -l H otpauth://totp/$un`?secret=$totp"
- if ($em -notlike "*@*") {
- Write-Host "User $un does not have an email address associated with their account!" -f Red
- Write-Host "You will need to give this user their secret key `"$totp`" and/or QR code`r`n $qrdir\$un.png`r`n"
- } else {
- $address = $em
- $img = "$qrdir\$un.png"
- <#####***** Below is the message in HTML format that your users will be sent to your users. I suggest setting up a test
- URL for your users (you will need to edit the message to fit your environment). Also, if you have a helpdesk email address edit
- that line as well. In order for the QR code and totp string to show up in the email make sure you leave the
- <img src="cid:Attachment"><br>
- $totp
- lines alone.
- *****#####>
- $message = @"
- <html>
- <body>
- <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>
- <img src="cid:Attachment"><br>
- $totp
- </p>
- <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>
- If authentication fails, please contact the <a href="mailto:helpdesk@yourcompany.com?subject=Two Factor Authentication Help">Help Desk</a> for assistance.
- </body>
- </html>
- "@
- Write-Host "Sending email to $un"
- sendMail
- }
- <#####***** 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
- logging in (or can put in anything in that box). This will remove the user from that group. If you have something similar
- edit the group name below. If not then remove or comment out the next 5 lines. *****#####>
- try {
- Remove-ADGroupMember 2factorexclude $un -Confirm:$false
- } catch {
- $null
- }
- }
- $SqlConnection.close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement