PowerShell Variable - powershell

I am looking up data and then want to add that to some kind of list which I can reference, for example if my data looked like this:
Name - John
Last Name - Doe
Age - 55
I would want to store that in a list and be able to do foreach $member in $myList and be able to print $member.Name
Hope that makes sense, not sure what the best way to do this is, would it be creating an object? or using a hashtable?

You can use a PowerShell Hashtable for this:
$NameObject= #{
'Name'='John'
'LastName'='Doe'
'Age'='55'
}
You can do this inside of a loop to add your data like so, to create a PowerShell Object.
$NameObjectCollection = foreach($Object in $MyData)
{
#Add Data to Object
[PSCustomObject] $NameObject= #{
'Name'=$Object.Property1
'LastName'=$Object.Property2
'Age'=$Object.Property3
}
#Output object
$NameObject
}
#Now you can loop over this data
foreach($NameObject in $NameObjectCollection)
{
#Access the properties of the NameObject`
$NameObject.Name
$NameObject.LastName
$NameObject.Age
}

Related

Getting data out of structure not working

I'm trying to get the data out of the data structure, and I can see it's in there, but the variable the info is assigned to is null.
Let me know if you need more info for the method that assigns $SizeBasedVideoStringDevice, but it looks like this:
$SizeBasedVideoStringDevice:
[Object[7]]:
[0]:#{Key=LOAD_MEDIA;VALUE=SYSTEM.OBJECT[]}
[1]:#{KEY=LOAD_MEDIAB;VALUE=}
[2]:#{KEY=LOAD_MEDIAC;VALUE=}
[3]:#{KEY=LOAD_MEDIAD;VALUE=SYSTEM.OBJECT[]}
...
So some of them have a Value, which is fine.
When I look closer at that first value, it looks like this:
Value: [Object[5]]
[0]: "LoadDialog.MediaA"
[1]: "LoadDialog.MediaB"
[2]: "LoadDialog.MediaC"
...
I have the following code, and for some reason it's not finding that first one. Once I get the data out, I will loop thru and process each Value.
$tmp = "LOAD_MEDIA" #this will match first in data structure
$sizeBasedVideoString = $SizeBasedVideoStringDevice.where{$_Key -eq $tmp}.Value ###this is null
if($null -ne $sizeBasedVideoString)
{
$resultDPS_sized = New-Object System.Collections.Generic.List[string]
foreach($vid in $sizeBasedVideoString)
{
#get vid location
$temp = ProcessDPSMDB -mdbLookupString $vid -mdbFilePath $dpsMdbPathFull
$resultDPS_sized.Add($temp)
}
}
This is using PowerShell 5.1 and VSCode.

Powershell - passing variable into loop for string with quotes

I'm trying to understand how to setup my script so that single quotes will wrap around my variable. I have a list of 1500 customers I need to repeat my script for, so my thought as to do a foreach loop.
$customerlist = Invoke-Sqlcmd -Query "SELECT [CustomerNo] FROM [TABLE]" -ServerInstance "SERVER\INSTANCE"
#Loop through
foreach ($customer in $customerlist)
{
$inputParams = #{
"CustomerNo" = "'"+$customer+"'";
}
....Do rest of script
}
I need the $customer variable in my $inputparams to show with the string value in single quotes, e.g. '01233456' instead of just 0123456. I've tried several different iterations of "'"+$customer+"'" but cannot seem to get the correct syntax. Could someone help me out?
Invoke-Sqlcmd returns [System.Data.DataRow] type objects with the field name(s) and value(s) of your query result as its properties (basically a table). To select the value in the CustomerNo, you must specify the value by name:
# Expand the CustomerNo property to strings
foreach ($customer in $customerlist.CustomerNo) { }
# Or, later in the script:
$inputParams = #{
CustomerNo = "'$($customer.CustomerNo)'"
}

Powershell pass complex object By Value, not By Reference

I am trying to process some data in an ordered dictionary, then add that to another ordered dictionary, and I can do that by reinitializing my temporary dictionary, like this...
$collection = [Collections.Specialized.OrderedDictionary]::new()
foreach ($id in 1..5) {
$tempCollection = [Collections.Specialized.OrderedDictionary]::new()
foreach ($char in [Char]'a'..[Char]'e') {
$letter = ([Char]$char).ToString()
if ($id % 2 -eq 0) {
$letter = $letter.ToUpper()
}
$int = [Int][Char]$letter
$tempCollection.Add($letter, $int)
}
$collection.Add($id, $tempCollection)
}
foreach ($id in $collection.Keys) {
Write-Host "$id"
foreach ($key in $collection.$id.Keys) {
Write-Host " $key : $($collection.$id.$key)"
}
}
However, I feel like reinitializing is a bit inefficient/inelegant, and I would rather just .Clear() that temporary variable. Which leads to this...
$collection = [Collections.Specialized.OrderedDictionary]::new()
$tempCollection = [Collections.Specialized.OrderedDictionary]::new()
foreach ($id in 1..5) {
foreach ($char in [Char]'a'..[Char]'e') {
$letter = ([Char]$char).ToString()
if ($id % 2 -eq 0) {
$letter = $letter.ToUpper()
}
$int = [Int][Char]$letter
$tempCollection.Add($letter, $int)
}
$collection.Add($id, $tempCollection)
$tempCollection.Clear()
}
foreach ($id in $collection.Keys) {
Write-Host "$id"
foreach ($key in $collection.$id.Keys) {
Write-Host " $key : $($collection.$id.$key)"
}
}
The problem is that while simple objects like string, int, char, etc are passed by value, all complex objects like a dictionary are passed by reference. So I pass the SAME dictionary in every iteration of $collection.Add($id, $tempCollection) and the final state of $tempCollection is cleared, so the result is 5 empty members of $collection.
I know I can force something that is normally passed By Value to be By Reference using [Ref] as outlined here. And [Ref] is just an accelerator for System.Management.Automation.PSReference. So what I need is a way to force an argument By Value, but neither [Val] nor [ByVal] works, and searching for System.Management.Automation.PSValue doesn't seem to return anything useful either. The PSReference doco linked above says
This class is used to describe both kinds of references:
a. reference to a value: _value will be holding the value being referenced.
b. reference to a variable: _value will be holding a PSVariable
instance for the variable to be referenced.
which makes me think I can get to the Value somehow, but for the life of me I can't grok HOW. Am I on the right track, and just missing something, or am I misunderstanding this documentation completely?
Cloning also seems like a potential solution, i.e. $collection.Add($id, $tempCollection.Clone()), but Ordered Dictionaries don't implement ICloneable. .CopyTo() also isn't an option, since it doesn't necessarily maintain the order of the elements. Nor does .AsReadOnly() since
The AsReadOnly method creates a read-only wrapper around the current
OrderedDictionary collection. Changes made to the OrderedDictionary
collection are reflected in the read-only copy. Nor does OrderedDictionary implement .copy() as PSObject does.
I also tried making a new variable, like this...
$newCollection = $tempCollection
$collection.Add($id, $newCollection)
$tempCollection.Clear()
And that doesn't work either. So it seems that complex objects by reference seems to apply to more than just passed arguments.
It seems almost like my Ordered Dictionary choice/need is the root of the problem, but it seems like needing a unconnected copy of an Ordered Dictionary would not be such an edge case that it isn't supported.

How do I populate an unknown number of variables from user input dynamically in powershell?

I am trying to figure out how to populate an unknown number of variables based on user input (writing a script that obtains certificates from a CA, and sometimes these certificates contain more than one name (SANs) and it is impossible to know how many so this needs to be dynamic).
I know I start with setting up params like this:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string[]]$SANs
)
And then I need to somehow take those values and assign them to $san1, $san2, $san3 and so on.
Being new to programming, I am not even sure what to call this. Would you use a foreach loop to somehow populate these variables?
ForEach ($SAN in $SANs) {
what do I do here?
}
The end result is a need to populate a string with these variables like dns=$san1&dns=$san2&dns=$san3 etc...
Functions and scripts can take parameters. The parameter block in your example looked like...
function foo {
Param([string[]]$SANs)
}
That parameter, $SANs, is an array of strings. A single string would look like this...
$stuff = 'barf'
An array of strings looks like this...
$stuff = #('barf', 'toot', 'ruff', 'meow')
So far so good? If you need to get each of the things in the array, you'd use a loop...
foreach ($thing in $stuff) { write-output $thing }
...for example...
$san_declaration
foreach ($thing in $stuff) {
if ($san_declaration.length -eq 0) {
$san_declaration = "dns=${thing}"
} else {
$san_declaration += "&dns=${thing}"
}
}
Now, if you (not that you asked) happen to be calling Get-Certificate, just remember the SANs parameter is a string array. In that case, you'd just pass in the string array instead of creating the string like you were doing.
Get-Certificate -DnsName $stuff

Powershell: Turn period delimited string into object properties

I have a string that looks something like this:
$string = "property1.property2.property3"
And I have an object, we'll call $object. If I try to do $object.$string it doesn't interpret it that I want property3 of property2 of property1 of $object, it thinks I want $object."property1.property2.property3".
Obviously, using split('.') is where I need to be looking, but I don't know how to do it if I have an unknown amount of properties. I can't statically do:
$split = $string.split('.')
$object.$split[0].$split[1].$split[2]
That doesn't work because I don't know how many properties are going to be in the string. So how do I stitch it together off of n amounts of properties in the string?
A simple cheater way to do this would be to use Invoke-Expression. It will build the string and execute it in the same way as if you typed it yourself.
$string = "property1.property2.property3"
Invoke-Expression "`$object.$string"
You need to escape the first $ since we don't want that expanded at the same time as $string. Typical warning: Beware of malicious code execution when using Invoke-Expression since it can do anything you want it to.
In order to avoid this you would have to build a recursive function that would take the current position in the object and pass it the next breadcrumb.
Function Get-NestedObject{
param(
# The object we are going to return a propery from
$object,
# The property we are going to return
$property,
# The root object we are starting from.
$rootObject
)
# If the object passed is null then it means we are on the first pass so
# return the $property of the $rootObject.
if($object){
return $object.$property
} else {
return $rootObject.$property
}
}
# The property breadcrumbs
$string = '"Directory Mappings"."SSRS Reports"'
# sp
$delimetedString = $String.Split(".")
$nestedObject = $null
Foreach($breadCrumb in $delimetedString){
$nestedObject = Get-NestedObject $nestedObject $breadcrumb $settings
}
$nestedObject
There are some obvious places where that function could be hardened and documented better but that should give you an idea of what you could do.
What's the use case here? You can split the string as you've described. This will create an array, and you can count the number of elements in the array so that n is known.
$string = "property1.property2.property3"
$split = $string.split('.')
foreach($i in 0..($split.Count -1)){
Write-Host "Element $i is equal to $($split[$i])"
$myString += $split[$i]
}