How to access sub objects from an array? - powershell

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

Related

Split values table for extract with powershell

I would like to make a new line in my hashtable to extract it in a csv.
I initialize my variable in hastable
$vlr=#{}
$vlr["OS"]=,#("test","test2")
I extract my variable in a .csv
$Output += New-Object PSObject -Property $vlr
$output | Convert-OutputForCSV | export-csv -NoTypeInformation -Delimiter ";" -Path $filepath
and the problem is in the extraction the result of the values ​​is on the same line
My goal is that each value is in a different line
You might want to use the Out-String cmdlet for this:
$vlr=#{}
$vlr["OS"]=,#("test","test2") | Out-String
$Object = New-Object PSObject -Property $vlr
$Object | ConvertTo-Csv
"OS"
"test
test2
"
this solution does not work because in the case where $vlr with several names the extraction will be complicated
$vlr=#{}
$vlr["OS"]=,#("test","test2")
$vlr["PS"]=,#("lous","tique")
it's a problem
https://gallery.technet.microsoft.com/scriptcenter/Convert-OutoutForCSV-6e552fc6
For the function Convert-OutputForCSV
I don't know what the posted function does, but you can make your own function to handle a single-key or multi-key hash table provided all of the key value counts are the same.
function Convert-OutputForCsv {
param(
[parameter(ValueFromPipeline)]
[hashtable]$hash
)
# Array of custom object property names
$keys = [array]$hash.Keys
# Loop through each key's values
for ($i = 0; $i -lt $hash.($keys[0]).count; $i++) {
# Custom object with keys as properties. Property values are empty.
$obj = "" | Select $keys
# Loop through key names
for ($j = 0; $j -lt $keys.Count; $j++) {
$obj.($keys[$j]) = $hash.($Keys[$j])[$i]
}
$obj
}
}
$vlr=[ordered]#{}
$vlr["OS"]='test','test2'
$vlr["PS"]='lous','tique'
$vlr | Convert-OutputForCsv | Export-Csv -NoTypeInformation -Delimiter ";" -Path $filepath
Honestly, if you are in control of the input data, I would just type out a CSV instead of typing out hash tables.
this solution is good in my simplified case but not adapted to my case unfortunately
I'm merging my old base2 array with my new base array and my goal is to concatenate the values ​​in an excel to make them usable
$base2 = Get-content $filepath2 | select -first 1
$base2 = $base2 -split ";"
$base2 = $base2.Replace("`"", "")
$cunt2 = $base2.count - 1
$h2 = ipcsv $filepath2 -Delimiter ";"
$HashTable2 = #{}
for ($i = 0 ; $i -le $cunt2 ; $i++) {
foreach ($r in $h2) {
$HashTable2[$base2[$i]] = $r.($base2[$i])
}
base2 = old tables
$base = Get-content $filepath2 | select -first 1
$base = $base -split ";"
$base = $base.Replace("`"", "")
$cunt = $base.count - 1
$h1 = ipcsv $filepath -Delimiter ";"
$HashTable = #{}
for ($i = 0 ; $i -le $cunt ; $i++) {
foreach ($r in $h1) {
$HashTable[$base[$i]] = $r.($base[$i])
}
New tables $base
once the two arrays are initialized, I merge them and this is where I have to separate the values ​​row by row
$csvfinal = $hashtable, $hashtable2 | Merge-Hashtables

How to use 2 conditions in one foreach loop

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
# ...
}

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
}
}
}

Output Values from gc into Hash Table

Trying to make a hash table with 2 categories: Users and Passwords.
This is my code thus far but the issue is the output only displays the command and does not execute it.
for ($i=1; $i -le 10; $i++){
$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy"
$nums = [char[]] "2346789"
$spl = [char[]] "!##$%^&*?+"
$first = $lows | Get-Random -count 1;
$second = $caps | Get-Random -count 1;
$third = $nums | Get-Random -count 1;
$forth = $lows | Get-Random -count 1;
$fifth = $spl | Get-Random -count 1;
$sixth = $caps | Get-Random -count 1;
$pwd = [string](#($first) + #($second) + #($third) + #($forth) + #($fifth) + #($sixth))
Out-File C:\Users\Administrator\Documents\L8_userpasswords.txt -InputObject $pwd -Append
}
$here = #'
$users=Get-Content C:\\Users\\Administrator\\Desktop\\L8_users.txt
$passwords=Get-Content C:\\Users\\Administrator\\Documents\\L8_userpasswords.txt
'#
convertfrom-stringdata -stringdata $here
This is the output I am getting:
PS C:\Users\Administrator> C:\Users\Administrator\Documents\l8.ps1
Name Value
---- -----
$users Get-Content C:\Users\Administrator\Desktop\Lab8_users.txt
$passwords Get-Content C:\Users\Administrator\Documents\L8_userpasswords.txt
I think you want this, which will turn the list of users and passwords into a HashTable, and then cast it to a PSCustomObject, which will have two properties: Users and Passwords.
$Data = [PSCustomObject]#{
Users = Get-Content -Path C:\Users\Administrator\Desktop\L8_users.txt;
Passwords = Get-Content -Path C:\Users\Administrator\Desktop\L8_userpasswords.txt;
}
$Data;
Or hey, you could probably just replace the entire script with a one liner:
GC C:\Users\Administrator\Desktop\L8_users.txt|%{[PSCustomObject]#{User=$_;Password=[System.Web.Security.Membership]::GeneratePassword(10,3)}}
Unless you are super attached to your password generation loop that is. [System.Web.Security.Membership]::GeneratePassword(X,Y) will generate complex passwords where X is the length and Y is the number of special characters (the rest will be a random mix of upper case letters, lower case letters, and numbers). So in my code (10,3) is a 10 character password with 3 non-alphanumeric characters.
You want it saved to a file? Pipe that to Export-CSV. Or assign it to a variable by prefixing it with something like $UserList = <code>.
Or if you really, really want a Hash Table you could make an empty one and then alter it just a little to add each pair to the table like this:
$UserList = #{}
GC C:\Users\Administrator\Desktop\L8_users.txt|%{$UserList.add($_,[System.Web.Security.Membership]::GeneratePassword(10,3))}
Assuming that L8_users.txt and L8_userpasswords.txt contain the same number of items, you could do something like this:
$users = Get-Content 'C:\Users\Administrator\Desktop\L8_users.txt'
$passwords = Get-Content 'C:\Users\Administrator\Documents\L8_userpasswords.txt'
$userpasswords = #{}
for ($i = 0; i -lt $users.Length; $i++) {
$userpasswords[$users[$i]] = $passwords[$i]
}