How can I access Powershell PSCustomObject properties? - powershell

I'm processing through Powershell script some API result processing.
API data (json) come from this :
$tree = Invoke-WebRequest -Uri "xxxxxxxxmonURLxxxxxxxxxx/130333"
$children = ($tree.Content | ConvertFrom-Json).data.12345.children
Then I loop through $children object using | ForEach
$_ within loop has "147852" as $_.Name, and the following object as $_.Definition
I'd like to parse the object within $_.Definition but cannot figure out how to access it.
The Definition object looks like this:
TypeName : System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
147852 NoteProperty System.Management.Automation.PSCustomObject 147852=#{nodeType=node; name=test1; flag=N0; creationDate=2022-02-17T14:50:16+00:00; hasAlerts=False; children=}
And I wish to access any property within the 147852 key (such as nodeType, name, flag, ..., children).
$_.147852 outputs an error saying 147852 was not found.
Thanks.
API json returned:
{
"data": {
"130333": {
"nodeType": "node",
"name": "Test name",
"flag": "N0",
"children": {
"147852": {
"nodeType": "node",
"name": "test1",
"flag": "N0",
"hasAlerts": false,
"children": {
"147853": {
"nodeType": "node",
"name": "test2",
"flag": "N0",
"children": {
"NP12-N9-S4": {
"nodeType": "agent",
"name": "Win10",
"type": "S"
}
}
}
}
}
}
}
}

Jeroen Mostert provided the crucial pointer in the comments, and Bender the Greatest links to what is effectively a duplicate question, but given that the latter is hashtable-focused, let me recapitulate the problem in the context of custom objects ([pscustomobject]):
Leaving the accidental use of Get-Member out of the picture, your problem ultimately boils down to a parser bug in PowerShell (see GitHub issue #14036):
To avoid it, quote property names that look like numbers - e.g., to access property 147852 on object $obj, use $obj.'147852'
Strictly speaking, the bug only surfaces if you attempt an additional (nested) property access:
# Nested sample custom object.
$obj = [pscustomobject] #{ 147852 = [pscustomobject] #{ name = 'test1' } }
# OK - number-like property is accessed without quoting, but *not nested*.
# However, $obj.'147852' is preferable.
$obj.147852
# *Nested* property access:
# !! BUG triggers error: "Missing property name after reference operator."
$obj.147852.name
# OK: Quoting avoids the problem.
$obj.'147852'.name # -> 'test1'

Related

Azure Function App Push-OutputBinding Adding Subscription Information to JSON Output using Powershell

I have written several Azure Functions over the past year in both powershell and C#. I am currently writing an API that extracts rows from a Storage Account Table and returns that data in a JSON format.
The data pulls fine.
The data converts to JSON just fine.
A JSON formatted response is displayed - which is fine - but the Push-OutputBinding shoves in additional data to my original JSON data - account information, environment information, subscription information, and tenant information.
I've tried a number of different strategies for getting past this. I gave up on using C# to interact with the Tables because the whole Azure.Data.Tables and Cosmos tables packages are a hot mess with breaking changes and package conflicts and .Net 6 requirements for new functions apps. So please don't offer up a C# solution unless you have a working example with specific versions for packages, etc.
Here is the code:
Note that I have verified that $certData and $certJson properly formatted JSON that contain only the data I want to return.
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$filter = $Request.Query.Filter
if (-not $filter) {
$filter = "ALL"
}
$certData = GetCerts $filter | ConvertTo-Json
#$certJson = $('{ "CertData":"' + $certData + '" }')
$body = "${CertData}"
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
ContentType = "application/json"
Body = $body
})
When I call the httpTrigger function, the response looks like this:
{ "CertData":"[
{
"Name": "MySubscriptionName blah blah",
"Account": {
"Id": "my user id",
"Type": "User",
....
},
"Environment": {
"Name": "AzureCloud",
"Type": "Built-in",
...
},
"Subscription": {
"Id": "SubscriptionID",
"Name": "SubscriptionName",
....
},
"Tenant": {
"Id": "TenandID",
"TenantId": "TenantId",
"ExtendedProperties": "System.Collections.Generic.Dictionary`2[System.String,System.String]",
...
},
"TokenCache": null,
"VersionProfile": null,
"ExtendedProperties": {}
},
{
"AlertFlag": 1,
"CertID": "abc123",
"CertName": "A cert Name",
"CertType": "an assigned cert type",
"DaysToExpire": 666,
"Domain": "WWW.MYDOMAIN.COM",
"Expiration": "2033-10-04T21:31:03Z",
"PrimaryDomain": "WWW.MYDOMAIN.COM",
"ResourceGroup": "RANDOM-RESOURCES",
"ResourceName": "SOMERESOURCE",
"Status": "OK",
"Subscription": "MYSUBSCRIPTIONNAME",
"Thumbprint": "ABC123ABC123ABC123ABC123ABC123",
"PartitionKey": "PARKEY1",
"RowKey": "ID666",
"TableTimestamp": "2022-02-03T09:00:28.7516797-05:00",
"Etag": "W/\"datetime'2022-02-03T14%3A00%3A28.7516797Z'\""
},
...
Not only does the returned values add data I don't want exposed, it makes parsing the return data that I do want to get when I make API calls problematic.
How do I get rid of the data added by the Push-OutputBinding?
Was able to resolve issue by modifying run.ps1 as follows:
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$filter = $Request.Query.Filter
if (-not $filter) {
$filter = "ALL"
}
$certData = ( GetCerts $filter | Select-Object -Skip 1 )
#write-information $certData | Format-List
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
Body = $certData
})

Powershell Azure function times out on processing queue

I have Azure function written in powershell and processing seems to be stopping for no apparent reason with timeout. Message never gets even to start being processed. This does not seem to be code related since code never even have a change to start since the first line of code is Write-Host "PowerShell queue trigger function processed work item: $QueueItem" which does not appear in log
Host.json
{
"version": "2.0",
"functionTimeout": "00:04:00",
"managedDependency": {
"enabled": true
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
},
"extensions": {
"queues": {
"maxPollingInterval": "00:00:02",
"visibilityTimeout": "00:00:30",
"batchSize": 16,
"maxDequeueCount": 2,
"newBatchThreshold": 8
}
}
}
Function.json
{
"bindings": [
{
"name": "QueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "metadataservicequeue",
"connection": "useastbootdiag_STORAGE"
}
]
}
Script
# Input bindings are passed in via param block.
param([string] $QueueItem, $TriggerMetadata)
# Write out the queue message and insertion time to the information log.
Write-Host "PowerShell queue trigger function processed work item: $QueueItem"
Write-Host "Queue item insertion time: $($TriggerMetadata.InsertionTime)"
Write-Host "Starting executing Invoke-AzureRMCommand with parameters $($TriggerMetadata["VMName"]), $($TriggerMetadata["ResourceGroup"])"
$return = Invoke-AzureCommand -vmName $TriggerMetadata["VMName"] -resourceGroup $TriggerMetadata["ResourceGroup"]
Write-Host "Finished executing Invoke-AzureRMCommand with parameters $($TriggerMetadata["VMName"]), $($TriggerMetadata["ResourceGroup"])"
$json = #"
[
{
"Return" : $($return | convertto-json),
"VMName" : "$($TriggerMetadata["VMName"])",
"ResourceGroup" : "$($TriggerMetadata["ResourceGroup"])"
}
]
"#
Write-Host "Outputing following to Log Analytics $json"
Post-LogAnalyticsData -customerId $env:CustomerID -sharedKey $env:SharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType "MetaDataLog"
Powershell Azure Functions by default are not scaling well on consumption plan and poorely scale on dedicated plan due to default settings. If you function is not CPU bound and expected to scale well due to high number of queue items. You'd need to modify FUNCTIONS_WORKER_PROCESS_COUNT (https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings) to higher number then default 1 and increase value of PSWorkerInProcConcurrencyUpperBound (https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-powershell#concurrency) to higher number of default 1

Powershell get Zabbix Hosts via API

Im trying to use powershell to get host data from the Zabbix API.
I want to get the following columns back for host groups 15, 24, 26:
hostid
host
status
interfaceid
ip
dns
useip
If I use Postman to submit the query, I would submit the following which works:
{
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": [
"hostid",
"host",
"status"
],
"groupids": [15, 24, 26],
"selectInterfaces": [
"interfaceid",
"ip",
"dns",
"useip"
]
},
"id": 2,
"auth": "xxxxxxxxxxxxx"
}
So far I have the following powershell which returns a lot of information
$params.body = #{
"jsonrpc"= "2.0"
"method"= "host.get"
"params"= #{
output = "extend"
selectHosts = "extend"
}
auth= "xxxxxxxxxxxxx"
id= 2
} | ConvertTo-Json
$result = Invoke-WebRequest #params
Write-Host $result
I'm having trouble understanding how to request only the information I want, I've not done a powershell script like this before so would appreciate any guidance.
You need to build your $params.body with the same fields and options you used in Postman:
$params.body = #{
"jsonrpc"= "2.0"
"method"= "host.get"
"params"= #{
output = #( "host", "hostid", "status" )
selectInterfaces = #( "interfaceid", "ip", "dns", "useip" )
groupids = #( "15", "24", "26")
}
auth = xxxxxxxxxxxxx
id = 2
} | ConvertTo-Json
You should get something like:
hostid host status interfaces
------ ---- ------ ----------
10017 somehost 0 {#{interfaceid=30251; ip=192.168.10.15; dns=; useip=1}}
10051 anotherone 0 {#{interfaceid=12353; ip=10.10.10.20; dns=tanotherone.mydomain.com; useip=1}}
10054 whatisthis 0 {#{interfaceid=43262; ip=172.16.1.20; dns=; useip=1}}

How do I perform a logical If..then in powershell against JSON data?

I'm trying to loop through a JSON array of desired registry values, and then inspect the registry value for the correct setting.
The issue I have is that I'm not correctly defining the 'if..()' logic test in my loop. The problem code is located in the line: if($protocols[$i][$tempdisabledKVString] -eq "true")
I have the following object:
$protocolsJSON = #"
[
{
"Name": "TLS 1.2",
"Server-Enabled": True,
"Client-Enabled": True
}
]
"#
$protocols = $protocolsJSON | ConvertFrom-Json
Which fails the nested if statement below (undesired behavior)
elseif ($isDefaultDisabled -eq 0) # Protocol is manually enabled in registry (part 1.A)
{
if($protocols[$i][$tempdisabledKVString] -eq "True") # Protocol should be manually enabled in registry (part 1.B)
{
# For TLS 1.2 to be enabled and negotiated on servers that run Windows Server 2008 R2,
# you MUST create the DisabledByDefault entry in the appropriate subkey (Client, Server)
# and set it to "0". The entry will not be seen in the registry and it is set to "1" by default.
$errorString = "Warning: Protocol is only partially enabled."
$TLSProtocolResult.Errors = $errorString
}
else
{
write-host "DEBUG " $protocols[$i][$tempdisabledKVString]
write-host "DEBUG " $protocols[$i]
write-host "DEBUG " [$tempdisabledKVString]
$errorString = "Error: Protocol should be disabled."
$TLSProtocolResult.Errors = $errorString
}
}
Which produces the following output
DEBUG
DEBUG #{Name=TLS 1.2; Server-Enabled=True; Client-Enabled=True}
DEBUG [Server-Disabled]
DEBUG
DEBUG #{Name=TLS 1.2; Server-Enabled=True; Client-Enabled=True}
DEBUG [Client-Disabled]
How do I edit the IF statement so that I can test the true/false status of $protocols[$i][$tempdisabledKVString]?
The problem is you're trying to access a property as if it were a nested array.
Try this:
$protocolsJSON = #"
[
{
"Name": "TLS 1.2",
"Server-Enabled": true,
"Client-Enabled": true
}
]
"#
$protocols = $protocolsJSON | ConvertFrom-Json
$property = "Server-Enabled"
Write-Host "RESULT: $($protocols[0].$property)"
Your issue is most likely the JSON not being parsed. Try including quotes around your values. i.e. replace: "Server-Enabled": True, with "Server-Enabled": "True",.
Also, when if $tempdisabledKVString is the name of a property, you need to access it as a property rather than an index. i.e. replace $protocols[$i][$tempdisabledKVString] with $protocols[$i]."$tempdisabledKVString".
Clear-Host
$protocolsJSON = #"
[
{
"Name": "TLS 1.2",
"Server-Enabled": "True",
"Client-Enabled": "True"
}
]
"#
$protocols = $protocolsJSON | ConvertFrom-Json
$i = 0
$tempdisabledKVString = 'Server-Enabled'
if ($protocols[$i]."$tempdisabledKVString" -eq 'True') {
":)"
} else {
":("
}
In theory these issues should have caused exceptions to be thrown. For some reason you're not seeing those, or that would have prompted you to find the cause. Please check the value of $ErrorActionPreference; by default it should be set to Continue; but it looks like it may have been updated to SilentlyContinue for your session. It's OK to have this setting in some scenarios; but generally better to have errors be thrown when they occur so that you can see what's going wrong.

Requesting help to parse a string

I am using Invoke-WebRequest to get back an HtmlWebResponseObject object. I want to get the value of the ID field, when the description field matches a particular string (in this case, "server1"). There are hundreds of results returned by Invoke-WebRequest (so the string is super long) Given the sample below, how would I extract the ID?
{
"status": 200,
"data": [
{
"nextRecipient": 0,
"clearSent": true,
"lastSentNotificationOn": 0,
"netscanVersion": "0",
"suppressAlertClear": "false",
"build": "19000",
"lastSentNotificationOnLocal": "",
"id": 6,
"resendIval": 15,
"watchdogUpdatedOn": "2016-04-11 10:28:02 MDT",
"escalatingChainId": 6,
"description": "domain\server1",
"ackComment": "",
"credential2": "",
"updatedOn": 1460392096,
"updatedOnLocal": "2016-04-11 10:28:16 MDT",
"agentConf": "product.code={guid}\r\n# Installer version, Shall not be modified\r\ninstaller.version=0001\r\n\r\n# Generated by Agent Configuration Wizard\r\nserver=url\r\ncompany=company\r\nid=6\r\ncredential==cred\r\n\r\n# logger settings. Set logger.size to 0 to no limitation on logger size, otherwise, size limited to Mbytes specified by that\r\nlogger.output=console\r\nlogger.logfile=\r\nlogger.size=64\r\nlogger.level=info\r\n\r\n#watchdog log level\r\nlogger.watchdog=info\r\n# for each component, add more detailed control here\r\n# e.g. \r\n# logger.level.controller=debug\r\n#\r\n# if not set, it will use default log level, i.e. value of logger.level\r\n#\r\n\r\n#if agent shall watch watchdog, default to false\r\nagentmonitorwatchdog=true\r\n\r\n#whether watchdog upgrades agent, default to true\r\nagent.autoupgrade=true\r\n\r\n#service connection timeouts. Default to 5 seconds for connecting and 30 seconds for sending / reading feeds from server\r\nservice.connect_timeout=5\r\nservice.read_timeout=30\r\n\r\n#SSL & Proxy settings\r\nssl.enable=true\r\nproxy.enable=false\r\nproxy.host= \r\nproxy.port=\r\nproxy.user=\r\nproxy.pass=\r\nproxy.exclude=\r\n\r\n#sbproxy settings\r\nsbproxy.address=127.0.0.1\r\nsbproxy.port=72\r\nsbproxy.logsize=64\r\nsbproxy.restartOn102=false\r\nsbproxy.pdhNoAuthentication=false\r\n\r\n#sbproxy connection pool settings\r\nsbproxy.pool.connections=50\r
Thanks.
Convert the response from a JSON string to an object and expand the relevant properties:
$response | ConvertFrom-Json |
Select-Object -Expand data |
Where-Object { $_.description -match 'server1' } |
Select-Object -Expand id