powershell hashtable (export-Csv) to CSV - powershell

The following code in powershell creates a file with key/value pairs.
$result = #()
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-5)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM")
{
$result += [PSCustomObject]#{
Time = $_.TimeGenerated
Workstation = $_.ReplacementStrings[11]
}
}
}
#$result | Export-Csv -Path .\Logins.csv -NoTypeInformation
$result | Out-File "C:\Temp\Logins.csv"
The above results in the following file contents:
However, I want the contents in CSV format. If I change the commented lines out as below:
$result = #()
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-5)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM")
{
$result += [PSCustomObject]#{
Time = $_.TimeGenerated
Workstation = $_.ReplacementStrings[11]
}
}
}
$result | Export-Csv -Path .\Logins.csv -NoTypeInformation
#$result | Out-File "C:\Temp\Logins.csv"
Then I get the following:
Googling around through myriad pages and examples, I (mis?)understand this to be a hashtable and that the Export-Csv should work to create a csv file. I cannot seem to get it to work.
Any help is greatly appreciated.

Hmmm ... the following code works exactly like I'd expect it:
$result =
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-5)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM") {
[PSCustomObject]#{
Time = $_.TimeGenerated
Workstation = $_.ReplacementStrings[11]
}
}
}
$result | Export-Csv -Path .\Logins.csv -NoTypeInformation
BTW: It is recommended not to use Get-Eventlog anymore. Use Get-WinEvent instead. ;-)

Another option could be just this:
Clear-Host
$result = #()
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-1)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM")
{
$result += [PSCustomObject]#{
Time = $PSItem.TimeGenerated
Workstation = $PSItem.ReplacementStrings[11]
}
}
}
$result | ConvertTo-Csv -NoTypeInformation | Out-File -FilePath 'Logins.csv'
Get-Content -Path 'Logins.csv'
# Results
<#
"Time","Workstation"
...
"06-Aug-22 16:45:37","-"
"06-Aug-22 16:45:17","-"
"06-Aug-22 16:44:29","-"
...
#>
Update as per my comment.
The results are the same as the above, either to the screen or the file:
Clear-Host
(Get-EventLog -LogName Security -After ((Get-Date).AddDays(-1)) -InstanceId 4624) -ne 'SYSTEM' |
Select-Object -Property #{Name = 'Time';Expression = {$PSItem.TimeGenerated}},
#{Name = 'Workstation';Expression = {$PSItem.ReplacementStrings[11]}} |
Export-Csv -Path 'Logins.csv'
Get-Content -Path 'Logins.csv'

Related

Script to remove user profiles using powershell

I'm trying to build a powershell script that I can use to delete all or some of the user profiles on multiple pc's since they often cause the drives to go full.
I found the current script which I got to work for me, but I'd like to optimize it so I can input or import a list of computers where I want him to remove all the user profiles from.
Can you guys help me to input this feature?
Current Code:
$ExcludedUsers ="admin","test"
$RunOnServers = $false
[int]$MaximumProfileAge = 0 # Profiles older than this will be deleted
$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem
if ($RunOnServers -eq $true -or $osInfo.ProductType -eq 1) {
New-EventLog -LogName Application -Source "Stone Profile Cleanup" -ErrorAction SilentlyContinue
$obj = Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special -and $_.Loaded -eq $false )}
#$output = #()
foreach ($littleobj in $obj) {
if (!($ExcludedUsers -like $littleobj.LocalPath.Replace("C:\Users\",""))) {
$lastwritetime = (Get-ChildItem -Path "$($littleobj.localpath)\AppData\Local\Microsoft\Windows\UsrClass.dat" -Force ).LastWriteTime
if ($lastwritetime -lt (Get-Date).AddDays(-$MaximumProfileAge)) {
$littleobj | Remove-WmiObject
# $output += [PSCustomObject]#{
# 'RemovedSID' = $littleobj.SID
# 'LastUseTime' = $litteobj.LastUseTime
# 'LastWriteTime' = $lastwritetime
# 'LocalPath' = $littleobj.LocalPath
# }
}
}
}
#$output | Sort LocalPath | ft
#$output | Sort LocalPath | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "C:\MyOutput.TXT" -append -Encoding Unicode
Write-EventLog –LogName Application –Source "Stone Profile Cleanup" –EntryType Information –EventID 1701 -Category 2 -Message ("Profiles older than $MaximumProfileAge days have been cleaned up")
}$ExcludedUsers ="adminbholemans","testbholemans1"
$RunOnServers = $false
[int]$MaximumProfileAge = 0 # Profiles older than this will be deleted
$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem
if ($RunOnServers -eq $true -or $osInfo.ProductType -eq 1) {
New-EventLog -LogName Application -Source "Stone Profile Cleanup" -ErrorAction SilentlyContinue
$obj = Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special -and $_.Loaded -eq $false )}
#$output = #()
foreach ($littleobj in $obj) {
if (!($ExcludedUsers -like $littleobj.LocalPath.Replace("C:\Users\",""))) {
$lastwritetime = (Get-ChildItem -Path "$($littleobj.localpath)\AppData\Local\Microsoft\Windows\UsrClass.dat" -Force ).LastWriteTime
if ($lastwritetime -lt (Get-Date).AddDays(-$MaximumProfileAge)) {
$littleobj | Remove-WmiObject
# $output += [PSCustomObject]#{
# 'RemovedSID' = $littleobj.SID
# 'LastUseTime' = $litteobj.LastUseTime
# 'LastWriteTime' = $lastwritetime
# 'LocalPath' = $littleobj.LocalPath
# }
}
}
}
#$output | Sort LocalPath | ft
#$output | Sort LocalPath | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "C:\MyOutput.TXT" -append -Encoding Unicode
Write-EventLog –LogName Application –Source "Stone Profile Cleanup" –EntryType Information –EventID 1701 -Category 2 -Message ("Profiles older than $MaximumProfileAge days have been cleaned up")
}
I found this piece of code for the computer input but I'm not sure how I can implement it properly.
Get-CimInstance -ComputerName SRV1,SRV2,SRV3 -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq 'UserA' } | Remove-CimInstance
Thanks for the help everyone.
Get-CimInstance -ComputerName SRV1,SRV2,SRV3 -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('')[-1] -eq 'UserA' } | Remove-CimInstance
Do u test it before? Work OK?

Out-File unnecessary characters

I have the script below which pings a list of machines, outputs the result to CSV and gets the lastlogontimestamp of the machine.
It works fine, except the lastlogontimestamp comes out like this:
CCC-APP01,172.22.100.15,#{lastLogonDate=07/25/2018 13:24:54}
How can I get rid of the extra characters: #{lastlogondate=...}?
$OutputCSV = "C:\TEMP\OUPingResults.csv"
$SearchLocation = "OU=AA,OU=Servers,DC=LocA,DC=XYZ,DC=com"
$Computers = Get-ADComputer -Filter * -SearchBase $SearchLocation |
Select Name |
Sort-Object Name
$Computers = $Computers.Name
$Headers = "ComputerName,IP Address,LastLogonTimeStamp"
$Headers | Out-File -FilePath $OutputCSV -Encoding UTF8
foreach ($computer in $Computers) {
Write-host "Pinging $Computer"
$Test = Test-Connection -ComputerName $computer -Count 1 -ErrorAction SilentlyContinue -ErrorVariable Err
if ($test -ne $null) {
$IP = $Test.IPV4Address.IPAddressToString
$LastLogonTimeStamp = Get-ADComputer $Computer -Prop CN,lastLogonTimestamp |
Select #{n="lastLogonDate";e={[datetime]::FromFileTime($_.lastLogonTimestamp)}}
$Output = "$Computer,$IP,$LastLogonTimeStamp"
$Output | Out-File -FilePath $OutputCSV -Encoding UTF8 -Append
} else {
$Output = "$Computer,$Err"
$Output | Out-File -FilePath $OutputCSV -Encoding UTF8 -Append
}
}
The expression ... |Select-Object #{N='SomeName';E={"SomeValue"}} will produce an object that has a property named SomeName with the value "SomeValue".
What you see in the output is a string representation of this object.
If you want only the value, change the $LastLogonTimeStamp assignment to:
$LastLogonTimeStamp = [datetime]::FromFiletime((Get-ADComputer $Computer -Prop lastLogonTimestamp).lastLogonTimestamp)

Get-EventLog with Append

So i have this code below that collects the stated EventIDs with the use of append. The problem is have is it only saves to a single file. What i want to do is save the collection to a daily file so i can do a daily report. A little help please?
$endtime = Get-Date
$starttime = (Get-Date).AddHours(-3)
$domain = "ComputerName"
$event = get-eventlog security -ComputerName $domain -after $starttime -before $endtime | where-object {($_.EventID -eq 4724) -or ($_.EventID -eq 4723) -or ($_.EventID -eq 4720)}
$event | select MachineName,EventID,TimeGenerated,Message | export-csv -path "E:\EventLogs\temp.csv"
get-content "E:\EventLogs\temp.csv" | out-File -filepath "E:\EventLogs\AccountAudit.csv" -append -enc ASCII -width 500
Export-Csv Has an -Append Parameter as well, you can shorten your code to:
$event = get-eventlog security -ComputerName $domain -after $starttime -before $endtime |
Where-object {($_.EventID -eq 4724) -or ($_.EventID -eq 4723) -or ($_.EventID -eq 4720)}
$event | select MachineName,EventID,TimeGenerated,Message |
Export-Csv -path "E:\EventLogs\AccountAudit.csv" -Append -Encoding ASCII
Simply add a get-start with some parameters to get a date that's filename friendly (no "/" for example) and save it in a variable. Then replace AccountAudit on the last line with the variable.

Parsing CSV Data to another CSV based on Samaccountmatch

I have 2 csv's, 1 from exchange and 1 from AD. I am querying for samaccountname, emailaddress, enabled in AD. I am querying for samaccountname, primarysmtpaddress where recipienttype is 'usermailbox'.
After these are compared it spits out a 3rd csv based on the mismatches it finds. I need to parse the primarysmtpaddress from the exchange csv to the next column and only the addresses based on the samaccountname.
Also, any suggestions are welcome. First time here :)
AD Script
$ErrorActionPreference = 'Continue'
$SamAccountPath = "Path\get_adusers.csv"
$SamAccountPathE = "Path\get_adusers_edit.csv"
$Folder = "Path\mismatch_script"
$logfile = "Path\mismatch_script\get-user.txt"
Try{
if (Test-Path $Folder)
{}
else
{
New-Item -Path "$Folder" -ItemType Directory -Force | Out-Null
}
} Catch {
Write-Output "Could not create log folder" | Out-File $logfile -Append
}
Try
{
Import-Module ActiveDirectory
} Catch {
Write-Output "Could not import ActiveDirectory module." | Out-File $logfile -Append
}
Try{
Get-Aduser -filter * -properties Samaccountname, emailaddress, enabled | ? { $_.enabled -eq $true } | select samaccountname, emailaddress |`
?{$_.emailaddress -ne $null} | Sort-Object samaccountname |`
Export-Csv -Path $SamAccountPath -NoTypeInformation -Force | Out-Null
Get-Content $SamAccountPath | select -Skip 1 | ConvertFrom-Csv -Header 'Samaccountname','primarysmtpaddress' | Export-Csv -Path $SamAccountPathE -NoTypeInformation -Force |Out-Null
Remove-Item $SamAccountPath -Force
} Catch {
Write-Output "Could not get ad users" | Out-File $logfile -Append
}
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -version 2.0 -noexit -command ". 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto; Path\get-exuser_information.ps1"
Exchange Script
$AliasPath = "Path\get_exusers.csv"
$SamAccountPath = "Path\get_adusers_edit.csv"
$MismatchPath = "Path\get_diff.csv"
$logfile = "Path\get-user.txt"
$mboxes = get-mailbox -Resultsize Unlimited -identity
$samobj.Samaccountname | Where-Object {$_.recipienttype -eq "UserMailbox"} | select samaccountname, primarysmtpaddress
$ExUserL = Import-Csv $AliasPath
$AdUserL = Import-Csv $SamAccountPath | Where-Object {$_.samaccountname}
$samobj = New-Object PSObject -Property #{ 'Samaccountname' = $_.samaccountname }
$Compare = Compare-Object -ReferenceObject $ExUserL -DifferenceObject
$AdUserL = Import-Csv $SamAccountPath | Where-Object {$_.samaccountname}
Try {
ForEach-Object{
Import-Csv $SamAccountPath | select samaccountname | Out-Null
}
$mboxes | Sort-Object samaccountname | Export-Csv $AliasPath -NoTypeInformation -Force | Out-Null
} Catch {
Write-Output "Could not get mailboxes" | Out-File $logfile -Append
}
Try {
$compare | foreach {
if ($_.sideindicator -eq '<=')
{$_.sideindicator = ''
$_.samaccountname = ''
$_.primarysmtpaddress = ''
}
if ($_.sideindicator -eq '=>')
{$_.sideindicator = "Mismatch"}
}
$compare | Export-Csv $MismatchPath -NoTypeInformation -Force | Out-Null
} Catch {
Write-Output "Could not compare ex and ad csvs" | Out-File $logfile -Append
}
Send-MailMessage -SmtpServer "server" -Attachments $MismatchPath -From "email" -to "email" -Subject "Mismatch Report"
I rewrote it and found this solution:
#Globals for warnings/error action
$WarningPreference = 'SilentlyContinue'
$ErrorActionPreference = 'SilentlyContinue'
#import activedirectory cmdlets
import-module -name activedirectory
#Collects Exchange cmdlets into PSSession and imports it into the current session.
$exchangeuser = "user"
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://exchange/powershell/ -Authentication kerberos
import-PSSession $session -AllowClobber
#Declare paths
$MismatchPath = "Path\AD_EX_Mismatches.csv"
$MismatchPathE = "Path\AD_EX_MismatchesE.csv"
#Creates empty array
$errorstatus = #()
#Gets all mailboxes that are 'UserMailboxes' and not 'Contacts' it then pipes sam and psmtp to get-aduser. The #{n="";e={}} is creating a table and running an expression or cmdlet.
$gUser = Get-Mailbox -ResultSize Unlimited | Where-Object {$_.recipienttype -eq "UserMailbox"} | select samaccountname, primarysmtpaddress,#{n="ADEmail";e = {Get-ADuser $_.samaccountname -Properties emailaddress | select -ExpandProperty emailaddress}},#{n="Error";e={$errorstatus}}
#Foreach object (Samaccountname,primarysmtpaddress,emailaddress,error). Check if the conditions are met, if so, output the $gUser.Error to the CSV
$gUser | ForEach-Object {
if($_.ADEmail -ne $_.PrimarySmtpAddress){
$_.Error = "Mismatch"
}
if($_.ADEmail -eq ' '){
$_.Error = "Corrupted"
}
}
$gUser | Export-Csv $MismatchPath -NoTypeInformation -Force | Out-Null
#Finds blanks in the csv and re-exports it to a new path.
Import-Csv $MismatchPath | Where-Object {$_.samaccountname -and $_.primarysmtpaddress -and $_.error -notlike ""} | select * | export-csv $MismatchPathE -NoTypeInformation -Force
#Finds EX session and removes it
Get-PSSession | Remove-PSSession
#Send email with args
Send-MailMessage -SmtpServer "mailserver" -Attachments $MismatchPathE -From "emailaddress" -to "email address" -Subject "Mismatch Report" -Body "Attached is a report of mismatches of email addresses between AD and Exchange."
#Globals for warnings/error action
$WarningPreference = 'SilentlyContinue'
$ErrorActionPreference = 'SilentlyContinue'
#import activedirectory cmdlets
import-module -name activedirectory
#Collects Exchange cmdlets into PSSession and imports it into the current session.
$exchangeuser = "user"
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://EX/powershell/ -Authentication kerberos
import-PSSession $session -AllowClobber
#Declare paths
$MismatchPath = "E:\Utilities\Scripts\Set_DashboardData\Mismatch\1.csv"
$MismatchPathE = "E:\Utilities\Scripts\Set_DashboardData\Mismatch\1E.csv"
$ReportPath = "E:\Reports\Exchange\EXDashboard\"
#Creates empty array
$errorstatus = #()
$Fix = #()
#Gets all mailboxes that are 'UserMailboxes' and not 'Contacts' it then pipes sam and psmtp to get-aduser. The #{n="";e={}} is creating a table and running an expression or cmdlet.
$gUser = Get-Mailbox -ResultSize Unlimited | Where-Object {$_.recipienttype -eq "UserMailbox"} | select samaccountname, primarysmtpaddress,#{n="ADEmail";e = {Get-ADuser $_.samaccountname -Properties emailaddress | select -ExpandProperty emailaddress}},#{n="Error";e={$errorstatus}}
#Foreach object (Samaccountname,primarysmtpaddress,emailaddress,error). Check if the conditions are met, if so, output the $gUser.Error to the CSV
$gUser | ForEach-Object {
if($_.ADEmail -ne $_.PrimarySmtpAddress){
$_.Error = "Mismatch"
}
if($_.ADEmail -eq ' '){
$_.Error = "Corrupted"
}
}
$gUser | Export-Csv $MismatchPath -NoTypeInformation -Force | Out-Null
#Finds blanks in the csv and re-exports it to a new path.
Import-Csv $MismatchPath | Where-Object {$_.samaccountname -and $_.primarysmtpaddress -and $_.error -notlike ""} | export-csv $MismatchPathE -NoTypeInformation -Force
#Deletes original file
Remove-Item $MismatchPath -Force
#Finds EX session and removes it
Get-PSSession | Remove-PSSession

Powershell Security Log Get-EventLog

I am trying to write something up in powershell and completely new to powershell, I need help. What I'm trying to do is get information from the Security Log. Specifically, the last login for users over the last two weeks. The code that I have so far is getting login's for the event ID 4624 based on the last 100 events. This is also returning not just users but computers as well. How can I limit the results to just users over a period of two weeks? Is this even possible?
$eventList = #()
Get-EventLog "Security" -After $Date `
| Where -FilterScript {$_.EventID -eq 4624 -and $_.ReplacementStrings[4].Length -gt 10} `
| foreach-Object {
$row = "" | Select UserName, LoginTime
$row.UserName = $_.ReplacementStrings[5]
$row.LoginTime = $_.TimeGenerated
$eventList += $row
}
$eventList
EDIT: Solved with code
$Date = [DateTime]::Now.AddDays(-14)
$Date.tostring("MM-dd-yyyy"), $env:Computername
$eventList = #()
Get-EventLog "Security" -After $Date `
| Where -FilterScript {$_.EventID -eq 4624 -and $_.ReplacementStrings[4].Length -gt 10 -and $_.ReplacementStrings[5] -notlike "*$"} `
| foreach-Object {
$row = "" | Select UserName, LoginTime
$row.UserName = $_.ReplacementStrings[5]
$row.LoginTime = $_.TimeGenerated
$eventList += $row
}
$eventList
Use -before and -after parameters to filter the log by date. Use get-help get-eventlog -full to see all the parameters.
The users's last logon is stored in Active Directory. Seems like it would be a lot easier to pull it from there than chewing through event logs.
Use PowerShell to search the Active Directory:
Import-Module ActiveDirectory
Get-QADComputer -ComputerRole DomainController | foreach {
(Get-QADUser -Service $_.Name -SamAccountName username).LastLogon.Value
} | Measure-Latest