How do I get unique email address and retain other fields - powershell

$AllMeetUsers = $null
[array]$AllMeetUsers = gam report meet user all | ConvertFrom-Csv | Select-Object 'actor.email','id.time'
[array]$result = $AllMeetUsers | ForEach-Object {
$Email = $_.'actor.email'
If (-not([string]::IsNullOrWhiteSpace($Email))) {
$IDTime = $_.'id.time'
$FormatIDTime = Get-date($IDTime) -Format("MM-dd-yy")
[PSCustomObject]#{
Email = $Email
Time = $FormatIDTime
}
}
}
Makes an output table like the following
06-07-21 <Email>
09-29-21 <Email>
06-15-21 <Email>
07-12-21 <Email>
07-20-21 <Email>
07-14-21 <Email>
I would like to remove the full duplicate email address line.
but this line is not working
[array]$result = $result | Sort-Object -Property email | Select-Object time,email -Unique
and
[array]$result = $result | Sort-Object -Property email | Select-Object email -Unique
removes the time field while giving me unique email addresses.
How do I accomplish this?

Use a Dictionnary #{} instead of an array.
$results = #{}
if (-not $results.ContainsKey($Email)) {
$results.Add($Email, [PSCustomObject]#{
Email = $Email
Time = $FormatIDTime
})
}
$results.Values

#MathiasR.Jessen made a good point in the comments and I think my code probably needs a conceptual change. However with the answer I accepted from #Hazrelle above this is how the code came out.
$AllMeetUsers = $null
[array]$AllMeetUsers = gam report meet user all | ConvertFrom-Csv | Select-Object 'actor.email','id.time'
$results = #{}
$AllMeetUsers | ForEach-Object {
$Email = $_.'actor.email'
if (-not $results.ContainsKey($Email) -and -not([string]::IsNullOrWhiteSpace($Email))) {
$IDTime = $_.'id.time'
$FormatIDTime = Get-date($IDTime) -Format("MM-dd-yy")
$results.Add($Email, [PSCustomObject]#{
Email = $Email
Time = $FormatIDTime
})
}
}
$results.values
For more information.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-7.1

Related

"Sort-Object email -Unique" and if there is duplicate leave the one with the higher role and remove the rest

Sort-Object email -Unique if there is duplicate leave the one with the higher role and remove the rest.
I think I am going to have to do something like this instead of the sort-object
$results = $results | foreach-object {
If ($results.email -contains $_.email) {
Foreach ($role in $results) {
<Find my place in the array then replace with the highest role using a switch>
}
}
}
$results = $results Sort-Object email -Unique
This seems like I am being too complicated and the results don't work out.
I am here for advice on how to do this. I will expand the middle section of my code if there is not a better way to do this ?
If your goal is to get the highest-roled email where emails can be duplicated, you want to ensure you sort by email, then role, and select out the unique emails:
$results |
Sort-Object -Property role, email |
Select-Object -Property email -Unique
This assumes your object shape is:
[pscustomobject]#{
email = 'string'
role = 'string'
type = 'string'
}
For me the answer worked out like the following. I needed to keep the other properties and always keep any duplicate with the highest role.
I could discard any duplicate less then the higher role.
$map = #{ ManagerORcontentManager = 3; writer = 2; commenter = 1 }
$results | Sort-Object email,{ $map[$_.role] } | Group-Object email | ForEach-Object { $_.Group[0] } | Sort-Object email | Export-Csv C:\Reports\$sharedrivename.csv -NoTypeInformation

Format-Table not taking effect (Exchange - powershell)

first of all sorry if my english is not the best. but ill try to explain my issue with as much detail as i can
Im having an issue where i cant get Format-Table to effect the output i give it.
below is the part im having issues with atm.
cls
$TotalSize = $($mailboxes. #{name = ”TotalItemSize (GB)”; expression = { [math]::Round((($_.TotalItemSize.Value.ToString()).Split(“(“)[1].Split(” “)[0].Replace(“,”, ””) / 1GB), 2) } });
$UserN = $($mailboxes.DisplayName)
$itemCount = $($mailboxes.ItemCount)
$LastLogonTime = $($mailboxes.ItemCount)
$allMailboxinfo = #(
#lager dataen som skal inn i et objekt
#{Username= $UserN; ItemCount = $itemCount; LastLogonTime = $($mailboxes.ItemCount); Size = $TotalSize}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Table = $allMailboxinfo | Format-Table | Out-String
$Table
the output of this gives me what almost looks like json syntax below each title of the table.
Username LastLogonTime ItemCount Size
-------- ------------- --------- ----
{username1, username2,username3,userna...} {$null, $null, $null, $null...} {$null, $null, $null, $null...} {$null, $null, $null, $null...}
running the commands by themselves seem to work tho. like $mailboxes.DisplayName gives the exact data i want for displayname. even in table-format.
the reason im making the table this way instead of just using select-object, is because im going to merge a few tables later. using the logic from the script below.
cls
$someData = #(
#{Name = "Bill"; email = "email#domain.com"; phone = "12345678"; id = "043546" }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$moreData = #(
#{Name = "Bill"; company = "company 04"}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Merge = #(
#plots the data into a new table
#{Name = $($someData.Name); e_mail = $($someData.email); phone = $($someData.phone); id = $($someData.id); merged = $($moreData.company) }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
#formatting table
$Table = $Merge | Format-Table | Out-String
#print table
$Table
if you are wondering what im doing with this.
My goal, all in all. is a table with using the info from Exchange;
DisplayName, TotalItemSize(GB), ItemCount, LastLogonTime, E-mail adress, archive + Maxquoata, Quoata for mailbox.
You're creating a single object where each property holds an array of property values from the original array of mailbox objects.
Instead, create 1 new object per mailbox:
# construct output objects with Select-Object
$allMailBoxInfo = $mailboxes |Select #{Name='Username';Expression='DisplayName'},ItemCount,#{Name='LastLogonTime';Expression='ItemCount'},#{Name='Size';Expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) }}
# format table
$Table = $allMailBoxInfo | Format-Table | Out-String
# print table
$Table

PowerShell Compare-Object to produce change report file

I've looked at numerous examples and have made headway in producing a change report. But, I'm stuck in one area. Here's the scenario...
File 1 CSV file sample data
ID,Name,Location,Gender
1,Peter,USA,Male
2,Paul,UK,Male
3,Mary,PI,Female
File 2 CSV file sample data (No ID column)
Name,Location,Gender
Peter,USA,Female
Paul,UK,Male
Mary,USA,Female
Tom,PI,Female
Barry,CAN,Male
File 2 has changes and additions, i.e. Peter turned female, Mary moved to the US, both Tom and Barry are the new people. Change report output file contain what the changes are. Problem is, I can't figure out how to get the ID for both Peter and Mary from File 1, into my Change Report. ID is always empty Here's my code...(I hope someone can shed some light. Thanks in advance.)
$MyCSVFields = #('Name','Location','Gender')
$CompareResults = Compare-Object $RefObj $DffObj -Property $MyCSVFields -IncludeEqual
$NewOrChangedData = #()
Foreach($Row in $CompareResults)
{
if( $Row.SideIndicator -eq "=>" )
{
$TempObject = [pscustomobject][ordered] #{
ID = $Row.ID
Name = $Row.Name
Location = $Row.Location
Gender = $Row.Gender
#Sanity check "Compare Indicator" = $Row.SideIndicator
}
$NewOrChangedData += $TempObject
}
}
Thanks to Theo for providing an understanding of how to use the Where-Object. Here is the updated code that keeps it simple for beginners and still works for us.
Foreach($Row in $CompareResults)
{
if( $Row.SideIndicator -eq "=>" )
{
$myOrgID = $RefObj | Where-Object Name -eq $Row.Name
$TempObject = [pscustomobject][ordered] #{
ID = $myOrgID.ID
Name = $Row.Name
Location = $Row.Location
Gender = $Row.Gender
#Sanity check "Compare Indicator" = $Row.SideIndicator
}
$NewOrChangedData += $TempObject
}
}
I'm also alway struggling with Compare-Object, so I hope there is a better answer than this:
$RefObj = #'
ID,Name,Location,Gender
1,Peter,USA,Male
2,Paul,UK,Male
3,Mary,PI,Female
'# | ConvertFrom-Csv
$DffObj = #'
Name,Location,Gender
Peter,USA,Female
Paul,UK,Male
Mary,USA,Female
Tom,PI,Female
Barry,CAN,Male
'# | ConvertFrom-Csv
$MyCSVFields = #('Name','Location','Gender')
$CompareResults = Compare-Object $RefObj $DffObj -Property $MyCSVFields -PassThru
$NewOrChangedData = $CompareResults | Where-Object { $_.SideIndicator -eq '=>' } | ForEach-Object {
$name = $_.Name
[PsCustomObject]#{
ID = ($RefObj | Where-Object { $_.Name -eq $name }).ID
Name = $name
Location = $_.Location
Gender = $_.Gender
#Sanity check "Compare Indicator" = $_.SideIndicator
}
}
$NewOrChangedData
Result:
ID Name Location Gender
-- ---- -------- ------
1 Peter USA Female
3 Mary USA Female
Tom PI Female
Barry CAN Male

Trying to strip out unwanted text in PowerShell

I'm making a PowerShell script which queries our Office 365 tenants and exports certain information into a .csv file. The two fields I'm struggling with are the users default email address and their assigned subscriptions. I can get the data, but not sure how to manipulate it and make it look more presentable.
Get-MSOLUser -All | select firstname,lastname,displayname,islicensed,{$_.Licenses.AccountSkuId},{$_.proxyaddresses -cmatch '^SMTP\:.*'},userprincipalname | sort FirstName | Export-Csv $directory\$tenantname\Export.csv -NoTypeInformation
1) I've managed to get their primary email address as lower cased smtp addresses will always be aliases, but how do I strip out the "SMTP:" part?
2) Instead of "reseller-account:SKU Part Number" I was hoping to shorten this to the names we usually refer them as! Such as:
"E3" instead of "reseller-account:ENTERPRISEPACK"
"E5" instead of "reseller-account:ENTERPRISEPREMIUM"
"ProjectPro" instead of "reseller-account:PROJECTPROFESSIONAL"
"Visio" instead of "reseller-account:VISIOCLIENT"
Two questions really but very similar! Hope you can help.
To Achieve that you can use Calculated Properties along with a small function to convert the SkuId's to a Friendly name and using -replace to remove the SMTP part , I've Created for you a simple function for the conversion, you can add other products just like i did:
The Microsoft Product Name/SKU's list can be found in this link
function Convert-SkuIdToFriendlyName
{
Param(
[string]$SkuId
)
switch ($SkuId)
{
{$SkuId -match "ENTERPRISEPACK"} {return "OFFICE 365 ENTERPRISE E3"}
{$SkuId -match "ENTERPRISEPREMIUM"} {return "OFFICE 365 ENTERPRISE E5"}
default { 'Unknown' }
}
}
Then use a Calculated properties to replace the 'SMTP' part and convert the SkuId:
Get-MSOLUser -All |
Select firstname,lastname,displayname,islicensed,
#{N="License";E={Convert-SkuIdToFriendlyName $_.Licenses.AccountSkuId}},
#{N="Email";E={$_.proxyaddresses -cmatch '^SMTP\:.*' -replace 'SMTP\:'}},userprincipalname |
Sort FirstName
You can use a hashtable as a lookup table for the wanted translations like so:
# create a hash with the desired translations.
# below are just the examples from your question. You need to fill in the rest..
$Licenses = #{
"ENTERPRISEPACK" = "E3"
"ENTERPRISEPREMIUM" = "E5"
"PROJECTPROFESSIONAL" = "ProjectPro"
"VISIOCLIENT" = "Visio"
}
Get-MSOLUser -All |
Select-Object firstname,lastname,displayname,islicensed,userprincipalname,
#{ Name = 'License'; Expression = { $Licenses[$(($_.Licenses.AccountSkuId) -replace '^.+:', '')] }},
#{ Name = 'PrimaryEmailAddress'; Expression = { ($_.proxyaddresses -cmatch '^SMTP\:.*') -replace "SMTP:", "" }} |
Sort-Object FirstName | Export-Csv $directory\$tenantname\Export.csv -NoTypeInformation
In order to get all licenses a user can have listed, the code could be extended to:
# create a hash with the desired translations for the license plans.
# below are just the examples from your question. You need to fill in the rest..
$Licenses = #{
"ENTERPRISEPACK" = "E3"
"ENTERPRISEPREMIUM" = "E5"
"PROJECTPROFESSIONAL" = "ProjectPro"
"VISIOCLIENT" = "Visio"
}
# this calculated property returns all (translated) licenses per user as comma delimited string
$userLicenses = #{
Name = 'Licenses'
Expression = {
$result = #()
foreach($lic in $_.Licenses) {
$result += $Licenses[$(($lic.AccountSkuId) -replace '^.+:', '')]
}
$result -join ', '
}
}
Get-MSOLUser -All |
Select-Object firstname,lastname,displayname,islicensed,userprincipalname,$userLicenses,
#{ Name = 'PrimaryEmailAddress'; Expression = { ($_.proxyaddresses -cmatch '^SMTP\:.*') -replace "SMTP:", "" }} |
Sort-Object FirstName | Export-Csv $directory\$tenantname\Export.csv -NoTypeInformation

Windows PowerShell Filtering by data range

I have a powershell script to get deactivated accounts from our SSO app but would like to filter it down to only those that were deactivated more than 90 days ago.
I then have another script to take the results and deletes those users from the SSO app.
Can you tell me how to add a filter to the below script to exclude results were the StatusChanged date is greater than 90 days from current date.
$users = oktaListDeprovisionedUsers -oOrg PREV
$toexport = New-Object System.Collections.ArrayList
Foreach ($u in $users)
{
$line = #{
status = $u.status
employeeid = $u.profile.employeeNumber
firstName = $u.profile.firstName
lastName = $u.profile.lastName
email = $u.profile.email
department = $u.profile.department
supervisor = $u.profile.manager
created = $u.created
lastUpdated = $u.lastUpdated
login = $u.profile.login
title = $u.profile.title
GroupName = $u.profile.Group_Name
Organization = $u.profile.organization
Location = $u.profile.workday_location
User_type = $u.profile.userType
StatusChanged = $u.StatusChanged
}
$obj = New-Object psobject -Property $line
$_c = $toexport.Add($obj)
}
#Path for utility will have to be changed to a more generic location.
$toexport | Select-Object "login", "StatusChanged", "employeeid", "firstName","lastName", "email", "title","supervisor","department","Organization","Location", "GroupName" | >Export-Csv -Path "C:\OktaExport\user-list.csv" -NoTypeInformation
You can filter the $users object by a Where-Object
$users = $users | Where-Object{((Get-Date) - $_.StatusChanged).TotalDays -gt 90}
Add this to the 2nd line of your script.