Export specific output from an array - arrays

I have the below code that displays all groups that users which have a SamAccountName starting with 'X' are members of, and that exports the output in a CSV file:
Get-ADUser -Filter {SamAccountName -like "X*"} -Properties memberof |
Select name, #{N="MemberOf";E={$_.memberof -join ";"}} |
Export-Csv "C:\Users\chicken\Desktop\export_powershell\extract_test.csv"
The code works fine, but I have some problems with the output. Indeed it returns all groups that users are members of, but I only need to export one specific group (the group that starts by 'G-ORG') for each user.
How can I filter the output to only have groups that start with 'G-ORG' for each user?
Example output:
User1,"CN=YVD_Access; CN=Proxy_ACCESS_GD; CN=RVH_BIS HSBC; CN=G-ORG-MULTI-CANAL-APPLICATION; CN=......"
USER2,"CN=G-ORG-ARCHI; CN=Proj_X; CN=Proj_X; CN=X_REF; CN=......"
USER3,"CN=HAV_SH; CN=G-ORG-IT; CN=EPH; CN=......"
USER...
Required output:
User1,"CN=G-ORG-MULTI-CANAL-APPLICATION"
USER2,"CN=G-ORG-ARCHI"
USER3,"CN=G-ORG-IT;"
USER...

Simply filter the member list for distinguished names that begin with CN=G-ORG- instead of joining all of them:
$_.memberof | Where-Object { $_.StartsWith('CN=G-ORG-') }
If there could be more than one match you can then either join the results
($_.memberof | Where-Object {...}) -join ';'
or select one of them (e.g. the first)
$_.memberof | Where-Object {...} | Select-Object -First 1
depending on the requirements.

Related

How to use Get-AzureADUser -Filter Parameter with Objects Saved in a Variable

I'm having difficulty using the Filter parameter to query AAD or MSoL Online using the built in filter paramater in Get-AzureADUser or Get-MsolUser or Get-User
I've imported a csv file and saved the file into an array $users with the structure:
EmailAddress, UserPrincipalName, ObjectID
Only the EmailAddress column has values. I've tried constructing a foreach loop to go through the array and add values to the other columns.
$users = import-csv .\users.csv
foreach($user in $users){
Get-AzureADUser -Filter "PrimarySMTPAddress eq '$($user.EmailAddress)'" | Select-Object UserPrincipalName, ObjectID
}
The trouble is that the command nested in the loop does not return any values.
Get-AzureADUser -Filter "PrimarySMTPAddress eq 'user#contoso.com'" | Select-Object ObjectID, UserPrincipalName
Does work. It looks like the Filter command doesn't ever read the values contained in the array $users
Questions:
What's the correct syntax for the Filter parameter so I can loop through the array?
How do I save the values obtained from the Get-AzureADUser cmdlet into my constructed array so that I cannot export it to a file?
Older issue, but you gave me a clue that helped me resolve the same issue right now. Need to backtick the single quotes:
Get-AzureADUser -Filter "mail eq '$($user.EmailAddress)'"

How does one add output from a cmdlet to an array?

I am trying to determine if specific Windows hotfixes are installed on our Windows servers. I am quite new to PowerShell scripting and this is what I have so far:
$servers = Get-ADComputer -Filter {(OperatingSystem -like "Windows Server 2019*") -and (enabled -ne $false)} -Property *
$result = #()
ForEach ($item in $servers) {
$testhotfix = Get-HotFix -Id KB4534310,KB4534314,KB4534283,KB4534288,KB4534297,KB4534309,KB4534271,KB4534273 -ComputerName $item.Name | `
select $item.Name,$item.CanonicalName,$item.OperatingSystem
$result += $testhotfix
}
$result | Export-Csv -Path C:\Users\user1\Desktop\Servers.csv -NoTypeInformation
The CSV file that is created includes one line with the information I'm looking for, followed by several lines of commas, like so:
Script Output
"SERVER1","somedomain.com/Servers/Non-Prod/New_Server_Staging/SERVER1","Windows Server 2019 Standard"
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
,,
We have several servers with at least one of the hotfixes installed. How do I add each server to the $result array?
Thank you
Generally speaking:
select $item.Name,$item.CanonicalName,$item.OperatingSystem
should be:
select Name, CanonicalName, OperatingSystem
That is, you need to pass the property names (e.g., Name), not the current input object's property values (e.g., $item.Name) to select (the Select-Objectcmdlet).
The net effect is that Select-Object creates custom objects whose properties are (mistakenly) named for the property values and themselves have no value, given that the input objects have no such properties.
This explains the output you saw.
However, the bigger problem is that even that won't work, given that the property names relate to the $item object, not to the objects output by Get-HotFix, which are the ones select operates on.
As it turns out, what you really need is to use the Get-HotFix call as a conditional, so as to only write a CSV row for the computer at hand if at least one of the specified hotfixes is installed:
$hotfixIds = 'KB4534310', 'KB4534314', 'KB4534283', 'KB4534288', 'KB4534297', 'KB4534309', 'KB4534271', 'KB4534273'
if (0 -ne (Get-HotFix -ErrorAction SilentlyContinue -Id $hotfixIds -ComputerName $item.Name).Count) {
$result += $item | select Name, CanonicalName, OperatingSystem
}
Note:
Note how it is now $item (the computer at hand) that is piped to select, to ensure that its properties are extracted (in the form of a custom object with these properties).
You could omit 0 -eq altogether and rely on PowerShell's implicit to-Boolean conversion, where any nonzero number evaluates to $true (see the bottom section of this answer for a summary of all rules.
If instead you want to test for all of the specified hotfixes being installed, replace 0 -ne with $hotfixIds.Count -eq.
-ErrorAction SilentlyContinue silences the errors from computers where none of the specified hotfixes are installed; you could examine the automatic $Error collection afterwards, or use -ErrorVariable err to collect all command-specific errors in variable $err.
Also, your overall command can be greatly streamlined - see the bottom section.
A solution for a different scenario, that may be of interest as well:
If you wanted to combine properties from the Get-HotFix output objects with properties from the $item objects (representing the computer at hand):
The following command:
selects all properties from the Get-HotFix output objects (-Property *)
adds the properties of interest from the current $item, using calculated properties
# Additional 'KB...' values omitted for brevity.
Get-HotFix -Id KB4534310, KB4534314 -ComputerName $item.Name |
Select-Object -Exclude Name -Property *,
#{ n = 'Name'; e = { $item.Name } },
#{ n = 'CanonicalName'; e = { $item.CanonicalName } },
#{ n = 'OperatingSystem'; e = { $item.OperatingSystem } }
Note that -Exclude Name excludes the Name property from the input objects (Get-HotFix output objects that have such a property, but it is empty), so that Name can be added as a property containing the computer name.
As for what you tried:
Aside from the Select-Object property-name problem mentioned above, your major problem was that you expected a pipeline segment as a conditional, which is not how pipelines work:
Get-HotFix ... | select ...
The above simply sends Get-HotFix's output objects to select (Select-Object), which then unconditionally processes them (and, as stated, looks for properties with the given names on these objects).
Now, if Get-HotFix produced no output, then conditional logic applies implicitly: the select command would then simply not be invoked.
Conversely, if Get-HotFix produces multiple outputs, select would be invoked on each.
That is, if we had naively tried to correct your command from:
Get-HotFix ... | select ...
to:
Get-HotFix ... | ForEach-Object { $item | select ... }
you would have potentially created multiple output objects per computer, namely whenever a given computer happens to have more than one among the given hotfixes installed.
A streamlined version of your (corrected) command:
Your command can be streamlined to use a single pipeline only, without the need for aux. variables:
Get-ADComputer -Filter '(OperatingSystem -like "Windows Server 2019*") -and (enabled -ne $false)' -Property * |
ForEach-Object {
if (0 -ne (Get-HotFix -ErrorAction SilentlyContinue -ComputerName $item.Name -Id KB4534310,KB4534314,KB4534283,KB4534288,KB4534297,KB4534309,KB4534271,KB4534273).Count) {
$item | select Name, CanonicalName, OperatingSystem
}
} | Export-Csv -Path C:\Users\user1\Desktop\Servers.csv -NoTypeInformation
Note:
If you end a line with |, you do not need a trailing ` to signal line continuation.
PowerShell [Core] v7.0+ now also allows placing | at the start of the very next line.
A single-quoted string ('...') is used instead of a script block ({ ... }) to pass the -Filter argument, because tt's best to avoid the use of script blocks ({ ... }) as -Filter arguments.
The output custom object instances created with $item | select Name, CanonicalName, OperatingSystem are sent directly to the pipeline.
I would use a PSCustomObject.
$array = foreach($item in $obj)
{
[PSCustomObject]#{
Name = $item.Name
CanonicalName = $item.CanonicalName
OS = $item.OperatingSystem
}
}

Get the latest date

I have an array that I call $data.
I want to get the earliest dates from the $data array for each host in my csv file. The user will input a host and it will find the earliest date it was modified.
Hostname LastModified
HD 9/8/2012
LOG 9/15/2004
NETMAN 12/25/2004
NETMAN 5/5/2015
LOG 1/4/2013
LOG 6/6/2011
So if they input LOG, I want it to give me the earliest date.
Created on 9/15/2004
Code:
$data= import-csv ".\Earliest Date Template.csv"
$Hostname=Read-Host "Please enter Host Name"
$data | Foreach-Object {$_."Last Modified" = [DateTime]$_."Last Modified"; $_} | Group-Object Hostname| Foreach-Object {$_.Group | Sort-Object LastModified | Select-Object -First 1}
Grouping them does seem like the way to go but you don't need to do that. Just sort the entire list by date then select the last option from the list (that matches the host name you are looking for).
$hostname = Read-Host "Please enter Hostname"
$data | Sort-Object {[DateTime]$_."LastModified"} | Where-Object{$_.Hostname -eq $hostname} | Select -Last 1
You might need to do some user validation but something like this seems to work with your sample data:
Please enter Hostname: log
Hostname LastModified
-------- ------------
LOG 1/4/2013
If you then only want the date it would just be a matter of expanding the value from the result.
$data |
Sort-Object {[DateTime]$_."LastModified"} |
Where-Object{$_.Hostname -eq $hostname} |
Select -Last 1 -ExpandProperty LastModified

How to find String from attribute proxyAddresses?

How to find string "SMTP:*" (primary user address) in attributte proxyAddresses, then save in to variable and compare with value in attribute company. If there is a match (or no match) then exported into CSV file.
The number of values proxyAddresses is different:
smtp:adam#ff.ju.com,SMTP:adam#zf.ju.com,smtp:adam#ju.com
or
smtp:adam#ff.ju.com,SMTP:adam#ef.ju.com
or
SMTP:adam#ff.ju.com
Values in attributte company are only two characters: ff or zf or ju. They are always two characters after the #.
I have about two thousand users in Active Directory.
I have a code
Get-ADUser -Filter * -SearchBase 'DC=ju,DC=com' -Properties company,proxyaddresses |
select company, #{L='ProxyAddress'; E={$_.proxyaddresses -join"; "}}
Result is a column Company (two char) and a column proxyaddresses (there are all values). I need only the value SMTP:xxx#xxx.
Select the proxyaddresses element that begins with an uppercase SMTP::
#{n='ProxyAddress';e={$_.proxyaddresses | Where-Object {$_ -clike 'SMTP:*'} | Select-Object -First 1}}
The operator -clike does a case-sensitive comparison.

How to Parse the Results of CMDlet into Array

I am sure there is an easy answer to this question, but I cannot find it anywhere. I would like to know how to parse the results of Get-Mailbox | select Alias into an array so each time I use the array it does not show the items as "#{Alias=username}".
I have tired this, but it seems to make the values not text:
$arrayname = Get-Mailbox | select Alias
I am sure this question has been asked before, but I cannot find it.
Essentially I would like to get the Alias' from the Get-Mailbox command into an array so that I can use the foreach cmdlet to get specific folder information from a user like so:
>> $aliases = Get-Mailbox | select Alias
>> Foreach ($username in $aliases) {Get-MailboxFolderStatistics -identity $username | select FolderPath | where {$_.FolderPath -like '*Deleted*'} | Export-CSV "C:\users\username\desktop\allusers-deletedfolder-postarchive.csv" -NoTypeInformation}
The cmdlet already produces an array, only that its elements are mailbox objects, not strings. Selecting the Alias property restricts the object properties, but still leaves you with an array of objects (hence the output you observed). You need to expand the property:
$arrayname = Get-Mailbox | select -Expand Alias
or echo it in a loop:
$arrayname = Get-Mailbox | % { $_.Alias }

Resources