Running a for loop in Powershell - powershell

I am new o scripting in powershell and am from a Python background. I want to know if I'm doing this right.
I created this array and want to extract each item one by one
$M365_E3_Grps = ("O365-CHN-DomainUser,O365-Vendor-Exchange-User")
ForEach ($Indiv_Grp in $M365_E3_Grps) {
ForEach ($Indiv_Grp in $M365_E3_Grps) {
`$ADGroup = $Indiv_Grp$ADGroup = $Indiv_Grp`
I want to know if we can extract vals with a for loop like this and assign it to a variable like this.

Construct of your array
Your array is not quite correct and will be populated as a string. To create a string array you will need to quote each item in comma separated list. The parentheses are also not required.
$M365_E3_Grps = "O365-CHN-DomainUser","O365-Vendor-Exchange-User"
Your foreach keyword syntax is however correct, even if the formatting in your question was slightly off.
foreach ($Indiv_Grp in $M365_E3_Grps) {
# Assigning $Indiv_Grp to $ADGroup here is kind of redundant since
# the value is already assinged to $Indiv_Grp
$Indiv_Grp
}

Related

Getting error when adding nested hashtable to array in Powershell

I have a nested hashtable with an array and I want to loop through the contents of another array and add that to the nested hashtable. I'm trying to build a Slack message block.
Here's the nested hashtable I want to add to:
$msgdata = #{
blocks = #(
#{
type = 'section'
text = #{
type = 'mrkdwn'
text = '*Services Being Used This Month*'
}
}
#{
type = 'divider'
}
)
}
$rows = [ ['azure vm', 'centralus'], ['azure sql', 'eastus'], ['azure functions', 'centralus'], ['azure monitor', 'eastus2'] ]
$serviceitems = #()
foreach ($r in $rows) {
$servicetext = "*{0}* - {1}" -f $r[1], $r[0]
$serviceitems += #{'type'='section'}
$serviceitems += #{'text'= ''}
$serviceitems.text.Add('type'='mrkdwn')
$serviceitems.text.Add('text'=$servicetext)
$serviceitems += #{'type'='divider'}
}
$msgdata.blocks += $serviceitems
The code is partially working. The hashtables #{'type'='section'} and #{'type'='divider'} get added successfully. Trying to add the nested hashtable of #{'text' = #{ 'type'='mrkdwn' 'text'=$servicetext }} fails with this error:
Line |
24 | $serviceitems.text.Add('type'='mrkdwn')
| ~
| Missing ')' in method call.
I tried looking through various Powershell posts and couldn't find one that applies to my specific situation. I'm brand new to using hashtables in Powershell.
Complementing mklement0's helpful answer, which solves the problem with your existing code, I suggest the following refactoring, using inline hashtables:
$serviceitems = foreach ($r in $rows) {
#{
type = 'section'
text = #{
type = 'mrkdwn'
text = "*{0}* - {1}" -f $r[1], $r[0]
}
}
#{
type = 'divider'
}
}
$msgdata.blocks += $serviceitems
This looks much cleaner and thus easier to maintain in my opinion.
Explanations:
$serviceitems = foreach ... captures all output (to the success stream) of the foreach loop in variable $serviceitems. PowerShell automatically creates an array from the output, which is more efficient than manually adding to an array using the += operator. Using += PowerShell has to recreate an array of the new size for each addition, because arrays are actually of fixed size. When PowerShell automatically creates an array, it uses a more efficient data structure internally.
By writing out an inline hash table, without assigning it to a variable, PowerShell implicitly outputs the data, in effect adding it to the $serviceitems array.
We output two hash tables per loop iteration, so PowerShells adds two array elements to $serviceitems per loop iteration.
Note:
This answer addresses your question as asked, specifically its syntax problems.
For a superior solution that bypasses the original problems in favor of streamlined code, see zett42's helpful answer.
$serviceitems.text.Add('type'='mrkdwn') causes a syntax error.
Generally speaking, IF $serviceitems.text referred to a hashtable (dictionary), you need either:
method syntax with distinct, ,-separated arguments:
$serviceitems.text.Add('type', 'mrkdwn')
or index syntax (which would quietly overwrite an existing entry, if present):
$serviceitems.text['type'] = 'mrkdwn'
PowerShell even lets you access hashtable (dictionary) entries with member-access syntax (dot notation):
$serviceitems.text.type = 'mrkdwn'
In your specific case, additional considerations come into play:
You're accessing a hashtable via an array, instead of directly.
The text entry you're trying to target isn't originally a nested hashtable, so you cannot call .Add() on it; instead, you must assign a new hashtable to it.
Therefore:
# Define an empty array
$serviceItems = #()
# "Extend" the array by adding a hashtable.
# Note: Except with small arrays, growing them with +=
# should be avoided, because a *new* array must be allocated
# every time.
$serviceItems += #{ text = '' }
# Refer to the hashtable via the array's last element (-1),
# and assign a nested hashtable to it.
$serviceItems[-1].text = #{ 'type' = 'mrkdwn' }
# Output the result.
$serviceItems

When does -Contains not work and/or should I use something else

If I have two arrays of information. One massive and the other much smaller.
$lista ="1,2,3,4,5...etc for a lot "
$listb ="1,9,11" (Much smaller)
If ($listA -contains $listB){
Do stuff
}
Does this make sense and work?
It seems to me like it doesn't always work and I am having trouble double checking it.
Containment Operators are ideal for your use case, however, you're trying to compare an array against another array and that wouldn't work, these operators can help you identify if an element (a scalar, single value) exists in an array.
Both pairs (-in / -notin) and (-contains / -notcontains) should be equally efficient, the only difference is where the scalar is placed, for the first pair the scalar is on the left-hand side of the operation whereas for the later the scalar is on the right-hand side.
Taken from the Docs to helps us give some perspective to the explanation:
<Collection> -contains <Test-object>
<Collection> -notcontains <Test-object>
<Test-object> -in <Collection>
<Test-object> -notin <Collection>
As for your current code, that would require a loop, likely over the small array. Why?
These operators stop comparing as soon as they detect the first match, whereas the equality operators evaluate all input members. In a very large collection, these operators return quicker than the equality operators.
Following that logic, you could loop over your small array and add the comparison, this is up to you which one is more readable for you. Your code could look like this:
$lista = 1,2,3,4,5,6,7,8,9,10
$listb = 2,4,6,8,10
foreach($item in $listb)
{
if($item -in $lista) {
$item
# Do something here
}
}
There might be use cases where a HashSet<T> can be very useful, for example, if we want to get unique values out of $lista (in this case, we want case-insensitive hence the use of out of StringComparer.OrdinalIgnoreCase Property on it's constructor) and see which values are also contained on $listb, in this case probably we would want to use the .Contains(..) or .IntersectWith(..) or .ExceptWith(..) methods of this class instead of the operators.
$lista = 'A','x','y','z','a','B','b','C','c'
$listb = 'a','B','c'
$hashset = [System.Collections.Generic.HashSet[string]]::new(
[string[]]$lista,
[System.StringComparer]::OrdinalIgnoreCase
)
# To find the elements of `$listB` present in `$hashset`
foreach($item in $listb)
{
if($hashset.Contains($item)) {
$item
}
}
# The other way around, find the elements of `$hashset`
# present in `$listb`
$hashset.IntersectWith([string[]]$listb)
$hashset
# And the opposite, elements of `$hashset` NOT present in `$listb`
$hashset.ExceptWith([string[]]$listb)
$hashset

Create array from keys of an array of hashtables

I have an array where each element is a hashtable. Each hashtable has the same keys. Here it is:
#(
#{"MarketShortCode"="abc";"MarketName"="Market1" },
#{"MarketShortCode"="def";"MarketName"="Market2" },
#{"MarketShortCode"="ghi";"MarketName"="Market3" },
#{"MarketShortCode"="jkl";"MarketName"="Market4" }
)
I want a nice elegant way to extract an array containing just the value of the MarketShortCode key. So I want this:
#("abc","def","ghi","jkl")
This is the best I've come up with:
$arr = #()
$hash | %{$arr += $_.MarketShortCode}
$arr
But I don't like that cos its three lines of code. Feels like something I should be able to do in one line of code. Is there a way?
Just do this:
$hash | %{$_.MarketShortCode}
That is, return the value from the block instead of adding it to an array and then dereferencing the array.
If you're using PowerShell 3+, there's even shorter way:
$hash.MarketShortCode
PowerShell automatically applies dot . to each item in an array when it's used this way, but it wasn't supported until v3.

Concatenate elements of a char array and strings in powershell

I'm probably over thinking this, but this is not coming out the way I expect. I've searched google, I've searched stackoverflow. Please Help.
Here are some variables for testing, and the invocation of a function:
$SQL_FirstName = "PowerShell";
$SQL_LastName = "CreateUser";
$SQL_Office = "TEST";
$SQL_IsAdmin = $true;
Create_User($SQL_FirstName.ToLower(), $SQL_LastName.ToLower(), $SQL_Office, $SQL_IsAdmin);
Here is the function, not much there yet:
Function Create_User([string]$FirstName, [string]$LastName, $Office, $IsAdmin)
{
$FirstNameCharArray = [char[]]$FirstName;
$UserName = [string]$FirstNameCharArray[0] + $LastName;
Write-Host $UserName;
}
Now I expect the output to be "pcreateuser". But it's not. I have tried casting different things, I have tried surrounding my variables with $(). I have tried using the + symbol and not using the + symbol. I have tried smashing the variables right up against each other. Every single time it just outputs "p".
Any help would be appreciated. Thanks!
It's because of how you are calling the function. You are not supposed to use brackets for function calls nor use commas to separate the parameters (unless you are sending array values on purpose or subexpressions). You have passed it a single array of those elements.
Create_User $SQL_FirstName.ToLower() $SQL_LastName.ToLower() $SQL_Office $SQL_IsAdmin
In your function call your sent an array to $firstname which was casted as a string "powershell createuser TEST True". The other parameters would have been blank. Hence your output.
They work just the same as cmdlet calls. Just use spaces to separate the parameters and their values.
Get-ChildItem -Filter "*.txt" -Path "C:\temp"
String to char array
For what it is worth you don't need to cast the string as a char array. You can just use array notation directly on the string.
PS C:\Users\Matt> [string]"Bagels"[0]
B
Heck you don't even need to cast it "Bagels"[0]

Changing each entry in a list returned from powershell

I have a list of paths (filtered by special criteria). I want to mutate each entry in this list, but can't find a way (I think it's immutable). What's the best way of going about this?
Thanks
I guess you have some collection, not only list (probably array).
PS> $myList = 'first','second','third'
You can mutate the collection by indexing or just by creating new array like this:
PS> $myList[1] = '2nd'
#or
PS> $myList | % { $_.Substring(0,2) }
fi
se
th