PowerShell Script getting JSON output to use in API - node.js

I am running a PowerShell script on a server to check on other machines on the network.
I want the result of the check to be outputted in JSON format so I can send this JSON data via an api request to a web dashboard build with angular.
My set up:
Get-Request from Angular front end -> Express server -> run PowerShell script with Node-Powershell
Now I want to return the result in proper format to be used in the front end. What is the best way to accomplish this? I want to use the data to fill a material data table.
PowerShell Script status.ps1:
$Computers = #('ExampleComputer01', 'ExampleComputer02', 'ExampleComputer03', 'ExampleComputer04')
foreach ($computer in $Computers) {
gsv -cn $computer -Name ExampleProgram -ErrorAction 'SilentlyContinue'| select-object machinename, status | ConvertTo-Json
}
Api from express server.js (using Node-Powershell):
app.get('/api/psjson', (request, response) => {
ps.addCommand('./status.ps1');
ps.invoke().then(output => {
console.log(output);
response.send(JSON.parse(output));
}).catch(err => {
console.log(err);
response.send(err);
ps.dispose();
});
});
I tried using | ConvertTo-Json inside the loop but it is causing in error in node:
SyntaxError: Unexpected token { in JSON at position 55
at JSON.parse ()

Please try the following:
$Computers = #('ExampleComputer01', 'ExampleComputer02', 'ExampleComputer03', 'ExampleComputer04')
$Results = #()
foreach ($computer in $Computers) {
$Result = $null
$Result = gsv -cn $computer -Name ExampleProgram -ErrorAction 'SilentlyContinue'| select-object machinename, status
If ($Result -ne $null){
$Results += $Result
}
}
$Results | ConvertTo-Json
This builds an array of the results and then converts the array to JSON.
I think the issue you are experiencing is due to converting inside a loop and therefore the structure is incorrect.

CraftyB's answer diagnoses the problem correctly, but there's a simpler, more PowerShell-idiomatic solution that uses a single pipeline:
$computers = 'ExampleComputer01', 'ExampleComputer02', 'ExampleComputer03', 'ExampleComputer04'
gsv -cn $computers -Name Example -ErrorAction SilentlyContinue |
| Select-Object machinename, status |
ConvertTo-Json
The crucial aspect is that all input objects are passed to a single ConvertTo-Json call - see the bottom section for an explanation.
In Windows PowerShell, Get-Service (gsv) the -ComputerName (-cn) parameter directly accepts an array of computer names.
Note: In PowerShell (Core) 7+, this form of remoting is no longer supported, so there is no -ComputerName parameter; there, assuming that the target computers are set up for PowerShell remoting, you could use:
Invoke-Command -ComputerName $computers { Get-Service -Name Example -ErrorAction SilentlyContinue }
As for what you tried:
If you call ConvertTo-Json inside a loop, per input object, you will implicitly output multiple, independent JSON strings instead of a single JSON string representing the input objects as a JSON array:
Given the following sample input objects:
$objects = [pscustomobject] #{ foo=1 }, [pscustomobject] #{ foo=2 }
It is the difference between:
# !! BROKEN: *multiple* ConvertTo-Json calls.
# !! When the result is stringified, you get a space-separated list of the
# !! individual JSON strings, which is NOT valid JSON.
PS> #"
$(
foreach ($object in $objects) { $object | ConvertTo-Json -Compress }
)
"#
{"foo":1} {"foo":2} # NOT valid JSON.
and:
# OK: *Single* ConvertTo-Json call, which outputs a valid JSON array.
PS> $objects | ConvertTo-Json -Compress
[{"foo":1},{"foo":2}] # OK: valid JSON array.

Related

Powershell 7 - Get-AzureADUser, AssignedLicenses, Convert Class to Object

When I retieve an AzureADUser while using the AzureAD module (imported with -UseWindowsPowershell), and trying to retrieve the property for AssignedLicenses, they return as a Class instead of an obect (e.g. just a string?)
class AssignedLicense {
DisabledPlans: System.Collections.Generic.List`1[System.String]
SkuId: zzzzzzzz-2c81-4ef7-yyyy-5b5392b571df}
class AssignedLicense {
DisabledPlans: System.Collections.Generic.List`1[System.String]
SkuId: zzzzzzz-fae0-4ca2-xxxx-7a907fd6c235
}
How would I convert such a thing to something I can actually use (e.g. an object?)
At first I thought it was JSON, but the ConvertFrom-JSON cmdlet gave errors on the notation, as far as I am aware there is no ConvertFrom-Class included in normal powershell :)
Did also try to just remove all the text and keep anything after SkuID, but that proves a challenge when the DisabledPlans is bigger/smaller than I expect.
Output received by running these commands:
$userList = Get-AzureADUser -filter "AccountEnabled eq true"
foreach ($u in $userList) {
$Assigned = ($u | Select-Object AssignedLicenses).AssignedLicenses
}
I tried to reproduce the scenario in my environment:
I tried the same script as yours:
$userList = Get-AzureADUser -filter "AccountEnabled eq true"
foreach ($u in $userList) {
$Assigned =($u | Select-Object AssignedLicenses ).AssignedLicenses
$Assigned
}
Getting expected result :
Alternately I tried with below commands:
$users = Get-AzureADUser -filter "AccountEnabled eq true"
$users | ForEach-Object {
foreach($user in $_.AssignedLicenses){
New-object -typename PSobject -property #{
ID = $_.Id
DisplayName = $_.DisplayName
DisabledPlans = $user.DisabledPlans
SkuId = $user.SkuId
}
}} | Sort-Object ID, DisplayName, DisabledPlans, SkuId | Export-Csv -Path C:\Assignedlicense2.csv -NoTypeInformation
I am getting the same result type for disabled plans System.Collections.Generic.List'1[System.String] as below image:
You can try the second way and check from your end or adding -join parameter to each object .
Reference: powershell - How to convert "System.Collections.Generic.List`1[System.String]" into readable format Using Get-AzureADMSConditionalAccessPolicy cmdlet - Stack Overflow
Or directly use:
$userList = Get-AzureADUser -filter "AccountEnabled eq true"
foreach ($u in $userList) {
$Assigned =$u.AssignedLicenses
$Assigned
}

Trying to list Azure Virtual Network and export to CSV using Powershell

I am trying to create a script that can list all the Azure virtual networks and export it into Csv using Powershell.
$day = Get-Date -Format " MMM-yyyy"
$path = "C:\Users\admin-vishal.singh\Desktop\Test\Report\"+ "$day-Vnet-Report.csv"
foreach ($Sub in $Subs) {
Select-AzSubscription -SubscriptionName $Sub.Name | Out-Null
$resource_grps = Get-AzResourceGroup
foreach ($resource_grp in $resource_grps) {
$networks = Get-AzVirtualNetwork
foreach ($vnet in $networks)
{
$null = Get-AzVirtualNetwork |Select-Object SubscriptionName,ResourceGroupName,Name,AddressSpace,Subnets,SubnetAddressSpace,RouteTable | Export-CSV -Path $path -NoTypeInformation -Encoding ASCII -Append
}
}
}
I am not able to retrieve data in the right format & getting errors when retrieving data.
Below is snippet of data
Lots of values I am not able to retrieve like Subnet AddressSpace, Route Tables and Routes.
Building on what Jim Xu provided, you don't need to have a separate loop for each ResourceGroup. Get-AzVirtualNetwork will return all virtual networks for the entire subscription. Also, you'll need an expression for the SubscriptionName in the Select-Object, so the code would look like this:
foreach ($Sub in $Subs) {
Select-AzSubscription -SubscriptionName $Sub.Name | Out-Null
Get-AzVirtualNetwork |
Select-Object `
#{label='SubscriptionName'; expression={$Sub.Name}}, `
ResourceGroupName, `
Name, `
#{label='AddressSpace'; expression={$_.AddressSpace.AddressPrefixes}}, `
#{label='SubnetName'; expression={$_.Subnets.Name}}, `
#{label='SubnetAddressSpace'; expression={$_.Subnets.AddressPrefix}} |
Export-CSV -Path $path -NoTypeInformation -Encoding ASCII -Append
}
When we call export-csv command, the property values are converted to strings using the ToString() method. But the result of Get-AzVirtualNetwork are object, we cannot directly convert the value to string. For more details, please refer to here and here
So regarding the issue, I suggest you create a custom object with the information you need then save it into csv.
For exmaple
$vents =Get-AzVirtualNetwork|
Select-Object SubscriptionName,ResourceGroupName,Name, #{
label='AddressSpace'
expression={$_.AddressSpace.AddressPrefix}}, #{
label='SubnetName'
expression={$_.Subnets.Name}
}, #{
label='SubnetAddressSpace'
expression={$_.Subnets.AddressPrefix}
}
$vents | convertto-csv

How to output hash table query result into Out-GridView?

I wish to export a hashtable result into Out-GridView using the Powershell.
The purpose of the below script is to export the Azure VM tags to Out-GridView, it throws error like the below blank result:
Error on the console:
Out-GridView : Syntax error in PropertyPath 'Syntax error in Binding.Path '[ Product] ' ... '(Tag)'.'.
At line:46 char:19
+ $Output | Out-GridView #Export-Csv -Path c:\temp\1a.csv -appe ...
+ ~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [Out-GridView], InvalidOperationException
+ FullyQualifiedErrorId : ManagementListInvocationException,Microsoft.PowerShell.Commands.OutGridViewCommand
This is the actual script which was executed under the Global Administrator role:
<#
.AUTHOR: https://stackoverflow.com/users/13390556/lukasz-g
#>
$Subscription = Get-AzSubscription | Out-GridView -Title 'Select subscription' -OutputMode 'Multiple'
# Initialise output array
$Output = #()
if ($Subscription) {
foreach ($item in $Subscription) {
$item | Select-AzSubscription
# Collect all the resources or resource groups (comment one of below)
$Resource = Get-AzResource
#$Resource = Get-AzResourceGroup
# Obtain a unique list of tags for these groups collectively
$UniqueTags = $Resource.Tags.GetEnumerator().Keys | Get-Unique -AsString | Sort-Object | Select-Object -Unique | Where-Object { $_ -notlike "hidden-*" }
# Loop through the resource groups
foreach ($ResourceGroup in $Resource) {
# Create a new ordered hashtable and add the normal properties first.
$RGHashtable = New-Object System.Collections.Specialized.OrderedDictionary
$RGHashtable.Add("Name", $ResourceGroup.ResourceGroupName)
$RGHashtable.Add("Location", $ResourceGroup.Location)
$RGHashtable.Add("Id", $ResourceGroup.ResourceId)
$RGHashtable.Add("ResourceType", $ResourceGroup.ResourceType)
# Loop through possible tags adding the property if there is one, adding it with a hyphen as it's value if it doesn't.
if ($ResourceGroup.Tags.Count -ne 0) {
$UniqueTags | Foreach-Object {
if ($ResourceGroup.Tags[$_]) {
$RGHashtable.Add("[$_] (Tag)", $ResourceGroup.Tags[$_])
}
else {
$RGHashtable.Add("[$_] (Tag)", "-")
}
}
}
else {
$UniqueTags | Foreach-Object { $RGHashtable.Add("[$_] (Tag)", "-") }
}
# Update the output array, adding the ordered hashtable we have created for the ResourceGroup details.
$Output += New-Object psobject -Property $RGHashtable
}
# Sent the final output to CSV
$Output | Out-GridView #Export-Csv -Path c:\temp\1a.csv -append -NoClobber -NoTypeInformation -Encoding UTF8 -Force
}
}
$RGHashtable.Add("[$_] (Tag)"
In above code, You are trying to add something like below :
In the output
Removed everthing and I tested with simple statements
$Output = #()
$RGHashtable = New-Object System.Collections.Specialized.OrderedDictionary
$RGHashtable.Add("[Testing] (Name)", "Temporary")
$Output += New-Object psobject -Property $RGHashtable
$Output | Out-GridView
I was provided with the same error.
After couple of testing, understood the error only occurs when there is a combination "[SomeString](SomeString)" --- [...](....) in the string.
The Out-GridView is trying to parse the "[<SomeString>](<SomeString>)" and hence the error.
You could try any 1 of the below combination in your code :
$RGHashtable.Add("[$_] [Tag]", $ResourceGroup.Tags[$_])
OR
$RGHashtable.Add("{$_} (Tag)", $ResourceGroup.Tags[$_])
OR
$RGHashtable.Add("[$_] [Tag]", $ResourceGroup.Tags[$_])
This should resolve your issue.
you will have change in 3 instances in your code if I am not wrong.

In Azure, To get Load balancer details in to csv file using powershell script

I wrote a powershell script in Azure DevOps pipeline to get Load Balancer details like FrontendIPConfigurationsName,FrontendIPAddress in to csv file. AM getting those details but FrontendIPConfigurationsNames which starts with same name like "ers-A1,ers-B1,ers-C1,ers-D1" are coming in same row. But I want to get them in different rows.Please suggest
$excel = #()
LBlist = Get-AZLoadBalancer | Where-Oject {$_.ResourceGroupName -clike '$(grp-wildcard)'} | Select-Object
foreach ($LB in LBlist)
$Array =""| Select-Object ResourceGroupName, FrontendIPConfigurationsName,FrontendIPAddress
$Array.ResourceGroupName =$LB.ResourcegroupName
$Array.FrontendIPConfigurationsName = ($LB.FrontendIpConfigurationsName.name -join ',')
$Array.FrontendIPAddress =($LB.FrontendIpConfigurations.PrivateIpAddress -join ',')
}
$excel +=$Array
$excel |Format-Table ResourceGroupName, FrontendIPConfigurationsName,FrontendIPAddress
$excel | Export-Csv -NTI -Path "($Build.ArtifactStagingDirectory)/LBlist.csv
You can do something similar to this to get the objects on separate rows.
$LBlist = Get-AZLoadBalancer | Where-Object { $_.ResourceGroupName -clike '$(grp-wildcard)' }
$LBlist | Export-Csv -NTI -Path "$(Build.ArtifactStagingDirectory)/LBlist.csv"
Mock for Get-AZLoadBalancer (not sure if it's entirely accurate as I've never used Az Powershell)
function Get-AZLoadBalancer() {
$obj = #(
[PSCustomObject] #{
ResourceGroupName = '$(grp-wildcard)'
FrontendIPConfigurationsName = 'ers-A1'
FrontEndIPAddress = '1.2.3.4'
},
[PSCustomObject] #{
ResourceGroupName = '$(grp-wildcard)'
FrontendIPConfigurationsName = 'ers-B1'
FrontEndIPAddress = '1.2.3.5'
}
)
return $obj
}

What is the best way to collect and transform output from multiple PowerShell threads?

I am new to PowerShell scripting and would like to do the following:
Given a list of config names and servers, return the values for the configs from each server.
Transform them in such a way to group them by config name, and not server.
Currently, I have a script that spawns one job per server and calls a script remotely on the server to return the list of configs for that server.
However, I do not know how to aggregate and transform the output from these jobs so that instead of getting config names by server, I would like to sort them by config name first, then server.
Current output:
Server1:
Config1 = 'abc'
Config2 = 'def'
Server2:
Config1 = 'xyz'
Config2 = '123'
Desired output:
Config1:
Server1 : 'abc'
Server2 : 'xyz'
Config2:
Server1 : 'def'
Server2 : '123'
I don't want to iterate over the config names because that would waste time in connecting to the server for every call. Therefore I'd like to iterate over the servers and do some kind of transformation.
I'm wondering if this is a matter of having each job return some kind of dictionary, then iterate over them after all the threads finish to transform?
Here is the code that calls the jobs:
$all_servers = #('server1', 'server2')
$config_names = #('config1', 'config2')
foreach($servername in $all_servers) {
Start-Job -FilePath C:\scripts\get_config_from_servers.ps1
-ArgumentList $servername,$config_names
}
Get-Job | Wait-Job
Get-Job | Receive-Job | Out-GridView
Here is the job script:
Param($servername,$config_names)
$session = Get-Session -computername $servername
-username $$$$
-pwd ####
try {
$sb = {
param($servername,$config_names)
$output = #{}
foreach ($cfg in $config_names) {
$config_value = Get-Config -configname $cfg
$output.Add("$servername : $cfg", "($config_value)")
}
write-host $output | Out-String
return $output | Out-String
}
$out = Invoke-Command -session $session
-ScriptBlock $sb
-ArgumentList $servername,$config_names
write-host $out
return $out
}
finally {
Remove-PSSession $session
}
Instead of making a hash table and converting to a string you could create some custom object in you job script just like this SO Question
Instead of this:
$output = #{}
foreach ($cfg in $config_names) {
$config_value = Get-Config -configname $cfg
$output.Add("$servername : $cfg", "($config_value)")
}
write-host $output | Out-String
return $output | Out-String
You could try something like this:
$output = New-Object System.Object
Add-Member -MemberType NoteProperty -Name Server -Value $servername -InputObject $output
foreach ($cfg in $config_names) {
$config_value = Get-Config -configname $cfg
Add-Member -MemberType NoteProperty -Name "Config$cfg" -Value $config_value -InputObject $output
}
write-host $output
return $output
I can't test this accurately as i'm not sure what Get-Config is but hopefully it should be enough to get you thinking.

Resources