How Can I Modify a Variable, Collection of Objects In Script

My Friends,
I have an excerpt here from a script that works very well.
Now I would like to change it but I cannot figure out how to improve it.
After getting some users into a variable, this is what is in the variable:

SamAccountName ExpirDate SamX


Joe.Reynolds 02/20/2022 Joe.ReynoldsX
Mac.Channell 02/20/2022 Mac.ChannellX
George.Frimpong 10/28/2022 George.FrimpongX
Hans.Kunbargi 11/26/2022 Hans.KunbargiX
Elmer.Mark.Martin 12/10/2022 Elmer.Mark.Martinx
AFrazier 01/27/2023 AFrazierX
Susan.Soremekun 02/09/2023 Susan.SoremekunX
Bill.Cunningham 02/09/2023 Bill.CunninghamX

And here is the excerpt of the script that works just fine:

$getUsers = @{
    Filter     = "*"
        SearchBase = $SEARCHBASE 
    
    Properties = "SamAccountName","AccountExpirationDate","description" , "DisplayName" , "extensionattribute3"
}

$Users = Get-ADUser @getUsers 
# store queried objects in $Expired
$Expired = 
$Users |
where-object { 
    $_.AccountExpirationDate -notlike '' -and 
    $_.AccountExpirationDate -lt $Tomorrow -and 
    $_.extensionattribute3  -match $ParticularUSMCAgency
} |

  
select-object  SamAccountName,  @{n="ExpirDate";e={get-date $_.AccountExpirationDate -Format "MM/dd/yyyy"}},  @{n="SamX";e={$_.SamAccountName + "X"}}  |
Sort-object   @{Expression = {$_."Expirdate" -as [datetime]}},  Samx

# display $Expired in table format
$Expired | Format-table -AutoSize 
# output $Expired to CSV
$Expired |
Export-Csv $TimeStampx -NoTypeInformation 

Now I want to refine the sort by testing each SamAccountName and putting the results in SamX - Then Sort by SamX

So I thought I would Isolate the Varible and loop through all the objects and change SamX. - Then Sort by SamX

I don’t know how to isolate the variable with all the objects and change all the values of SamX and write a new Varible.
Testing the SamAccountName is long, but all it does is separate SamAccountNames like this:
First.Last
First.Middle.Last
Last

Here it is:

{ 
  
  $Split1 = $_.SamAccountName.Split(".")[0] 
  $Split2 = $_.SamAccountName.Split(".")[1]
  $Split3 = $_.SamAccountName.Split(".")[2] 
       
  $Periodcount = 0 
  $PeriodCount = ($_.SamAccountName.ToCharArray() | Where-Object {$_ -eq '.'} | Measure-Object).Count
          
  If ($PeriodCount -eq 0)
       {
           $_.SamX = "Z." + $_.SamAccountName
       }
  
       ElseIF 
            ($PeriodCount -eq 1)
               {
                $_.Samx =  $_.SamAccountName
               } 
            
            ElseIF
                 ($PeriodCount -eq 2)
                  {
                    $_.Samx = $Split1 + "."  + $Split3
                  }
  }  

Surely this can be done. How can a variable with a collection of objects be isolated, each one be updated and written to and passed on to Sort ? I do not care if a new Variable/Collection must be created, but then, it needs to be Sorted.

Thanks in advance for your help on this. I have spent days on this problem.

I read your question twice and tried to understand what you want to do looking at your code but I’m still unsure what you want to achieve.

If I got it right you just need 1 or 2 more calculated properties to get the result you want.

Could you share an example what result you’d expect from the given input data you shared in your question? Please format it as code as well. So we can copy it and play with it.

Olaf, Thank you for your reply.
I will go into more detail.

Our SamAccountNames have one of three formats and look like one of these:
Joe.Jones (First.Last) - With one period.
JJones (Could be anything) - With no period.
Joe.T.Jones (First.Middle.Last) - With two periods.

And the script excerpt works, the output to the console and export file looks like this:

SamAccountName      ExpirDate  SamX                
--------------      ---------  ----                
AFrazier            02/20/2022 AFrazier    
Joe.Able            02/20/2022 Joe.Able
Mac.Brown           02/20/2022 Mac.Brown    
George.Central      02/20/2022 George.Central
Hans.M.Zucker       02/20/2022 Hans.M.Zucker    
Joe.Mann            02/20/2022 Joe.Mann
Dean.Martin         02/20/2022 Dean.Martin    
Susan.Adams         09/30/2022 Susan.Adams 
Bill.Adrian         09/30/2022 Bill.Adrian    
Barbra.Apple        09/30/2022 Barbra.Apple     
Zack.Avon           09/30/2022 Zack.Avon    
Bob.B.Zing          09/30/2022 Bob.B.Zing       
Paul.Bee            09/30/2022 Paul.Bee  

But the sort is not precisely correct.
AFrazier sorts to the top of the list as if there were no last name, so it sorts unpredictably.
Hans.M.Zucker sorts as if the last name were M. But as you can see it is Zucker.

So a Correct Sort would be:

SamAccountName      ExpirDate  SamX                
--------------      ---------  ----                
Joe.Able            02/20/2022 Joe.Able
AFrazier            02/20/2022 AFrazier    
Mac.Brown           02/20/2022 Mac.Brown    
George.Central      02/20/2022 George.Central
Joe.Mann            02/20/2022 Joe.Mann
Dean.Martin         02/20/2022 Dean.Martin  
Hans.M.Zucker       02/20/2022 Hans.M.Zucker  
Susan.Adams         09/30/2022 Susan.Adams 
Bill.Adrian         09/30/2022 Bill.Adrian    
Barbra.Apple        09/30/2022 Barbra.Apple     
Zack.Avon           09/30/2022 Zack.Avon    
Bob.B.Zing          09/30/2022 Bob.B.Zing       
Paul.Bee            09/30/2022 Paul.Bee  

Because Able comes before AFrazier -Alphabetically.
Because Hans.M.Zucker goes to the bottom of the 02/20/2022 date.

(And the “X” at the end of the names was just for show, so I took that away, in case you noticed)

So I thought if I added “SamX” Property, I could change the values of SamAccountName and sort on “SamX”

Now if all that could be done within the expression somehow, great!

I was sure that I was going to have to loop through the $Expired variable with a ForEach-Object, while changing the SamX to things like this:
Joe.Able (One Period)
Z.AFrazier (AFrazier had no periods but this is changed to one period. The “Z” is stuck in there just as
a placeholder).
Hans.Zucker (This one had two periods but is chnaged to one period).

…Because the above three SamAccountNames would sort accurately.

I cannot find any example of how to loop through all the objects of a collection, ($Expired variable) change something, then write to it to make it permanent. That would solve this for me.

But if you know of a better way, Great!

You’re making more working for yourself. Grab the Surname in your initial query, and sort on surname.

2 Likes

But even if you insist and want to use the last part of the splitted sAMAccountName it would be just a simple calcualted property …

$InputCSV = @'
SamAccountName,ExpirDate,SamX            
AFrazier,02/20/2022,AFrazier    
Joe.Able,02/20/2022,Joe.Able
Mac.Brown,02/20/2022,Mac.Brown    
George.Central,02/20/2022,George.Central
Hans.M.Zucker,02/20/2022,Hans.M.Zucker    
Joe.Mann,02/20/2022,Joe.Mann
Dean.Martin,02/20/2022,Dean.Martin    
Susan.Adams,09/30/2022,Susan.Adams 
Bill.Adrian,09/30/2022,Bill.Adrian    
Barbra.Apple,09/30/2022,Barbra.Apple     
Zack.Avon,09/30/2022,Zack.Avon    
Bob.B.Zing,09/30/2022,Bob.B.Zing       
Paul.Bee,09/30/2022,Paul.Bee
'@ |
    ConvertFrom-Csv |
        Select-Object -Property *,
        @{
            Name = 'SamY'
            Expression = {
                ($_.SamAccountName -split '\.')[-1]
            }
        } |
            Sort-Object -Property ExpirDate,SamY
$InputCSV

The output would be this:


SamAccountName ExpirDate  SamX           SamY    
-------------- ---------  -----          ----
Joe.Able       02/20/2022 Joe.Able       Able
AFrazier       02/20/2022 AFrazier       AFrazier
Mac.Brown      02/20/2022 Mac.Brown      Brown
George.Central 02/20/2022 George.Central Central
Joe.Mann       02/20/2022 Joe.Mann       Mann
Dean.Martin    02/20/2022 Dean.Martin    Martin
Hans.M.Zucker  02/20/2022 Hans.M.Zucker  Zucker
Susan.Adams    09/30/2022 Susan.Adams    Adams
Bill.Adrian    09/30/2022 Bill.Adrian    Adrian
Barbra.Apple   09/30/2022 Barbra.Apple   Apple
Zack.Avon      09/30/2022 Zack.Avon      Avon
Paul.Bee       09/30/2022 Paul.Bee       Bee
Bob.B.Zing     09/30/2022 Bob.B.Zing     Zing

Matt, Thank you for your reply. Yes, I can make it work by grabbing the Surname and sorting on that. But sometimes we get .CSV files with hundreds of names and without a Surname. So I wanted to get this logic working.

Olaf, Thank you very much. I will go with this. Also, you showed me a good way to test the data!
And if you know of a very simple example of a script where a $Variable , a Collection of Objects, can be modified and changed and put back into the pipeline to export, that would be helpful.
I hope I am using the correct terminology. I have several books and I am trying to study but I am not a full time PowerShell Programmer. I am just a lowly Active Directory Administrator. I am trying to assemble many scripts for my co-workers to help with our Bulk Requests, before I retire in a couple of years.

But that’s actually what this code snippet does. You get the data from the CSV file, you modify it with Select-Object and you output it with Sort-Object. Instead of calculated properties with Select-Object you can use loops with

or

And you can do literally whatever you like. :man_shrugging:t4:

Please always read the help for the cmdlets you’re abouit to use completely including the examples to learn how to use them.

And you may keep in mind … in the vast majority of the cases you’re not the very first one with a given task. Search for code examples online and steal or borrow whatever you find and adapt it to your particular needs - no need to re-invent the wheel again and again and again … :wink:

Thank you, Olaf,

Select-Object -Property *,
        @{
            Name = 'SamY'
            Expression = {
                ($_.SamAccountName -split '\.')[-1]

You say the Select statement updates ALL the ‘SamY’ in the collection ??
The Select statement updates ‘SamY’ from $_.SamAccountName …yes.

I will go try it. It is too good to be true.

And so If I want to refer to the Collection again, I would write:

$InputCSV

I had time to test and play with it.
The lightbulb came on
I thought the Select Statement changed the Collection one by one as the sort was sorting.
But I see now, the Select Statement makes the changes to the whole collection before the sort begins.
I was missing a very important concept.
This is going to make my PowerShell life a lot easier!

In this example is not “updating the collection” it is outputting an updated copy of each entry. The original values remain the same. If you want to update it, you need to reassign the desired properties with their new values. You can choose to emit the changed object at that time as well or just use the variable again afterwards that contains the updated values.

Krzydoug, thank you for explaining. I get the ideas you explained. But I need to see the syntax too.
Would these Ideas and syntax be in explanations about the SELECT statement ???
I must find where I can read and learn about these phrases you used.
update
reassign the desired properties with their new values
choose to emit the changed object or

It is beyond the focus of a forum like this to teach you basic PowerShell. You may do a big step back and start with learning the very fundamentals of PowerShell first. That will save you from a lot of wasted time and frustrations. And it will enable you to understand the help you get in forums like this.

Here you have some places you can start with:

Don’t worry that most of this topics are not about the current version of PowerShell. The basics are still the same and these are great sources for beginners. :+1:t4:

Thanks Olaf I will look and read.