Combine two arrays into a hashtable - arrays

Is there a way to combine two arrays to a hashtable ?
$Name = Get-ChildItem C:\Users | Select-Object Name
$CreationTime = Get-ChildItem C:\Users | Select-Object CreationTime
$Table = New-Object psobject
foreach ($item in $Name) {
foreach ($item2 in $CreationTime) {
Add-Member -InputObject $Table -MemberType NoteProperty -Name $item -Value $item2 } }
If I simply print out $item and $item2 I get every result multiple times, I know that this is because of the nested foreachs.
The example above is not very good, i acutally would need this to import different csv files and create a hashtable to export them again.

Have a single for loop were you iterate through the first array with an index. Assuming the same index in both arrays gives the correct pair, add the pair to the hashtable. No need for nested loops.
Some Pseudo code:
for ($i = 0; $i < $Name.length; $i++) {
Add-Member -Name $Name[$i] -Value $CreationTime[$i]
}

What I requested would look something like this:
$Name = Get-ChildItem C:\Users | Select-Object Name
$CreationTime = Get-ChildItem C:\Users | Select-Object CreationTime
$Table = New-Object psobject
$i = 0
do {
Add-Member -InputObject $Table -MemberType NoteProperty -Name $Name[$i].Name -Value $CreationTime[$i].CreationTime
$i++ }
while ($i -ne $name.Length)
$Table
However, this solution is more like what I wanted:
$Name = Get-ChildItem C:\Users | Select-Object Name
$CreationTime = Get-ChildItem C:\Users | Select-Object CreationTime
$TableData =#()
$i = 0
do {
$Table = New-Object psobject
Add-Member -InputObject $Table -MemberType NoteProperty -Name "FolderName:" -Value $Name.Name[$i]
Add-Member -InputObject $Table -MemberType NoteProperty -Name "Creation Time:" -Value $CreationTime.CreationTime[$i]
$TableData += $Table
$i++ }
while ($i -ne $name.Length)
$TableData | Export-csv -Delimiter ";" -Path $PSScriptRoot\out.csv -NoTypeInformation
Sorry for being unspecific! Is there an easier way to do this ?

Related

Looping and adding extra properties to a Object

I'm running a simple script to check if several specific services are running on an array of computers. It works fine, except the each 'row' appears' for every iteration of the loop. In this case 3 times. How do I edit/add to a property of an existing object? I.e. I want to add extra properties for the same object/row...
ComputerName Qualys Cloud Agent SplunkForwarder Service Cb Defense
------------ ------------------ ----------------------- ----------
dc01 Found Found Found
dc01 Found Found Found
dc01 Found Found Found
dc02 Found Found Found
dc02 Found Found Found
dc02 Found Found Found
ds01 Found Found Found
ds01 Found Found Found
ds01 Found Found Found
$ComputerName = 'dc01','ds01','dc02'
$ServiceList ='Qualys Cloud Agent',
'SplunkForwarder Service',
'Cb Defense'
$objarray = #()
$obj = #()
$ServiceArray = get-service -ComputerName $ComputerName -DisplayName $ServiceList | select-object -property MachineName,DisplayName,Name,Status
Foreach ($Computer in $ComputerName){
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $Computer
Foreach($Service in $ServiceList){
If ((gsv -computername $computer -displayname $Service).Status -eq 'Running'){
#write-output "Found $Service on $Computer"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Found'
$objArray += $obj
}
else{
#write-output "Not Found"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Not Found'
$objArray += $obj
}
}
}
$objarray | sort computername
Move the $objArray += $obj statement outside the inner loop - this way each object is only referenced by the array once:
Foreach ($Computer in $ComputerName){
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $Computer
Foreach($Service in $ServiceList){
If ((gsv -computername $computer -displayname $Service).Status -eq 'Running'){
#write-output "Found $Service on $Computer"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Found'
}
else{
#write-output "Not Found"
$obj | Add-Member -MemberType NoteProperty -Name $service -Value 'Not Found'
}
}
# Only need to add the resulting object ONCE
$objArray += $obj
}
I personally prefer constructing each list of object properties as a dictionary, and then convert + output it at the very end of the loop - at which point we simply assign the result of the entire foreach() statement to the desired variable:
$objArray = foreach($Computer in $ComputerName){
# Create dictionary to keep track of the new properties
$props = [ordered]#{ ComputerName = $Computer}
foreach($Service in $ServiceList){
# Populate dictionary with remaining properties inside the inner loop
if((gsv -computername $computer -displayname $Service).Status -eq 'Running'){
$props["$service"] = 'Found'
}
else{
$props["$service"] = 'Not Found'
}
}
# convert to object,
[pscustomobject]$props
}
If you want to add a property to an object, pipe the object to "add-member". Here is a quick example:
$files = Get-childitem "$env:USERPROFILE\Documents\*"
Foreach ($item in $files)
{
$item | Add-Member -MemberType NoteProperty -Name "extention" -Value $item.Name.Substring($item.name.Length - 3)
}
$files.extention

Powershell Array: HOWTO Dedup Output

When I run this parser script on my contacts.xml, which shows one line per user, I get multiple instances of the same data. I only want a single entry for the same data. How do I dedup the data before it writes to the CSV?
#https://stackoverflow.com/questions/29999682/powershell-parsing-a-text-file
$input = Get-Content $env:USERPROFILE\Downloads\contacts.xml\Downloads\contacts.xml
$array = #()
$input | % {
$writeobj = $false
$obj = New-Object System.Object
if ($_ -match 'email*') {
$Email = ($_ -split ':')[1]
}
if ($_ -match 'FN*') {
$NAME = ($_ -split ':')[1]
$writeobj = $true
}
if ($writeobj) {
$obj | Add-Member -Type NoteProperty -Name Email -Value $Email
$obj | Add-Member -Type NoteProperty -Name Name -Value $NAME
$array += $obj
}
Write-Host $Name, $email
}
$array | Export-Csv -Path C:\scripts\reports\test.csv -NoTypeInformation
I expect this to produce single entries but I get duplicates (and they don't line up right either).
(And yes I checked the XML file for single entries)
Select the unique objects.
$array |
Select-Object -Property * -Unique |
Export-Csv -Path 'C:\scripts\reports\test.csv' -NoType
As a side note, you may want to avoid appending to an array in a loop, as that is bound to perform poorly. Just pipe your ForEach-Object loop directly into Export-Csv.
I figured it out.
I REVERSED the $obj Add-Member variables *that fixed the order) and added another "$writeobj = $true" line to the FN match, and VOILĂ€ no more dupes.
Is that weird or what?
#https://stackoverflow.com/questions/29999682/powershell-parsing-a-text-file
$input = Get-Content $env:USERPROFILE\Downloads\contacts.xml $array =
#() $input | % {
$writeobj = $false
$obj = New-Object System.Object
If ($_ -match 'email') {
$Email = ($_ -split ':')[1]
$writeobj = $true
}
If ($_ -match 'FN') {
$NAME = ($_ -split ':')[1]
$writeobj = $true # <-- right here
}
If ($writeobj){
$obj | Add-Member -type NoteProperty -name Email -value **$NAME**
$obj | Add-Member -type NoteProperty -name Name -value **$Email**
$array += $obj
}
Write-Host $Name, $email } $array | Export-Csv -path C:\scripts\reports\test.csv -NoTypeInformation

Parsing a text file line by line and extracting string if it matches

We have a text log file similar to below with many users
<user>sandip</user>
something
<time>4:38 PM</time>
anything
<elapsed time> 60 mins </elapsed time>
We want to extract all users and we did the same simply by
Get-Content "C:\LOG\test.txt" | Select-String '(<user>.+</user>)' | ForEach-Object {
$_.Matches[0].Groups[1].Value
}
We want to parse test file line by line, check if it contains
user/time/elapsed time and [insert it in a dynamic variable if required] make a table of the same
Considering that your Log File follows the same format that i tested with:
(i.e something like this one:)
LogFile
This code should work just fine:
*
$Lines = get-content .\log.txt
$array = #()
foreach ($line in $lines)
{
if($line -like "<user>*")
{
$obj = New-Object psobject
Add-Member -InputObject $obj -MemberType NoteProperty -Name "UserName" -value $line.Replace("<user>","").Replace("</user>","")
}
if($line -like "<time>*")
{
Add-Member -InputObject $obj -MemberType NoteProperty -Name "Time" -value $line.Replace("<time>","").Replace("</time>","")
}
if($line -like "<elapsed time>*")
{
Add-Member -InputObject $obj -MemberType NoteProperty -Name "ElapsedTime" -value $line.Replace("<elapsed time>","").Replace("</elapsed time>","")
$array += $obj
}
}
$array | Export-Csv .\test.csv
*
A quite compact solution using two regular expressions,
with a nonconsuming positive
lookahead to
split the source file into sections starting with <user>
inside an if to get -match the current line with an alternation and a backreference to insert the found key and value into the current Row of the new table. See this RegEx live on regex101.com
## Q:\Test\2018\06\22\SO_50988379.ps1
$Table = ForEach ($Section in ((Get-Content .\Test.log -raw) -split '(?=<user>)' -ne '')) {
$Row = New-Object psobject
ForEach ($Line in ($Section -split "`r?`n")) {
if($Line -match "<(user|time|elapsed time)>([^<]+)</\1>"){
Add-Member -InputObject $Row -MemberType NoteProperty `
-Name "$($Matches[1])" -value $Matches[2].Trim()
}
}
$Row
}
$Table #| Export-Csv .\test.csv
Sample output:
> .\SO_50988379.ps1
user time elapsed time
---- ---- ------------
sandip 4:38 PM 60 mins
Joshi 8:15 PM 60 mins

Create Powershell Object From Loop Containing Multiple Users

I need to create a Powershell object, array or hash table to store a list of users and assorted details, these are extracted from a CSV file and located using Get-ADUser. As below:
$userList = Import-CSV $CSVInputFile
$users = #{}
Foreach ($csvUser in $userList)
{
$userSearchString = $csvUser | Select -ExpandProperty SamAccountName
$currentUser = (Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"})
If ($currentUser.EmailAddress -ne $null)
{
$currentUserEmailString = $csvUser | Select -ExpandProperty EmailAddress
$currentUserEmailString = ($currentUserEmailString -as [string])
$currentUser.EmailAddress = $currentUserEmailString
}
$Users = New-Object PSObject -Property #{
DistinguishedName = $currentUser.DistinguishedName
EmailAddress = $currentUser.EmailAddress
Enabled = $currentUser.Enabled
GivenName = $currentUser.GivenName
Name = $currentUser.Name
PasswordExpired = $currentUser.PasswordExpired
PasswordLastSet = $currentUser.PasswordLastSet
SamAccountName = $currentUser.SamAccountName
Surname = $currentUser.Surname
}
$Users
}
How can I add the details of each user for each iteration of the loop to the object.
I want to end up with an object containing the details of a number of users, same as the output directly from Get-ADUser:
Name SamAccountName EmailAddress
---- -------------- ------------
User1 user1 user1#domain.com
User2 user2 user2#domain.com
User3 user3 user3#domain.com
Any help would be greatly appreciated!
Not sure if I'm missing the point on this but I see you are building a custom object right in your loop. The only issue I do see is you are not keeping the results after each loop. Rather you are destroying the objects history.
I would change the declaration of $users to an array $users = #() and instead of populating a user hashtable into users add the current object into the array. You will then have an array of hashtables:
$Users += New-Object PSObject -Property #{...
Then you could the $Users output line outside the loop and you will have the whole thing. Then you could just output to a Select to get the output you desire.
$Users | Select-Object name,SamAccountName,EmailAddress
There is a potential major drawback of this approach though. When using += on arrays a new array is created and resized for the new element and the old array is discarded. This has huge performance implications for larger arrays.
An even better way to approach this would be to leverage the pipeline. This would be a performance boost when you have larger user groups.
Import-CSV $CSVInputFile | ForEach-Object{
$userSearchString = $_.SamAccountName
$currentUser = Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"}
If ($currentUser.EmailAddress -ne $null){
$currentUser.EmailAddress = $_.EmailAddress
}
[pscustomobject][ordered]#{
DistinguishedName = $currentUser.DistinguishedName
# ..... truncated
Surname = $currentUser.Surname
}
}
Now you could send that to something like Export-CSV or just save it into a variable. Your options are open now. [pscustomobject][ordered] are type accelerators available in PowerShell v3.0+
Define an $users as Array
$users = #()
and append the New-Object into $Users.
$Users += New-Object
Can't believe both of you guys got in before me! Oh well.
Hope this helps anyway.
$userList = Import-CSV $CSVInputFile
$users = #()
Foreach ($csvUser in $userList)
{
$userSearchString = $csvUser | Select -ExpandProperty SamAccountName
$currentUser = (Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"})
If ($currentUser.EmailAddress -ne $null)
{
$currentUserEmailString = $csvUser | Select -ExpandProperty EmailAddress
$currentUserEmailString = ($currentUserEmailString -as [string])
$currentUser.EmailAddress = $currentUserEmailString
}
#clears the properties of the previous object and starts collecting properties
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "DistinguishedName" -Value $($currentUser.DistinguishedName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "EmailAddress" -Value $($currentUser.EmailAddress)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Enabled" -Value $($currentUser.Enabled)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "GivenName" -Value $($currentUser.GivenName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "UserName" -Value $($currentUser.Name)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "PasswordExpired" -Value $($currentUser.PasswordExpired)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "PasswordLastSet" -Value $($currentUser.PasswordLastSet)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "SamAccountName" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Surname" -Value $($currentUser.Surname)
#saves the properties in an array that exists outside of the loop to preserve information beyond one interation
$users += $UserObj
}
$users | Format-Table -Property UserName,SamAccountName,EmailAddress

Creating and populating a hash table

I am looking to create either a non-jagged array or a hash table (I am not sure the difference nor what I need to get the job done). Here is what I am trying to do.
I would like to query a list of servers for several values and then store those values for output to a CSV file. Here is the code.
$allServers = "svr1","svr2","svr3"
$a = #{}
$outData = New-Object PSObject
$allServers | ForEach-Object {
$cpu = (Get-WmiObject win32_processor -ComputerName $_).length
$mem = (Get-WmiObject win32_physicalmemory -ComputerName $_).Capacity /1GB
$outData | Add-Member -Type NoteProperty -Name "SERVERNAME" -Value $_
$outData | Add-Member -Type NoteProperty -Name "#CPU" -Value $cpu
$outData | Add-Member -Type NoteProperty -Name "#GBRAM" -Value $mem
}
Write-Host $outData
I am getting errors because it seems like it is trying to create the same entries repeatedly. Is it possible to make an empty hash table (or non-jagged array) with column names and then just populate values?
Create the object inside the loop, output it, and assign the value of the whole pipeline to your array variable:
$allServers = "svr1","svr2","svr3"
$a = $allServers | ForEach-Object {
$cpu = (Get-WmiObject win32_processor -ComputerName $_).length
$mem = (Get-WmiObject win32_physicalmemory -ComputerName $_).Capacity /1GB
$outData = New-Object PSObject
$outData | Add-Member -Type NoteProperty -Name "SERVERNAME" -Value $_
$outData | Add-Member -Type NoteProperty -Name "#CPU" -Value $cpu
$outData | Add-Member -Type NoteProperty -Name "#GBRAM" -Value $mem
$outData
}
$a | Export-Csv $path -NoTypeInformation

Resources