SvcAcctPwdRefresh.ps1
<#======================================================================================
File Name : SvcAcctPwdRefresh.ps1 Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com) : Description : Will refresh a list of service account passwords from an encrypted file. : The script can create the encrypted file and also decode it. : Notes : Command line options: (default is $false unless noted) : $Debug - $true/$false. Use for debugging : $Decode - $true/$false. Set to $true to reverse the encrytion to the console !!! ONLY !!! : $Enabled - $true/$false. Set to $true to perform the reset (defaults to $false to be safe) : $Console - $true/$false. Set to $true to display output to console : $Testmode - $true/$false. Enabled "what if" mode. : Warnings : Yes Virginia, this will reset your passwords. Use it wisely. : 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: : Last Update by : Kenneth C. Mazie Version History : v1.00 - 04-07-15 - Original Change History : v1.01 - 10-05-16 - Added external config file : v1.02 - 09-17-18 - Added credential to XML file. Added test options. Removed Quest requirement. : Added more info to console output. : v1.03 - 09-17-18 - Replaced line that adds user name to email report. : v1.04 - 09-18-18 - Fixed issue with secure string input to setad-accountpassword command. : =======================================================================================#> <#PSScriptInfo .VERSION 1.04 .GUID 3444c429-6ef5-40c2-a29b-170032052685 .AUTHOR Kenneth C. Mazie (kcmjr AT kcmjr.com) .DESCRIPTION Creates an AES encrypted password file in the script folder. Reads and decrypts the file and will reset AD passwords using the results. Can also read the file and display original passwords to console. Use for scheduled refresh of service account passwords that cannot change but still need to be refreshed. Emails results. #> Param ( [switch]$Debug = $False, #--[ Set via command line to true to disable reset action and email only to alt [switch]$Decode = $False, [switch]$Enabled = $False, #--[ Blocked by default. Force to true via command line to perform update [switch]$Console = $False, [switch]$TestMode = $false #--[ Enables "whatif" mode if set to true from command line ) Clear-Host if (!(Get-Module ActiveDirectory)){Import-Module "activedirectory" -ErrorAction SilentlyContinue} [void][Reflection.Assembly]::LoadWithPartialName("System.Security") $SendEmail = $true #--[ Set to true to email results $ErrorActionPreference = "Stop" #ilentlyContinue" #--[ Error action preference. #--[ Hard settings for Testing ]---------------------- #$Enabled = $true #$Decode = $true #$Console = $true #$TestRun = $true #$Debug = $true #----------------------------------------------------- If ($Debug){ $Script:Debug = $True $Script:Console = $True } If ($Decode){ $Script:Console = $True } If ($Enabled){$Script:Enabled = $True} If ($Console){$Script:Console = $True} If ($TestRun){$Script:TestRun = $True} If ($Script:Decode){ $Script:Enabled = $False $Script:SendEmail = $False $Script:Console = $True } #--[ Read and load configuration file ]----------------------------------------- If (!(Test-Path "$PSScriptRoot\Configuration.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\Configuration.xml" #--[ Load configuration ]-- $Script:ScriptName = $Script:Configuration.Settings.General.ReportTitle $Script:InputFile = $Script:Configuration.Settings.General.InputFile $Script:SecureFile = $Script:Configuration.Settings.General.SecureFile $Script:MaxDays = $Script:Configuration.Settings.General.MaxDays $Script:DebugUser = $Script:Configuration.Settings.Email.DebugUser $Script:DebugSubject = $Script:Configuration.Settings.Email.DebugSubject $Script:EmailTo = $Script:Configuration.Settings.Email.To $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:EncryptedPW = $Script:Configuration.Settings.Credentials.Password $Script:Base64String = $Script:Configuration.Settings.Credentials.Key $ByteArray = [System.Convert]::FromBase64String($Base64String) $Script:Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserName, ($EncryptedPW | ConvertTo-SecureString -Key $ByteArray) $Script:Password = $Credential.GetNetworkCredential().Password $Script:Passphrase = $Script:Configuration.Settings.Credentials.Passphrase $Script:Salt = $Script:Configuration.Settings.Credentials.Salt $Script:Init = $Script:Configuration.Settings.Credentials.Init $Script:EventlogName = $Script:Configuration.Settings.General.EventlogName $Script:EventlogID = $Script:Configuration.Settings.General.EventlogID $Script:EventlogType = $Script:Configuration.Settings.General.EventlogType $Script:EmailAttach1 = $Script:Configuration.Settings.Email.Attachments.Attach1 } If ($Script:Debug){ $Script:eMailRecipient = $Script:DebugUser #--[ Alt destination email address during debug. $Script:EmailBody = "This is an automated email message.<br><br>This is a <font color=red>SIMULATED</font> Service Account password refresh:<br><br>" }Else{ $Script:eMailRecipient = $Script:EmailTo #--[ Destination email address. $Script:EmailBody = "This is an automated email message.<br><br>The following Service Account passwords have been refreshed:<br><br>" } Function SendEmail { $email = New-Object System.Net.Mail.MailMessage $email.From = $Script:EmailFrom $email.IsBodyHtml = $Script:EmailHTML $email.To.Add($Script:eMailRecipient) $email.Subject = $Script:EmailSubject + " Results" $email.Body = $Script:EmailBody $smtp = new-object Net.Mail.SmtpClient($Script:SmtpServer) If ($Script:SendEmail){ $smtp.Send($email) If ($Script:Console){Write-Host "`nEmail has been sent`n" -ForegroundColor Green } } } Function Encrypt-String($String, [switch]$arrayOutput){ If ($Script:Console){Write-Host "." -NoNewline } $r = new-Object System.Security.Cryptography.RijndaelManaged $Pass = [Text.Encoding]::UTF8.GetBytes($Script:Passphrase) $Salt = [Text.Encoding]::UTF8.GetBytes($Script:Salt) $r.Key = (new-Object Security.Cryptography.PasswordDeriveBytes $Pass, $Salt, "SHA1", 5).GetBytes(32) #256/8 $r.IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($Script:Init) )[0..15] $c = $r.CreateEncryptor() $ms = new-Object IO.MemoryStream $cs = new-Object Security.Cryptography.CryptoStream $ms,$c,"Write" $sw = new-Object IO.StreamWriter $cs $sw.Write($String) $sw.Close() $cs.Close() $ms.Close() $r.Clear() [byte[]]$result = $ms.ToArray() if($arrayOutput) { return $result } else { return [Convert]::ToBase64String($result) } } Function Decrypt-String($Script:Encrypted){ if($Script:Encrypted -is [string]){ $Script:Encrypted = [Convert]::FromBase64String($Script:Encrypted) } $r = new-Object System.Security.Cryptography.RijndaelManaged $Pass = [System.Text.Encoding]::UTF8.GetBytes($Script:Passphrase) $Salt = [System.Text.Encoding]::UTF8.GetBytes($Script:Salt) $r.Key = (new-Object Security.Cryptography.PasswordDeriveBytes $Pass, $Salt, "SHA1", 5).GetBytes(32) #256/8 $r.IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($Script:Init) )[0..15] $d = $r.CreateDecryptor() $ms = new-Object IO.MemoryStream @(,$Script:Encrypted) $cs = new-Object Security.Cryptography.CryptoStream $ms,$d,"Read" $sr = new-Object IO.StreamReader $cs Write-Output $sr.ReadToEnd() $sr.Close() $cs.Close() $ms.Close() $r.Clear() } if (test-path "$PSScriptroot\$Script:InputFile"){ If ($Script:Console){Write-Host "New input file found. Converting to secure text..." -ForegroundColor Magenta} $Script:EmailBody = "<br>New input file found. Converting to secure text...<br>" If (test-path "$PSScriptroot\$Script:SecureFile"){Remove-Item "$PSScriptroot\$Script:SecureFile" -force} #--[ Remove the old encrypted file ]-- $SecString = Get-Content "$PSScriptroot\$Script:InputFile" ForEach ($LineItem in $SecString){ $Encr = Encrypt-String $LineItem $Script:Passphrase Add-Content "$PSScriptroot\$Script:SecureFile" $Encr } While (test-path "$PSScriptroot\$Script:InputFile"){Remove-Item -Path "$PSScriptroot\$Script:InputFile" -Force -Confirm:$false } #-whatif If ($Script:Console){Write-Host "`nEncryption completed...`nSource file deleted..." -ForegroundColor Red } } If ($Script:Decode){ if (test-path "$PSScriptroot\$Script:SecureFile"){ $Script:Debug = $true If ($Script:Console){Write-Host "Decrypting secure text to screen..." -ForegroundColor magenta } $SecString = Get-Content "$PSScriptroot\$Script:SecureFile" ForEach ($LineItem in $SecString){ $Clear = Decrypt-String $LineItem If ($Script:Console){Write-Host "-----------------------------------------" -ForegroundColor Cyan } If ($Script:Console){Write-Host "Decoding...." -ForegroundColor Magenta } If ($Script:Console){Write-Host "`tUser = " -ForegroundColor Cyan -NoNewline } If ($Script:Console){Write-Host $Script:ThisUser -ForegroundColor Yellow } If ($Script:Console){Write-Host "`tPass = " -ForegroundColor Cyan -NoNewline } If ($Script:Console){Write-Host $Clear.Split(",")[1]`n -ForegroundColor Yellow } } }Else{ If ($Script:Console){Write-Host "`nInput file is missing`n" -ForegroundColor Red } $Script:EmailBody = "<br>The Service Account Password input file was not found... Nothing to do.<br>" } } If ($Script:Enabled){ $Script:LastSet = "" $Script:LastSetDate = "" $Script:Now = "" $Span = "" if (test-path "$PSScriptRoot\$Script:SecureFile"){ $SecString = Get-Content "$PSScriptRoot\$Script:SecureFile" ForEach ($LineItem in $SecString){ $Clear = Decrypt-String $LineItem $Script:Passphrase #--[ Decodes line item user and pwd ]-- $SecString = ConvertTo-SecureString -String $Clear.Split(",")[1] -AsPlainText -Force #--[ Converts pwd string to secure string ]-- $Script:ThisUser = $Clear.Split(",")[0] If (Get-ADUser $Script:ThisUser ){ Try{ If ($Script:Debug -or $Script:Testrun){ #--[ Do nothing ]-- If ($Script:Console){Write-Host "`n-----------[ DEBUG MODE (Simulating Changes) ]----------------" -ForegroundColor cyan} $Result = Set-ADAccountPassword -Credential $Script:Credential -Reset -NewPassword $Secstring -Identity $Script:ThisUser -PassThru -Confirm:$false -ErrorAction "Stop" -WhatIf Write-host $Result -ForegroundColor Magenta }Else{ If ($Script:Console){Write-Host "`n------------[ LIVE MODE ]----------------" -ForegroundColor Cyan } If ($Script:Console){Write-Host "Updating...." -ForegroundColor Red } $Result = Set-ADAccountPassword -Credential $Script:Credential -Reset -NewPassword $Secstring -Identity $Script:ThisUser -PassThru -Confirm:$false -ErrorAction "Stop" } $Script:EmailBody += "<br>User <strong>" + $Script:ThisUser+ "</strong> password refresh successful...<br>" }Catch{ $Script:EmailBody += "<br>User <strong>" + $Script:ThisUser+ "</strong> password refresh failed...<br>" $Script:EmailBody += $_.Exception.Message $Script:EmailBody += $_.Exception.ItemName } If ($Script:Console){Write-Host "`t User = " -ForegroundColor Cyan -NoNewline } If ($Script:Console){Write-Host $Script:ThisUser -ForegroundColor Yellow } If ($Script:Console){Write-Host "`t Pass = " -ForegroundColor Cyan -NoNewline } If ($Script:Console){Write-Host $Clear.Split(",")[1] -ForegroundColor Yellow } If ($Script:Console){Write-Host "`t Hash = " -ForegroundColor Cyan -NoNewline } If ($Script:Console){Write-Host $LineItem -ForegroundColor Yellow } }Else{ If ($Script:Console){Write-Host "-- User "$Script:ThisUser" was not found in Active Directory..." -ForegroundColor Red } $Script:EmailBody += "<br>NOTICE -- User " + $Script:ThisUser+ " was not found in Active Directory...<br>" } $Script:LastSet = Get-ADUser ($Script:ThisUser) -Properties PasswordLastSet $Script:LastSet = ($Script:LastSet.Passwordlastset.tostring()).split(" ")[0] $Script:Now = "{0:MM/dd/yyyy}" -f (get-date) $Span = NEW-TIMESPAN –Start $Script:LastSet –End $Script:Now If ($Script:Console){ Write-Host " Last Refresh = " -NoNewline -ForegroundColor Cyan Write-host "AD Password last refreshed as of"$Script:LastSet -ForegroundColor Yellow Write-Host " Today = " -NoNewline -ForegroundColor Cyan Write-host $Script:Now -ForegroundColor Yellow Write-Host " Pwd Age = " -NoNewline -ForegroundColor Cyan Write-host $Span.Days -ForegroundColor Yellow Write-Host " Max Age (Days) = " -NoNewline -ForegroundColor Cyan Write-host $Script:MaxDays -ForegroundColor Yellow Write-Host " Password is = " -NoNewline -ForegroundColor Cyan } If($Span.Days -le 45){ Write-Host "GOOD" -ForegroundColor Green }else{ write-host "ERROR" -ForegroundColor Red } } }Else{ If ($Script:Console){Write-Host "`nInput file is missing`n" -ForegroundColor Red } $Script:EmailBody = "<br>The Service Account Password input file was not found... Nothing to do.<br>" } }Else{ If ($Script:Console){Write-Host "`nThis script defaults to DISABLED. It has been run DISABLED. Please re-run with ENABLED command line option set to ""TRUE"". No actions taken.`n" -ForegroundColor Red } $Script:EmailBody = "<br>This script defaults to DISABLED. It has been run DISABLED. Please re-run with ENABLED command line option set to ""TRUE"". No actions taken.<br>" } If (!($Script:Debug)){ SendEmail } If ($Script:Console){Write-Host "Completed..." -for Red;break } <# <!-- Settings & Configuration File --> <Settings> <General> <ScriptName>SvcAcctPwdRefresh.ps1</ScriptName> <EventlogName>PowerShell</EventlogName> <EventlogID>12345</EventlogID> <EventlogType>Information</EventlogType> <InputFile>svcpass.csv</InputFile> <SecureFile>secure.txt</SecureFile> <MaxDays>45</MaxDays> </General> <Email> <From>MonthlyReports@domain.com</From> <To>you@$eMailDomain,them@$eMailDomain,her@$eMailDomain</To> <Subject>Monthly Service Account PWD Update</Subject> <Domain>domain.com</Domain> <HTML>$true</HTML> <SmtpServer>10.1.1.50</SmtpServer> <DebugUser>you@domain.com</DebugUser> <Enable>$True</Enable> </Email> <Credentials> <UserName>mydomain\serviceaccount</UserName> <Password>encrypted-password-goes-here</Password> <Passphrase>OOGA-BOOGA</Passphrase> <Salt>P455W0RDs Are A Pain!</Salt> <Init>Yet another key</Init> </Credentials> </Settings> #> |