Files
2026-08-02 21:41:19 -04:00

639 lines
52 KiB
PowerShell

#region Get-OmadaInventory
Function Get-OmadaInventory
{
<#
.SYNOPSIS
Collects the layer two inventory from an Omada controller and returns a normalized model.
.DESCRIPTION
Collection prefers the controller interface and degrades to the Open API, which is arranged by Connect-OmadaController and
enforced per request by Invoke-OmadaRequest. The normalized model is identical either way; the controller interface simply
populates more of it.
Available from both transports.
Sites, device inventory, switch port lists, and wired and wireless client sessions.
Available only from the controller interface.
Switch uplinks including the peer port, which is what allows an inter switch cable to be drawn with both ends known.
Switch downlinks, which describe the same topology from the other direction.
Per port LLDP neighbour detail, VLAN membership, media type, and power over Ethernet state.
VLAN definitions with their gateway subnets.
The real SSID configuration, rather than SSID names inferred from whichever clients happen to be associated.
Enumerations observed on the controller interface and relied upon here.
Port type 1 is copper RJ45 and 3 is a fibre or direct attach cage.
Link speed 0 auto, 1 ten megabit, 2 one hundred megabit, 3 one gigabit, 4 two and a half gigabit, 5 ten gigabit.
Link status 1 is up.
Device status 0 is disconnected and 1 is connected.
.PARAMETER Context
The connection context produced by Connect-OmadaController.
.PARAMETER SiteName
Optionally restricts collection to one or more named sites.
.PARAMETER PageSize
The number of rows requested per page.
.EXAMPLE
$OmadaInventory = Get-OmadaInventory -Context $OmadaContext -SiteName 'Headquarters'
.NOTES
Author: Grace Solutions
Version: 2026.08.02.0000
#>
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[PSObject]$Context,
[Parameter(Mandatory=$False)]
[AllowNull()]
[String[]]$SiteName,
[Parameter(Mandatory=$False)]
[ValidateRange(10, 1000)]
[Int]$PageSize = 100
)
Try
{
#region Define the paged retrieval logic, which differs only in the name of the page parameters
[ScriptBlock]$GetPagedResult = {
Param
(
[String]$ControllerPath,
[String]$OpenAPIPath
)
$AggregateList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$CurrentPage = 1
$TotalRowCount = 0
:PageLoop Do
{
$ControllerQuery = "currentPage=$($CurrentPage)&currentPageSize=$($PageSize)"
$OpenAPIQuery = "page=$($CurrentPage)&pageSize=$($PageSize)"
#region The two transports name their page parameters differently, so each attempt carries its own
$PageResult = Invoke-OmadaRequest -Context ($Context) -ControllerPath ($ControllerPath) -QueryString ($ControllerQuery)
#region Some controller routes reject paging parameters outright while others require them
# A route such as the device list answers a bare request and rejects one that carries paging, whereas a route
# such as the network list does the opposite. Rather than maintaining a list of which is which, a rejected
# first page is retried without paging and the answer is then treated as complete.
Switch (($PageResult.IsSuccess -eq $False) -and ($CurrentPage -eq 1) -and ([String]::IsNullOrWhiteSpace($ControllerPath) -eq $False))
{
{($_ -eq $True)}
{
$PageResult = Invoke-OmadaRequest -Context ($Context) -ControllerPath ($ControllerPath)
}
}
#endregion
Switch ((($PageResult.IsSuccess -eq $False) -or ($PageResult.Transport -ieq 'OpenAPI')) -and ([String]::IsNullOrWhiteSpace($OpenAPIPath) -eq $False))
{
{($_ -eq $True)}
{
$PageResult = Invoke-OmadaRequest -Context ($Context) -OpenAPIPath ($OpenAPIPath) -QueryString ($OpenAPIQuery)
}
}
#endregion
Switch ($PageResult.IsSuccess)
{
{($_ -eq $False)}
{
Break PageLoop
}
}
#region A collection arrives either as a bare array or as a data array alongside a total
# The envelope test has to be exact. Asking a bare array for a data property does not return null, because
# PowerShell enumerates the members of the array and hands back an empty array, which would otherwise be
# mistaken for an envelope carrying no rows and would silently discard the entire collection.
$PageRowList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$ResultIsEnvelope = ($PageResult.Result -isnot [System.Array]) -and ($PageResult.Result -isnot [System.Collections.IList]) -and ($Null -ine $PageResult.Result.PSObject.Properties['data'])
Switch ($ResultIsEnvelope)
{
{($_ -eq $True)}
{
$TotalRowCount = [Int]"0$($PageResult.Result.totalRows)"
ForEach ($PageRow In @($PageResult.Result.data)) {$PageRowList.Add($PageRow)}
}
{($_ -eq $False)}
{
ForEach ($PageRow In @($PageResult.Result)) {$PageRowList.Add($PageRow)}
$TotalRowCount = $PageRowList.Count
}
}
#endregion
ForEach ($PageRow In $PageRowList)
{
Switch ($Null -ine $PageRow)
{
{($_ -eq $True)}
{
$AggregateList.Add($PageRow)
}
}
}
Switch ($PageRowList.Count -eq 0)
{
{($_ -eq $True)}
{
Break PageLoop
}
}
$CurrentPage++
}
While ($AggregateList.Count -lt $TotalRowCount)
Write-Output -InputObject (,$AggregateList)
}
#endregion
#region Define the link speed translation
$LinkSpeedTable = New-Object -TypeName 'System.Collections.Generic.Dictionary[[System.String], [System.String]]'
$LinkSpeedTable.Add('0', '')
$LinkSpeedTable.Add('1', '10M')
$LinkSpeedTable.Add('2', '100M')
$LinkSpeedTable.Add('3', '1G')
$LinkSpeedTable.Add('4', '2.5G')
$LinkSpeedTable.Add('5', '10G')
[ScriptBlock]$ConvertToLinkSpeed = {
Param
(
[System.Object]$SpeedValue
)
$SpeedName = ''
Switch ($LinkSpeedTable.ContainsKey("$($SpeedValue)"))
{
{($_ -eq $True)}
{
$SpeedName = "$($LinkSpeedTable["$($SpeedValue)"])"
}
}
Write-Output -InputObject ("$($SpeedName)")
}
#endregion
#region Define the device status translation
[ScriptBlock]$ConvertToDeviceStatus = {
Param
(
[System.Object]$StatusValue
)
$DeviceStatus = 'unknown'
Switch ("$($StatusValue)")
{
{($_ -ieq '0')}
{
$DeviceStatus = 'offline'
}
{($_ -iin @('1', '4', '11'))}
{
$DeviceStatus = 'online'
}
{($_ -iin @('2', '3', '5', '6'))}
{
$DeviceStatus = 'warning'
}
}
Write-Output -InputObject ("$($DeviceStatus)")
}
#endregion
#region Enumerate sites
$SiteObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$DiscoveredSiteList = $GetPagedResult.InvokeReturnAsIs('sites', 'sites')
Write-Verbose -Message "[$([System.DateTime]::UtcNow.ToString('yyyy/MM/dd HH:mm:ss.FFF'))] - [INFO] - $($DiscoveredSiteList.Count) Omada site(s) were discovered."
For ($DiscoveredSiteIndex = 0; $DiscoveredSiteIndex -lt $DiscoveredSiteList.Count; $DiscoveredSiteIndex++)
{
$DiscoveredSite = $DiscoveredSiteList[$DiscoveredSiteIndex]
#region The two transports name the site identifier differently
$SiteID = "$($DiscoveredSite.siteId)$($DiscoveredSite.id)"
Switch ([String]::IsNullOrWhiteSpace("$($DiscoveredSite.siteId)") -eq $False)
{
{($_ -eq $True)}
{
$SiteID = "$($DiscoveredSite.siteId)"
}
}
#endregion
$SiteIsRequested = $True
Switch (($Null -ine $SiteName) -and ($SiteName.Count -gt 0))
{
{($_ -eq $True)}
{
$SiteIsRequested = "$($DiscoveredSite.name)" -iin $SiteName
}
}
Switch ($SiteIsRequested)
{
{($_ -eq $True)}
{
$WriteProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$WriteProgressParameters.Activity = 'Collecting the Omada inventory'
$WriteProgressParameters.Status = "Processing site `"$($DiscoveredSite.name)`" ($($DiscoveredSiteIndex + 1) of $($DiscoveredSiteList.Count))"
$WriteProgressParameters.PercentComplete = [System.Math]::Round((($DiscoveredSiteIndex + 1) / $DiscoveredSiteList.Count) * 100, 2)
$WriteProgressParameters.Id = 20
$Null = Write-Progress @WriteProgressParameters
#region VLAN definitions, which only the controller interface exposes
$VLANObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$NetworkIdentifierTable = New-Object -TypeName 'System.Collections.Generic.Dictionary[[System.String], [System.Int32]]'
ForEach ($DiscoveredNetwork In $GetPagedResult.InvokeReturnAsIs("sites/$($SiteID)/setting/lan/networks", ''))
{
$NetworkVLANID = [Int]"0$($DiscoveredNetwork.vlan)"
Switch ($NetworkVLANID -gt 0)
{
{($_ -eq $True)}
{
$NetworkIdentifierTable["$($DiscoveredNetwork.id)"] = $NetworkVLANID
$VLANProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$VLANProperties.VLANID = $NetworkVLANID
$VLANProperties.Name = "$($DiscoveredNetwork.name)"
$VLANProperties.Purpose = "$($DiscoveredNetwork.purpose)"
$VLANProperties.GatewaySubnet = "$($DiscoveredNetwork.gatewaySubnet)"
$VLANProperties.NetworkID = "$($DiscoveredNetwork.id)"
$VLANObjectList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($VLANProperties)))
}
}
}
#endregion
#region SSID configuration, which only the controller interface exposes
$SSIDObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$SecurityTable = New-Object -TypeName 'System.Collections.Generic.Dictionary[[System.String], [System.String]]'
$SecurityTable.Add('0', 'Open')
$SecurityTable.Add('1', 'WEP')
$SecurityTable.Add('2', 'WPA Personal')
$SecurityTable.Add('3', 'WPA Enterprise')
$SecurityTable.Add('4', 'WPA2/WPA3 Personal')
$SecurityTable.Add('5', 'WPA2/WPA3 Enterprise')
ForEach ($DiscoveredWLAN In $GetPagedResult.InvokeReturnAsIs("sites/$($SiteID)/setting/wlans", ''))
{
ForEach ($DiscoveredSSID In $GetPagedResult.InvokeReturnAsIs("sites/$($SiteID)/setting/wlans/$($DiscoveredWLAN.id)/ssids", ''))
{
$SSIDVLANID = ''
Switch (("$($DiscoveredSSID.vlanEnable)" -ieq 'True') -and ([Int]"0$($DiscoveredSSID.vlanId)" -gt 0))
{
{($_ -eq $True)}
{
$SSIDVLANID = "$($DiscoveredSSID.vlanId)"
}
}
$SSIDProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$SSIDProperties.Name = "$($DiscoveredSSID.name)"
$SSIDProperties.Security = Switch ($SecurityTable.ContainsKey("$($DiscoveredSSID.security)")) {{($_ -eq $True)} {"$($SecurityTable["$($DiscoveredSSID.security)"])"} Default {''}}
$SSIDProperties.IsHidden = ("$($DiscoveredSSID.broadcast)" -ieq 'False')
$SSIDProperties.IsGuest = ("$($DiscoveredSSID.guestNetEnable)" -ieq 'True')
$SSIDProperties.VLANID = "$($SSIDVLANID)"
$SSIDProperties.WLANGroupName = "$($DiscoveredWLAN.name)"
$SSIDObjectList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($SSIDProperties)))
}
}
#endregion
#region Devices
$DeviceObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$DiscoveredDeviceList = $GetPagedResult.InvokeReturnAsIs("sites/$($SiteID)/devices", "sites/$($SiteID)/devices")
For ($DiscoveredDeviceIndex = 0; $DiscoveredDeviceIndex -lt $DiscoveredDeviceList.Count; $DiscoveredDeviceIndex++)
{
$DiscoveredDevice = $DiscoveredDeviceList[$DiscoveredDeviceIndex]
$DeviceMACAddress = "$($DiscoveredDevice.mac)".ToUpperInvariant()
#region Per device detail, which carries the port list and the topology
$DetailSegment = Switch ("$($DiscoveredDevice.type)")
{
{($_ -ieq 'switch')}
{
'switches'
}
{($_ -ieq 'ap')}
{
'eaps'
}
{($_ -ieq 'gateway')}
{
'gateways'
}
Default
{
''
}
}
$DeviceDetail = $Null
Switch ([String]::IsNullOrWhiteSpace($DetailSegment) -eq $False)
{
{($_ -eq $True)}
{
$DetailResult = Invoke-OmadaRequest -Context ($Context) -ControllerPath "sites/$($SiteID)/$($DetailSegment)/$($DeviceMACAddress)" -OpenAPIPath "sites/$($SiteID)/$(Switch ($DetailSegment) {{($_ -ieq 'eaps')} {'aps'} Default {$DetailSegment}})/$($DeviceMACAddress)"
Switch ($DetailResult.IsSuccess)
{
{($_ -eq $True)}
{
$DeviceDetail = $DetailResult.Result
}
}
}
}
#endregion
#region Ports, taken from the controller port list when present and from the Open API port list otherwise
$DevicePortList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$RawPortList = @($DeviceDetail.ports) + @($DeviceDetail.portList)
ForEach ($DiscoveredPort In $RawPortList)
{
Switch ($Null -ine $DiscoveredPort)
{
{($_ -eq $True)}
{
$PortStatus = $DiscoveredPort.portStatus
#region Media is inferred from the cage type reported by the controller, falling back to the model heuristic elsewhere
$PortKind = ''
Switch ("$($DiscoveredPort.type)")
{
{($_ -ieq '1')}
{
$PortKind = 'rj45'
}
{($_ -ieq '3')}
{
$PortKind = 'sfp_plus'
}
}
$PortSpeed = $ConvertToLinkSpeed.InvokeReturnAsIs($DiscoveredPort.maxSpeed)
Switch (("$($PortKind)" -ieq 'sfp_plus') -and ("$($PortSpeed)" -ieq '1G'))
{
{($_ -eq $True)}
{
$PortKind = 'sfp'
}
}
#endregion
#region VLAN membership, where a native network identifier resolves to a VLAN number
$PortVLANID = ''
Switch ($NetworkIdentifierTable.ContainsKey("$($DiscoveredPort.nativeNetworkId)"))
{
{($_ -eq $True)}
{
$PortVLANID = "$($NetworkIdentifierTable["$($DiscoveredPort.nativeNetworkId)"])"
}
}
$PortMode = Switch (@($DiscoveredPort.tagNetworkIds).Count -gt 0) {{($_ -eq $True)} {'trunk'} Default {'access'}}
Switch ("$($DiscoveredPort.profileName)" -ieq 'All')
{
{($_ -eq $True)}
{
$PortMode = 'trunk'
}
}
#endregion
$PortProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$PortProperties.Number = [Int]"$($DiscoveredPort.port)"
$PortProperties.Name = "$($DiscoveredPort.name)"
$PortProperties.ProfileName = "$($DiscoveredPort.profileName)"
$PortProperties.Kind = "$($PortKind)"
$PortProperties.Speed = "$($PortSpeed)"
$PortProperties.Mode = "$($PortMode)"
$PortProperties.VLANID = "$($PortVLANID)"
$PortProperties.IsDisabled = ("$($DiscoveredPort.disable)" -ieq 'True')
$PortProperties.IsPoweredOverEthernet = ("$($PortStatus.poe)" -ieq 'True')
$PortProperties.IsLinkAggregationMember = ("$($DiscoveredPort.lagPort)" -ieq 'True')
$PortProperties.NeighbourChassisID = "$($PortStatus.chassisId)".ToUpperInvariant()
$PortProperties.NeighbourPortID = "$($PortStatus.portId)"
#region Link state arrives as a status object from the controller and as a bare status from the Open API
$LinkStateValue = Switch ($Null -ine $PortStatus) {{($_ -eq $True)} {"$($PortStatus.linkStatus)"} Default {"$($DiscoveredPort.status)"}}
$PortProperties.LinkState = Switch ("$($LinkStateValue)")
{
{($_ -ieq '1')}
{
'up'
}
{($_ -ieq '0')}
{
'down'
}
Default
{
'unknown'
}
}
#endregion
$DevicePortList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($PortProperties)))
}
}
}
#endregion
#region Uplink and downlinks, which describe the same topology from either end
$UplinkObject = $Null
Switch ($Null -ine $DeviceDetail.uplink)
{
{($_ -eq $True)}
{
$UplinkProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$UplinkProperties.LocalPortNumber = "$($DeviceDetail.uplink.port)"
$UplinkProperties.UpstreamMACAddress = "$($DeviceDetail.uplink.mac)".ToUpperInvariant()
$UplinkProperties.UpstreamDeviceName = "$($DeviceDetail.uplink.name)"
$UplinkProperties.UpstreamPortNumber = "$($DeviceDetail.uplink.linkPeerPortInfo.port)"
$UplinkProperties.Speed = $ConvertToLinkSpeed.InvokeReturnAsIs($DeviceDetail.uplink.linkSpeed)
$UplinkObject = New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($UplinkProperties)
}
}
$DownlinkObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
ForEach ($DiscoveredDownlink In @($DeviceDetail.downlinkList))
{
Switch ($Null -ine $DiscoveredDownlink)
{
{($_ -eq $True)}
{
$DownlinkProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$DownlinkProperties.LocalPortNumber = "$($DiscoveredDownlink.port)"
$DownlinkProperties.PeerMACAddress = "$($DiscoveredDownlink.mac)".ToUpperInvariant()
$DownlinkProperties.PeerDeviceName = "$($DiscoveredDownlink.name)"
$DownlinkProperties.PeerPortNumber = "$($DiscoveredDownlink.linkPeerPortInfo.port)"
$DownlinkProperties.Speed = $ConvertToLinkSpeed.InvokeReturnAsIs($DiscoveredDownlink.linkSpeed)
$DownlinkObjectList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($DownlinkProperties)))
}
}
}
#endregion
$DeviceTagList = New-Object -TypeName 'System.Collections.Generic.List[String]'
ForEach ($DeviceTag In "$($DiscoveredDevice.tagName)".Split(',', [System.StringSplitOptions]::RemoveEmptyEntries))
{
$DeviceTagList.Add("$($DeviceTag)".Trim())
}
$DeviceProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$DeviceProperties.MACAddress = "$($DeviceMACAddress)"
$DeviceProperties.Name = "$($DiscoveredDevice.name)"
$DeviceProperties.Type = "$($DiscoveredDevice.type)"
$DeviceProperties.Model = "$($DiscoveredDevice.model)"
$DeviceProperties.ModelName = Switch ([String]::IsNullOrWhiteSpace("$($DiscoveredDevice.showModel)")) {{($_ -eq $False)} {"$($DiscoveredDevice.showModel)"} Default {"$($DiscoveredDevice.modelName)"}}
$DeviceProperties.IPAddress = "$($DiscoveredDevice.ip)"
$DeviceProperties.SerialNumber = "$($DiscoveredDevice.sn)"
$DeviceProperties.FirmwareVersion = "$($DiscoveredDevice.firmwareVersion)"
$DeviceProperties.Status = $ConvertToDeviceStatus.InvokeReturnAsIs($DiscoveredDevice.status)
$DeviceProperties.Uptime = "$($DiscoveredDevice.uptime)"
$DeviceProperties.Location = "$($DiscoveredDevice.location)"
$DeviceProperties.TagList = $DeviceTagList
$DeviceProperties.LastSeen = Try {[System.DateTimeOffset]::FromUnixTimeMilliseconds([Int64]"$($DiscoveredDevice.lastSeen)").UtcDateTime} Catch {$Null}
$DeviceProperties.PortList = $DevicePortList
$DeviceProperties.Uplink = $UplinkObject
$DeviceProperties.DownlinkList = $DownlinkObjectList
$DeviceProperties.SiteID = "$($SiteID)"
$DeviceProperties.SiteName = "$($DiscoveredSite.name)"
$DeviceObjectList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($DeviceProperties)))
}
#endregion
#region Clients, which use identical field names on both transports
$ClientObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
ForEach ($DiscoveredClient In $GetPagedResult.InvokeReturnAsIs("sites/$($SiteID)/clients", "sites/$($SiteID)/clients"))
{
$ClientProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ClientProperties.MACAddress = "$($DiscoveredClient.mac)".ToUpperInvariant()
$ClientProperties.Name = "$($DiscoveredClient.name)"
$ClientProperties.IPAddress = "$($DiscoveredClient.ip)"
$ClientProperties.Vendor = "$($DiscoveredClient.vendor)"
$ClientProperties.DeviceCategory = "$($DiscoveredClient.deviceCategory)"
$ClientProperties.IsWireless = ("$($DiscoveredClient.wireless)" -ieq 'True')
$ClientProperties.VLANID = "$($DiscoveredClient.vid)"
$ClientProperties.NetworkName = "$($DiscoveredClient.networkName)"
$ClientProperties.SwitchMACAddress = "$($DiscoveredClient.switchMac)".ToUpperInvariant()
$ClientProperties.SwitchName = "$($DiscoveredClient.switchName)"
$ClientProperties.SwitchPort = "$($DiscoveredClient.port)"
$ClientProperties.SSID = "$($DiscoveredClient.ssid)"
$ClientProperties.AccessPointMACAddress = "$($DiscoveredClient.apMac)".ToUpperInvariant()
$ClientProperties.AccessPointName = "$($DiscoveredClient.apName)"
$ClientProperties.RadioID = "$($DiscoveredClient.radioId)"
$ClientProperties.Channel = "$($DiscoveredClient.channel)"
$ClientProperties.SignalDbm = Switch ($Null -ine $DiscoveredClient.rssi) {{($_ -eq $True)} {[Int]"$($DiscoveredClient.rssi)"} Default {$Null}}
$ClientProperties.LastSeen = Try {[System.DateTimeOffset]::FromUnixTimeMilliseconds([Int64]"$($DiscoveredClient.lastSeen)").UtcDateTime} Catch {$Null}
$ClientProperties.SiteID = "$($SiteID)"
$ClientProperties.SiteName = "$($DiscoveredSite.name)"
$ClientObjectList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($ClientProperties)))
}
#endregion
$SiteProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$SiteProperties.SiteID = "$($SiteID)"
$SiteProperties.Name = "$($DiscoveredSite.name)"
$SiteProperties.Region = "$($DiscoveredSite.region)"
$SiteProperties.TimeZone = "$($DiscoveredSite.timeZone)"
$SiteProperties.DeviceList = $DeviceObjectList
$SiteProperties.ClientList = $ClientObjectList
$SiteProperties.VLANList = $VLANObjectList
$SiteProperties.SSIDList = $SSIDObjectList
$SiteObjectList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($SiteProperties)))
Write-Verbose -Message "[$([System.DateTime]::UtcNow.ToString('yyyy/MM/dd HH:mm:ss.FFF'))] - [INFO] - Collected $($DeviceObjectList.Count) device(s), $($ClientObjectList.Count) client(s), $($VLANObjectList.Count) VLAN(s), and $($SSIDObjectList.Count) SSID(s) from Omada site `"$($DiscoveredSite.name)`"."
}
}
}
$Null = Write-Progress -Activity 'Collecting the Omada inventory' -Id 20 -Completed
#endregion
$InventoryProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$InventoryProperties.SourceType = 'Omada'
$InventoryProperties.Vendor = 'TP-Link'
$InventoryProperties.ControllerName = 'Omada Controller'
$InventoryProperties.BaseURL = [System.URI]"$($Context.BaseAddress)"
$InventoryProperties.OmadacID = "$($Context.OmadacID)"
$InventoryProperties.ControllerVersion = "$($Context.ControllerVersion)"
$InventoryProperties.CollectionMode = "$($Context.Mode)"
$InventoryProperties.RetrievedAt = [System.DateTime]::UtcNow
$InventoryProperties.SiteList = $SiteObjectList
Write-Output -InputObject (New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($InventoryProperties))
}
Catch
{
Throw
}
}
#endregion