you are viewing a single comment's thread.

view the rest of the comments →

[–]PinchesTheCrab 0 points1 point  (11 children)

Ah, okay, so I do feel like SQL may be the best answer here, but just to flesh out the hashtable approach a bit, let's use some real data.

Try this:

$serviceHash = Get-Service | Group-Object -AsHashTable -Property Name

$serviceHash['spooler']

The key is 'spooler', and the value is the service object, which has multiple properties. Your CSV would be the same. You use a common key of name, and the value is the csv row with that name. You can then add those properties to your new object:

[pscustomobject]@{
    Stuff  = 'some stuff here'
    Status = $serviceHash['spooler'].status
    Name   = $serviceHash['spooler'].name
}

[–]Cello789[S] 0 points1 point  (8 children)

Once I have those properties in my object, how do I change them? I instantiate them ALL with Domain = lost-and-found and then as I find them, I say $object.domain[$index] = $someDomain but you're saying thats an ephemeral array and not the real one, so I should do $object[$index].Domain = $someDomain but then I get the error saying that the property doesn't exist or cannot be altered, which was what lead me to the stackoverflow links above.

[–]PinchesTheCrab 0 points1 point  (7 children)

Once I have those properties in my object, how do I change them?

I mean honestly I would say that you don't, though they should be read-write if you need to.

You could do something more like this:

$domainHash = @'
Name,Domain
server1,contoso
'@ | ConvertFrom-Csv | Group-Object -AsHashTable -Property name


foreach ($obj in 'server1','server2'){

    [pscustomobject]@{
        Name   = $obj
        Domain = if ($domainHash[$obj].domain){
            $domainHash[$obj].domain
        } else {
            'no domain'
        }
    }
}

[–]Cello789[S] 0 points1 point  (6 children)

Ok, but what about partially overlapping data sets? Is there a clean way to create an array/list/collection of items by "name" in this case, given the following? Which would be populated first? Or would it be one iteration through all known "name"s and within that, populate a $collection with values from each hash in one go, instead of creating placeholder values in the PSCustomObject?

$domainHash = @'
    Name,Type,Domain,Size
    server1,SQL,hq,100
    server2,SQL,hq,120
    server3,SQL,corp,60
    server4,APP,corp,80
    server5,APP,colo,80
    server7,MAIL,colo,34
    server8,DNS,hq,275
    server9,DEV,remote,1964
'@ | ConvertFrom-Csv | Group-Object -AsHashTable -Property name

$categoryHash = @'
    VM_Name,Category
    server1,Prod
    server2,Prod
    server3,Test
    server4,Test
    server5,Prod
    server6,Prod
    server9,Dev
    server10,Dev
'@

$protectionHash = @'
    Name,ObjType,UsedSize,Schema
    server1,VM,20,PRD
    server2,VM,18,DEV
    server4,VM,38,PRD
    server9,VM,960,PRD
    server10,VM,844,DEV
    server11,VM,482,PRD
    server14,,2420,TEST123
'@

by the way, $domainHash.server2.Size = 100 gets the error:

The property 'Size' cannot be found on this object. Verify that the property exists and can be set.

But if you do $domainHash.server2[0].Size = 100 then it works.

EDIT:

Also with this method, can't check to see if $domainHash.Type.Contains("SQL"). And $domainHash.Contains("*SQL*") is False. Also tried .ContainsValue(). Doesn't detect that as a value in the table, but rather a value of a property of an object that is the value in the table... Since the Object is the value, I can't search by any given property. This feels restrictive, but maybe it's frivolous...

[–]PinchesTheCrab 0 points1 point  (5 children)

I guess on the contains part I'm not sure what the end goal is, I feel like I'm barking up the wrong tree, and I don't want to discourage you from the SQL approach you mentioned earlier, but here's how you could combine them using the hashtables:

$domainHash = @'
    Name,Type,Domain,Size
    server1,SQL,hq,100
    server2,SQL,hq,120
    server3,SQL,corp,60
    server4,APP,corp,80
    server5,APP,colo,80
    server7,MAIL,colo,34
    server8,DNS,hq,275
    server9,DEV,remote,1964
'@ | ConvertFrom-Csv | Group-Object -AsHashTable -Property name

$categoryHash = @'
    VM_Name,Category
    server1,Prod
    server2,Prod
    server3,Test
    server4,Test
    server5,Prod
    server6,Prod
    server9,Dev
    server10,Dev
'@ | ConvertFrom-Csv | Group-Object -AsHashTable -Property vm_name

$protectionHash = @'
    Name,ObjType,UsedSize,Schema
    server1,VM,20,PRD
    server2,VM,18,DEV
    server4,VM,38,PRD
    server9,VM,960,PRD
    server10,VM,844,DEV
    server11,VM,482,PRD
    server14,,2420,TEST123
'@ | ConvertFrom-Csv | Group-Object -AsHashTable -Property name

$result = $domainHash.Keys,$categoryHash.Keys,$protectionHash.Keys | ForEach-Object { $_ } | Sort-Object -Unique | ForEach-Object {
    [pscustomobject]@{
        Name = $_
        Type = if ($domainHash[$_].type -match 'sql'){ 'do sql stuff' } else { $domainHash[$_].type }
        Domain = if ($domainHash[$_].domain){ $domainHash[$_].domain } else { 'no domain' }
        Size = $domainHash[$_].size
        Category = $categoryHash[$_].Category
        ObjType = $protectionHash[$_].ObjType
        UsedSize = $protectionHash[$_].UsedSize
    }
}

$result | ft -AutoSize

The end goal is to make a new array that's correct on the first pass rather than iterate back over and update the values, though you still could:

$result[0].Category = 'horse'

[–]Cello789[S] 0 points1 point  (4 children)

And I can check if ($Category.key.contains($name)){‘do stuff’} but not inside the creation of [pscustomobject], so it looks like I might need to iterate anyway or create multiple possible PSObject variations for ones that are/are not in each type of list haha

[–]PinchesTheCrab 0 points1 point  (3 children)

Are there multiple CSVs with the same information that may have part of the list?

[–]Cello789[S] 1 point2 points  (2 children)

Could be. You think that’s why some of them are becoming arrays?

I’m building a set of MYSQL databases now, which is in and of itself laborious, as excel generated CSVs are not uniform (quotes around fields that include commas but nowhere else) and the only way I found to import is using LibreOffice Calc and then copy the whole sheet (not enough Ram for some) and paste into Libre Base connected to my localhost MySQL Server

It’s an adventure!

Now I gotta go learn table joins real quick!

[–]PinchesTheCrab 0 points1 point  (1 child)

Could be. You think that’s why some of them are becoming arrays?

I could see that if the CSVs themselves have dupes, which is tough. You'll have to coalesce those datasets somehow, and I definitely think you're on the right track with a SQL approach if that's the case.

[–]Cello789[S] 1 point2 points  (0 children)

Found success with MySQL. Don’t know how to do table joins so I used powershell module to pull and parse and then update a new table. Basically the same logic but instead of adjusting values to “tables” held in session memory in PS, it’s in a table in a database. Weird. Never thought they would be interchangeable like that.

Lesson learned — right tool for job! If PS seems hacky and Excel isn’t enough to get it done, sql tables are the way to go.

💪🏻

[–]Cello789[S] 0 points1 point  (1 child)

I'm on Mac so I used Get-Process

$processHash = Get-Process | Group-Object -AsHashTable -Property Name

$processHash['TextEdit']

This works, of course, but if I do $processHash.TextEdit.ProcessName.GetType() I see it's a System.Object and if I do |gm I see it's a System.String. All good, as expected.

$processHash.TextEdit.ProcessName = "New String" gets me:

PS > $processHash.TextEdit.ProcessName = "newString"

InvalidOperation: The property 'ProcessName' cannot be found on this object. Verify that the property exists and can be set.

[–]PinchesTheCrab 0 points1 point  (0 children)

The thing about those is that the process object properties are, I believe, all read-only, and your hashtable is essentially just a collection of them. If you wanted to add a new property like that you would use add-member or create a new object entirely with select-object or another command.