Change nested object values in a powershell object - powershell

I have the following object, and I would like to change a nested value in the object without changing all the other nested objects. The object I would like to change is in a variable.
$object.Tophashtable.MidHashtable1.array1[0].linenumber = 1
Value = Bottom-array1-1
array1[1].linenumber = 2
Value = Bottom-array1-2
array1[2].linenumber = 3
Value = Bottom-array1-3
array2[0].linenumber = 1
Value = Bottom-array2-1
array2[1].linenumber = 2
Value = Bottom-array2-2
MidHashtable2.array3[0].linenumber = 1
Value = Bottom-array3-1
array3[1].linenumber = 2
Value = Bottom-array3-2
array3[2].linenumber = 3
Value = Bottom-array3-3
array4[0].linenumber = 1
Value = Bottom-array4-1
array4[1].linenumber = 2
Value = Bottom-array4-2
MidHashtable3.array5[0].linenumber = 1
Value = Bottom-array5-1
I would like to do the following:
$newobject = #{
linenumber = "1"
value = "newobject-1"}
$object.$change = $newobject
I'm able to read the content of the subobject with the following code:
iex "`$object.$change"
But I can't find a way to set/change the subobject.
code example:
$object = #{
tophashtable = #{
MidHashtable1 = #{
array1 = #(
#{
linenumber = "1"
value = "Bottum-array1-1"
},
#{
linenumber = "2"
value = "Bottum-array1-2"
},
#{
linenumber = "3"
value = "Bottum-array1-3"
},
#{
linenumber = "4"
value = "Bottum-array1-4"
}
)
array2 = #(
#{
linenumber = "1"
value = "Bottum-array2-1"
},
#{
linenumber = "2"
value = "Bottum-array2-2"
}
)
}
MidHashtable2 = #{
array3 = #(
#{
linenumber = "1"
value = "Bottum-array3-1"
},
#{
linenumber = "2"
value = "Bottum-array3-2"
},
#{
linenumber = "3"
value = "Bottum-array3-3"
},
#{
linenumber = "4"
value = "Bottum-array3-4"
}
)
array4 = #(
#{
linenumber = "1"
value = "Bottum-array4-1"
},
#{
linenumber = "2"
value = "Bottum-array4-2"
}
)
}
MidHashtable3 = #{
array5 = #(
#{
linenumber = "1"
value = "Bottum-array5-1"
},
#{
linenumber = "2"
value = "Bottum-array5-2"
},
#{
linenumber = "3"
value = "Bottum-array5-3"
}
)
}
}
}
$newobject = #(
#{
linenumber = "1"
value = "newobject-1"
},
#{
linenumber = "2"
value = "newobject-2"
}
)
$query = "tophashtable.MidHashtable1.array2"
# This does work to read the content of the subobject true a variable
Invoke-Expression "`$object.$query"
# This does work to set the content of the subobject to the newobject
$object.tophashtable.MidHashtable1.array2 = $newobject
# I would like to set the content of the subobject by the value of the query variable.
# The value of the query variable can be of different length as wel.
# Sometime's i want to change the array's but sometime's i would like to change the content of the midhashtable's completely so $query would only be "tophashtable.MidHashtable1"

This does work, i Know the Invoke-Expression's are pure evil, but for now it's the only solution i can find.
Be verry carefull using this !!!
Invoke-expression "`$Object.$query = `$newobject”

Related

A null key is not allowed in a hash literal

I would like to be able to save this hashtable in a variable called: $arr.
As you can see we define two variables $displayName and $typekind and I put them in the hashtable.
But when i try to run the script it gives me an error:
InvalidOperation: A null key is not allowed in a hash literal.
I have tried some different things but i am not sure where error is happening. Hope you can help me
...
if (!( $resourceTypes -like ("*" + $resource.type + "*")) -and ($azDiagSetting)) {
$displayName = $resource.type.replace('/', ' ').Split(' ')[-1]
$typekind = $resource.type
$arr = #{
type = "Microsoft.Authorization/policyDefinitions"
apiVersion = "2020-09-01"
properties = #{
metadata = #{
category = "Monitoring"
}
description = "This policy automatically deploys and enable diagnostic settings to Log Analytics"
displayName = "Apply diagnostic settings for $($displayName) - Log Analytics"
policyRule = #{
then = #{
details = #{
roleDefinitionIds = #(
"/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293"
)
name = "setByPolicy"
type = "Microsoft.Insights/diagnosticSettings"
existenceCondition = #{
AllOf = #(
#{
matchInsensitively = "[[parameters('logAnalytics')]"
field = "Microsoft.Insights/diagnosticSettings/workspaceId"
}
)
}
deployment = #{
properties = #{
template = #{
contentVersion = "1.0.0.0"
resources = #(
#{
type = "$($typekind)/providers/diagnosticSettings"
apiVersion = "2021-05-01-preview"
properties = #{
metrics = #(
#{ }
)
workspaceId = "[[parameters('logAnalytics')]"
logs = #(
#{}
)
}
location = "[[parameters('location')]"
name = "[[concat(parameters('resourceName'), '/', 'Microsoft.Insights/setByPolicy')]"
}
)
parameters = #{
location = #{
type = "string"
}
logAnalytics = #{
type = "string"
}
resourceName = #{
type = "string"
}
}
$schema = "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#"
}
parameters = #{
location = #{
value = "[[field('location')]"
}
logAnalytics = #{
value = "[[parameters('logAnalytics')]"
}
resourceName = #{
value = "[[field('name')]"
}
}
mode = "incremental"
}
}
}
}
}
}
} | ConvertTo-Json -Depth 20
$arr
}
PowerShell is attempting to assign the value of $schema (a variable) as a Key of your hash table. As it seems and could be the only possible explanation for your error, this variable hasn't been defined hence is effectively $null:
$schema = $null
$hash = #{
$schema = 'hello'
}
# Above errors with the exception message:
# "A null key is not allowed in a hash literal."
If you want to assign $schema as the literal name of your Key, you can use single-quoted strings:
A string enclosed in single-quotation marks is a verbatim string. The string is passed to the command exactly as you type it. No substitution is performed.
$hash = #{
'$schema' = 'hello'
}
# Results in:
Name Value
---- -----
$schema hello

Powershell Expression Hashtables to CSV

I am attempting to ouput some data Im pulling from ActiveDirectory into a CSV but the output is not what I am expecting. How do I export these hashtables value into a CSV? I have no doubt there is a simple answer to this but my google fu seems to be failing me today.
foreach ($user in $user_data)
{
#{ name = 'DisplayName'; expression = { $user.DisplayName } },
#{ name = 'Email'; expression = { $user.EmailAddress } },
#{ name = 'AccountName'; expression = { $user.SamAccountName } },
#{ name = 'Description'; expression = { $user.Description } },
#{ name = "PasswordLastSet"; expression = { [datetime]::FromFileTime($user.pwdLastSet) } },
#{ name = 'Staff Number'; expression = { $user.pager } },
#{ name = 'LastBadPasswordAttempt'; expression = { $user.LastBadPasswordAttempt } },
#{ name = 'LastLogonDate'; expression = { $user.LastLogonDate } },
#{ name = 'logonCount'; expression = { $user.logonCount } },
#{ name = 'Enabled'; expression = { $user.Enabled } } |
Export-Csv $path2 -Append -NoTypeInformation
}
Since you are using calculated properties syntax, you can simply just use Select-Object rather than looping:
$user_data | Select-Object #{ name = 'DisplayName'; expression = { $_.DisplayName } },
#{ name = 'Email'; expression = { $_.EmailAddress } },
#{ name = 'AccountName'; expression = { $_.SamAccountName } },
#{ name = 'Description'; expression = { $_.Description } },
#{ name = "PasswordLastSet"; expression = { [datetime]::FromFileTime($_.pwdLastSet) } },
#{ name = 'Staff Number'; expression = { $_.pager } },
#{ name = 'LastBadPasswordAttempt'; expression = { $_.LastBadPasswordAttempt } },
#{ name = 'LastLogonDate'; expression = { $_.LastLogonDate } },
#{ name = 'logonCount'; expression = { $_.logonCount } },
#{ name = 'Enabled'; expression = { $_.Enabled } } |
Export-Csv $path2 -NoTypeInformation
For the output you want, I guess it would be better to use an array of custom objects.
$users = #()
ForEach($user in $user_data)
{
$users += New-Object PSObject -Property #{
DisplayName = [String]$user.DisplayName;
Email = [String]$user.EmailAddress;
AccountName = [String]$user.SamAccountName;
Description = [String]$user.Description;
PasswordLastSet = [datetime]::FromFileTime($user.pwdLastSet);
StaffNumber = [String]$user.pager;
LastBadPasswordAttempt = [datetime]::FromFileTime($user.LastBadPasswordAttempt);
LastLogonDate = [datetime]::FromFileTime($user.LastLogonDate);
logonCount = [Int]$user.logonCount;
Enabled = [Bool]$user.Enabled;
}
}
$users | Export-Csv -Path $path2 -NoTypeInformation

How to convert a nested hashtable to PSObjects in PowerShell

If you have a hashtable containing nested hashtables, how to convert that to a PsObject recursively?
#{
Foo = #{
Bar = #{
Key = 'Value'
Test = 1
}
}
}
The result should be
$_.Foo.Bar.Key = 'Value'
$_.Foo.Bar.Test = 1
One approach is to create a recursive function:
function ConvertTo-PsObject {
param (
[hashtable] $Value
)
foreach ( $key in $Value.Keys | Where-Object { $Value[$_].GetType() -eq #{}.GetType() } ) {
$Value[$key] = ConvertTo-PsObject $Value[$key]
}
New-Object PSObject -Property $Value | Write-Output
}
Another way of doing it which hasn't been mentioned so far, and is more appropriate in many cases is to use New-Object PSObject.
$a = #{
Foo = #{
Bar = #{
Key = 'Value'
Test = 1
}
}
}
$b = New-Object -Type PSObject -Property $a
Doing it this way makes it work correctly with Format-Table for instance, and probably other places too.
Cast it to a [PsCustomObject]:
$a = [PsCustomObject]#{
Foo = #{
Bar = #{
Key = 'Value'
Test = 1
}
}
}
$a.Foo.Bar.Key # --> "Value"
$a.Foo.Bar.Test # --> 1

Find duplicates in array of hashtables

I have an array of hashtables and I need to find if there are elements who has the same Name.
I have this HasDuplicate function which return True or False if the array contains duplicate or not.
What I am doing here is that I am iterating through each element and add Name of it to another array, and then check it if it exists. But this code does not looks good, and I was thinking if there is another way of achieving this
# object looks like this
$array = #(
#{ Name = 'First', Passed = $True }
#{ Name = 'First', Passed = $False }
)
Function HasDuplicate
{
param($array)
$all = #()
foreach($item in $array)
{
$item_name = $item.Name
if($all -contains $item_name)
{
Write-Error "Duplicate name ""$item_name"""
return $True
}
else
{
$all += $item_name
}
}
return $False
}
Group-Object is probably the easiet, something like this:
$array = #(
#{ Name = 'First'; Passed = $True }
#{ Name = 'First'; Passed = $False }
)
$array.Name | Group-Object | Where-Object Count -GT 1
Another way you could do it using an hash table:
$array = #(
#{ Name = 'First'; Passed = $True }
#{ Name = 'First'; Passed = $False }
)
$h = #{}
$array | % {$h[$_.Name] += 1 }
$h.GetEnumerator() | Where value -GT 1
This might not be very good looking compared to the other answers, but you could just count your names in another hashtable then output the duplicates afterwards.
$array = #(
#{ Name = 'First'; Passed = $True }
#{ Name = 'First'; Passed = $False }
);
# Count names in array
$counts = #{}
foreach ($object in $array) {
$name = $object.Name
if (-not $counts.ContainsKey($name)) {
$counts[$name] = 0
}
$counts[$name] += 1
}
# Output duplicates
foreach ($name in $counts.Keys) {
if ($counts[$name] -gt 1) {
Write-Output ("Duplicate Name: " + $name)
}
}
Output:
Duplicate Name: First

Powershell Error creating entry through WSDL SOAP connection

So I am getting a conversion error when submitting something via WSDL SOAP in PowerShell.
I am trying to create entries in a system called Kablink however although the entry has all the values etc. in the variable $soapData when I attempt the actual creation I get an error about being unable convert argument entry from one value to another even though both specified values are the same.
Cannot convert argument "entry", with value: "folder_addEntry.FolderEntry", for "folder_addEntry" to type "folder_addEntry.FolderEntry": "Cannot convert the "folder_addEntry.FolderEntry" value of type
"folder_addEntry.FolderEntry" to type "folder_addEntry.FolderEntry"."
Any ideas would be hugely appreciated as this is now driving me insane.
Below is the script I am using
$kablinkSoapUrl = "http://servername/ssr/secure/ws/TeamingServiceV1?wsdl"
$kablinkConnector = New-WebServiceProxy -Uri $kablinkSoapUrl -Credential $Credential -namespace "folder_addEntry"
#Set Binder ID to Shift Rota entries folder
$binderID = 155
#write-host $soapData
#Import Objects and Populate Default Values
$soapDescription = New-Object ("folder_addEntry.Description")
$soapDescription.text = "Some Description"
$soapDescription.format = 0
$soapAttachment = New-Object("folder_addEntry.attachmentsField")
$soapAttachment.name = ""
$soapAttachment.type = ""
$soapAttachment.attachments = #()
$soapCreate = New-Object ("folder_addEntry.Timestamp")
$soapCreate.date = get-date
$soapCreate.principal = ""
$soapCreate.principalId = 2
$soapModify = New-Object ("folder_addEntry.Timestamp")
$soapModify.date = get-date
$soapModify.principal = ""
$soapModify.principalId = 2
$soapRating = New-Object ("folder_addEntry.AverageRating")
$soapRating.averageRating = ""
$soapRating.ratingCount = ""
$soapCustBool = New-Object ("folder_addEntry.CustomBooleanField")
$soapCustBool = #()
$soapCustDate = New-Object ("folder_addEntry.CustomDateField")
$soapCustDate = #()
$soapCustEven = New-Object ("folder_addEntry.CustomEventField")
$soapCustEven = #()
$soapCustLong = New-Object ("folder_addEntry.CustomLongArrayField")
$soapCustLong = #()
$soapCustStriArr = New-Object ("folder_addEntry.CustomStringArrayField")
$soapCustStriArr = #()
$soapCustStr = New-Object ("folder_addEntry.CustomStringField")
$soapCustStr = #()
#Construct Entry
$soapData = New-Object -TypeName folder_addEntry.FolderEntry
$soapData.attachmentsField = $soapAttachment
$soapData.averageRating = $soapRating
$soapData.creation = $soapCreate
$soapData.customBooleanFields = $soapCustBool
$soapData.customDateFields = $soapCustDate
$soapData.customEventFields = $soapCustEven
$soapData.customLongArrayFields = $soapCustLong
$soapData.customStringArrayFields = $soapCustStriArr
$soapData.customStringFields = $soapCustStr
$soapData.definitionId = "8a8ab38c62d40d5c0162fc8330eb01ad"
$soapData.description = $soapDescription
$soapData.entityType = "fileEntry"
$soapData.eventAsIcalString = 0
$soapData.family = "file"
$soapData.id = ""
$soapData.modification = $soapModify
$soapData.parentBinderId = 155
$soapData.permaLink = ""
$soapData.title = "PowerShell Entry"
$soapData.docLevel = 1
$soapData.docNumber = ""
$soapData.href = ""
$soapData.preDeleted = 0
$soapData.preDeletedBy = ""
$soapData.preDeletedWhen = ""
$soapData.reservedBy = ""
$soapData.workflows = #()
# Print Soap Data
$soapData
# Submit Entry
$kablinkConnector.folder_addEntry("",$soapData,"")
Thanks in advance.
Added -class "folder_getEntry" when creating the $kablinkConnector variable making the line
$kablinkConnector = New-WebServiceProxy -Uri $kablinkSoapUrl -Credential $Credential -namespace "folder_addEntry" -class "folder_getEntry"
And now it works