Search code examples
arraysjsonpowershellzabbix

How to output Get-ADComputer PowerShell results to JSON format containing IP and a parent object (Eg: Windows Servers)?


I did the following ps1 script:

Import-Module ActiveDirectory
Get-ADComputer -Filter "OperatingSystem -Like '*Windows Server*' -and Enabled -eq 'True'" | Select-Object Name, DNSHostName | ConvertTo-Json | Out-File "C:\adServers.json"

The output is:

[
    {
        "Name":  "exampleServer1",
        "DNSHostName":  "exampleServer1.domain.com"
    },
    {
        "Name":  "exampleServer2",
        "DNSHostName":  "exampleServer2.domain.com"
    } ]

It generates a list with all the objects that are described as "Windows Servers". But what i want to achieve is:

I will do more of this, but instead of Windows Servers i will also include lists with Linux Servers, and some other devices.

I also need that one object contains the value IP Address. (Name, DNSHostName, IP Address)

Since i will gather multiple devices with different descriptions, i need a title for each list i generate. Below a example for easier understanding...

Here's what i want to achieve, the output would be on the following JSON format:

{
    "Linux Servers": [{
            "Name": "exampleServer3",
            "DNSHostName": "exampleServer3.domain.com",
            "IP": "192.168.1.3"
        },
        {
            "Name": "exampleServer3",
            "DNSHostName": "exampleServer3.domain.com",
            "IP": "192.168.1.3"
        }
    ],
    "Windows Servers": [{
            "Name": "exampleServer1",
            "DNSHostName": "exampleServer1.domain.com",
            "IP": "192.168.1.1"
        },
        {
            "Name": "exampleServer2",
            "DNSHostName": "exampleServer2.domain.com",
            "IP": "192.168.1.2"
        }
    ]
}

Does anyone knows how can i improve my code in order to do that? I'm working on this because i will use it as a Discovery Rule for Zabbix Monitoring Software.

I don't really know on how or where to start. Any tips or suggestion is really appreciate it...

PS: I will keep updating this answer with the codes i come up with, and all the tests i do.

Thanks in advance...

Edit:

I was able to solve this by using mklement0' and Mathias' excelent explanation for grouping the objects using Group-Object.

Final result:

Import-Module ActiveDirectory
$listaServidores = [ordered] @{}

# Raw command:
# Get-ADComputer -Filter "(OperatingSystem -Like '*' -and Enabled -eq 'True')" -Property OperatingSystem, IPv4Address | 
# Select-Object Name, DNSHostName, IPv4Address, OperatingSystem |

# Exportar para JSON: ConvertTo-Json | Set-Content -Encoding Utf8 "C:\adServers.json"
Get-ADComputer -Filter "(OperatingSystem -Like '*Windows Server*' -and Enabled -eq 'True')" -Property OperatingSystem, IPv4Address | 
Select-Object Name, DNSHostName, IPv4Address, OperatingSystem |
Group-Object {
    switch -Wildcard ($_.OperatingSystem) {
        '*Windows Server*'  { 'Windows Servers' }
        '*Windows 7*'   { 'Outdated Computers' } # I will use this later. Outdated users, need to update to Windows 10
        Default             { 'Others' } # Other devices Discovery Rules. Will dig around later
    }
} | 
ForEach-Object {
    $listaServidores[$_.Name] = 
        @($_.Group | Select-Object Name, DNSHostName, @{ Name='IP'; Expression='IPv4Address'})
}

$listaServidores | ConvertTo-Json -Depth 3 # !! -Depth is needed to avoid truncation
    # Set-Content -Encoding Utf8 "C:\adServers.json"

Solution

  • You can use the Group-Object cmdlet to group computers by their .OperatingSystem property, which allows you to construct an ordered hashtable[1] that translates into the desired JSON structure when passed to ConvertTo-Json:

    Import-Module ActiveDirectory
    
    # Initialize an ordered hashtable that will collect the groups.
    $orderedHash = [ordered] @{}
    
    Get-ADComputer -Filter "Enabled -eq 'True'" -Property DNSHostName, IPv4Address, OperatingSystem | 
      Group-Object OperatingSystem | # See below re higher-level grouping.
      ForEach-Object {
        # Add an entry named for the current group (OS)
        # with the array of the group's members as the value.
        $orderedHash[$_.Name] = 
          @($_.Group | Select-Object Name, DNSHostName, @{ Name='IP'; Expression='IPv4Address'})
      }
    
    # Convert to JSON and save to a file.
    $orderedHash | 
      ConvertTo-Json -Depth 3 | # !! -Depth is needed to avoid truncation
        Set-Content -Encoding Utf8 C:\adServers.json
    

    Mathias' helpful answer shows how to create groups explicitly, based on individually filtered Get-ADComputer calls, which allows arbitrary, higher-level groupings, as well as limiting processing to only computers of interest.

    If processing all (enabled) computers is desired, you can achieve higher-level grouping with a tweak to the above solution too, by passing a script block ({ ... }) that performs the mapping as the (positionally implied) -Property argument.

    E.g., instead of Group-Object OperatingSystem, you could to the following:

    Group-Object {
      switch -Wildcard ($_.OperatingSystem) {
        '*Windows*' { 'Windows Servers' }
        '*Linux*'   { 'Linux Servers' }
        Default     { 'Others' } 
      }
    }
    

    [1] An ordered hashtable ([ordered] @{ ... }) - unlike a regular hashtable (@{ ... }) - preserves the definition order of its entries.
    [ordered] is the only qualifier supported, and while it looks like a cast to a (nonexistent) [ordered] type, it isn't; instead, it is syntactic sugar that that translates into a System.Collections.Specialized.OrderedDictionary instance (vs. System.Collections.Hashtable for a regular hashtable).
    See the conceptual about_Hash_Tables help topic.