It’s Thursday at 12:27pm. YOu are sitting in a meeting. Meanwhile, someone has just gained access t o your domain admin group and now has the keys to your companies entire compute platform. But all is not lost. If you have the right alerting in place. Less than five minutes later, you receive an alert to say that the group membership has changed and you can now take immediet action. Saving the world… and your company… from absolute disaster! That’s what this PowerShell script does. Along with lots of checks. For example, let’s say someone get’s in through a back door and start’s messing around with systems. Well, if it doesn’t see the right number of logs in the folder, it starts to get a bit jumpy. That results in an alert as well. Or if the previous logs can’t be read, same thing happens. The world get’s an Email.
So here you go. Have fun with this. I encourage you to add more checks. This is been expanded since I published it to add even more validation to make sure that the script and the infrastructure doesn’t change. But this will get you well on the right road.
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 |
Try { Import-Module ActiveDirectory -ErrorAction Stop } Catch { Write-Warning $_.Exception.Message Read-Host "Script will end. Press enter to close the window" Exit } Function SendEmail { param ( [Parameter(Mandatory = $true, Position = 0)] [string] $To, [Parameter(Mandatory = $true, Position = 0)] [string] $Subject, [Parameter(Mandatory = $true, Position = 0)] [string] $Body ) # To do: Move to Credential Manager away from this horrible password file. $File = "EmailPassword.txt" $SMTPUsername = "<YourSMTPUserName>" $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $SMTPUsername, (Get-Content $File | ConvertTo-SecureString) $SMTPServer = "<YourSMTP.Server.com>" $SMTPPort = 587 $From = "<SomeEmailAddress@YourDomain.com>" $SMTPMessage = New-Object System.Net.Mail.MailMessage($From, $To, $Subject, $Body) $SMTPMessage.IsBodyHtml = $true $SMTPClient = New-Object Net.Mail.SmtpClient($SMTPServer, $SMTPPort) $SMTPClient.EnableSsl = $true $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($cred.UserName, $cred.Password); $SMTPClient.Send($SMTPMessage) } Function PrepareEmailBody { param ( [Parameter(Mandatory = $true, Position = 0)] [string] $IntroductionMessage, [Parameter(Mandatory = $true, Position = 0)] [string] $GroupTable, [Parameter(Mandatory = $true, Position = 0)] [string] $Errors ) $GroupTableHTML = $GroupTable | ConvertTo-Html -Fragment $ErrorsHTML = $Errors | ConvertTo-Html -Fragment $EmailBodyRaw = Get-Content -Path "EmailTemplate.html" [string] $EmailBodyReady = $EmailBodyRaw -replace "IntroductionMessage", $IntroductionMessage If ($GroupTable -eq "None") { $EmailBodyReady = $EmailBodyReady -replace "GroupTable", "" } else { $EmailBodyReady = $EmailBodyReady -replace "GroupTable", $GroupTableHTML } If ($Errors -eq "None") { $EmailBodyReady = $EmailBodyReady-replace "ErrorHeading", "" $EmailBodyReady = $EmailBodyReady -replace "ErrorText", "" } else { $EmailBodyReady = $EmailBodyReady -replace "ErrorHeading", "Errors were encountered" $EmailBodyReady = $EmailBodyReady -replace "ErrorText", $ErrorsHTML } Return $EmailBodyReady } Function DeleteOldFiles { Get-ChildItem –Path $PWD.Path -Filter *.xml | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))} | Remove-Item } Function ExportCurrentGroupMembers { } Function ValidateTier0Admins { $Errors = @() # Get the current group members and the previously saved group members. $Tier0Admins = Get-ADGroupMember -Identity "<YourVerySensitiveGroupNameHere>" # Get the last XML file written to this directory Try { $InputFile = (Get-ChildItem -Filter *.xml | sort LastWriteTime | select -last 1)[0].Name } catch { $Errors += "No XML file found" } # Make sure this is valid XML and if it is, store it to a variable. Try { $StoredTier0Admins = Import-Clixml -Path .\$InputFile -ErrorAction Stop } catch { Write-Warning $_.Exception.Message $Errors += $_.Exception.Message } If ($StoredTier0Admins.count -le 0) { $Errors += "There is an issue with the input file." } # Check if there are differences in the groups. If there are, determine what these changes mean. $GroupDifferences = Compare-Object -ReferenceObject $Tier0Admins -DifferenceObject $StoredTier0Admins -Property SamAccountName If ($GroupDifferences.count -ge 1) { $result = @() ForEach ($Item in $GroupDifferences) { Switch ($Item.SideIndicator) { "<=" { $NotedChange = "Recently added to group" } "=>" { $NotedChange = "Recently removed from group" } } $array = ([ordered]@{ 'User' = $Item.SamAccountName 'Change' = $NotedChange }) $result += New-Object -TypeName PSCustomObject -Property $array } } # Create a new filename for the next invocation of the script to check. [string] $OutputFile = $PWD.Path + "\Tier0Admins-" + (get-date -Format "dd-MM-yyyy-HH_mm") + ".xml" # Export this check to the file. $Tier0Admins | Export-Clixml -Path H:\DEV\ps\AD\GroupValidation\$OutputFile # Delete old files. But first, check that there are the expected number of files in the directory. # After 7 days, there should be 2016 XML files. But I'm going with 2010 to give a minor margin for error until I test this for a week or two. # Why am I holding so many files? It might be useful in the event that a group changes during a weekend, public holiday or annual leave. If ((Get-ChildItem –Path $PWD.Path -Filter *.xml | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))}).count -ge 2010) { DeleteOldFiles } Else { $Errors += "There are less log files than expected." } # To help make a decision when compiling the Email body I set variables to None if they have a count of 0. If ($Errors.count -eq 0) { $Errors = "None" If ($result.count -eq 0) { $GroupDifferences = "None" } } # Finally I will send an Email if there are group changes or errors were encountered while processing the script. If ($GroupDifferences.count -ge 1 -or $Errors.count -ne "None") { $EmailBody = PrepareEmailBody -IntroductionMessage "Changes were found in the <YourVerySensitiveGroupNameHere> group" -GroupTable $GroupDifferences -Errors $Errors SendEmail -To "<SomeEmailAddress@YourDomain.com>" -Subject "Changes found in <YourVerySensitiveGroupNameHere> group." -Body $EmailBody } } |