How can I format this output better? - powershell

I would like to pipe the output to a .csv, but when I do, I cannot add the host name, so I have settled on shooting it to a .txt, however, I don't have much latitude to manipulate the results.
The original one-liner was:
$([ADSI]"WinNT://$env:COMPUTERNAME").Children | where {$_.SchemaClassName -eq 'user'} | select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} | export-csv C:\csv.csv
I have modified it to run against a list, however, the original code does not denote the host name... I would love to know how to do this. Here is the modified code:
$computers = Get-Content C:\LocalLogin.txt
ForEach ($Computer in $Computers)
{
$COMPUTER | Out-File C:\StaleLocalLogins.txt -Append
$([ADSI]"WinNT://$COMPUTER").Children |
where {$_.SchemaClassName -eq 'user'} |
select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} |
Out-File C:\StaleLocalLogins.txt -Append
}

So basically you can add the hostname from $env:COMPUTERNAME to a later part of the script. Below is a 1 liner but spaced for ease of reading
$([ADSI]"WinNT://$env:COMPUTERNAME").Children |
where {$_.SchemaClassName -eq 'user'} |
select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} |
%{
$_ |
Add-Member -MemberType NoteProperty -Name "HostName" -Value "$env:COMPUTERNAME"
$_ | Select-Object "HostName", "Name", "LastLogin"
} | Export-Csv "C:\Test\test.csv"
This part adds a new property to the PSCustomObject that was created. It stores the hostname. Then it reorders the customobject in the order HostName, Name, LastLogin
%{
$_ | Add-Member -MemberType NoteProperty -Name "HostName" -Value "$env:COMPUTERNAME"
$_ | Select-Object "HostName", "Name", "LastLogin"
}
here it is as a one-liner
$([ADSI]"WinNT://$env:COMPUTERNAME").Children | where {$_.SchemaClassName -eq 'user'} | select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} | foreach-object {$_ | Add-Member -MemberType NoteProperty -Name "HostName" -Value "$env:COMPUTERNAME"; $_ | Select-Object "HostName", "Name", "LastLogin"} | Export-Csv "C:\scripts\test.csv" -NoTypeInformation

Related

when exporting to the text file compare-object issue

I want to get an output like below.
My output now :
Name Active PrimarySmtpAddress
---- ------ ------------------
DG_Group1 True mail1#contoso.com
DG_Group2 False mail2#contoso.com
DG_Group3 True mail3#contoso.com
My desired output :
mail1#contoso.com
mail2#contoso.com
mail3#contoso.com
script :
$DistroLists = Get-DistributionGroup -ResultSize Unlimited
$MessageTrace = Get-MessageTrace -RecipientAddress $DistroLists.PrimarySmtpAddress -startdate (Get-Date).AddDays(-8) -EndDate (Get-Date)
$DistroLists |
Foreach-Object {
$_ | Add-Member -MemberType NoteProperty -Name Active -Value (
$_.PrimarySmtpAddress -in $MessageTrace.RecipientAddress
) -PassThru
} |
Select-Object Name, Active, PrimarySmtpAddress | Where-Object Active -EQ "FALSE"
Out-File C:\output.txt
You can use Select-Object -ExpandProperty or ForEach-Object -MemberName to grab only the value of a specific property from one or more piped input objects:
... |Select-Object Name, Active, PrimarySmtpAddress | Where-Object Active -eq $false | ForEach-Object -MemberName PrimarySmtpAddress | Out-File...
You can skip first two lines and split the line using space characters. Finally pick the last column as given below:
Get-Content C:\Projects\logtext.txt | Select-Object -Skip 2 | ForEach-Object { "$(($_
-split '\s+',3)[2])" }
mail1#contoso.com
mail2#contoso.com
mail3#contoso.com

Powershell - Export-CSV outside loop only last line is printed/exported

Is it possible to adjust this code to export all lines outside foreach loop:
This works fine (inside loop):
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
$vm_name = $out.VMName | Get-Unique
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$results = #()
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
Write-Output $results
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append
}
However, when i move $results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append outside loop,
only one (last) line is saved to CSV
Inside loop, $results variable contains all lines, when i move this variable outside loop write-host $results only one (last) line is printed
For what it's worth, your code can be condensed quite a bit. Many of your steps are not necessary:
$results = Get-VM | Where State –eq Running | Get-VMNetworkAdapter | ForEach-Object {
[pscustomobject]#{
'VM NAME' = $_.VMName
'IP ADDRESS' = ($_.IPAddresses -notmatch ':') -join ' '
'MAC ADDRESS' = ($_.MacAddress -replace '(..)(..)(..)(..)(..)','$1:$2:$3:$4:$5:') -join ' '
'HYPER-V HOST' = $env:COMPUTERNAME
}
}
$results | Export-Csv -Path "c:\1.csv" -NoTypeInformation
Notes:
You can pipe the VMs that Get-VM returns directly into Get-VMNetworkAdapter
If you filter on a single property you don't need a script block for Where-Object. Where State -eq Running is a bit easier to write and read than Where { $_.State -eq 'Running' }.
$_.IPAddresses -notmatch ':' Operators like -notmatch work on arrays. 'a','b','0','c' -notmatch '\d' will return 'a','b','c'.
The same goes for -replace. 'a0','b1','c2' -replace '\d','' will return return 'a','b','c'. No foreach loops necessary at all.
$env:COMPUTERNAME should be faster than using WMI to get the computer name
Any object you create in a script block (like the ForEach-Object {...} script block) that you do not assign to a variable will be in the script block's output. This is why $results = ... | ForEach-Object {...} works. There is no need to explicitly create arrays with #() and add values to them.
Casting a hash table to [pscustomobject] is much easier than using Add-Member.
Figured it out:
moved $results variable outside loop (make it "global")
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
$results = #()
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
# Remove duplicate VM names
$vm_name = $out.VMName | Get-Unique
# In case more than 1 IP, put it in same row separated by space (192.168.1.1, 192.168.1.2)
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
}
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation

Dump connected drives serials and users

I need to list users of a connected drive and it's serial # in an output file. I'll be connecting between 12-24 drives in arrays at a time. I would like to be able to put the assigned drive letters into a variable. And then have the entire script loop for each connected drive. dumping serial + linking it to the users of that drive in a CSV output file
How can I put the assigned drive letters into an array?
$(get-physicaldisk; get-childitem -path (array variable):\Users) | add-content C:\path\to\my\output.csv
almost gets the output I need when I try this on a single drive. But I'd really like to clean it up and only display the important info (PSChildName) excluding all default, public admin accounts to reduce duplicate un-needed info.
I wanted this to work
$(get-physicaldisk | select-object FriendlyName, SerialNumber)-$(get-childitem -path L:\Users| select-object PSChildName)
but it did not
I need it to grab the serial for each drive - and output the users associated with that drive … i'm struggling with making the output look the way I want.
For each - drive in array - output ((serial #) + (users on the drive)) amending my .csv
After much plugging and chugging i'm now here, thanks to everyone's help
function Get-UsersOnDrive([string[]]$DriveLetters){
if (!$DriveLetters){
$DriveLetters = Get-WmiObject Win32_Logicaldisk | %{$_.Name -replace ":", ""}
}
foreach($DriveLetter in $DriveLetters)
{
$SerialNumber = get-partition -DriveLetter $DriveLetter -ErrorAction Ignore | get-disk | select -ExpandProperty SerialNumber
$path = $DriveLetter + ":\Users"
$Users = get-childitem -path $path | select-object PSChildName
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
}
}
Get-UsersOnDrive -DriveLetters #("C") | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
Ok so here is what i came up with and its rough
Get-WmiObject Win32_Logicaldisk | %{
$DriveLetter = $_.Name -replace ":", ""
$SerialNumber = get-partition -DriveLetter $DriveLetter | get-disk | select -ExpandProperty SerialNumber
$Users = Get-WmiObject Win32_UserProfile | select -ExpandProperty LocalPath | ?{$_ -like "$DriveLetter*"} | %{
$_ -replace '.*\\'
}
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
} | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
A. Get WMI LogicalDisk (gets you the drive letters)
B. Pass the $DriveLetter into a get-partition and get the SerialNumber property value.
C. Get Users Profile path, then find the ones on the current drive and replace everything except for the last slash, which is the username
D. Foreach user on drive we create a Custom Object and add the properties SerialNumber and Username, then return output and export to CSV
Here is a function that you can call to get users on drive as well
function Get-UsersOnDrive([string[]]$DriveLetters){
if (!$DriveLetters){
$DriveLetters = Get-WmiObject Win32_Logicaldisk | %{$_.Name -replace ":", ""}
}
foreach($DriveLetter in $DriveLetters){
$SerialNumber = get-partition -DriveLetter $DriveLetter -ErrorAction Ignore | get-disk | select -ExpandProperty SerialNumber
$Users = Get-WmiObject Win32_UserProfile | select -ExpandProperty LocalPath | ?{$_ -like "$DriveLetter*"} | %{
$_ -replace '.*\\'
}
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
}
}
Get-UsersOnDrive -DriveLetters #("C","V","F") | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
If you remove -DriveLetters parameter and the drives then it will check all drives
The following code gets the disk serial number. I am not sure why that is needed. Will this give you a start?
function Get-DiskSerialNumber {
param(
[Parameter(Mandatory = $true,Position=0)]
[string] $DriveLetter
)
Get-CimInstance -ClassName Win32_DiskDrive |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Where-Object DeviceId -eq $DriveLetter |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition |
Select-Object -Property SerialNumber
}
& openfiles /query /fo csv |
Select-Object -Skip 5 |
ConvertFrom-Csv -Header #('ID','USER','TYPE','PATH') |
Select-Object -Property USER,#{name='DRIVE';expression={$_.PATH.Substring(0,2)}} |
Sort-Object -Property DRIVE,USER -Unique |
Select-Object -Property *,
#{name='SERIALNUMBER';expression={(Get-DiskSerialNumber -Drive $_.DRIVE).SerialNumber}}

Script output that will work on the console as well as with Export-Csv

I'm working on a basic PowerShell script that inputs a pair of dates then gets all accounts with passwords expiring between those times. I'd like to output the data to the console in a way that is compatible with Export-Csv. That way the person running the script can either just view in the console, or get a file.
Here is my script:
[CmdletBinding()]
param(
[string]$StartDate = $(throw "Enter beginning date as MM/DD/YY"),
[string]$EndDate = $(throw "Enter end date as MM/DD/YY")
)
$start = Get-Date($StartDate)
$end = Get-Date($EndDate)
$low = $start.AddDays(-150)
$high = $end.AddDays(-150)
$passusers = Get-ADUser -Filter { PasswordLastSet -gt $low -and PasswordLastSet -lt $high -and userAccountControl -ne '66048' -and userAccountControl -ne '66080' -and enabled -eq $true} -Properties PasswordLastSet,GivenName,DisplayName,mail,LastLogon | Sort-Object -Property DisplayName
$accts = #()
foreach($user in $passusers) {
$passLastSet = [string]$user.PasswordLastSet
$Expiration = (Get-Date($passLastSet)).addDays(150)
$obj = New-Object System.Object
$obj | Add-Member -MemberType NoteProperty -Name Name -Value $user.DisplayName
$obj | Add-Member -MemberType NoteProperty -Name Email -Value $user.mail
$obj | Add-Member -MemberType NoteProperty -Name Expiration -Value $expiration
$accts += $obj
}
Write-Output ($accts | Format-Table | Out-String)
This prints to the console perfectly:
Name Email Expiration
---- ----- ----------
Victor Demon demonv#nsula.edu 1/3/2016 7:16:18 AM
However when called with | Export-Csv it doesn't:
#TYPE System.String
Length
5388
I've tried multiple variations using objects, and data tables, however it seems like I can only get it to work for console or for CSV, not for both.
Replace
Write-Output ($accts | Format-Table | Out-String)
with
$accts
That way your users can run your script any way they like, e.g.
.\your_script.ps1 | Format-Table
.\your_script.ps1 | Format-List
.\your_script.ps1 | Export-Csv
.\your_script.ps1 | Out-GridView
...
Format-Table | Out-String converts your output to a single string whereas Export-Csv expects a list of objects as input (the object properties then become the columns of the CSV). If Export-Csv is fed a string, the only property is Length, so you get a CSV with one column and one record.
$accts | ConvertTo-Csv | Tee -File output.csv | ConvertFrom-Csv

Include if/else logic

Hello I am trying to if/else and write two separate files, if PST exists then do the following. Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
Else Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-NOPSTs-$Date.csv
Could anyone please suggest.
$Date = Get-Date -format d-M-yyyy
$UserName = $env:USERNAME
$ComputerName = $env:COMPUTERNAME
$Outlook = New-Object -comObject Outlook.Application
$object = $Outlook.Session.Stores | Where {$_.FilePath -like "*.PST"} | Select `
#{Expression={$_.DisplayName}; Label="PST Name in Outlook"},`
#{Expression={$_.FilePath}; Label="PST Location/FileName"},`
#{Expression={$_.IsOpen}; Label="PST Open in Outlook"},`
#{Expression={(Get-Item $_.FilePath).Length / 1KB}; Label="PST File Size (KB)"}
$object | Add-Member -MemberType NoteProperty -Name 'ComputerName' -Value $ComputerName
$object | Add-Member -MemberType NoteProperty -Name 'UserName' -Value $UserName
$object | Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
Start-Sleep 5
Get-Process | Where {$_.Name -like "Outlook*"} | Stop-Process
You could replace the Where-Object filter with a ForEach-Object loop and a nested conditional:
$Outlook.Session.Stores | % {
if ($_.FilePath -like '*.pst') {
$_ | select ... | Export-Csv 'OpenPST.csv' -NoType -Append
} else {
$_ | select ... | Export-Csv 'NoPST.csv' -NoType -Append
}
}
That might not perform too well, though, because it repeatedly appends to the output files. It might be better to just run 2 pipelines with complementary filters:
$stores = $Outlook.Session.Stores
$stores | ? { $_.FilePath -like '*.pst' } | select ... |
Export-Csv 'OpenPST.csv' -NoType
$stores | ? { $_.FilePath -notlike '*.pst' } | select ... |
Export-Csv 'NoPST.csv' -NoType