How to use 2 conditions in one foreach loop - powershell

I have a script snippet. This gives me an array with 2 propertys: Account and AccessRights. Now I want to build a foreach loop, but I also need to store the second value in a variable for further use.
So if I do:
foreach ($id in $ACLFile.Account) {
# do stuff
}
I only have the Account property saved in $id. But how can I also get its AccessRights value?
$ACLFile = GetNTFSAccess | select Account, AccessRights
$ACLGroup = $ACLFile | Group-Object Account
$Singles = $ACLGroup.Where({$_.Count -eq 1}).Group
$Duplicates = $ACLGroup.Where({$_.Count -gt 1})
$ItemizedDuplicates = $Duplicates | foreach {
[PSCustomObject][ordered]#{
"Account"=$_.Group.Account[0];
"AccessRights" = $_.Group.AccessRights -join ", "
}
}
#($ItemizedDuplicates, $Singles)

Iterate over the objects instead of just one property.
foreach ($acl in $ACLFile) {
$id = $acl.Account
$access = $acl.AccessRights
# ...
}

Related

Comparing all rows of a table Powershell

I have a DataTable ($dt = New-Object system.Data.datatable) which contains entries as below:
My objective is :
Find servers which same names ,ie , from ServerID column trim the part after underscore (_) (which I achieved via Split()) and then compare with rest of the rows.
If the Server Name is same, check the value of all respective "Status" column
If none of the columns have "IN PROCESS" in them for the respective server, then print the ServerID.
This is what I came up with but got stuck since values are not returned correctly:
foreach($backupid in ($dt.'ServerID' | %{foreach ($y in $_){$y.Split('_')[0]}} | sort -Unique)){
foreach ($row in $dt){
if ($row.'ServerID ' -match "^$backupid" -and $row.Status -ne "IN PROCESS" ){
$row.'ServerID '
}
}
}
Just use a hash table to check whether a server id is (not) IN PROCESS, like:
$dt = ConvertFrom-Csv #'
Server,Status
abc_123,"IN PROCESS"
abc_345,"INACTIVE"
abc_546,"INACTIVE"
xyz_123,"INACTIVE"
xyz_457,"INACTIVE"
xyz_230,"INACTIVE"
'#
$InProcess = #{}
$dt | Foreach-Object {
$Id = $_.Server.Split('_')[0]
if (!$InProcess.Contains($Id)) { $InProcess[$Id] = $False }
if ($_.Status -eq 'IN PROCESS') { $InProcess[$Id] = $True }
}
$dt | Foreach-Object {
$Id = $_.Server.Split('_')[0]
if ($InProcess[$Id] -eq $False) { $_ }
}
Server Status
------ ------
xyz_123 INACTIVE
xyz_457 INACTIVE
xyz_230 INACTIVE
Instead of nested loops, try Group-Object!
# Group objects by the first part of the Server ID
$dt |Group { $_.ServerID.Split('_')[0] } |Where-Object {
# Then find only the groups with no objects where Status is IN_PROGRESS
$_.Group.Status -notcontains 'IN_PROGRESS'
} |ForEach-Oject -MemberName ServerID # Output just the Server value

Creating and passing an array from one function to another [duplicate]

This question already has answers here:
Boolean variable gets returned as an Object[]
(2 answers)
Closed 3 years ago.
What I'm trying to do is make it so I can create the array and check it in a single a single function as I call it in other functions so it'd be easier to just add $list = GetUserList instead of verifying the $list each time I plan on calling the GetUserList function.
https://pastebin.com/6h4MJH9n
What works:
function GetUserList {
$name = Read-Host "Please enter the users name (multiple users can be separated by commas)"
$names = $name.Split(",")
for ( $i = 0; $i -lt $names.Count; $i++ ) {
$firstn = $names[$i].Trim().Split(" ")[0]
$lastn = $names[$i].Trim().Split(" ")[-1]
$ulist += #([PSCustomObject]#{
First = "$firstn";
Last = "$lastn"
})
}
return $ulist
}
function UserList {
do {
$userlist = GetUserList
$userlist | Format-Table -AutoSize -Wrap
$again = Read-Host "Is this correct? (y/n)"
} until ( $again -eq "y" )
$userlist | ForEach-Object {
"First: $($_.First)"
"Last: $($_.Last)"
}
}
UserList
What doesn't work:
function GetUserList {
do {
$ulist = #()
$name = Read-Host "Please enter the users name (multiple users can be separated by commas)"
$names = $name.Split(",")
for ( $i = 0; $i -lt $names.Count; $i++ ) {
$firstn = $names[$i].Trim().Split(" ")[0]
$lastn = $names[$i].Trim().Split(" ")[-1]
$ulist += #([PSCustomObject]#{
First = "$firstn";
Last = "$lastn"
})
}
$ulist | Format-Table -AutoSize -Wrap
$again = Read-Host "Is this correct? (y/n)"
} until ( $again -eq "y" )
return $ulist
}
function UserList {
$userlist = GetUserList
$userlist | ForEach-Object {
"First: $($_.First)"
"Last: $($_.Last)"
}
}
UserList
I don't get an errors, it's just the code that doesn't work completely skips the for loop and I have no idea why.
the problem is situated in $ulist | Format-Table -AutoSize -wrap since you're not either
storing the formatted content in a variable,
nore sending the formatted content to the PowerShell host for printing (as stated in #Lee_Daily's comment in the OP)
PowerShell will return the formatted content to the output stream. Additionally to the formatted content you're also sending the content of $ulist to the output stream (via the Return $ulist statement). Based on that $userlist (via $userlist = GetUserList) contains the $ulist content PLUS the formatted $ulist content.
These can also be seen when debugging your code (see Set-PsBreakPoint):
[DBG]:> $userlist
First Last
----- ----
user 1
user 2
user 3
First Last
----- ----
user 1
user 2
user 3
As #Lee_Daily suggests change the line $ulist | Format-Table -AutoSize -wrap to $ulist | Format-Table -AutoSize -wrap | Out-Host. Piping to Out-Host will prevent that the output of Format-Table is written to the output-stream.
Further suggested reading:
about_Redirection
PowerShell streams devblog

How to access sub objects from an array?

I have the following
$roles = 'role1;role2;role3;role4' -split ';'
$members = 'member1,member2;;member3;member4' -split ';'
$i = 0
$array = #(foreach ($role in $roles) {
[pscustomobject] #{
Role = $role
Members = $members[$i++] #-split ','
}
})
$array outputs:
Role Members
---- -------
role1 member1,member2
role2
role3 member3
role4 member4
testing this: $array.members
member1,member2
member3
member4
i am trying to access members for each role
$i=-1;
foreach($role in $array.Role)
{
$i++;
foreach($member in $role[$i].Members){$member}
}
to my understanding, i should get back
instead, i get nothing printed back!
how come?
also, if i want to access objects by name, i have an alternative using hashtable, but for some reason, its not working properly.
$roleMembers = #{}
for ($i = 0; $i -lt $roles.Count; $i++) {
# `Where Length -ne 0` to filter out empty strings
$roleMembers[$roles[$i]] = $members[($i*2)..($i*2+1)] | Where Length -ne 0
}
$roleMembers outputs
instead of outputting:
how come the output is different from $array?
Use foreach over $array, and you can access the individual properties inside the loop:
foreach($item in $array){
foreach($member in $item.Members){
"$($item.Role): $member"
}
}
which will result in
role1: member1,member2
role2:
role3: member3
role4: member4

PowerShell hash table key issues

Maybe I am doing it wrong, but when I try and reference a hash table member using the key, I get no results, however when I filter a .GetEnumerator() output with the same key, I get a result.
This doesn't work:
$year = "2015"
$msol_year_members_table = #{}
foreach ($member in $(Get-MsolGroupMember -GroupObjectId $(Get-MsolGroup | ?{ $_.DisplayName -eq $("Class of " + $year) }).ObjectId)) {
$msol_year_members_table[$member.ObjectId] = $member
}
foreach ($mb in $(Get-Mailbox -ResultSize Unlimited)) {
if ($msol_year_members_table.ContainsKey($($mb.ExternalDirectoryObjectId))) {
$msol_year_members_table[$($mb.ExternalDirectoryObjectId)]
}
}
Doing this works though:
foreach ($mb in $(Get-Mailbox -ResultSize Unlimited)) {
if ($result = $msol_year_members_table.GetEnumerator() | ?{ $_.Name -eq $($mb.ExternalDirectoryObjectId) }) {
$result
}
}
Any pointers would be appreciated - assuming it is some stupid mistake.
Are you sure you don't have a type mismatch between the keys and the test values?
When you use the .containskey() method, the argument value must be the same type as the key, but when you use the .getenumerator() method, the -eq test is going to try to coerce the .Name value and the test value to the same type for the operation:
$ht = #{
1 = 'one'
2 = 'two'
3 = 'three'
}
$ht['1']
$ht.GetEnumerator() |? { $_.name -eq '1'}
Name Value
---- -----
1 one
Here is my working code - just in case it helps anyone, I used it to assign address book policies for dir sync'd groups to Office 365 mailboxes:
# Years to be processed
$years = #("2015","2016","2017","2018","2019","2020","2021","2022","2023","2024","2025","2026","2027")
# Loop through each year and retrieve the members for the groups
$msol_year_members_table = #{}
foreach ($year in $years) {
foreach ($member in $(Get-MsolGroupMember -GroupObjectId $(Get-MsolGroup | ?{ $_.DisplayName -eq $("Class of " + $year) }).ObjectId)) {
$key = $member.ObjectId.ToString()
$msol_year_members_table[$key] = $member
}
}
# Loop through the mailboxes and set the mailbox poilicies for matching members
foreach ($mb in $(Get-Mailbox -ResultSize Unlimited)) {
$key = $mb.ExternalDirectoryObjectId.ToString()
if ($msol_year_members_table.ContainsKey($key)) {
$alias = $msol_year_members_table[$key].Alias; $alias_split = $alias.Split(".")
$year = $alias_split[$alias_split.Length-1)]
Set-Mailbox -Identity $alias -AddressBookPolicy $("Class of " + $year + " ABP")
}
}

compare two csv using powershell and return matching and non-matching values

I have two csv files, i want to check the users in username.csv matches with userdata.csv copy
to output.csv. If it does not match return the name alone in the output.csv
For Ex: User Data contains 3 columns
UserName,column1,column2
Hari,abc,123
Raj,bca,789
Max,ghi,123
Arul,987,thr
Prasad,bxa,324
username.csv contains usernames
Hari
Rajesh
Output.csv should contain
Hari,abc,123
Rajesh,NA,NA
How to achieve this. Thanks
Sorry for that.
$Path = "C:\PowerShell"
$UserList = Import-Csv -Path "$($path)\UserName.csv"
$UserData = Import-Csv -Path "$($path)\UserData.csv"
foreach ($User in $UserList)
{
ForEach ($Data in $UserData)
{
If($User.Username -eq $Data.UserName)
{
# Process the data
$Data
}
}
}
This returns only matching values. I also need to add the non-matching values in output
file. Thanks.
something like this will work:
$Path = "C:\PowerShell"
$UserList = Import-Csv -Path "$($path)\UserName.csv"
$UserData = Import-Csv -Path "$($path)\UserData.csv"
$UserOutput = #()
ForEach ($name in $UserList)
{
$userMatch = $UserData | where {$_.UserName -eq $name.usernames}
If($userMatch)
{
# Process the data
$UserOutput += New-Object PsObject -Property #{UserName =$name.usernames;column1 =$userMatch.column1;column2 =$userMatch.column2}
}
else
{
$UserOutput += New-Object PsObject -Property #{UserName =$name.usernames;column1 ="NA";column2 ="NA"}
}
}
$UserOutput | ft
It loops through each name in the user list. Line 9 does a search of the userdata CSV for a matching user name if it finds it it adds the user data for that user to the output if no match is found it adds the user name to the output with NA in both columns.
had to change your userList csv:
usernames
Hari
Rajesh
expected output:
UserName column1 column2
-------- ------- -------
Hari abc 123
Rajesh NA NA
I had a similar situation, where I needed a "changed record collection" holding the entire record when the current record was either new or had any changes when compared to the previous record. This was my code:
# get current and previous CSV
$current = Import-Csv -Path $current_file
$previous = Import-Csv -Path $previous_file
# collection with new or changed records
$deltaCollection = New-Object Collections.Generic.List[System.Object]
:forEachCurrent foreach ($row in $current) {
$previousRecord = $previous.Where( { $_.Id -eq $row.Id } )
$hasPreviousRecord = ($null -ne $previousRecord -and $previousRecord.Count -eq 1)
if ($hasPreviousRecord -eq $false) {
$deltaCollection.Add($current)
continue forEachCurrent
}
# check if value of any property is changed when compared to the previous
:forEachCurrentProperty foreach ($property in $current.PSObject.Properties) {
$columnName = $property.Name
$currentValue = if ($null -eq $property.Value) { "" } else { $property.Value }
$previousValue = if ($hasPreviousRecord) { $previousRecord[0]."$columnName" } else { "" }
if ($currentValue -ne $previousValue -or $hasPreviousRecord -eq $false) {
$deltaCollection.Add($currentCenter)
continue forEachCurrentProperty
}
}
}