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"
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.