Matching SID from AD and Event Viewer - powershell

I'm trying to make a script that searches AD for locked accounts, as well as parses the Security log in Event Viewer and then compare the SID's, and if they match, display information of the user that has the SID.
Import-Module ActiveDirectory
$PDC = "DOMAINCONTROLLER"
$UserInfo = Search-ADAccount -LockedOut
$LockedOutEvents = Get-WinEvent -ComputerName $PDC -FilterHashtable
#{LogName='Security';Id=4740} | Sort-Object -Property * -Descending
Foreach($Event in $LockedOutEvents){
If($Event.Properties[2] -Match $UserInfo.SID.value)
{
$Event | Select-Object -Property #(
#{Label = 'User'; Expression = {$_.Properties[0].Value}}
#{Label = 'DomainController'; Expression = {$_.MachineName}}
#{Label = 'EventId'; Expression = {$_.Id}}
#{Label = 'LockoutTimeStamp'; Expression = {$_.TimeCreated}}
#{Label = 'Message'; Expression = {$_.Message -split "`r" | Select -First 1}}
#{Label = 'LockoutSource'; Expression = {$_.Properties[1].Value}}
)
}}
There seems to be an issue with the arguments in the If statement If($Event.Properties[2] -Match $UserInfo.TargetSID)
The output of $Event.Properties[2] is like this:
Value
-----
S-1-1-1-111111111-111111111-111111111-22222
The output of $UserInfo.SID.Value:
S-1-1-1-111111111-111111111-111111111-11111
S-1-1-1-111111111-111111111-111111111-11111
S-1-1-1-111111111-111111111-111111111-22222
S-1-1-1-111111111-111111111-111111111-11111
S-1-1-1-111111111-111111111-111111111-11111
As you can see one SID is found in both outputs but when matching these two i get "False" as a response. Does anyone have any idea why this happens?
Thank you for your time.

It looks like you're comparing a SecurityIdentifier object to an array of strings (at least that output looks like it's an array - you can use $UserInfo.SID.value.GetType() to make sure). There are two issues with your current code:
The -Match operator only works with two strings, so you can't use that here. But you can use Contains() on the array.
You need to convert the SecurityIdentifier to a string. The Value property does that.
Try this:
If ($UserInfo.SID.value.Contains($Event.Properties[2].Value))

Related

(PowerShell) How do I filter usernames with Get-EventLog

I'm working on a Powershell script to get all users who have logged in/out of a server in the past 7 days, where their name is not like "*-organization". The below works, but no matter what I try I'm not able to filter names
$logs = get-eventlog system -ComputerName $env:computername -source Microsoft-Windows-Winlogon -After (Get-Date).AddDays(-7)
$res = #()
ForEach ($log in $logs)
{
if($log.instanceid -eq 7001){
$type = "Logon"
}
Elseif ($log.instanceid -eq 7002){
$type = "Logoff"
}
Else { Continue }
$res += New-Object PSObject -Property #{Time = $log.TimeWritten; "Event" = $type; User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}};
$res
I've tried adding this line in various places and ways, but no matter what I can't get it to filter. It either fails and tells me my operator must have a property and value, or it runs fine and ignores any username filtering.
| Where-Object $_.User -notlike "*-organization"
Is it even possible to filter the login username with this method? If so, what am I doing wrong? If it's not possible, is there another way I can get what I need?
There would have to be a property named 'user' for that to work. Get-eventlog is actually obsolete now, and replaced by get-winevent. Unfortunately, you have to get into the xml to filter by usersid. I've included a time filter.
$a = get-winevent #{logname='system';
providername='Microsoft-Windows-Winlogon'} -MaxEvents 1
$e = $a.ToXml() -as 'xml'
$e.event.EventData
Data
----
{TSId, UserSid}
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
data='S-2-6-31-1528843147-473324174-2919417754-2001';starttime=(Get-Date).AddDays(-7);
id=7001,7002}
In powershell 7 you can refer to the eventdata named data fields directly:
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
usersid='S-2-6-31-1528843147-473324174-2919417754-2001'}
The get-winevent docs say you can use "userid" in the filterhashtable, but I can't get that to work.
EDIT: Actually this works. But without limiting it too much, at least for me.
get-winevent #{logname='system';userid='js2010'}
get-winevent #{providername='Microsoft-Windows-Winlogon';userid='js2010'}
You can do this with the -FilterXPath parameter like below:
$filter = "(*[System/EventID=7001] or *[System/EventID=7002]) and *[System/Provider[#Name='Microsoft-Windows-Winlogon']]"
$result = Get-WinEvent -LogName System -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
$eventData = $eventXml.EventData.Data
$userSID = ($eventData | Where-Object { $_.Name -eq 'UserSid' }).'#text'
$userName = [System.Security.Principal.SecurityIdentifier]::new($userSID).Translate([System.Security.Principal.NTAccount])
# you can add username filtering here if you like.
# remember the $userName is in formal DOMAIN\LOGONNAME
# if ($username -notlike "*-organization") {
# output the properties you need
[PSCustomObject]#{
Time = [DateTime]$eventXml.System.TimeCreated.SystemTime
Event = if ($eventXml.System.EventID -eq 7001) { 'LogOn' } else { 'LogOff' }
UserName = $userName
UserSID = $userSID
Computer = $eventXml.System.Computer
}
# }
}
# output on screen
$result
# output to CSV file
$result | Export-Csv -Path 'X:\TheOutputFile.csv' -NoTypeInformation
Note, I have commented out the username filtering in the code. It is just there to give you an idea of where to put it. Of course, you can also filter the $result afterwards:
$result | Where-Object { $_.UserName -notlike "*-organization" }
Adding to #js2010's helpful answer, and with the assumption you're using PowerShell 5.1. I usually identify the property array index and use Select-Object to create a custom property as needed.
$WinEvents =
get-winevent #{logname='system'; providername='Microsoft-Windows-Winlogon'} |
Select-Object #{Name = 'Time'; Expression = {$_.TimeCreated}},
#{Name = 'Event'; Expression = { If($_.ID -eq 7001){'Logon'} ElseIf($_.ID -eq 7002){ 'Logoff' } } },
#{Name = 'User'; Expression = { [System.Security.Principal.SecurityIdentifier]::new( $_.Properties[1].Value ).Translate([System.Security.Principal.NTAccount]) } }
In your case this should add a property called User with a value like DomainName\UserName to the objects. I also added expressions to derive the other properties you were adding to your custom objects. Select-Object emits custom objects as well so this should give the result you're looking for.
Let me know if this helps.
Update
Respectfully, the other 2 answers make the assumption that you are looking for logon/off events for a specific user. That's not how I read the question; in particular:
"get all users who have logged in/out of a server"
While PowerShell 7+ does let you directly cite UserID in the FilterHashtable, it's not very useful here because we're not seeking events for a specific user. Furthermore, it seems unhelpful for the ultimate output as by default it echoes as a SID. It would still need to be translated, not only for display but for further filtering. I'm also not positive that UserID will always be the same as Properties[1], there's certainly some variance when looking at other event IDs.
The XML work is very cool, but I don't think it's called for here.
There were some issues with my answer as well. I overlooked filtering the event IDs & dates up front. I also realized we don't need to instantiate [System.Security.Principal.SecurityIdentifier] class because the property is already typed as such. Along with some readability improvements I corrected those issues below.
# Should be the 1st line!
using NameSpace System.Security.Principal
$ResolveEventType = #{ 7001 = 'Logon'; 7002 = 'Logoff' }
$FilterHashTable =
#{
LogName = 'system'
ProviderName = 'Microsoft-Windows-Winlogon'
ID = 7001,7002
StartTime = (Get-Date).AddDays(-7)
}
[Array]$WinEvents =
Get-WinEvent -FilterHashtable $FilterHashTable |
Select-Object #{ Name = 'Time'; Expression = { $_.TimeCreated } },
#{ Name = 'Event'; Expression = { $ResolveEventType[ $_.ID ] } },
#{ Name = 'User'; Expression = { $_.Properties[1].Value.Translate( [NTAccount] ) } }
$WinEvents |
Where-Object{ $_.UserName -notlike "*-organization" } |
Format-Table -AutoSize
This tested good in PowerShell 5.1 & 7.0. I added Format-Table to display the output, but you can just change that out for an Export-Csv command as needed
Note: The last 2 pipelines can be combined, but I thought this was a
little more readable.
Let me know if this helps.

CSV export is missing data in one of the columns

I have a csv file that this script works fine on.
$CCure = Import-csv C:\Scripts\file1.csv
ForEach ($user in $CCure) {
[string]$1Name = $User.FirstName
[string]$2Name = $User.LastName
[string]$GivenName = $1Name.Split(" ")[0]
[string]$SN = $2Name.Split(",")[0]
[string]$ID = $User.ObjectID
[string]$EmpID = $User.Int5 |
Select #{Name="First Name";Expression={$GivenName}}, #{Name="Last Name";Expression={$SN}}, #{Name="CCure ObjectID";Expression={$ID}}, #{Name="CCure Int5 Row";Expression={$EmpID}} |
Export-csv C:\Scripts\CCure\CCure-Names-Test.csv -Append -notypeinformation
}
However, when I try a similar script going out to AD and want to use RegEx, things don't work. It seems to hang or when it does run, the SurName is missing. Why doesn't the second one work? I must be missing something simple?
$Users = Get-ADUser -LDAPFilter "(&(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))" -properties samaccountname,givenname,sn
ForEach ($User in $Users) {
[string]$1Name = $User.GivenName
[string]$2Name = $User.SN
[string]$GivenName = $1Name.Split(" ")[0]
[string]$SN = $2Name.Split(",")[0] |
Select #{Name="First Name";Expression={$GivenName}}, #{Name="Last Name";Expression={$SN}},#{Name="SID";Expression={$User.samaccountname}}| Export-Csv C:\scripts\ccure\AD-Active-Names3.csv -NoTypeInformation -append
}
In your second example $sn would be null. This is because of the trailing pipe character that you have. This issue is also present in your first code block for $EmpID. The last command Export-CSV returns nothing to the output stream so the variable in both cases would be $null.
You are taking the result of $2Name.Split(",")[0] and sending that into the pipeline which is then left unused for Select-Object.
So the simple answer is remove the pipeline character and those two lines will now work separately.
[string]$SN = $2Name.Split(",")[0]
Select #{Name="First Name";Expression={$GivenName}}, #{Name="Last Name";Expression={$SN}},#{Name="SID";Expression={$User.samaccountname}}| Export-Csv C:\scripts\ccure\AD-Active-Names3.csv -NoTypeInformation -append
Also consider making your own object if you are changing property names and values that much. This should accomplish the same thing, be easier to read and take advantage of the pipeline.
$Users = Get-ADUser -LDAPFilter "(&(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$Users | ForEach-Object {
$props = #{
"First Name" = ($_.GivenName).Split(" ")[0]
"Last Name" = ($_.SurName).Split(",")[0]
"SID" = $_.samaccountname
}
New-Object -TypeName psobject -Property $props
} | Select-Object "First Name","Last Name",SID | Export-Csv C:\scripts\ccure\AD-Active-Names3.csv -NoTypeInformation
You will note that I used $_.SurName as the property names used since PowerShell AD Objects do not directly match their LDAP attribute names. If you tried to access $_.SN you would get a null value. GivenName,Surname and SID are part of the value set returned by default so you don't need to request them.
Looking at about_ActiveDirectory_ObjectModel you will see under AdUser:
Surname - A property of type System.String, derived from the directory attribute: sn
You will still get errors with this if Surname or GivenName are not populated. Accounting for that would be a simple if statement if that is going to be an issue.

Powershell passing variable as a filter string

I'm trying to write a script to retrieve the username of logged in users from servers, (This is an exercise in working with unformatted string data I'm aware of other methods to get this data so please don't suggest them)
I'm trying to pass a numeric string from a WMI query into a where-object filter
This is reading the WMI output from the Win32_LoggedOnUser class
$Name | where {$_.Dependent -like $ID } | select Antecedent
the problem seems to be in reading the ID variable, I've tried several variations which is a value concatenated with some wildcards for the filter format, I was tipped off to this when I converted the string to CSV format so I could more easily do a [regex]::Split, I get an error that the InputObject is null, If I run the above line alone I just get back a null result, if I manually enter the filter string I get the output I want.
[String]$ID = "'*"+$UserSessions.LogonId+"*'"
If I do a write-host I just get back '146771' which is what I seem to want,and get-member shows it to be a [System.String]
then I'm throwing this to a split that grabs the last token which is the username the whole script works fine if I manually enter the filter string just not with the variable in any format I've tried
${ID} ($ID) ""$ID"" $($ID)
Here's the full script for reference
$UserSessions = GWMI Win32_LogonSession | where { $_.LogonType -eq 2}
[String]$ID = "'*"+$UserSessions.LogonId+"*'"
$Name = GWMI Win32_LoggedOnUser
$Results = $Name | where {$_.Dependent -like $ID } | select Antecedent
$Split = $Results | ConvertTo-Csv
$Splat = [regex]::Split($Split, """")[9]
Write-Host "User = $Splat"
gwmi Win32_LogonSession may produce more than one result, so you need to account for that. Also, splitting produces less fields than you expect.
This worked for me:
gwmi Win32_LogonSession | ? { $_.LogonType -eq 2 } | % {
$ID = "*$($_.LogonId)*"
gwmi Win32_LoggedOnUser | ? { $_.Dependent -like $ID } | select Antecedent
} | % {
$name = ($_.Antecedent -split '"')[3]
Write-Host "User = $name"
}

How to use the "-notcontains" conditional parameter when querying a BlockedSendersAndDomains list in PowerShell?

I am trying to make a list of all mailboxes who DO NOT have "conostco.com" domain on their BlockedSendersAndDomains list.
by using
$test= Get-MailboxJunkemailconfiguration -Identity * | fl Displayname, BlockedSendersAndDomains
I can get a list of all the names and the Blocked domains/email addresses associated to those names but for my purpose I want to get a list of all of the names of the mailboxes that DO NOT contain the domain "conostco.com". Is there such way to do it?
Based on the sample data you have given I am "assuming" BlockSendersAndDomains is an array.
So maybe you could do something like this then
$test= Get-MailboxJunkemailconfiguration -Identity * | % { $domains = $_.BlockedSendersAndDomains.GetEnumerator() | ? {$_ -notmatch 'conostco.com'}; $name = $_.displayname ; new-object PSObject -Property #{name=$displayname;domains=$domains} }
Again, there is no way for me to test, so see if that gives you a $test object collection which you can then re-use or just display, if you like.
Get-MailboxJunkEmailConfiguration -Identity * |
Where-Object {$_.BlockedSendersAndDomains -notlike '*conostco.com*' }

In PowerShell, how can I combine the results of two commands that have a 1-to-1 relashionship?

This particular example is Get-User and Get-Mailbox (Exchange 2010). Get-User returns some of the columns I need, and Get-Mailbox some others. I am having difficulty figuring out how I can combine the results of the two into a single table with the results from both.
Get-User -Filter "..." | Get-Mailbox -Filter "..."
How do I take the results of a command similar to the above and turn it into results similar to below?
FirstName LastName Alias CustomAttribute1
--------- -------- ------ ----------------
Bob Smith bsmith Example
Johnny NoMail
Adam Blye ablye Has a Mailbox
Note that FirstName and LastName are not returned by Get-Mailbox, and conversely Alias and CustomAttributes are not returned from Get-User. Not every user has a mailbox, so sometimes a portion of the columns would be null. But I'm having a devil of a time figuring out the best way to return a combined table like this.
Get the users, save each user in a variable, get the mailbox for each user and then create a new object with properties from both variables
Get-User -Filter ... | Foreach-Object{
$user = $_
$mbx = Get-Mailbox $user
New-Object -TypeName PSObject -Property #{
FirstName = $user.FirstName
LastName = $user.LastName
Alias = $mbx.Alias
CustomAttribute1 = $mbx.CustomAttribute1
}
}
I make a custom object, which may be overkill, but it's the simplest way I've found.
Here's some sample code to play with. Let me know if it generates any trouble or additional questions:
$outputCollection = #()
$users = Get-User -Filter "..."
$mailboxes = Get-Mailbox -Filter "..."
$users | Foreach-Object {
#Associate objects
$userObject = $_
$mailboxObject = $mailboxes | Where-Object {$_.Name -eq $userObject.Name}
#Make a combined object
$outputObject = "" | Select Name, UserAttribute, MailboxAttribute
$outputObject.Name = $userObject.Name
$outputObject.UserAttribute = $userObject.UserAttribute
$outputObject.MailboxAttribute = $mailboxObject.MailboxAttribute
#Add the object to the collection
$outputCollection += $outputObject
}
$outputCollection
Another option that should work is called calculated properties:
Get-User -Filter "..." | Select Name, UserAttribute, #{Name="OtherAttribute"; Expression={(Get-Mailbox $_.Name).MailboxAttribute}}
...note that this will run a new Get-Mailbox command for each entry, potentially increasing execution time
Thank you guys. I spent a hell of a lot of time trying to figure out my own issue and your code helped me get it right. I needed to find all the calendars were the default account was set to none. Below is what I needed up using.
Get-Mailbox | ForEach-Object{
$user = $_
$calPerms = Get-MailboxFolderPermission $user":\calendar" -User Default | Where-Object {$_.AccessRights -eq "none"}
New-Object -TypeName PSObject -Property #{
Name = $user
Permissions = $calPerms.AccessRights
Users = $calPerms.user
}
}
One liner to get FirstName, LastName, Alias and CustomAttribute1:
Get-User | Select Firstname, Lastname, #{Name="Alias"; Expression={(Get-Mailbox $_.Name).Alias}}, #{Name="CustomAttribute1"; Expression={(Get-Mailbox $_.Name).CustomAttribute1}}