Powershell get Zabbix Hosts via API - powershell

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

Related

How can I access Powershell PSCustomObject properties?

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'

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

How to Send & receive Websocket client Requests, responses in JSON format in PowerShell

With the below PowerShell commands, I am able to open the connection using Websocket.
$mySock = New-Object System.Net.WebSockets.ClientWebSocket
$CT = New-Object System.Threading.CancellationToken($false)
$CTS = New-Object System.Threading.CancellationTokenSource
$mySock.Options.UseDefaultCredentials = $true
$connectTask = $mySock.ConnectAsync($URL, $CTS.Token)
The result as below:
PS C:\PS1> $mySock
Options : System.Net.WebSockets.ClientWebSocketOptions
CloseStatus :
CloseStatusDescription :
SubProtocol :
State : Open
PS C:\PS1>
My question is how do I send a request JSON format & also expect to receive the response in JSON format? Any help/guide would be much appreciated.
The document states as below:
LOGIN Request
When WebSocket connection is opened, the first command to the Streamer Server must be a LOGIN command with the following parameters.
Sample login request:
{
"service": "ADMIN",
"requestid": "1",
"command": "LOGIN",
"account": "your_account",
"source": "your_source_id",
"parameters": {
"token": "027363a5a5acd5",
"version": "1.0",
"credential": "userid%3DMYUSER20%26token%abcd"
}
}
Sample login successful response:
{
"response": [
{
"service": "ADMIN",
"requestid": "1",
"command": "LOGIN",
"timestamp": 1400607506478,
"content": {
"code": 0,
"msg": "02-1"
}
}
]
}
Not sure if you figured it out yet or not. How I did it was to convert the logon request to JSON and encode it in UTF8 and put it into an 1024 bit array segment using example code I found. Then I send it asynchronously back to the target.
$Array = [byte[]] #(,0) * 1024
$Command = [System.Text.Encoding]::UTF8.GetBytes($LoginJSON)
$Send = New-Object System.ArraySegment[byte] -ArgumentList #(,$Command)
$Conn = $WS.SendAsync($Send, [System.Net.WebSockets.WebSocketMessageType]::Text, $True, $CT)
When you receieve the response it must also be an array segement data type.
$Recv = New-Object System.ArraySegment[byte] -ArgumentList #(,$Array)
$Conn = $WS.ReceiveAsync($Recv, $CT)
This line will then get the return
[System.Text.Encoding]::utf8.GetString($Recv.array)
To keep getting all future messages I then created a Do until loop that continues to run until the web socket is no longer open.

How to figure out the physical machines on which the build tasks were run with Azure DevOps REST Api?

We have an on-premise Azure DevOps 2019. I need to know what builds run on what machines, not agents.
Motivation: When the builds are slow I would like to know what builds are running on a particular physical machine (which could be a virtual machine, but for this purpose I call them physical to distinguish from the agents). This could help us figure out if some builds should not be running on the same machines.
Given a build object, I can extract the worker name from the build tasks:
C:\> (Invoke-RestMethod $Build._links.timeline.href -UseDefaultCredentials).records.workerName |? { $_ } | sort -unique
TDC5DFC1BLD10_02
C:\>
So, I know all the tasks in the build were run on the build agent TDC5DFC1BLD10_02. But I want to know the physical machine name. So, I query the agent using its name:
C:\> (Invoke-RestMethod "$TfsInstanceUrl/_apis/distributedtask/pools/$($build.queue.pool.id)/agents?agentName=TDC5DFC1BLD10_02" -UseDefaultCredentials).value
_links : #{self=; web=}
maxParallelism : 1
createdOn : 2019-05-16T19:33:31.567Z
authorization : #{clientId=c4cebb22-e14f-4fdb-844c-079150766efc; publicKey=}
id : 308
name : TDC5DFC1BLD10_02
version : 2.131.0
osDescription : Microsoft Windows 10.0.14393
enabled : True
status : online
provisioningState : Provisioned
C:\>
But it does not give me the physical machine. I have no idea what queue or pool is, but I can check them too:
C:\> $Build.queue | ConvertTo-Json
{
"id": 1929,
"name": "GC-Master-TDC5DFC1BLD08-11",
"pool": {
"id": 90,
"name": "GC-Master-TDC5DFC1BLD08-11"
}
}
C:\> Invoke-RestMethod "$TfsInstanceUrl/SharpTop/_apis/distributedtask/queues/1929" -UseDefaultCredentials | ConvertTo-Json
{
"id": 1929,
"projectId": "ecff38d6-a219-4739-8b97-5e5d8d00e7ed",
"name": "GC-Master-TDC5DFC1BLD08-11",
"pool": {
"id": 90,
"scope": "a984b12d-89d2-47d6-998e-b9bfaa69ee85",
"name": "GC-Master-TDC5DFC1BLD08-11",
"isHosted": false,
"poolType": "automation",
"size": 8
}
}
C:\> Invoke-RestMethod "$TfsInstanceUrl/_apis/distributedtask/pools/90" -UseDefaultCredentials
createdOn : 2019-05-16T19:13:33.493Z
autoProvision : True
autoSize :
agentCloudId :
createdBy : #{displayName=Doe, John;
url=http://tdc1tfsapp01.xyz.com:8080/tfs/_apis/Identities/cc71b5eb-9dd6-436a-b722-6790d7ef4877; _links=;
id=cc71b5eb-9dd6-436a-b722-6790d7ef4877; uniqueName=xyz\P120A76; imageUrl=http://tdc1tfsapp01.xyz.com:8080/t
fs/_api/_common/identityImage?id=cc71b5eb-9dd6-436a-b722-6790d7ef4877;
descriptor=win.Uy0xLTUtMjEtNDg3MjU1NDc3LTE2MzE1MjcwMjItMzUxNzQ0NDQyLTE1NzQy}
owner : #{displayName=Doe, John;
url=http://tdc1tfsapp01.xyz.com:8080/tfs/_apis/Identities/cc71b5eb-9dd6-436a-b722-6790d7ef4877; _links=;
id=cc71b5eb-9dd6-436a-b722-6790d7ef4877; uniqueName=xyz\P120A76; imageUrl=http://tdc1tfsapp01.xyz.com:8080/t
fs/_api/_common/identityImage?id=cc71b5eb-9dd6-436a-b722-6790d7ef4877;
descriptor=win.Uy0xLTUtMjEtNDg3MjU1NDc3LTE2MzE1MjcwMjItMzUxNzQ0NDQyLTE1NzQy}
id : 90
scope : a984b12d-89d2-47d6-998e-b9bfaa69ee85
name : GC-Master-TDC5DFC1BLD08-11
isHosted : False
poolType : automation
size : 8
And I still have no idea of the physical machine. How do I do it?
Agent.ComputerName holds the value of hostname of the agent which executes your job.

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.