AutoCert.ps1
Param ( [bool]$Console = $false, [bool]$Debug = $false, [bool]$Status = $False ) <#====================================================================================== File Name : AutoCert.ps1 Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com) : Description : Automatically processes VPN certificates on a weekly schedule. : Notes : Normal operation is with no command line options. Requires certutil.exe. exists on system execting script. : Create files in cert store named "username.update" to trigger the update section manually. : Run via a scheduled task to run as needed. : Arguments : Note - Any combination of arguments may be used at once. : "-console $true" option to display runtime info on the console. : "-debug $true" includes a status email to debug user(s) only. Use for troubleshooting : NOTE - As currently set a "debugging" email goes out BCC to the debug user(s) each : time a user email is sent. See "sendemail" function... : "-status $true" sends status results to the status user(s) only. Use for managers, etc who insist on reports. : Warnings : See end of file for configuration file settings and example !!! : Legal : Public Domain. Modify and redistribute freely. No rights reserved. : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED. : Credits : Code snippets and/or ideas came from many sources including but : not limited to the following: : Original Author : Unknown via the Internet. Modded by Tyler Applebaum for use in production environemnt. Last Update by : Kenneth C. Mazie Version History : v1.00 - 06-01-14 - Original Change History : v2.00 - 07-13-15 - Major rewrite. : v2.10 - 08-13-15 - Added option to send update notices. : v2.10 - 04-18-16 - Added bypass for admin accounts : v3.00 - 05-26-16 - Added option to create stand alone certs : v3.10 - 08-10-16 - fixed bug that caused script to abort on every run : v3.20 - 08-15-16 - Added option to include requestor in email if force is used. : v3.30 - 08-19-16 - Added eventlog tracking and improved debug messaging : v4.00 - 08-29-16 - Branched to AutoCert variant. Added try/catch to snag errors. : v4.10 - 09-20-16 - Numerous coding changes and bug fixes. Mostly in output. : v5.00 - 01-13-17 - Fixed for use with DFS share. Numerous changes. Fixed status email. : v5.10 - 02-08-17 - fixed notice email attachments throwing error and stopping email from sending. : adjusted message text. changed so reply to is no longer the user. Added BCC. : disabled force option. Added user first name to email. : v5.10 - 02-16-17 - fixed issue with detecting console color. : v5.20 - 03-29-17 - added error checking to generation function. : v5.30 - 04-14-17 - Added better handling of stsus users. : v5.40 - 04-20-17 - Added error checks to make sure temp folder works for service account running script. : Expanded list of bypass patterns. : v5.50 - 04-21-17 - Retooled email system. Adjusted logging. Removed un-needed code. : v5.60 - 06-20-17 - Commented out line 635 for cleaning up variables. : v5.61 - 03-02-18 - Minor notation tweak for PS Gallery upload. Moved more variables out to config file. : v5.62 - 03-02-18 - Corrected description. : =======================================================================================#> <#PSScriptInfo .VERSION 5.62 .GUID 39d43bd3-0d21-4060-b4ce-eafc88302f57 .AUTHOR Kenneth C. Mazie (kcmjr AT kcmjr.com) .DESCRIPTION Automatically processes VPN certificates on a weekly schedule. #> Clear-Host $ErrorActionPreference = "Stop" #ilentlyContinue" If($Console){$Script:Console = $true} If($Debug){$Script:Debug = $true} If($Status){ $Script:Status = $true} $Computer = $Env:ComputerName $Script:ScriptName = ($MyInvocation.MyCommand.Name).split(".")[0] $Script:LogFile = $PSScriptRoot+"\"+$ScriptName+"_{0:MM-dd-yyyy_HHmmss}.log" -f (Get-Date) $Script:ConfigFile = "$PSScriptRoot\$ScriptName.xml" $Script:ConsoleEmail = $true #--[ Use this to enable sending of a debug email any time an operation is performed. ]-- $Script:ConsoleEmailOK = $false #--[ Used as a flag by the script to determine whether to send a debug email. Don't change this ]-- $Script:Datetime = Get-Date -Format "MM-dd-yyyy_HH:mm" $Script:EmailMsg = "" $ErrorMessage = "" $FailedItem = "" $Script:Installed = $False $Script:Counter = 0 $Script:DebugLogMsg = @() $Script:EventLogMsg = "" $Script:TempDir = "$env:temp" #--[ *** Temporary local folder *** ]-- $IllegalUser = $False $Script:Attach = $false $Script:NotifyUser = $False $DarkConsole = ((Get-Host).UI.RawUI.BackgroundColor -like "*Dark*") #--[ Detect console color and adjust accordingly ]------------------------------ $ScriptColor = @{ 1 = "Green" 2 = "Red" 3 = "Yellow" 4 = "Blue" 5 = "Cyan" 6 = "Magenta" 7 = "Gray" 8 = "White" 9 = "Black" 11 = "DarkGreen" 12 = "DarkRed" 13 = "DarkCyan" 14 = "DarkBlue" 15 = "DarkCyan" 16 = "DarkMagenta" 17 = "DarkGray" 18 = "Gray" 19 = "Black" } #--[ Read and load configuration file ]----------------------------------------- If (!(Test-Path "$PSScriptRoot\$ScriptName.xml")){ #--[ Error out if configuration file doesn't exist ]-- Write-host "MISSING CONFIG FILE. Script aborted." -ForegroundColor red break }Else{ [xml]$Script:Configuration = Get-Content "$PSScriptRoot\$ScriptName.xml" #--[ Load configuration ]-- $Script:CompanyName = $Script:Configuration.Settings.General.CompanyName $Script:CompanyInitials = $Script:Configuration.Settings.General.CompanyInitials #--[ Used to prefix files and notations ]-- $Script:SupportPhone = $Script:Configuration.Settings.General.SupportPhone $Script:Title = $Script:Configuration.Settings.General.ReportTitle $Script:DebugUser = $Script:Configuration.Settings.Email.DebugUser $Script:StatusUser = $Script:Configuration.Settings.Email.StatusUser $Script:DebugSubject = $Script:Configuration.Settings.Email.DebugSubject #$Script:EmailTo = $Script:Configuration.Settings.Email.To #--[ Determined by script ]-- $Script:EmailHTML = $Script:Configuration.Settings.Email.HTML $Script:EmailSubject = $Script:Configuration.Settings.Email.Subject $Script:EmailFrom = $Script:Configuration.Settings.Email.From $Script:EmailDomain = $Script:Configuration.Settings.Email.Domain $Script:SmtpServer = $Script:Configuration.Settings.Email.SmtpServer $Script:UserName = $Script:Configuration.Settings.Credentials.Username $Script:Password = $Script:Configuration.Settings.Credentials.Password $Script:VPNPassword = $Script:Configuration.Settings.Credentials.VPNPassword $Script:VPNUsers = $Script:Configuration.Settings.General.VPNGroup $Script:EventlogName = $Script:Configuration.Settings.General.EventlogName $Script:EventlogID = $Script:Configuration.Settings.General.EventlogID $Script:EventlogType = $Script:Configuration.Settings.General.EventlogType $Script:CertStore = $Script:Configuration.Settings.General.CertStore $Script:Attach1 = $Script:Configuration.Settings.Email.Attachments.Attach1 $Script:Attach2 = $Script:Configuration.Settings.Email.Attachments.Attach2 $Script:Attach3 = $Script:Configuration.Settings.Email.Attachments.Attach3 $Script:BadPattern = $Script:Configuration.Settings.General.BadPattern #--[ Anything matching this pattern is bypassed. Use for admin accounts ]-- $Script:TemplateName = $Script:Configuration.Settings.General.TemplateName $Script:CAName = $Script:Configuration.Settings.General.CaName } #------------------------------------------------------------------------------- #$Credential = New-Object PSCredential($UserName, (ConvertTo-SecureString $Password.SubString(64) -k ($Password.SubString(0,64) -split "(?<=\G[0-9a-f]{2})(?=.)" | % { [Convert]::ToByte($_,16) }))) #$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserName, ($Password | ConvertTo-SecureString) [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") #--[ Assembly required for balloon tips ]-- New-EventLog -LogName Application -Source $Script:EventlogName -ErrorAction SilentlyContinue #--[ Register new eventlog type ]-- #==[ Functions ]================================================================ Function MessageLog ($Mode, $MsgText, [Int]$MsgColor){ #--[ Compiles and writes log out to the local eventlog and for email ]-- $Script:EventLogMsg += "$MsgText`n" If ($Script:Console -and ($mode -ne "DebugLogOnly")){ #--[ Only used in certain places. Don't alter this or you will break the status email ]-- If ($DarkConsole){ $Color = $ScriptColor[$MsgColor] }Else{ $Color = $ScriptColor[($MsgColor+10)] } If ($MsgText -eq "HR"){ Write-host "`n===============================================================`n" -ForegroundColor $Color }Else{ write-host $MsgText -ForegroundColor $Color } } If ($Mode -ne "ConsoleOnly"){ #--[ Only used in certain places. Don't alter this or you will break the status email ]-- $Color = $ScriptColor[($MsgColor + 10)] If ($MsgText -eq "HR"){$MsgText = "<br>===============================================================<br>"} $Script:DebugLogMsg += "<font color="+$Color+">"+"$MsgText</font><br>" } } Function SendEmail { $Smtp = new-object Net.Mail.SmtpClient($Script:SmtpServer) $Email = New-Object System.Net.Mail.MailMessage $Email.From = "$Script:EmailFrom@$Script:EmailDomain" $Email.Subject = $Script:EmailSubject $Email.Body = $Script:EmailMsg If ($Script:EmailHTML){$Email.IsBodyHtml = $true} If ($Script:Attach){ #--[ Attachments should include "USER.pfx", "Root.p7b", "Intermediate.p7b, install doc" ]-- Try{ $Email.Attachments.Add($Script:EmailAttach1) $Email.Attachments.Add($Script:EmailAttach2) $Email.Attachments.Add($Script:EmailAttach3) $Email.Attachments.Add($Script:EmailAttach4) }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Email.Subject = $Script:EmailSubject+"- EMAIL FAILURE REPORT -" $Email.Body = "- ATTACHMENT FAILURE REPORT -<br>"+$_.Exception.Message+' <br> '+$_.Exception.ItemName ForEach ($User in $Script:DebugUser.User){ $Email.To.Add("$User@$Script:EmailDomain") } $Script:NotifyUser = $False } } If ($Script:NotifyUser){ #--[ Email target user ]-- $Email.To.Add("$Script:TargetUser@$Script:EmailDomain") MessageLog "Both" "-- Target user added to email --" "3" } If (!($Script:Debug)){ #--[ Email debug user(s) ]-- ForEach ($User in $Script:DebugUser.User){ $Email.Bcc.Add("$User@$Script:EmailDomain") #--[ Always BCC debug user(s) as a safety check ]-- } MessageLog "Both" "-- Debug user(s) added as BCC to email --" "3" } If ($Script:NotifyUser){ $Smtp.Send($Email) MessageLog "Both" "===== Target user notification email Sent =====`n" "1" }Else{ MessageLog "Both" "===== NO TARGET USER EMAIL SENT =====`n" "3" } $Smtp.Dispose() $Email.Dispose() $Script:NotifyUser = $False StatusEmail } Function StatusEmail { If (((Get-Date -Format dddd) -eq "Monday") -or ($Script:Debug) -or ($Script:Status)){ #--[Only send status mail on mondays or when debugging ]-- $StatusEmail = New-Object System.Net.Mail.MailMessage $StatusSmtp = new-object Net.Mail.SmtpClient($Script:SmtpServer) $StatusEmail.From = "$Script:EmailFrom@$Script:EmailDomain" If ($Script:EmailHTML){$StatusEmail.IsBodyHtml = $true} If ($Script:Debug){ $StatusEmail.Subject = $Script:DebugSubject ForEach ($User in $Script:DebugUser.User){ $StatusEmail.To.Add("$User@$Script:EmailDomain") #--[ Always copy debug user(s) as a safety check ]-- } MessageLog "Both" "-- Debug user(s) added to email --" "3" } If (((Get-Date -Format dddd) -eq "Monday") -or ($Script:Status)){ $StatusEmail.Subject = "VPN Certificate Status Check" # $Script:DebugSubject ForEach ($User in $Script:StatusUser.User){ $StatusEmail.To.Add("$User@$Script:EmailDomain") #--[ Send to status user only if option was specified ]-- } MessageLog "Both" "-- Status user(s) added to email --" "3" } MessageLog "Both" "===== Status and Debugging Email Sent =====`n" "1" $StatusEmail.Body = $Script:DebugLogMsg $StatusSmtp.Send($StatusEmail) }Else{ MessageLog "Both" "====== NO STATUS EMAILS SENT =====`n" "2" } } Function GenerateCert { $ErrorActionPreference = "stop" MessageLog "Both" '-- Generating new VPN Certificate --' "3" #[string]$TemplateName = $Script:TemplateName [string]$CertAuthName = $Script:CaName+"\"+$Script:CaName [string]$UID = $Script:TargetUser [string]$Email = "$Script:TargetUser@$Script:EmailDomain" #--[ Generate request file ]------------------------------------------------ MessageLog "Both" "-- Generating Certificate Request File --" "3" #--[ Clean out temp folder ]-- remove-item $Script:TempDir\usercert.inf -ErrorAction silentlycontinue -Force:$true -Confirm:$false remove-item $Script:TempDir\usercert.req -ErrorAction silentlycontinue -Force:$true -Confirm:$false remove-item $Script:TempDir\*.cer -ErrorAction silentlycontinue -Force:$true -Confirm:$false remove-item $Script:TempDir\*.p7b -ErrorAction silentlycontinue -Force:$true -Confirm:$false remove-item $Script:TempDir\*.pfx -ErrorAction silentlycontinue -Force:$true -Confirm:$false remove-item $Script:TempDir\*.rsp -ErrorAction silentlycontinue -Force:$true -Confirm:$false add-content $Script:TempDir\usercert.inf "[NewRequest]`r Subject = `"CN=$UID`"`r Exportable = TRUE`r RequestType = CMC`r [RequestAttributes]`r CertificateTemplate = `"$Script:TemplateName`"`r SAN = `"Email=$Email`"" #--[ Build Cert Request ]-------------------------------------------- MessageLog "Both" "-- Building Certificate Request --" "3" Try{ C:\Windows\system32\certreq.exe -new $Script:TempDir\usercert.inf $Script:TempDir\usercert.req }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Script:DebugSubject = $ErrorMessage MessageLog "Both" "Build Cert : $ErrorMessage" "7" StatusEmail break;break;break } #--[ Send request to authority ]-------------------------------------------- MessageLog "Both" "-- Sending Certificate Request --" "3" Try{ C:\Windows\system32\certreq.exe -submit -config "$CertAuthName" $Script:TempDir\usercert.req $Script:TempDir\$UID.cer }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Script:DebugSubject = $ErrorMessage MessageLog "Both" "Request Cert : $ErrorMessage" "7" StatusEmail break;break;break } #--[ Install certificate on local machine ]--------------------------------- MessageLog "Both" "-- Installing Certificate --" "3" Try{ C:\Windows\system32\certreq.exe -accept $Script:TempDir\$UID.cer $Script:Installed = $true }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Script:DebugSubject = $ErrorMessage MessageLog "Both" "Install cert on local : $ErrorMessage" "7" StatusEmail break;break;break } #--[ Export certificate and e-mail ]---------------------------------------- Try{ #--[ If the next line fails using a variable hard code the ca search as "CN=CA1\CA1*". ]-- #dir cert:\currentuser\my | ? { $_.hasPrivateKey -and $_.Subject -like "*$Script:TargetUser*" -and $_.PrivateKey.CspKeyContainerInfo.Exportable -and $_.Subject -notlike "*E=*" -and $_.Issuer -like "CN=CA1\CA1*"} | Foreach-Object {[system.IO.file]::WriteAllBytes("$Script:CertStore\$Script:TargetUser.pfx", ($_.Export('PFX', $Script:VPNPassword)))} #Find VPN cert and exclude internal User cert dir cert:\currentuser\my | ? { $_.hasPrivateKey -and $_.Subject -like "*$Script:TargetUser*" -and $_.PrivateKey.CspKeyContainerInfo.Exportable -and $_.Subject -notlike "*E=*" -and $_.Issuer -like "CN=$CertAuthName*"} | Foreach-Object {[system.IO.file]::WriteAllBytes("$Script:CertStore\$Script:TargetUser.pfx", ($_.Export('PFX', $Script:VPNPassword)))} #Find VPN cert and exclude internal User cert }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Script:DebugSubject = $ErrorMessage MessageLog "Both" "Export Cert : $ErrorMessage" "7" StatusEmail break;break;break } MessageLog "Both" "-- Exporting Certificate. Emailing user. --" "3" While (!(Test-Path "$Script:CertStore\$Script:TargetUser.pfx")){ Sleep 2 $Script:Counter ++ If ($Script:Counter -ge 5){ MessageLog "Both" "-- Failure to detect newly generated certificate in backup location." "2" } } #--[ Copy cert files to local temp folder for email attach, otherwise path causes an error ]-- Copy-Item -Path ($Script:CertStore+"\"+$Script:Attach1) -Destination $Script:TempDir -Force:$true -Confirm:$false $Script:EmailAttach1 = "$Script:TempDir\$Script:Attach1" Copy-Item -Path ($Script:CertStore+"\"+$Script:Attach2) -Destination $Script:TempDir -Force:$true -Confirm:$false $Script:EmailAttach2 = "$Script:TempDir\$Script:Attach2" Copy-Item -Path ($Script:CertStore+"\"+$Script:Attach3) -Destination $Script:TempDir -Force:$true -Confirm:$false $Script:EmailAttach3 = "$Script:TempDir\$Script:Attach3" Copy-Item -Path ($Script:CertStore+"\"+$Script:TargetUser+".pfx") -Destination $Script:TempDir -Force:$true -Confirm:$false $Script:EmailAttach4 = $Script:TempDir+"\"+$Script:TargetUser+".pfx" $Script:Attach = $true If (Test-Path $Script:EmailAttach4){ $Script:EmailMsg = $Script:TargetFName.givenName+',<br><br>This is an automated email from the '+$Script:CompanyName+' VPN server.<br> The system has generated an updated VPN certificate for you. The required files are attached.<br><br> Please download and install the attached certificates on your personal computer in order to<br> allow you to connect to, or continue to connect to the '+$Script:CompanyName+' VPN.<br><br> If four files are NOT attached to this email please contact IT Support.<br><br> Once the attachments are saved, right click on the <strong>'+$Script:CompanyInitials+'-Root.p7b</strong> file and select "Install Certificate"<br> (Click Next, Browse and select Trusted Root Certification Authorities, click OK,<br> Next, Finish, click Yes on the warning). After that, right click on the <strong>'+$Script:CompanyInitials+'-Issuing.p7b</strong><br> file and select "Install Certificate" (Click Next, Next, Finish).<br><br>Do the same on your personal '+$Script:TargetUser+' certificate (Click Next, Next, enter the password, Next, Next, Finish).<br><br> The password to import your personal certificate is lowercase "'+$Script:VPNPassWord+'" (no quotes).<br><br> Please retain this email for reference. This will be the only notice sent.<br><br> Contact Support at '+$Script:SupportPhone+' or reply to this email if you have issues or questions.<br><br> '+$Script:CompanyName+' IT Network Department' $Script:NotifyUser = $true SendEmail #--[ Send the user email ]-- }Else{ $Script:EmailMsg = 'This is an automated email from the '+$Script:CompanyName+' VPN server.<br><br> There was an issue in the creation of your VPN certificate. IT Support has been<br> notified automatically. They will contact you shortly.' $Script:NotifyUser = $true SendEmail #--[ Send the user email ]-- } } #==[ End Of Functions ]========================================================= MessageLog "Both" "=============== VPN Certificate Inpection Script Initializing ===============" "5" MessageLog "Both" "Script executed from server : $Env:ComputerName on $Script:DateTime" "7" MessageLog "Both" "Certificate store : $Script:CertStore" "7" Try{ Add-Content -Value "X" -Path $Script:CertStore'\-ScriptFlag-.txt' -Force:$true -ErrorAction Stop -Confirm:$false $X = Get-Content -Path $Script:CertStore'\-ScriptFlag-.txt' -ErrorAction:Stop Remove-Item -Path $Script:CertStore'\-ScriptFlag-.txt' -Force:$true -ErrorAction Stop -Confirm:$false }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Script:DebugSubject = "Script failure during CERTSTORE check. " MessageLog "Both" "Script failure during CERTSTORE check. " "7" MessageLog "Both" "Error Message : $ErrorMessage" "7" MessageLog "Both" "Failed Item : $FailedItem" "7" $Script:Debug = $true $Script:NotifyUser = $False StatusEmail Break;break;break } Try{ Add-Content -Value "X" -Path $Script:Temp'\-ScriptFlag-.txt' -Force:$true -ErrorAction Stop -Confirm:$false $X = Get-Content -Path $Script:Temp'\-ScriptFlag-.txt' -ErrorAction Stop Remove-Item -Path $Script:Temp'\-ScriptFlag-.txt' -Force:$true -ErrorAction Stop -Confirm:$false }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName $Script:DebugSubject = "Script failure during TEMP check." MessageLog "Both" "Script failure during TEMP check. " "7" MessageLog "Both" "Error Message : $ErrorMessage" "7" MessageLog "Both" "Failed Item : $FailedItem" "7" $Script:Debug = $true $Script:NotifyUser = $False StatusEmail Break;break;break } Try{ $GroupMembers = Get-ADGroupMember -Identity $Script:VPNUsers | Sort Name MessageLog "Both" "HR" "2" #--[ Inserts a hard rule ]-- Foreach ($Member in $GroupMembers){ $Script:EventLogMsg = @() #New-Object System.Collections.ArrayList $Script:TargetUser = $Member.SamAccountName.ToUpper() $Script:TargetFName = Get-AdUser -Identity $Script:TargetUser -Properties * MessageLog "Both" "-- VPN Certificate Inspection is executing for target user: $Script:TargetUser --" "5" #==[ This section is for MANUAL processing. It has been disabled for this version of the script but left in for reference ]== # #$Script:TargetUser = $env:username #--[ Sets the target user to the current session username variable ]-- #$Script:EmailTo = $env:username #--[ Set the destination email to the same as above ]-- # #If($Script:Force){ #--[ Adjustments for targeted execution ]-- # $Script:TargetUser = Read-Host -Prompt "Enter Target User ID: (- DO NOT include a domain and extension - To abort leave the User ID blank -)" # If (($Script:TargetUser -eq "") -or($Script:TargetUser -eq $Null)){ # MessageLog "Both" "NOTICE: VPN Certificate Script execution terminated due to a blank user ID..." "2" # Write-EventLog -LogName Application -Source VPN_PowerShell -EntryType Information -EventId 12345 -Message $Script:EventLogMsg # break # } # $Script:EmailAlternate = Read-Host -Prompt "Enter YOUR user ID. A copy of the email will be sent to you as a failsafe." # $Script:EmailTo = $Script:TargetUser #} # #If (($env:username -eq "testuser") -or ($env:username -eq $Script:DebugUser)){ #--[ Additional adjustments for debugging ]-- # $Script:TargetUser = "testuser" # $Script:EmailTo = "user1" #} #============================================================================================================================== If ((!(Test-Path "c:\Windows\System32\certutil.exe")) -or (!(Test-Path "c:\Windows\Syswow64\certutil.exe"))){ MessageLog "Both" "-- Certutil was not found on system ! --" "2" Write-EventLog -LogName Application -Source $Script:EventlogName -EntryType $Script:EventlogType -EventId $Script:EventlogID -Message ($Script:EventLogMsg | out-string) $Script:Debug = $true $Script:NotifyUser = $False StatusEmail Break;break;break } #--[ Detection ]---------------------------------------------------------------- ForEach ($Pattern in $Script:BadPattern.Pattern){ if ($Script:TargetUser -like $Pattern){ $IllegalUser = $True } } if (!($IllegalUser)){ If (Test-Path "$Script:CertStore\$Script:TargetUser.update"){ #--[ NON-STANDARD OPERATION: Force an email to user due to some out-of-cycle update ]-- #--[ This section normally will never fire. Manually add an "user.update" file to trigger for a user. ]-- #--[ This flag file will preempt ALL other detection. ]-- MessageLog "Both" "-- Detected an UPDATE flag file. Removing update flag file and sending notification email --" "3" Remove-item "$Script:CertStore\$Script:TargetUser.update" -ErrorAction stop -Force -Confirm:$False $Script:EmailMsg = $Script:TargetFName.givenName+',<br><br>This is an automated courtesy email from the '+$Script:CompanyName+' VPN server.<br><br> Please do NOT reply to it, this is an unmonitored email address.<br><br> We have made changes to the encryption protocols used on the VPN that require you to update the security<br> certificates installed on your PC so that they match the ones used for the '+$Script:CompanyName+' computer systems.<br><br> Attached to this email you will find copies of the '+$Script:CompanyName+' updated Public Key Infrastructure root certificates.<br><br> Please install the files as described in the included instruction document.<br><br> It is not necessary to re-install your personal certificate, it is being included for completeness.<br><br> Please contact Support at '+$Script:SupportPhone+' if you have issues or questions.<br><br> This will be the only notice sent.<br><br> '+$Script:CompanyName+' IT Network Department' $Script:NotifyUser = $True SendEmail }Else{ #--[ NORMAL OPERATION continues here... ]-- If (Test-Path "$Script:CertStore\$Script:TargetUser.pfx"){ MessageLog "Both" "-- Existing certificate found --" "1" $CertDump = certutil -p $Script:VPNPassword -dump ("$Script:CertStore\$Script:TargetUser.pfx").tostring() $CertLogDump = @() $CertLogDump = certutil -p $Script:VPNPassword -dump ("$Script:CertStore\$Script:TargetUser.pfx").tostring() $CertTmp1 = "" $CertTmp2 = "" foreach ($LineIn in $CertLogDump){ $CertTmp1 += ($LineIn+"`n") $CertTmp2 += "<font color="+$ScriptColor[16]+">$LineIn</font><br>" } MessageLog "ConsoleOnly" $CertTmp1 "6" MessageLog "DebugLogOnly" $CertTmp2 "6" #--[ NOTE: Dumpfile parsing is unreliable and gives differing results on various machines. This routine finds the first date ]-- $Line3 = (((($CertDump -split "`r")[3]) -split " ")[2]) -as [datetime] $Line4 = (((($CertDump -split "`r")[4]) -split " ")[2]) -as [datetime] $Line5 = (((($CertDump -split "`r")[5]) -split " ")[2]) -as [datetime] $Line6 = (((($CertDump -split "`r")[6]) -split " ")[2]) -as [datetime] If ($Line3){ $CreationDate = ((($CertDump -split "`r")[3]) -split " ")[2] #command formatted to work on PowerShell v2 $ExpireDate = ((($CertDump -split "`r")[4]) -split " ")[2] #command formatted to work on PowerShell v2 }elseIf ($Line4){ $CreationDate = ((($CertDump -split "`r")[4]) -split " ")[2] #command formatted to work on PowerShell v3 $ExpireDate = ((($CertDump -split "`r")[5]) -split " ")[2] #command formatted to work on PowerShell v3 }elseif ($Line5){ $CreationDate = ((($CertDump -split "`r")[5]) -split " ")[2] #command formatted to work on PowerShell v4 $ExpireDate = ((($CertDump -split "`r")[6]) -split " ")[2] #command formatted to work on PowerShell v4 }elseif ($Line6){ $CreationDate = ((($CertDump -split "`r")[6]) -split " ")[2] #command formatted to work on PowerShell v5 $ExpireDate = ((($CertDump -split "`r")[7]) -split " ")[2] #command formatted to work on PowerShell v5 } $CreationDate = get-date $CreationDate -f "MM-dd-yyyy" MessageLog "Both" ($MsgText = '-- Cert Creation Date : '+$CreationDate) "3" $ExpireDate = get-date $ExpireDate -f "MM-dd-yyyy" MessageLog "Both" ($MsgText = '-- Cert Expire Date : '+$ExpireDate) "3" $Today = Get-Date -f "MM-dd-yyyy" MessageLog "Both" ($MsgText = '-- Today is : '+$Today) "3" $Remaining = NEW-TIMESPAN –Start $Today –End $ExpireDate $Remaining = [math]::abs($Remaining.Days) MessageLog "Both" "-- $Remaining days left until expiration --" "1" #--[ UNCOMMENT AND CHANGE THESE TO TEST EXPIRATION TRIGGERS DURING DEBUGGING ]------------- <#If ($Script:TargetUser -eq $Script:DebugUser){ $Remaining = 25 MessageLog "Both" ("-- TESTING: Days until expire adjusted to : "+$Remaining) "2" }#> #--[ UNCOMMENT AND CHANGE THESE TO TEST EXPIRATION TRIGGERS DURING DEBUGGING ]------------- #--[ Determine what to do depending on number of remaining days ]---------------- If ($Remaining -le 15){ #--[ 15 days until certificate expires. Generate a new one and send. ]-- MessageLog "Both" "-- Less than 15 days is remaining. Clearing flag file. Generating new cert. Archiving old cert." "3" If (Test-Path "$Script:CertStore\$Script:TargetUser.flag"){remove-item "$Script:CertStore\$Script:TargetUser.flag" -Force -Confirm:$False } [string]$Script:BackupName = $Script:TargetUser+"_$ExpireDate.pfx.old" If (Test-Path "$Script:CertStore\$Script:TargetUser.pfx"){ If ($Script:Console){ Write-Host "Renaming and Relocating: " -ForegroundColor "3" -NoNewline Write-Host $Script:CertStore"\"$Script:TargetUser.pfx -ForegroundColor "5" -NoNewline Write-Host " to: " -ForegroundColor "3" -NoNewline Write-Host $Script:CertStore"\Backups\"$Script:BackupName -ForegroundColor "6" } try{ If (Test-Path "$Script:CertStore\$Script:TargetUser.pfx"){ rename-item "$Script:CertStore\$Script:TargetUser.pfx" $Script:CertStore"\"$Script:BackupName -ErrorAction stop -Force -Confirm:$False move-Item $Script:CertStore"\"$Script:BackupName -Destination $Script:CertStore"\backups\"$Script:BackupName -ErrorAction stop -Force -Confirm:$False } }catch{ MessageLog "Both" $_.Exception.Message "2" MessageLog "Both" $_.Exception.ItemName "2" } } GenerateCert }ElseIf ($Remaining -le 30){ #--[ NOTIFY USER. Certificate due to expire. ]-- If (!(Test-Path "$Script:CertStore\$Script:TargetUser.flag")){ MessageLog "Both" "-- Less than 30 days is remaining. Writing flag file. Sending warning email --" "3" Add-Content -Path "$Script:CertStore\$Script:TargetUser.flag" -Value "30 day notice" $Script:EmailMsg = $Script:TargetFName.givenName+',<br><br>This is an automated courtesy email from the '+$Script:CompanyName+' VPN server.<br><br> We store a copy of your VPN certificate as a backup. This backup is automatically scanned<br> every two to three days and the expiration date is checked. <br><br> '+$Script:TargetUser+' Certificate Statistics:<br> Created on: '+$CreationDate+'<br> <strong>Expires on: '+$ExpireDate+'</strong><br><br> Your '+$Script:CompanyName+' VPN certificate is due to expire within 30 days. There is <br> nothing you need to do except be aware of this fact. Within approximately 15 days you will<br> be emailed a new certificate with instructions on how to install it. Please watch for it.<br><br> If after 3 weeks you have not received it, please contact support at '+$Script:SupportPhone+'.<br><br> If your certificate expires you will be unable to use the company VPN to connect to the network.<br><br> This will be the only notice sent. Please reply to this email should you have any questions.<br><br> '+$Script:CompanyName+' IT Network Department' $Script:NotifyUser = $true SendEmail #--[ This is just a notification. ]-- }Else{ MessageLog "Both" "-- Less than 30 days is remaining. Flag file exists. Exiting --" "3" } }Else{ #--[ Greater than 30 days remaining. Cleanup any old lingering flag files. ]-- If (Test-Path "$Script:CertStore\$Script:TargetUser.flag"){ Try{ MessageLog "Both" "-- Removing stale flag file" "3" Remove-Item "$Script:CertStore\$Script:TargetUser.flag" -Force -Confirm:$false }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName } MessageLog "Both" $ErrorMessage "2" MessageLog "Both" $FailedItem "2" } MessageLog "Both" "-- Greater than 30 days until certificate expiration. NOTHING TO DO --" "1" } }Else{ #--[ No existing certificate found in archive. Generate a new one. ]-- MessageLog "Both" "-- No VPN cert was found. Generating a new one --" "3" $Global:EmailBody = $Script:EmailMsg GenerateCert } } }Else{ MessageLog "Both" "-- Detected an invalid user account. Admin and Service accounts are not permitted to use VPN. Bypassing... --" "2" } If($Script:Installed){ MessageLog "Both" ('-- Removing any '+$Script:TargetUser+' certificates from local store --') "3" If ($Script:TargetUser -ne $Env:Username){ dir Cert:\CurrentUser -Recurse | ? subject -match $Script:TargetUser | Remove-Item #-WhatIf } $Script:Installed = $False } MessageLog "Both" "`n-- VPN Certificate Processing Completed --" "5" MessageLog "Both" "HR" "2" #--[ Inserts a hard rule ]-- $IllegalUser = $False $Script:Attach = $false $Script:NotifyUser = $False Write-EventLog -LogName Application -Source $Script:EventlogName -EntryType $Script:EventlogType -EventId $Script:EventlogID -Message ($Script:EventLogMsg | out-string) } }Catch{ $ErrorMessage = $_.Exception.Message $FailedItem = $_.Exception.ItemName MessageLog "Both" $ErrorMessage "2" MessageLog "Both" $FailedItem "2" } If ($Script:Console){ Write-host "-- Debug Setting : $Script:Debug" -Foreground Yellow Write-host "-- Status Setting : $Script:Status" -Foreground Yellow Write-host "-- Attach Setting : $Script:Attach" -Foreground Yellow } MessageLog "Both" "`n-- VPN Script Completed --`n" "5" StatusEmail [System.GC]::Collect() <#--[ Configuration file example. The file must be named same as script (autocert.xml) and be located with the script ]-- <!-- Settings & Configuration File --> <Settings> <General> <CompanyName>My Company</CompanyName> <CompanyInitials>IBM</CompanyInitials> <SupportPhone></SupportPhone> <ScriptName>AutoCert.ps1</ScriptName> <VPNGroup>vpnusers</VPNGroup> <!-- AD group for VPN access --> <EventlogName>VPN_PowerShell</EventlogName> <EventlogID>12345</EventlogID> <EventlogType>Information</EventlogType> <CertStore>\\mydomain\VPN$\VPN</CertStore> <!-- Network share where certs are stored --> <TemplateName>VPNUserTemplate</TemplateName> <!-- cert template name on your CA --> <CaName>PKICA1</CaName> <!-- name of your CA --> <BadPattern> <Pattern>*admin*</Pattern> <!-- AD accounts to exclude --> <Pattern>*service*</Pattern> </BadPattern> </General> <Email> <From>itnetwork</From> <To>This_field_not_used</To> <Subject>My Company VPN</Subject> <DebugSubject>VPN Certificate Status and Debugging Information</DebugSubject> <Domain>MyCompany.com</Domain> <HTML>$true</HTML> <SmtpServer>10.10.50.51</SmtpServer> <DebugUser> <User>User1</User> </DebugUser> <StatusUser> <User>user1</User> <User>user2</User> <User>user3</User> <User>user4</User> </StatusUser> <Attachments> <Attach1>Root.p7b</Attach1> <Attach2>Issuing.p7b</Attach2> <Attach3>SSLVPN-Install.docx</Attach3> </Attachments> </Email> <Credentials> <UserName></UserName> <Password></Password> <VPNPassword>vpnpwd</VPNPassword> </Credentials> </Settings> #> |