ADDSAuditTasks.psm1
#Region '.\Private\Export-AuditCSVtoZip.ps1' 0 function Export-AuditCSVtoZip { [CmdletBinding()] param ( [PSCustomObject]$ExportObject, [string]$CSVName, [string]$ZipName ) process { Write-TSLog "The $($script:MyInvocation.MyCommand.Name -replace '\..*') Export was successful. There are $($ExportObject.Count) objects listed with the following properties: " ($ExportObject | Get-Member | Where-Object { $_.membertype -eq "NoteProperty" }).Name | Write-TSLog Write-TSLog "Exporting CSV to path: $CSVName" try { $ExportObject | Export-CSV -Path $CSVName -NoTypeInformation -ErrorVariable ExportErr -ErrorAction Stop } catch { Write-TSLog "The CSV export failed with error: " Write-TSLog -End Write-TSLog -LogErrorEnd throw $ExportErr } Write-TSLog "Compressing file: $CSVName" Write-TSLog "to destination zip file: $ZipName" try { Compress-Archive -Path $CSVName -DestinationPath $ZipName -ErrorVariable ZipErr -ErrorAction Stop } catch { Write-TSLog "Failed compressing file: " Write-TSLog $CSVName Write-TSLog "to destination zip file: " Write-TSLog $ZipName Write-TSLog "with error: " Write-TSLog -End throw $ZipErr } Write-TSLog "Removing CSV file: " Write-TSLog $CSVName Write-TSLog "from the file system." try { Remove-Item $CSVName -Force -ErrorVariable CSVDeleteErr -ErrorAction Stop } catch { Write-TSLog "Failed to remove CSV file: $CSVName" Write-TSLog -LogError -LogErrorVar $CSVDeleteErr } Write-TSLog "Removed CSV file: " Write-TSLog $CSVName Write-TSLog "from the file system." } } #EndRegion '.\Private\Export-AuditCSVtoZip.ps1' 51 #Region '.\Private\Get-ADExtendedRight.ps1' 0 Function Get-AdExtendedRight([Microsoft.ActiveDirectory.Management.ADObject] $ADObject) { $ExportER = @() Foreach ($Access in $ADObject.ntsecurityDescriptor.Access) { # Ignore well known and normal permissions if ($Access.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Deny) { continue } if ($Access.IdentityReference -eq "NT AUTHORITY\SYSTEM") { continue } if ($Access.IdentityReference -eq "NT AUTHORITY\SELF") { continue } if ($Access.IsInherited) { continue } # Check extended right if ($Access.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight) { $Right = ""; # This is the list of dangerous extended attributs # see : https://technet.microsoft.com/en-us/library/ff405676.aspx switch ($Access.ObjectType) { "00299570-246d-11d0-a768-00aa006e0529" { $Right = "User-Force-Change-Password" } "45ec5156-db7e-47bb-b53f-dbeb2d03c40" { $Right = "Reanimate-Tombstones" } "bf9679c0-0de6-11d0-a285-00aa003049e2" { $Right = "Self-Membership" } "ba33815a-4f93-4c76-87f3-57574bff8109" { $Right = "Manage-SID-History" } "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" { $Right = "DS-Replication-Get-Changes-All" } } # End switch if ($Right -ne "") { $Rights = [ordered]@{ Actor = $($Access.IdentityReference) CanActOnThePermissionof = "$($ADObject.name)" + " " + "($($ADObject.DistinguishedName))" WithExtendedRight = $Right } $ExportER += new-object -TypeName PSObject -Property $Rights #"$($Access.IdentityReference) can act on the permission of $($ADObject.name) ($($ADObject.DistinguishedName)) with extended right: $Right" } # Endif } # Endif } # End Foreach return $ExportER } # End Function #EndRegion '.\Private\Get-ADExtendedRight.ps1' 35 #Region '.\Private\Get-ADGroupMemberof.ps1' 0 function Get-ADGroupMemberof { [CmdletBinding()] param ( [string]$SamAccountName ) process { $GroupStringArray = ((Get-ADuser -Identity $SamAccountName -Properties memberof).memberof | Get-ADGroup | Select-Object name | Sort-Object name).name $GroupString = $GroupStringArray -join " | " return $GroupString } } #EndRegion '.\Private\Get-ADGroupMemberof.ps1' 12 #Region '.\Private\Get-TimeStamp.ps1' 0 function Get-TimeStamp { return "[{0:yyyy/MM/dd} {0:HH:mm:ss}]" -f (Get-Date) } #EndRegion '.\Private\Get-TimeStamp.ps1' 7 #Region '.\Private\Initialize-AuditBeginBlock.ps1' 0 function Initialize-AuditBeginBlock { <# .SYNOPSIS This is a sample Private function only visible within the module. .DESCRIPTION This sample function is not exported to the module and only return the data passed as parameter. .EXAMPLE $null = Initialize-AuditBeginBlock -PrivateData 'NOTHING TO SEE HERE' .PARAMETER PrivateData The PrivateData parameter is what will be returned without transformation. #> [cmdletBinding()] [OutputType([string])] param( [Parameter( ValueFromPipeline = $true )] [string]$AttachmentFolderPathBegin = "C:\temp\ADDSAuditTasks", [Parameter( ValueFromPipelineByPropertyName = $true )] [string]$ScriptFunctionName, [Parameter( ValueFromPipelineByPropertyName = $true )] [bool]$SendEmailMessageBegin, [Parameter( ValueFromPipelineByPropertyName = $true )] [bool]$CleanBegin ) process { # Create Directory Path $AttachmentFolderPathCheck = Test-Path -Path $AttachmentFolderPathBegin If (!($AttachmentFolderPathCheck)) { Try { # If not present then create the dir New-Item -ItemType Directory $AttachmentFolderPathBegin -Force -ErrorAction Stop } Catch { Write-TSLog -Begin Write-TSLog "Directory: $AttachmentFolderPathBegin was not created." Write-TSLog -LogErrorEnd throw } } # Begin Logging to $script:Logs Write-TSLog -Begin $script:LogOutputPath = "$AttachmentFolderPathBegin\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($ScriptFunctionName)_$($env:USERDNSDOMAIN)_ADDSAuditlog.log" Write-TSLog "Log output path: $LogOutputPath" # If not Clean if (!($CleanBegin)) { # Import Active Directory Module $module = Get-Module -Name ActiveDirectory -ListAvailable if (-not $module) { Add-WindowsFeature RSAT-AD-PowerShell -IncludeAllSubFeature -Verbose -ErrorAction Stop } try { Import-Module "activedirectory" -Global -ErrorAction Stop } catch { Write-TSLog "The Module Was not installed. Use `"Add-WindowsFeature RSAT-AD-PowerShell`" or install using server manager under `"Role Administration Tools>AD DS and AD LDS Tools>Active Directory module for Windows Powershell`"." Write-TSLog -LogErrorEnd throw } if ($SendEmailMessageBegin) { # Install / Import required modules. $module = Get-Module -Name Send-MailKitMessage -ListAvailable if (-not $module) { Install-Module -Name Send-MailKitMessage -AllowPrerelease -Scope AllUsers -Force } try { Import-Module "Send-MailKitMessage" -Global -ErrorAction Stop } catch { # End run and log to file. Write-TSLog "The Module Was not installed. Use `"Save-Module -Name Send-MailKitMessage -AllowPrerelease -Path C:\temp`" on another Windows Machine." Write-TSLog -End throw } } elseif ($WinSCPBegin) { $module = Get-Module -Name WinSCP -ListAvailable if (-not $module) { Install-Module WinSCP -Scope CurrentUser } try { Import-Module WinSCP -Global -ErrorAction Stop } catch { Write-TSLog "The Module Was not installed. Export WinSCP using: `"Save-Module WinSCP -Path <Path>`" and import to this machine." Write-TSLog -LogErrorEnd throw } } } # If SendMailMessage return $LogOutputPath } } #EndRegion '.\Private\Initialize-AuditBeginBlock.ps1' 109 #Region '.\Private\Initialize-AuditEndBlock.ps1' 0 function Initialize-AuditEndBlock { <# .SYNOPSIS This is a sample Private function only visible within the module. .DESCRIPTION This sample function is not exported to the module and only return the data passed as parameter. .EXAMPLE $null = Initialize-AuditEndBlock -PrivateData 'NOTHING TO SEE HERE' .PARAMETER PrivateData The PrivateData parameter is what will be returned without transformation. #> [cmdletBinding()] param ( [string]$SmtpServerEnd, [int]$PortEnd, [string]$UserNameEnd, #[switch]$ssl, [string]$FromEnd, [string]$ToEnd, #[string]$subject = "$($script:MyInvocation.MyCommand.Name -replace '\..*') report ran for $($env:USERDNSDOMAIN).", [string]$AttachmentFolderPathEnd, #[string]$body, [securestring]$Password, [string]$FunctionEnd, [string]$FunctionAppEnd, [string]$ApiTokenEnd, [string[]]$ZipEnd, [bool]$CleanEnd, [bool]$LocalDiskEnd, [bool]$SendEmailMessageEnd, [bool]$WinSCPEnd, [string]$FTPHostEnd, [string]$SshHostKeyFingerprintEnd, [string]$RemotePathEnd ) process { Write-TSLog "The Value of Clean is $CleanEnd." if ($CleanEnd) { Write-TSLog "Removing Send-MailKitMessage Module" try { # Remove Modules Remove-Module -Name "Send-MailKitMessage" -Force -Confirm:$false -ErrorAction Stop } catch { Write-TSLog "Error removing Send-MailKitMessage Module" Write-TSLog -LogError } Write-TSLog "Uninstalling Send-MailKitMessage Module" try { # Uninstall Modules Uninstall-Module -Name "Send-MailKitMessage" -AllowPrerelease -Force -Confirm:$false } catch { Write-TSLog -LogError if (Get-Module -Name Send-MailKitMessage -ListAvailable) { Write-TSLog "Error uninstalling Send-MailKitMessage Module" } } Write-TSLog "Removing directories and files in: " Write-TSLog "$AttachmentFolderPathEnd" try { Remove-Item -Path $AttachmentFolderPathEnd -Recurse -Force -ErrorAction Stop } catch { Write-TSLog "Directory Cleanup error!" Write-TSLog -LogError throw } Write-TSLog -End Write-TSLog -LogOutputPath C:\temp\ADDSAuditTaskCleanupLogs.log } else { if ($SendEmailMessageEnd) { if ($Password) { <# Send Attachment using O365 email account and password. Must exclude from conditional access legacy authentication policies. #> Write-TSLog "Account: $UserNameEnd," Write-TSLog "SENDING email to: $ToEnd," Write-TSLog "From USER: $FromEnd," Write-TSLog "Using PORT: $PortEnd," Write-TSLog "Using RELAY $SMTPServerEnd, with SSL" Write-TSLog "With: PASSWORD" Write-TSLog "Logs included in body" Write-TSLog -End Send-AuditEmail -smtpServer $SMTPServerEnd -port $PortEnd -username $UserNameEnd ` -body $script:Logs -pass $Password -from $FromEnd -to $ToEnd -attachmentfiles ($ZipEnd).Split(" ") -ssl $Password.Dispose() Remove-Item -Path $AttachmentFolderPath -Recurse -Force -ErrorAction Stop } # End if else { Write-TSLog "Account: $UserNameEnd," Write-TSLog "SENDING email to: $ToEnd," Write-TSLog "From USER: $FromEnd," Write-TSLog "Using PORT: $PortEnd," Write-TSLog "Using RELAY: $SMTPServerEnd, with SSL" Write-TSLog "Without: PASSWORD" Write-TSLog "Logs included in body" Write-TSLog -End Send-AuditEmail -smtpServer $SMTPServerEnd -port $PortEnd -username $UsernameEnd ` -body $script:Logs -from $FromEnd -to $ToEnd -attachmentfiles ($ZipEnd).Split(" ") -ssl Remove-Item -Path $AttachmentFolderPathEnd -Recurse -Force -ErrorAction Stop } } elseif ($FunctionAppEnd) { <# Send Attachment using O365 email account and Keyvault retrived password. Must exclude email account from conditional access legacy authentication policies. #> Write-TSLog "Account: $UserNameEnd," Write-TSLog "SENDING email to: $ToEnd," Write-TSLog "From USER: $FromEnd," Write-TSLog "Using PORT: $PortEnd," Write-TSLog "Using RELAY: $SMTPServerEnd, with SSL" Write-TSLog "Using FUNCTION APP: $FunctionAppEnd," Write-TSLog "With FUNCTION: $FunctionEnd" Write-TSLog "Logs included in body" Write-TSLog -End Send-AuditEmail -smtpServer $SMTPServerEnd -port $PortEnd -username $UserNameEnd ` -body $script:Logs -Function $FunctionEnd -FunctionApp $FunctionAppEnd -token $ApiTokenEnd -from $FromEnd -to $ToEnd -attachmentfiles ($ZipEnd).Split(" ") -ssl Remove-Item -Path $AttachmentFolderPathEnd -Recurse -Force -ErrorAction Stop } elseif ($WinSCPEnd) { Write-TSLog "Account: $UserNameEnd," Write-TSLog "SENDING SFTP to: $FTPHostEnd," Write-TSLog "For SshHostKeyFingerprint: " Write-TSLog "Files: $ZipEnd" Write-TSLog $SshHostKeyFingerprintEnd Submit-FTPUpload -FTPUserName $UserNameEnd -Password $Password ` -FTPHostName $FTPHostEnd -LocalFilePath ($ZipEnd).Split(" ") -SshHostKeyFingerprint $SshHostKeyFingerprintEnd -RemoteFTPPath $RemotePathEnd -ErrorVariable SubmitFTPErr if ($?) { Write-TSLog "The ADDSAuditTask archive have been uploaded to ftp." Write-TSLog -End Write-TSLog -LogOutputPath $LogOutputPath } else { Write-TSLog -LogError $SubmitFTPErr } } elseif ($LocalDiskEnd) { #Confirm output path to console. Write-TSLog "The ADDSAuditTask archive have been saved to: " Write-TSLog "$ZipEnd" Write-TSLog -End Write-TSLog -LogOutputPath $LogOutputPath } } } } #EndRegion '.\Private\Initialize-AuditEndBlock.ps1' 153 #Region '.\Private\Send-AuditEmail.ps1' 0 function Send-AuditEmail { <# .SYNOPSIS This is a sample Private function only visible within the module. It uses Send-MailkitMessage To send email messages. .DESCRIPTION This sample function is not exported to the module and only return the data passed as parameter. .EXAMPLE Send-AuditEmail -smtpServer $SMTPServer -port $Port -username $Username -Function $Function -FunctionApp $FunctionApp -token $ApiToken -from $from -to $to -attachmentfilePath "$FilePath" -ssl .PARAMETER PrivateData The PrivateData parameter is what will be returned without transformation. #> param ( [string]$smtpServer, [int]$port, [string]$username, [switch]$ssl, [string]$from, [string]$to, [string]$subject = "$($script:MyInvocation.MyCommand.Name -replace '\..*') report ran for $($env:USERDNSDOMAIN).", [string[]]$attachmentfiles, [string]$body, [securestring]$pass, [string]$Function, [string]$FunctionApp, [string]$token ) Import-Module Send-MailKitMessage # Recipient $RecipientList = [MimeKit.InternetAddressList]::new() $RecipientList.Add([MimeKit.InternetAddress]$to) # Attachment $AttachmentList = [System.Collections.Generic.List[string]]::new() foreach ($currentItem in $attachmentfiles) { $AttachmentList.Add("$currentItem") } # From $from = [MimeKit.MailboxAddress]$from # Mail Account variable $User = $username if ($pass) { # Set Credential to $Password parameter input. $Credential = ` [System.Management.Automation.PSCredential]::new($User, $pass) } elseif ($FunctionApp) { $url = "https://$($FunctionApp).azurewebsites.net/api/$($Function)" # Retrieve credentials from function app url into a SecureString. $a, $b = (Invoke-RestMethod $url -Headers @{ 'x-functions-key' = "$token" }).split(',') $Credential = ` [System.Management.Automation.PSCredential]::new($User, (ConvertTo-SecureString -String $a -Key $b.split('')) ) } # Create Parameter hashtable $Parameters = @{ "UseSecureConnectionIfAvailable" = $ssl "Credential" = $Credential "SMTPServer" = $SMTPServer "Port" = $Port "From" = $From "RecipientList" = $RecipientList "Subject" = $subject "TextBody" = $body "AttachmentList" = $AttachmentList } Send-MailKitMessage @Parameters Clear-Variable -Name "a", "b", "Credential", "token" -Scope Local -ErrorAction SilentlyContinue } #EndRegion '.\Private\Send-AuditEmail.ps1' 74 #Region '.\Private\Submit-FTPUpload.ps1' 0 function Submit-FTPUpload { [CmdletBinding()] param ( [string]$FTPUserName, [securestring]$Password, # New-WinSCPSessionOption [string]$FTPHostName, [ValidateSet("Sftp", "SCP", "FTP", "Webdav", "s3")] # New-WinSCPSessionOption [string]$Protocol = "Sftp", [ValidateSet("None", "Implicit ", "Explicit")] # New-WinSCPSessionOption [string]$FTPSecure = "None", # New-WinSCPSessionOption #[int]$FTPPort = 0, # New-WinSCPSessionOption # Mandatory with SFTP/SCP [string[]]$SshHostKeyFingerprint, # New-WinSCPSessionOption #[string]$SshPrivateKeyPath, [string[]]$LocalFilePath, # Send-WinSCPItem # './remoteDirectory' [string]$RemoteFTPPath ) process { # This script will run in the context of the user. Please be sure it's a local admin with cached credentials. # Required Modules Import-Module WinSCP # Capture credentials. $Credential = [System.Management.Automation.PSCredential]::new($FTPUserName, $Password) # Open the session using the SessionOptions object. $sessionOption = New-WinSCPSessionOption -Credential $Credential -HostName $FTPHostName -SshHostKeyFingerprint $SshHostKeyFingerprint -Protocol $Protocol -FtpSecure $FTPSecure # New-WinSCPSession sets the PSDefaultParameterValue of the WinSCPSession parameter for all other cmdlets to this WinSCP.Session object. # You can set it to a variable if you would like, but it is only necessary if you will have more then one session open at a time. $WinSCPSession = New-WinSCPSession -SessionOption $sessionOption if (!(Test-WinSCPPath -Path $RemoteFTPPath -WinSCPSession $WinSCPSession)) { New-WinSCPItem -Path $RemoteFTPPath -ItemType Directory -WinSCPSession $WinSCPSession } # Upload a file to the directory. foreach ($File in $LocalFilePath) { try { Send-WinSCPItem -Path $File -Destination $RemoteFTPPath -WinSCPSession $WinSCPSession } catch { write-tslog "Didn't send $File" } } # Close and remove the session object. Remove-WinSCPSession -WinSCPSession $WinSCPSession } } #EndRegion '.\Private\Submit-FTPUpload.ps1' 53 #Region '.\Private\Write-TSLog.ps1' 0 function Write-TSLog { <# .SYNOPSIS This is a sample Private function only visible within the module. .DESCRIPTION This sample function is not exported to the module and only return the data passed as parameter. .EXAMPLE $null = Write-Logs -PrivateData 'NOTHING TO SEE HERE' .PARAMETER PrivateData The PrivateData parameter is what will be returned without transformation. #> [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([string])] param( [Parameter( ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Default', Position = 0 )] [String[]]$LogString, [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Begin', Position = 0 )] [switch]$Begin, [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'End', Position = 0 )] [switch]$End, [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'LogError', Position = 0 )] [switch]$LogError, [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'LogErrorEnd', Position = 0 )] [switch]$LogErrorEnd, [Parameter(ParameterSetName = 'LogError', Position = 1)] [Parameter( ValueFromPipelineByPropertyName = $true, ParameterSetName = 'LogErrorEnd', Position = 1 )] [System.Management.Automation.ErrorRecord[]]$LogErrorVar, [Parameter( ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'LogToFile', Position = 0 )] [string]$LogOutputPath ) process { # Change the ErrorActionPreference to 'Stop' $ErrorActionPreference = "SilentlyContinue" $ModuleName = $script:MyInvocation.MyCommand.Name.ToString() -replace '\..*' $ModuleVer = $MyInvocation.MyCommand.Version.ToString() $ErrorActionPreference = "Continue" if ($Begin) { Clear-Variable Logs -Scope Script -ErrorAction SilentlyContinue $TSLogString = "$(Get-TimeStamp) Begin Log for Module version $ModuleVer of Module: $ModuleName `n" $script:Logs += $TSLogString } elseif ($End) { $TSLogString = "$(Get-TimeStamp) End Log for Module version $ModuleVer of Module: $ModuleName `n" $script:Logs += $TSLogString Write-Output $script:Logs } elseif ($LogError) { if ($LogErrorVar) { #$TSLogString += "$(($LogErrorVar.Exception).ToString()) `n" $TSLogString += "$(($LogErrorVar.Exception).ToString()) `n" } else { $TSLogString = "$(($global:Error[0].Exception).ToString()) `n" } $script:Logs += $TSLogString } elseif ($LogErrorEnd) { if ($LogErrorVar) { $TSLogString = "$(Get-TimeStamp) An Error Occured. The Error Variable was: `n" $script:Logs += $TSLogString $TSLogString += "$(($LogErrorVar.Exception).ToString()) `n" $script:Logs += $TSLogString $TSLogString = "$(Get-TimeStamp) End Log for Module version $ModuleVer of Module: $ModuleName `n" $script:Logs += $TSLogString $TSLogString = "$(Get-TimeStamp) ErrorLog output to 'C:\temp\ADDSAuditTasksErrors.log' `n" $script:Logs += $TSLogString } else { $TSLogString = "$(Get-TimeStamp) An Error Occured. The exception was: `n" $script:Logs += $TSLogString $TSLogString = "$(($global:Error[0].Exception).ToString()) `n" $script:Logs += $TSLogString } Write-Output $script:Logs $script:Logs | Out-File "C:\temp\ADDSAuditTasksErrors.log" -Encoding utf8 -Append -Force } elseif ($LogOutputPath) { $script:Logs | Out-File $LogOutputPath -Encoding utf8 -Append -Force Write-Output "Logs saved to $LogOutputPath" } else { $TSLogString = "$(Get-TimeStamp) $logstring `n" $script:Logs += $TSLogString } } } #EndRegion '.\Private\Write-TSLog.ps1' 122 #Region '.\Public\Get-ADDSActiveAccountAudit.ps1' 0 function Get-ADDSActiveAccountAudit { <# .SYNOPSIS Active Directory Audit with Keyvault retrieval option. .DESCRIPTION Audit's Active Directory taking "days" as the input for how far back to check for a last sign in. Output can be kept locally, or sent remotely via email or sftp. Function App is the same as SendEmail except that it uses a password retrieved using the related Function App. The related function app would need to be created. Expects SecureString and Key as inputs to function app parameter set. .EXAMPLE PS C:\> Get-ADDSActiveAccountAudit -LocalDisk -Verbose .EXAMPLE PS C:\> Get-ADDSActiveAccountAudit -SendMailMessage -SMTPServer $SMTPServer -UserName "helpdesk@domain.com" -Password (Read-Host -AsSecureString) -To "support@domain.com" -Verbose .EXAMPLE PS C:\> Get-ADDSActiveAccountAudit -FunctionApp $FunctionApp -Function $Function -SMTPServer $SMTPServer -UserName "helpdesk@domain.com" -To "support@domain.com" -Verbose .EXAMPLE PS C:\> Get-ADDSActiveAccountAudit -WinSCP -UserName "ftphostname.UserName" -Password (Read-Host -AsSecureString) -FTPHost "ftphost.domain.com" -SshHostKeyFingerprint "<SShHostKeyFingerprint>" -Verbose .EXAMPLE PS C:\> Get-ADDSActiveAccountAudit -Clean -Verbose .PARAMETER LocalDisk Only output data to local disk. .PARAMETER SendMailMessage Adds parameters for sending Audit Report as an Email. .PARAMETER WinSCP Adds parameters for sending Audit Report via SFTP. .PARAMETER AttachmentFolderPath Default path is C:\temp\ADDSActiveAccountAuditLogs. This is the folder where attachments are going to be saved. .PARAMETER FunctionApp Azure Function App Name. .PARAMETER Function Azure Function App's Function Name. Ex. "HttpTrigger1" .PARAMETER ApiToken Private Function Key. .PARAMETER SMTPServer Defaults to Office 365 SMTP relay. Enter optional relay here. .PARAMETER Port SMTP Port to Relay. Ports can be: "993", "995", "587", or "25" .PARAMETER UserName Specify the account with an active mailbox and MFA disabled. Ensure the account has delegated access for Send On Behalf for any UPN set in the "$From" Parameter .PARAMETER Password Use: (Read-Host -AsSecureString) as in Examples. May be omitted. .PARAMETER To Recipient of the attachment outputs. .PARAMETER From Defaults to the same account as $UserName unless the parameter is set. Ensure the Account has delegated access to send on behalf for the $From account. .PARAMETER FTPHost SFTP Hostname. .PARAMETER RemotePath Remove FTP path. Will be created in the user path under functionname folder if not specified. .PARAMETER SshHostKeyFingerprint Adds parameters for sending Audit Report via SFTP. .PARAMETER DaysInactive Defaults to 90 days in the past. Specifies how far back to look for accounts last logon. If logon is within 90 days, it won't be included. .PARAMETER ADDSAccountIsNotEnabled Defaults to not being set. Choose to search for disabled Active Directory Users. .PARAMETER Clean Remove installed modules during run. Remove local files if not a LocalDisk run. .NOTES Can take password as input into secure string using (Read-Host -AsSecureString). #> [CmdletBinding(DefaultParameterSetName = 'LocalDisk', HelpURI = "https://criticalsolutionsnetwork.github.io/ADDSAuditTasks/#Get-ADDSActiveAccountAudit")] param ( [Parameter( Mandatory = $true, ParameterSetName = 'LocalDisk', HelpMessage = 'Output to disk only', Position = 0 )] [switch]$LocalDisk, [Parameter( Mandatory = $true, ParameterSetName = 'SendMailMessage', HelpMessage = 'Send Mail to a relay', Position = 0 )] [switch]$SendMailMessage, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Send using SFTP via WinSCP Module', Position = 0, ValueFromPipelineByPropertyName = $true )] [switch]$WinSCP, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter the FunctionApp name', Position = 0 )] [string]$FunctionApp, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter the FunctionApp Function name', ValueFromPipelineByPropertyName = $true, Position = 1 )] [string]$Function, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the SMTP hostname' , ValueFromPipelineByPropertyName = $true )] [string]$SMTPServer = "smtp.office365.com", [Parameter(ParameterSetName = 'FunctionApp')] [Parameter(ParameterSetName = 'SendMailMessage')] [Parameter(ParameterSetName = 'WinSCP')] [Parameter( ParameterSetName = 'LocalDisk', HelpMessage = 'Enter output folder path', ValueFromPipeline = $true )] [string]$AttachmentFolderPath = "C:\temp\ADDSActiveAccountAuditLogs", [Parameter(ParameterSetName = 'WinSCP')] [Parameter(ParameterSetName = 'FunctionApp')] [Parameter(ParameterSetName = 'SendMailMessage')] [Parameter( ParameterSetName = 'LocalDisk', HelpMessage = 'Active Directory User Enabled or not', ValueFromPipelineByPropertyName = $true )] [switch]$ADDSAccountIsNotEnabled, [Parameter(ParameterSetName = 'WinSCP')] [Parameter(ParameterSetName = 'FunctionApp')] [Parameter(ParameterSetName = 'SendMailMessage')] [Parameter( ParameterSetName = 'LocalDisk', HelpMessage = 'Days back to check for recent sign in', ValueFromPipelineByPropertyName = $true )] [int]$DaysInactive = '90', [Parameter(Mandatory = $true, ParameterSetName = 'WinSCP')] [Parameter(Mandatory = $true, ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', Mandatory = $true, HelpMessage = 'Enter the Sending Account UPN Ex:"user@contoso.com"', ValueFromPipelineByPropertyName = $true )] [string]$UserName, [Parameter(ParameterSetName = 'WinSCP', Mandatory = $true)] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Copy Paste the following: $Password = (Read-Host -AsSecureString)', ValueFromPipelineByPropertyName = $true )] [securestring]$Password, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the port n umber for the mail relay', ValueFromPipelineByPropertyName = $true )] [ValidateSet("993", "995", "587", "25")] [int]$Port = 587, [Parameter(Mandatory = $true, ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', Mandatory = $true, HelpMessage = 'Enter the recipient email address', ValueFromPipelineByPropertyName = $true )] [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] [string]$To, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the name of the sender', ValueFromPipelineByPropertyName = $true )] [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] [string]$From = $UserName, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter output folder path', ValueFromPipelineByPropertyName = $true )] [string]$ApiToken, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Enter FTP HostName', ValueFromPipelineByPropertyName = $true )] [string]$FTPHost, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Enter SshHostKeyFingerprint like: "ecdsa-sha2-nistp256 256 <Key>" ', ValueFromPipelineByPropertyName = $true )] [string]$SshHostKeyFingerprint, [Parameter( ParameterSetName = 'WinSCP', HelpMessage = 'Enter ftp remote path "/path/" ', ValueFromPipelineByPropertyName = $true )] [string]$RemotePath = ("./" + $($MyInvocation.MyCommand.Name -replace '\..*')) , [Parameter( Mandatory = $true, ParameterSetName = 'Clean', HelpMessage = 'Clean Modules and output path', Position = 0 )] [switch]$Clean ) Begin { $ScriptFunctionName = $MyInvocation.MyCommand.Name -replace '\..*' try { Initialize-AuditBeginBlock -AttachmentFolderPathBegin $AttachmentFolderPath -ScriptFunctionName $ScriptFunctionName -SendEmailMessageBegin $SendMailMessage -CleanBegin $Clean -ErrorVariable InitBeginErr -ErrorAction Stop } catch { Write-TSLog "End Block last error for log: " Write-TSLog -LogError -LogErrorVar InitBeginErr } if ($ADDSAccountIsNotEnabled) { $Enabled = $false } else { $Enabled = $true } } Process { if (!($Clean)) { $time90 = (Get-Date).Adddays( - (90)) $time60 = (Get-Date).Adddays( - (60)) $time30 = (Get-Date).Adddays( - (30)) # Establish timeframe to review. $time = (Get-Date).Adddays( - ($DaysInactive)) # Add Datetime to filename $csvFileName = "$AttachmentFolderPath\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($ScriptFunctionName)_$($env:USERDNSDOMAIN)" # Create FileNames $csv = "$csvFileName.csv" $zip = "$csvFileName.zip" Write-TSLog "Searching for users who have signed in within the last $DaysInactive days, where parameter Enabled = $Enabled" # Audit Script with export to csv and zip. Paramters for Manager, lastLogonTimestamp and DistinguishedName normalized. # GetActiveUsers Get-aduser -Filter { LastLogonTimeStamp -lt $time -and Enabled -eq $Enabled } -Properties ` GivenName, Surname, Mail, UserPrincipalName, Title, Description, Manager, lastlogontimestamp, samaccountname, DistinguishedName ` | Select-Object ` @{N = 'UserName'; E = { $_.samaccountname } }, ` @{N = 'FirstName'; E = { $_.GivenName } }, ` @{N = 'LastName'; E = { $_.Surname } }, ` @{N = 'UPN'; E = { $_.UserPrincipalName } }, ` @{N = "Last Sign-in"; E = { ([DateTime]::FromFileTime($_.lastLogonTimestamp)) } }, ` Enabled, ` @{Label = 'Last Seen?'; ` Expression = { switch (([DateTime]::FromFileTime($_.lastLogonTimestamp))) { # Over 90 Days { ($_ -lt $time90) } { '3+ months'; break } # Over 60 Days { ($_ -lt $time60) } { '2+ months'; break } # Over 90 Days { ($_ -lt $time30) } { '1+ month'; break } default { 'Recently' } } } }, ` @{N = 'OrgUnit'; E = { $_.DistinguishedName -replace '^.*?,(?=[A-Z]{2}=)' } }, ` Title, ` @{N = 'Manager'; E = { (Get-ADUser -Identity $_.manager).Name } }, ` Department, "Access Required?", "Need Mailbox?" -OutVariable Export -ErrorVariable ExportErr | Out-Null try { Export-AuditCSVtoZip -Export $Export -csv $csv -zip $zip -ErrorAction Stop -ErrorVariable ExportAuditCSVZipErr } catch { Write-TSLog -LogErrorEnd -LogErrorVar $ExportAuditCSVZipErr throw $ExportAuditCSVZipErr } } # End If Clean Region } ## End Process Region End { try { Initialize-AuditEndBlock -SendEmailMessageEnd $SendMailMessage -WinSCPEnd $WinSCP -FTPHostend $FTPHost -SshHostKeyFingerprintEnd $SshHostKeyFingerprint -SmtpServerEnd $SMTPServer -PortEnd $Port -UserNameEnd $UserName -FromEnd $From -ToEnd $To ` -AttachmentFolderPathEnd $AttachmentFolderPath -Password $Password -FunctionEnd $function -FunctionAppEnd $FunctionApp ` -ApiTokenEnd $ApiToken -ZipEnd $zip -RemotePathEnd $RemotePath -LocalDiskEnd $LocalDisk -CleanEnd $Clean -ErrorVariable InitEndErr } catch { Write-TSLog "End Block last error for log: " Write-TSLog -LogError } # Clear Variables Clear-Variable -Name "Function", "FunctionApp", "ApiToken" } } #EndRegion '.\Public\Get-ADDSActiveAccountAudit.ps1' 302 #Region '.\Public\Get-ADDSDepartedUsersAccountAudit.ps1' 0 function Get-ADDSDepartedUsersAccountAudit { <# .SYNOPSIS Active Directory Audit with Keyvault retrieval option. .DESCRIPTION Audit's Active Directory taking a Prefix used as a Wildcard as input for checking user accounts. Output can be kept locally, or sent remotely via email or sftp. Function App is the same as SendEmail except that it uses a password retrieved using the related Function App. The related function app would need to be created. Expects SecureString and Key as inputs to function app parameter set. .EXAMPLE PS C:\> Get-ADDSDepartedUsersAccountAudit -LocalDisk -WildCardIdentifier "<StringToSearchFor>" -Verbose .EXAMPLE PS C:\> Get-ADDSDepartedUsersAccountAudit -SendMailMessage -SMTPServer $SMTPServer -UserName "helpdesk@domain.com" -Password (Read-Host -AsSecureString) -To "support@domain.com" -WildCardIdentifier "<StringToSearchFor>" -Verbose .EXAMPLE PS C:\> Get-ADDSDepartedUsersAccountAudit -FunctionApp $FunctionApp -Function $Function -SMTPServer $SMTPServer -UserName "helpdesk@domain.com" -To "support@domain.com" -WildCardIdentifier "<StringToSearchFor>" -Verbose .EXAMPLE PS C:\> Get-ADDSDepartedUsersAccountAudit -WinSCP -UserName "ftphostname.UserName" -Password (Read-Host -AsSecureString) -FTPHost "ftphost.domain.com" -SshHostKeyFingerprint "<SShHostKeyFingerprint>" -WildCardIdentifier "<StringToSearchFor>" -Verbose .EXAMPLE PS C:\> Get-ADDSDepartedUsersAccountAudit -Clean -Verbose .PARAMETER LocalDisk Only output data to local disk. .PARAMETER SendMailMessage Adds parameters for sending Audit Report as an Email. .PARAMETER WinSCP Adds parameters for sending Audit Report via SFTP. .PARAMETER AttachmentFolderPath Default path is C:\temp\ADDSDepartedUsersAuditLogs. This is the folder where attachments are going to be saved. .PARAMETER FunctionApp Azure Function App Name. .PARAMETER Function Azure Function App's Function Name. Ex. "HttpTrigger1" .PARAMETER ApiToken Private Function Key. .PARAMETER SMTPServer Defaults to Office 365 SMTP relay. Enter optional relay here. .PARAMETER Port SMTP Port to Relay. Ports can be: "993", "995", "587", or "25" .PARAMETER UserName Specify the account with an active mailbox and MFA disabled. Ensure the account has delegated access for Send On Behalf for any UPN set in the "$From" Parameter .PARAMETER Password Use: (Read-Host -AsSecureString) as in Examples. May be omitted. .PARAMETER To Recipient of the attachment outputs. .PARAMETER From Defaults to the same account as $UserName unless the parameter is set. Ensure the Account has delegated access to send on behalf for the $From account. .PARAMETER WildCardIdentifier Name wildcard appended to user account. .PARAMETER Clean Remove installed modules during run. Remove local files if not a LocalDisk run. .NOTES Can take password as input into secure string using (Read-Host -AsSecureString). #> [CmdletBinding(DefaultParameterSetName = 'LocalDisk' , HelpURI = "https://criticalsolutionsnetwork.github.io/ADDSAuditTasks/#Get-ADDSDepartedUsersAccountAudit")] param ( [Parameter( Mandatory = $true, ParameterSetName = 'LocalDisk', HelpMessage = 'Output to disk only', Position = 0 )] [switch]$LocalDisk, [Parameter( Mandatory = $true, ParameterSetName = 'SendMailMessage', HelpMessage = 'Send Mail to a relay', Position = 0 )] [switch]$SendMailMessage, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Send using SFTP via WinSCP Module', Position = 0, ValueFromPipelineByPropertyName = $true )] [switch]$WinSCP, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter the FunctionApp name', Position = 0 )] [string]$FunctionApp, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter the FunctionApp Function name', ValueFromPipelineByPropertyName = $true, Position = 1 )] [string]$Function, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the SMTP hostname' , ValueFromPipelineByPropertyName = $true )] [string]$SMTPServer = "smtp.office365.com", [Parameter(ParameterSetName = 'FunctionApp')] [Parameter(ParameterSetName = 'SendMailMessage')] [Parameter(ParameterSetName = 'WinSCP')] [Parameter( ParameterSetName = 'LocalDisk', HelpMessage = 'Enter output folder path', ValueFromPipeline = $true )] [string]$AttachmentFolderPath = "C:\temp\ADDSDepartedUsersAuditLogs", [Parameter(Mandatory = $true, ParameterSetName = 'WinSCP')] [Parameter(Mandatory = $true, ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', Mandatory = $true, HelpMessage = 'Enter the Sending Account UPN Ex:"user@contoso.com"', ValueFromPipelineByPropertyName = $true )] [string]$UserName, [Parameter(ParameterSetName = 'WinSCP', Mandatory = $true)] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Copy Paste the following: $Password = (Read-Host -AsSecureString)', ValueFromPipelineByPropertyName = $true )] [securestring]$Password, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the port n umber for the mail relay', ValueFromPipelineByPropertyName = $true )] [ValidateSet("993", "995", "587", "25")] [int]$Port = 587, [Parameter(Mandatory = $true, ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', Mandatory = $true, HelpMessage = 'Enter the recipient email address', ValueFromPipelineByPropertyName = $true )] [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] [string]$To, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the name of the sender', ValueFromPipelineByPropertyName = $true )] [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] [string]$From = $UserName, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter output folder path', ValueFromPipelineByPropertyName = $true )] [string]$ApiToken, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Enter FTP HostName', ValueFromPipelineByPropertyName = $true )] [string]$FTPHost, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Enter SshHostKeyFingerprint like: "ecdsa-sha2-nistp256 256 <Key>" ', ValueFromPipelineByPropertyName = $true )] [string]$SshHostKeyFingerprint, [Parameter( ParameterSetName = 'WinSCP', HelpMessage = 'Enter ftp remote path "/path/" ', ValueFromPipelineByPropertyName = $true )] [string]$RemotePath = ("./" + $($MyInvocation.MyCommand.Name -replace '\..*')) , [Parameter( Mandatory = $true, ParameterSetName = 'Clean', HelpMessage = 'Clean Modules and output path', Position = 0 )] [switch]$Clean, [Parameter(ParameterSetName = 'WinSCP', Mandatory = $true)] [Parameter(ParameterSetName = 'FunctionApp',Mandatory = $true)] [Parameter(ParameterSetName = 'SendMailMessage', Mandatory = $true)] [Parameter( Mandatory = $true, ParameterSetName = 'LocalDisk', HelpMessage = 'Name filter attached to users.', ValueFromPipelineByPropertyName = $true )] [string]$WildCardIdentifier ) begin { $ScriptFunctionName = $MyInvocation.MyCommand.Name -replace '\..*' try { Initialize-AuditBeginBlock -AttachmentFolderPathBegin $AttachmentFolderPath -ScriptFunctionName $ScriptFunctionName -SendEmailMessageBegin $SendMailMessage -CleanBegin $Clean -ErrorVariable InitBeginErr -ErrorAction Stop } catch { Write-TSLog "End Block last error for log: " Write-TSLog -LogError -LogErrorVar InitBeginErr } } process { if (!($Clean)) { $time90 = (Get-Date).Adddays( - (90)) $time60 = (Get-Date).Adddays( - (60)) $time30 = (Get-Date).Adddays( - (30)) # Add Datetime to filename $csvFileName = "$AttachmentFolderPath\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($ScriptFunctionName)_$($env:USERDNSDOMAIN)" # Create FileNames $csv = "$csvFileName.csv" $zip = "$csvFileName.zip" Write-TSLog "Searching for users appended with:`"$WildCardIdentifier`" in Active Directory." # Audit Script with export to csv and zip. # Get ad user with Name String Filter $WildCardIdentifierstring = '*' + $WildCardIdentifier + '*' Get-ADUser -Filter { Name -like $WildCardIdentifierstring } -Properties ` GivenName, Surname, Mail, UserPrincipalName, Title, Description, Manager, lastlogontimestamp, samaccountname, DistinguishedName | ` Select-Object ` @{N = 'UserName'; E = { $_.samaccountname } }, ` @{N = 'FirstName'; E = { $_.GivenName } }, ` @{N = 'LastName'; E = { $_.Surname } }, ` @{N = 'UPN'; E = { $_.UserPrincipalName } }, ` @{N = "Last Sign-in"; E = { ([DateTime]::FromFileTime($_.lastLogonTimestamp)) } }, ` @{Label = 'Last Seen?'; ` Expression = { switch (([DateTime]::FromFileTime($_.lastLogonTimestamp))) { # Over 90 Days { ($_ -lt $time90) } { '3+ months'; break } # Over 60 Days { ($_ -lt $time60) } { '2+ months'; break } # Over 90 Days { ($_ -lt $time30) } { '1+ month'; break } default { 'Recently' } } } }, ` @{N = 'OrgUnit'; E = { $_.DistinguishedName -replace '^.*?,(?=[A-Z]{2}=)' } }, ` Title, ` @{N = 'Manager'; E = { (Get-ADUser -Identity $_.manager).Name } }, ` Department, "Access Required?", "Need Mailbox?" -OutVariable Export | Out-Null try { Export-AuditCSVtoZip -Export $Export -csv $csv -zip $zip -ErrorAction Stop -ErrorVariable ExportAuditCSVZipErr } catch { Write-TSLog -LogErrorEnd -LogErrorVar $ExportAuditCSVZipErr throw $ExportAuditCSVZipErr } } # End if (!($Clean)) {...} } # End process region End { try { Initialize-AuditEndBlock -SendEmailMessageEnd $SendMailMessage -WinSCPEnd $WinSCP -FTPHostend $FTPHost -SshHostKeyFingerprintEnd $SshHostKeyFingerprint -SmtpServerEnd $SMTPServer -PortEnd $Port -UserNameEnd $UserName -FromEnd $From -ToEnd $To ` -AttachmentFolderPathEnd $AttachmentFolderPath -Password $Password -FunctionEnd $function -FunctionAppEnd $FunctionApp ` -ApiTokenEnd $ApiToken -ZipEnd $zip -RemotePathEnd $RemotePath -LocalDiskEnd $LocalDisk -CleanEnd $Clean -ErrorVariable InitEndErr } catch { Write-TSLog "End Block last error for log: " Write-TSLog -LogError } # Clear Variables Clear-Variable -Name "Function", "FunctionApp", "ApiToken" } } #EndRegion '.\Public\Get-ADDSDepartedUsersAccountAudit.ps1' 275 #Region '.\Public\Get-ADDSPrivilegedAccountAudit.ps1' 0 function Get-ADDSPrivilegedAccountAudit { <# .SYNOPSIS Active Directory Audit with Keyvault retrieval option. .DESCRIPTION Audit's Active Directory for priviledged users and groups, and extended rights. Output can be kept locally, or sent remotely via email or sftp. Function App is the same as SendEmail except that it uses a password retrieved using the related Function App. The related function app would need to be created. Expects SecureString and Key as inputs to function app parameter set. .EXAMPLE PS C:\> Get-ADDSPrivilegedAccountAudit -LocalDisk -Verbose .EXAMPLE PS C:\> Get-ADDSPrivilegedAccountAudit -SendMailMessage -SMTPServer $SMTPServer -UserName "helpdesk@domain.com" -Password (Read-Host -AsSecureString) -To "support@domain.com" -Verbose .EXAMPLE PS C:\> Get-ADDSPrivilegedAccountAudit -FunctionApp $FunctionApp -Function $Function -SMTPServer $SMTPServer -UserName "helpdesk@domain.com" -To "support@domain.com" -Verbose .EXAMPLE PS C:\> Get-ADDSPrivilegedAccountAudit -WinSCP -UserName "ftphostname.UserName" -Password (Read-Host -AsSecureString) -FTPHost "ftphost.domain.com" -SshHostKeyFingerprint "<SShHostKeyFingerprint>" -Verbose .EXAMPLE PS C:\> Get-ADDSPrivilegedAccountAudit -Clean -Verbose .PARAMETER LocalDisk Only output data to local disk. .PARAMETER SendMailMessage Adds parameters for sending Audit Report as an Email. .PARAMETER WinSCP Adds parameters for sending Audit Report via SFTP. .PARAMETER AttachmentFolderPath Default path is C:\temp\ADDSPrivilegedAccountAuditLogs. This is the folder where attachments are going to be saved. .PARAMETER FunctionApp Azure Function App Name. .PARAMETER Function Azure Function App's Function Name. Ex. "HttpTrigger1" .PARAMETER ApiToken Private Function Key. .PARAMETER SMTPServer Defaults to Office 365 SMTP relay. Enter optional relay here. .PARAMETER Port SMTP Port to Relay. Ports can be: "993", "995", "587", or "25" .PARAMETER UserName Specify the account with an active mailbox and MFA disabled. Ensure the account has delegated access for Send On Behalf for any UPN set in the "$From" Parameter .PARAMETER Password Use: (Read-Host -AsSecureString) as in Examples. May be omitted. .PARAMETER To Recipient of the attachment outputs. .PARAMETER From Defaults to the same account as $UserName unless the parameter is set. Ensure the Account has delegated access to send on behalf for the $From account. .PARAMETER FTPHost SFTP Hostname. .PARAMETER RemotePath Remove FTP path. Will be created in the user path under functionname folder if not specified. .PARAMETER SshHostKeyFingerprint Adds parameters for sending Audit Report via SFTP. .PARAMETER Clean Remove installed modules during run. Remove local files if not a LocalDisk run. .NOTES Can take password as input into secure string using (Read-Host -AsSecureString). #> [CmdletBinding(DefaultParameterSetName = 'LocalDisk' , HelpURI = "https://criticalsolutionsnetwork.github.io/ADDSAuditTasks/#Get-ADDSPrivilegedAccountAudit")] param ( [Parameter( Mandatory = $true, ParameterSetName = 'LocalDisk', HelpMessage = 'Output to disk only', Position = 0 )] [switch]$LocalDisk, [Parameter( Mandatory = $true, ParameterSetName = 'SendMailMessage', HelpMessage = 'Send Mail to a relay', Position = 0 )] [switch]$SendMailMessage, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Send using SFTP via WinSCP Module', Position = 0, ValueFromPipelineByPropertyName = $true )] [switch]$WinSCP, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter the FunctionApp name', Position = 0 )] [string]$FunctionApp, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter the FunctionApp Function name', ValueFromPipelineByPropertyName = $true, Position = 1 )] [string]$Function, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the SMTP hostname' , ValueFromPipelineByPropertyName = $true )] [string]$SMTPServer = "smtp.office365.com", [Parameter(ParameterSetName = 'FunctionApp')] [Parameter(ParameterSetName = 'SendMailMessage')] [Parameter(ParameterSetName = 'WinSCP')] [Parameter( ParameterSetName = 'LocalDisk', HelpMessage = 'Enter output folder path', ValueFromPipeline = $true )] [string]$AttachmentFolderPath = "C:\temp\ADDSPrivilegedAccountAuditLogs", [Parameter(Mandatory = $true, ParameterSetName = 'WinSCP')] [Parameter(Mandatory = $true, ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', Mandatory = $true, HelpMessage = 'Enter the Sending Account UPN or FTP Username if using WinSCP. Ex:"user@contoso.com" or "ftphost.helpdesk"', ValueFromPipelineByPropertyName = $true )] [string]$UserName, [Parameter(ParameterSetName = 'WinSCP', Mandatory = $true)] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Copy Paste the following: $Password = (Read-Host -AsSecureString)', ValueFromPipelineByPropertyName = $true )] [securestring]$Password, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the port number for the mail relay', ValueFromPipelineByPropertyName = $true )] [ValidateSet("993", "995", "587", "25")] [int]$Port = 587, [Parameter(Mandatory = $true, ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', Mandatory = $true, HelpMessage = 'Enter the recipient email address', ValueFromPipelineByPropertyName = $true )] [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] [string]$To, [Parameter(ParameterSetName = 'FunctionApp')] [Parameter( ParameterSetName = 'SendMailMessage', HelpMessage = 'Enter the name of the sender', ValueFromPipelineByPropertyName = $true )] [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] [string]$From = $UserName, [Parameter( Mandatory = $true, ParameterSetName = 'FunctionApp', HelpMessage = 'Enter output folder path', ValueFromPipelineByPropertyName = $true )] [string]$ApiToken, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Enter FTP HostName', ValueFromPipelineByPropertyName = $true )] [string]$FTPHost, [Parameter( Mandatory = $true, ParameterSetName = 'WinSCP', HelpMessage = 'Enter SshHostKeyFingerprint like: "ecdsa-sha2-nistp256 256 <Key>" ', ValueFromPipelineByPropertyName = $true )] [string]$SshHostKeyFingerprint, [Parameter( ParameterSetName = 'WinSCP', HelpMessage = 'Enter ftp remote path "/path/" ', ValueFromPipelineByPropertyName = $true )] [string]$RemotePath = ("./" + $($MyInvocation.MyCommand.Name -replace '\..*')) , [Parameter( Mandatory = $true, ParameterSetName = 'Clean', HelpMessage = 'Clean Modules and output path', Position = 0 )] [switch]$Clean ) begin { $ScriptFunctionName = $MyInvocation.MyCommand.Name -replace '\..*' try { Initialize-AuditBeginBlock -AttachmentFolderPathBegin $AttachmentFolderPath -ScriptFunctionName $ScriptFunctionName -SendEmailMessageBegin $SendMailMessage -CleanBegin $Clean -ErrorVariable InitBeginErr -ErrorAction Stop } catch { Write-TSLog "End Block last error for log: " Write-TSLog -LogError -LogErrorVar InitBeginErr } } # End begin process { if (!($Clean)) { $csvFileName = "$AttachmentFolderPath\$((Get-Date).ToString('yyyy-MM-dd_hh.mm.ss'))_$($ScriptFunctionName)_$($env:USERDNSDOMAIN)" # Create FileNames $csv = "$csvFileName.csv" $zip = "$csvFileName.zip" # AD Privileged Groups Array $AD_PrivilegedGroups = @( 'Enterprise Admins', 'Schema Admins', 'Domain Admins', 'Administrators', 'Cert Publishers', 'Account Operators', 'Server Operators', 'Backup Operators', 'Print Operators', 'DnsAdmins', 'DnsUpdateProxy', 'DHCP Administrators' ) # Time Variables $time90 = (Get-Date).Adddays( - (90)) $time60 = (Get-Date).Adddays( - (60)) $time30 = (Get-Date).Adddays( - (30)) # Create Arrays $members = @() $ADUsers = @() foreach ($group in $AD_PrivilegedGroups) { Clear-Variable GroupMember -ErrorAction SilentlyContinue Get-ADGroupMember -Identity $group -Recursive -OutVariable GroupMember | Out-Null $GroupMember | Select-Object SamAccountName, Name, ObjectClass, ` @{N = 'PriviledgedGroup'; E = { $group } }, ` @{N = 'Enabled'; E = { (Get-ADUser -Identity $_.samaccountname).Enabled } }, ` @{N = "LastSign-in"; E = { [DateTime]::FromFileTime((Get-ADUser -Identity $_.samaccountname -Properties lastLogonTimestamp).lastLogonTimestamp) } }, ` @{N = 'LastSeen?'; E = { switch ([DateTime]::FromFileTime((Get-ADUser -Identity $_.samaccountname -Properties lastLogonTimestamp).lastLogonTimestamp)) { # Over 90 Days { ($_ -lt $time90) } { '3+ months'; break } # Over 60 Days { ($_ -lt $time60) } { '2+ months'; break } # Over 90 Days { ($_ -lt $time30) } { '1+ month'; break } default { 'Recently' } } } }, ` @{N = 'OrgUnit'; E = { $_.DistinguishedName -replace '^.*?,(?=[A-Z]{2}=)' } }, ` @{N = 'GroupMemberships'; E = { Get-ADGroupMemberof -SamAccountName $_.samaccountname } }, ` Title, ` @{N = 'Manager'; E = { (Get-ADUser -Identity $_.manager).Name } }, ` Department, "AccessRequired?", "NeedMailbox?" -OutVariable members | Out-Null $ADUsers += $members } $Export = @() # Create $Export Object foreach ($User in $ADUsers) { new-object -TypeName PSCustomObject -Property @{ SamAccountName = $User.SamAccountName Name = $User.Name PriviledgedGroup = $User.PriviledgedGroup Enabled = $User.Enabled "LastSign-in" = $User."LastSign-in" "LastSeen?" = $User."LastSeen?" Title = $User.Title Manager = $User.Manager Department = $User.Department OrgUnit = $User.OrgUnit "AccessRequired?" = $User."AccessRequired?" "NeedMailbox?" = $User."NeedMailbox?" ObjectClass = $User.ObjectClass GroupMemberships = $User.GroupMemberships } -OutVariable PSObject | Out-Null $Export += $PSObject } # Create filenames $csv2 = $csv -replace ".csv", ".ExtendedPermissions.csv" $zip2 = $zip -replace ".zip", ".ExtendedPermissions.zip" # Get PDC $dc = (Get-ADDomainController -Discover -DomainName $env:USERDNSDOMAIN -Service PrimaryDC).Name # Get DN of AD Root. $rootou = (Get-ADRootDSE).defaultNamingContext # Get ad objects from the PDC for the root ou. #TODO Check $Allobjects = Get-ADObject -Server $dc -Searchbase $rootou -SearchScope subtree -LDAPFilter ` "(|(objectClass=domain)(objectClass=organizationalUnit)(objectClass=group)(sAMAccountType=805306368)(objectCategory=Computer)(&(objectclass=user)(objectcategory=person)))" -Properties ntSecurityDescriptor -ResultSetSize $null # Create $Export2 Object $Export2 = Foreach ($ADObject in $Allobjects) { Get-AdExtendedRight $ADObject } # Try first export. Export-AuditCSVtoZip -ExportObject $Export -CSVName $csv -ZipName $zip -ErrorVariable ExportAuditCSVZipErr # Try second export. Export-AuditCSVtoZip -ExportObject $Export2 -CSVName $csv2 -ZipName $zip2 -ErrorVariable ExportAuditCSVZipErr2 } # End If } # End process End { try { Initialize-AuditEndBlock -SendEmailMessageEnd $SendMailMessage -WinSCPEnd $WinSCP -FTPHostend $FTPHost -SshHostKeyFingerprintEnd $SshHostKeyFingerprint -SmtpServerEnd $SMTPServer -PortEnd $Port -UserNameEnd $UserName -FromEnd $From -ToEnd $To ` -AttachmentFolderPathEnd $AttachmentFolderPath -Password $Password -FunctionEnd $function -FunctionAppEnd $FunctionApp ` -ApiTokenEnd $ApiToken -ZipEnd $zip, $zip2 -RemotePathEnd $RemotePath -LocalDiskEnd $LocalDisk -CleanEnd $Clean -ErrorVariable InitEndErr } catch { Write-TSLog "End Block last error for log: " Write-TSLog -LogError } # Clear Variables Clear-Variable -Name "Function", "FunctionApp", "ApiToken" } # End end } #EndRegion '.\Public\Get-ADDSPrivilegedAccountAudit.ps1' 316 #Region '.\Public\Get-ADUsersLastLogon.ps1' 0 function Get-ADUsersLastLogon { <# .SYNOPSIS Takes SamAccountName as input to retrieve most recent LastLogon from all DC's. .DESCRIPTION Takes SamAccountName as input to retrieve most recent LastLogon from all DC's and output as DateTime. .EXAMPLE Get-ADUsersLastLogon -SamAccountName "UserName" .PARAMETER SamAccountName The SamAccountName of the user being checked for LastLogon. #> [CmdletBinding(HelpURI = "https://criticalsolutionsnetwork.github.io/ADDSAuditTasks/#Get-ADUsersLastLogon")] [OutputType([datetime])] param ( [Alias("Identity","UserName","Account")] [Parameter( Mandatory = $true, HelpMessage = 'Enter the SamAccountName', ValueFromPipeline = $true )] $SamAccountName ) process { $dcs = Get-ADDomainController -Filter { Name -like "*" } $user = Get-ADUser -Identity $SamAccountName $time = 0 $dt = @() foreach ($dc in $dcs) { $hostname = $dc.HostName $usertime = $user | Get-ADObject -Server $hostname -Properties lastLogon if ($usertime.LastLogon -gt $time) { $time = $usertime.LastLogon } $dt += [DateTime]::FromFileTime($time) } return ($dt | Sort-Object -Descending)[0] } } #EndRegion '.\Public\Get-ADUsersLastLogon.ps1' 39 |