Trying to strip out unwanted text in PowerShell - 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

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

matching data across two arrays and combining with additional data in array

The Goal
See if $SP.ip is in $NLIP.IpRanges and if it is, add $NLIP.IpRanges and $NLIP.DisplayName to the $SP array or all into a new array.
The Arrays
Array 1 is $SP, it's a CSV import and has the properties 'name' and 'ip', it looks like this:
name: bob
ip: 1.9.8.2
Array 2 is $NLIP and has the relevant properties 'IpRanges' and 'DisplayName'. It's fetched from: $NLIP = Get-AzureADMSNamedLocationPolicy | where-object {$_.OdataType -eq "#microsoft.graph.ipNamedLocation"}, it looks like this:
DisplayName : Named Location 1
IpRanges : {class IpRange {
CidrAddress: 16.29.28.9/28 #fictitious CIDR
}
, class IpRange {
CidrAddress: 1.9.8.3/28 #fictitious CIDR
}
}
The Code / the problem
I'm using IPInRange.ps1 function from https://github.com/omniomi/PSMailTools to find if the IP is in the range. It works like so:
> IPInRange 1.9.8.2 1.9.8.3/28
True
I also worked out that $NLTP.IpRanges.split() | Where-Object ($_ -like "*/*"} can return all the ranges, but $NLIP | Where-Object {$_.IpRanges.split() -like "*/*"} doesn't. I would naturally use the second to keep the variable in the pipe to return the DisplayName. So I'm struggling on how to pull the individual ranges out in such a way that I can then add the 'IpRange' and 'DisplayName' to an array.
Also, maybe it's because I haven't worked out the above issue, but I'm struggling to think how I would iterate through both arrays and combine them into one. I know I would probably enter into a foreach ($item in $SP) and create a temporary array, but after that it's getting hazy.
The result
What I'm hoping to have in the end is:
name: bob
ip: 1.9.8.2
IpRange: 1.9.8.3/28 #fictitious CIDR
DisplayName: Named Location 1
thanks in advance.
I believe this will work for you if I understood the NLIP construct correctly.
We will loop through all the SP objects and see if we can find any NLIP that match the IP range using the IPinRange function you linked. We will then add the 2 properties you want to the SP object if matched and finally pass thru to the pipeline or you can append | export-csv -path YourPath to the end if you would like to send to a csv file
$SP | ForEach-Object {
$target = $_
$matched = $NLIP | ForEach-Object {
$item = $_
# Using where to single out matching range using IPinRange function
$_.IpRanges.Where({ IPInRange -IPAddress $target.ip -Range $_.CidrAddress }) |
ForEach-Object {
# for matching range output custom object containing the displayname and iprange
[PSCustomObject]#{
DisplayName = $item.DisplayName
IpRange = $_.CidrAddress
}
}
}
# add the 2 properties (DisplayName and IpRange) from the match to the original $SP
# object and then pass thru
$target | Add-Member -NotePropertyName DisplayName -NotePropertyValue $matched.DisplayName
$target | Add-Member -NotePropertyName IpRange -NotePropertyValue $matched.IpRange -PassThru
}
By the way, this is how I envisioned the NLIP objects and what I tested with
$NLIP = #(
[pscustomobject]#{
DisplayName = 'Named location 1'
IpRanges = #(
[pscustomobject]#{
CidrAddress = '16.29.28.9/28'
},
[pscustomobject]#{
CidrAddress = '1.9.8.3/28'
}
)
},
[pscustomobject]#{
DisplayName = 'Named location 2'
IpRanges = #(
[pscustomobject]#{
CidrAddress = '16.29.28.25/28'
},
[pscustomobject]#{
CidrAddress = '1.9.8.25/28'
}
)
}
)
Let's to shed some lights in the hazy darkness by first creating a Minimal, Reproducible Example (mcve):
$SP = ConvertFrom-Csv #'
IP, Name
1.9.8.2, BOB
10.10.10.10, Apple
16.29.28.27, Pear
16.30.29.28, Banana
'#
$NLIP = ConvertFrom-Csv #'
IPRange, SubNet
16.29.28.9/28, NetA
1.9.8.3/28, NetB
'#
To tackle this, you need two loops where the second loop is inside the first loop. For the outer loop you might use the ForEach-Object cmdlet which lets you stream each object and with that actually use less memory (assuming that you import the data from a file and eventually export it to a new file). Within the inner loop you might than cross link each IP address with the IPRange using the function you refer to and in case the condition is true create a new PSCustomObject:
$SP |ForEach-Object { # | Import-Csv .\SP.csv |ForEach-Object { ...
ForEach($SubNet in $NLIP) {
if (IPInRange $_.IP $SubNet.IPRange) {
[PSCustomObject]#{
IP = $_.IP
Name = $_.Name
IPRange = $SubNet.IPRange
SubNet = $SubNet.SubNet
}
}
}
} # | Export-Csv .\Output.csv
Which results in:
IP Name IPRange SubNet
-- ---- ------- ------
1.9.8.2 BOB 1.9.8.3/28 NetB
16.29.28.27 Pear 16.29.28.9/8 NetA
16.30.29.28 Banana 16.29.28.9/8 NetA
But as you are considering 3rd party scripts anyways, you might as well use this Join-Object script/Join-Object Module (see also: In Powershell, what's the best way to join two tables into one?):
$SP |Join $NLIP -Using { IPInRange $Left.IP $Right.IPRange }
Which gives the same results.

Powershell Cascading Foreach to export to Csv

I'm exporting my AAD users to CSV files, works fine with this code.
$allUsers = Get-AzureADUser -All $true
$users = $allUsers |
Select-Object -Property ObjectId,ObjectType,UserPrincipalName,DisplayName,AccountEnabled,AgeGroup,City,CompanyName,ConsentProvidedForMinor,Country,CreationType,Department,DirSyncEnabled,FacsimileTelephoneNumber,GivenName,IsCompromised,ImmutableId,JobTitle,LastDirSyncTime,LegalAgeGroupClassification,Mail,MailNickName,Mobile,OnPremisesSecurityIdentifier,PasswordPolicies,PhysicalDeliveryOfficeName,PostalCode,PreferredLanguage,RefreshTokensValidFromDateTime,ShowInAddressList,State,StreetAddress,Surname,TelephoneNumber,UsageLocation,UserState,UserStateChangedOn,UserType,DeletionTimestamp,AssignedLicenses,AssignedPlans,ProvisionedPlans |
ForEach-Object{
$_.DisplayName = $_.DisplayName -replace "\n", ' ' -replace '"', '`'
$_
}
$users | Export-Csv -Path $TempFileName -NoTypeInformation
$provisionedPlans = $users = $allUsers |
Select-Object -Property ObjectId,DisplayName,ProvisionedPlans
But, ProvisionedPlans comes out as a list, so I would like to export it for each entry in the list as 1 line.
This is a sample of the field
ProvisionedPlans : {class ProvisionedPlan {
CapabilityStatus: Enabled
ProvisioningStatus: Success
Service: MicrosoftCommunicationsOnline
}
, class ProvisionedPlan {
CapabilityStatus: Deleted
ProvisioningStatus: Success
Service: MicrosoftCommunicationsOnline
}
, class ProvisionedPlan {
CapabilityStatus: Deleted
ProvisioningStatus: Success
Service: MicrosoftCommunicationsOnline
}
, class ProvisionedPlan {
CapabilityStatus: Enabled
ProvisioningStatus: Success
Service: SharePoint
}
...}
So bottom line what I would like to see in the output would be
ObjectId,DisplayName,CapabilityStatus,ProvisioningStatus,Service
id1,User1,Enabled,Success,MicrosoftCommunicationsOnline
id1,User1,Deleted,Success,MicrosoftCommunicationsOnline
id1,User1,Deleted,Success,MicrosoftCommunicationsOnline
id1,User1,Enabled,Success,SharePoint
id2,User2,Enabled,Success,Whatever
So please feel free, i'm not a Powershell specialist.
Following your guidance of what you want as a bottom line please see the below adjustments to your script:
$allUsers = Get-AzureADUser -All $true
$Results = Foreach ($user in $allusers){
Foreach($Plan in $user.ProvisionedPlans){
$Plan | Select #{Name = 'ObjectId'; Expression = {$user.Objectid}},#{Name = 'DisplayName';Expression = {$user.DisplayName}},CapabilityStatus,ProvisioningStatus,Service
}
}
$Results | Export-Csv -Path $TempFileName -NoTypeInformation
I am not sure why you are replacing characters in the DisplayName but I have added this to the calculated properties in the Select Statemant.
I have also done this on the iteration of each provisioned plan as this seemed to be the easiest route.
I have removed the initial select with all of the properties as the properties you are interested in are being exported. (If you require all properties I would advise using Select * as it will pull the majority of properties in most cases and will look tidier in the code.)

Powershell CSV ordered Export with specific columns

i have following problem:
I have a query with Get-ADUser and have there specific fields to get (only test query)
$AllADUsers = get-aduser -Identity m.mustermann -Properties * | select mail, title, l, GivenName, Surname, company, department, mobile
Also i must add some static fields how language or comment in to the CSV, i take some variable for that:
$Language = "german"
$Comment = ""
$Link = ""
$gender = ""
$exportto = "C:\Temp\Export01.csv"
Than i want to export all entries in a ordered UTF8 CSV with Delimiter ":".
I do a foreach but always i get mistakes or not complete lists - i show you my code (but i have mistakes)
Foreach ($ADUser in $AllADUsers) {
$MailAddress = $ADUser.mail
$FullName = "$ADUser.GivenName" + "$ADUser.Surname"
$Title = $AdUser.Title
$Location = $ADUser.l
$Comment = $Comment
$Link = $Link
$MobilePhone = $ADUser.mobile
$Language = $Language
$Divison = $ADUser.Department
$GivenName = $ADUser.GivenName
$Surname = $ADUser.Surname
}
$ADUser | Export-CSV -Path $exportto -NoTypeInformation -Delimiter ":" -Encoding UTF8
Can you help me?
The export must be with the ordered list and without "" and just in this order.
Thank you.
Max
Your code doesn't work, mainly because you are creating a series of variables that in the end you do not use.
To output to CSV, you need to collect objects with properties, not single variables.
A very elegant way is to create those objects using [PsCustomObject] syntax, where at the same time the order in which you set the properties make up for the order in the final output file.
Using the colon as delimiter character is unusual and may get you into trouble when other applications need to read and understand this, but that is up to you.
Another thing is that you mainly seem to use LDAP attribute names, where PowerShell has most of them mapped to more friendly attribute names (like 'City' which maps to LDAP 'l').
Your code revised:
# Get-ADUSer already returns these properties by default:
# DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname, UserPrincipalName
# your hardcoded static variables:
$Language = "german"
$Comment = ""
$Link = ""
$gender = ""
$exportto = "C:\Temp\Export01.csv"
# these are the extra properties you want to fetch
$userprops = 'EmailAddress','Title','Company','City','Department','MobilePhone'
# if you want this for all users, remove "-Identity 'm.mustermann'" and use "-Filter *" instead
$result = Get-ADUser -Identity 'm.mustermann' -Properties $userprops | ForEach-Object {
# output an object to be collected in variable $result
# the order in which you put them also determines the field order in the CSV
[PsCustomObject]#{
MailAddress = $_.EmailAddress
FullName = $_.DisplayName
Title = $_.Title
Company = $_.Company
Location = $_.City
Comment = $Comment # static field
Link = $Link # static field
MobilePhone = $_.MobilePhone
Language = $Language # static field
Department = $_.Department
GivenName = $_.GivenName
Surname = $_.Surname
}
}
$result | Export-CSV -Path $exportto -NoTypeInformation -Delimiter ":" -Encoding UTF8
A note about Export-Csv:
Export-Csv will always quote everything, be it a header or data field.
Simply trying to remove all quotes in a CSV file is risking mis-alignment in the field order and rendering the csv as unusable.
With PowerShell version 7 you have the option to use parameter -UseQuotes AsNeeded, but for older versions, you may safely do this using my function ConvertTo-CsvNoQuotes
Since this code is all about getting AD information, I don't see why you would want to construct the FullName here. Just use the DisplayName property that is set in the AD as the code above does
There are some high-level issues in your code.
Incorrect assignment
In your code you should assign the values to specific properties of $AdUser. Something like:
foreach ($ADUser in $AllADUsers) {
$ADUser.FullName = "$($ADUser.GivenName) $($ADUser.Surname)"
$ADUser.Comment = $Comment
$ADUser.Link = $Link
}
No export
Currently you export only last entry, you should pipe $AllADUsers to Export-Csv:
$AllADUsers| Export-CSV -Path $exportto -NoTypeInformation -Delimiter ":" -Encoding UTF8
No use of Select-Object calculated properties
In general, you could have your code shorten to one-liner like:
$AllADUsers | Select-Object #{n="Comment";{$Comment}},#{n="FullName";e={"$($ADUser.GivenName) $($ADUser.Surname)"}} ... | Export-CSV ...
Read more about calculated properties here.

How to convert the data from a wildcard into a string

I am trying to trim down strings located inside of a .csv so that they only contain the data I need. An example string might be "\path\folder\folder\folder\folder Owner = John Doe".
I just want the John doe part.
Currently I'm importing a list of about 6000 of these strings some have the line some don't.
What I'm trying is:
$owners = Import-Csv .\Owners.csv
ForEach($owner in $owners){
if ($owner.Description -like "= *") {$owner.Description = "*"}
}
This doesn't seem to work. Is it possible to save the wildcard as a variable?
One method would be to use a Regular Expression rather than Wildcard globbing.
$owners = Import-Csv .\Owners.csv
$output = foreach ($owner in $owners) {
# The Description Property has an equal sign followed by a space
if ($owner.Description -like '*= *') {
# Use a regex then capture the the text after the equals and space, replace the property with that capture group.
$owner.Description = $owner.Description -replace '.*=\s(.*)$', '$1'
$owner
}
}
$output | Export-Csv .\Owners.update.csv -NoTypeInformation
You can do it with regex and match:
CSV:
Description
\path\folder\folder\folder\folder Owner = John Doe
\path\folder\folder\folder\folder Owner = Captain Black
Code:
$owners = Import-Csv .\Owners.csv
ForEach($owner in $owners){
$match = $owner.Description -match "= .*"
if ($match){
$owner.Description = $matches[0] -replace "= ", ""
}
echo $owner
}
Output:
Description
-----------
John Doe
Captain Black