Scripts/DeviceGroups.ps1
############################################################################## #.SYNOPSIS # Gets information about a device group by its name or ID. # #.DESCRIPTION # Note, that since device groups may contain rules or other device groups, # this function does not necessarily represent every device included in the # group implicitly. For that, you should use the Get-EM7DeviceGroupMember # function. ############################################################################## function Get-EM7DeviceGroup { [CmdletBinding(DefaultParameterSetName='ByID')] param( # If specified, retrieves the device group with the specified ID, URI, or Name. # When using name match, wildcards can be used at either end of the name # to check for partial matches. [Alias('Name')] [Parameter(ParameterSetName='ByID', Position=0)] [String]$ID, # If specified, retrieves the device groups that are children of the specified # parent device group. [Parameter(ParameterSetName='ByParent', Mandatory=$true)] [String]$Parent, # If specifieed, the keys of this hashtable are prefixed with # 'filter.' and used as filters. For example: @{state='PA'} [Parameter()] [Hashtable]$Filter = @{}, # Limits the results to the specified number. The default is 1000. [Parameter()] [Int32]$Limit = $Globals.DefaultLimit, # The starting offset in the results to return. # If retrieving objects in pages of 100, you would specify 0 for page 1, # 100 for page 2, 200 for page 3, and so on. [Parameter()] [Int32]$Offset = 0, # Optionally sorts the results by this field in ascending order, or if # the field is prefixed with a dash (-) in descending order. # You can also pipe the output to PowerShell's Sort-Object cmdlet, but # this parameter is processed on the server, which will affect how # results are paginated when there are more results than fit in a # single page. [Parameter()] [String]$OrderBy, # Specifies one or more property names that ordinarily contain a link # to a related object to automatically retrieve and place in the # returned object. [Parameter()] [String[]]$ExpandProperty, # If specified, the cache will be checked for device group data before # making a call to the server for a given device group id. # This is primarily designed to be used internally, when functions are being called # repeatedly, such as in recursion scenarios, which may result in duplicated lookups. [Parameter()] [Hashtable]$Cache = @{} ) begin { EnsureConnected -ErrorAction Stop } process { if ($PSCmdlet.ParameterSetName -eq 'ByID') { if ($ID) { if ($ID -as [Int32]) { # Treat it as an ID # Get-EM7Object device_group -ID:$ID -ExpandProperty:$ExpandProperty Return } elseif ($ID -match $Globals.UriPattern -and $Matches.r -eq 'device_group') { # Treat it as a URI $DeviceGroup = $Cache[$ID] if (!$DeviceGroup) { $DeviceGroup = Get-EM7Object -URI:$ID -ExpandProperty:$ExpandProperty $Cache[$ID] = $DeviceGroup } if ($DeviceGroup) { $DeviceGroup } Return } else { # Treat it as a name $Operator = '' if ($ID.StartsWith('*') -and $ID.EndsWith('*')) { $Operator = '.contains' } elseif ($ID.StartsWith('*')) { $Operator = '.ends_with' } elseif ($ID.EndsWith('*')) { $Operator = '.begins_with' } $Filter["name$Operator"] = $ID.Trim('*') } } Find-EM7Object device_group -Filter:$Filter -Limit:$Limit -Offset:$Offset -OrderBy:$OrderBy -ExpandProperty:$ExpandProperty | ForEach-Object { $Cache[$_.__URI] = $_; $_ } | Sort-Object Name } elseif ($PSCmdlet.ParameterSetName -eq 'ByParent') { $ParentGroups = @(Get-EM7DeviceGroup -ID:$Parent -Filter @{'group_count.min'=1} -Cache:$Cache) Get-EM7Object -URI:$ParentGroups.groups -ExpandProperty:$ExpandProperty | ForEach-Object { $Cache[$_.__URI] = $_; $_ } | Sort-Object Name } } } ############################################################################## #.SYNOPSIS # Gets a list of devices that are members of the specified device group. ############################################################################## function Get-EM7DeviceGroupMember { [CmdletBinding()] param( # The device group ID, URI, or Name. # When using name match, wildcards can be used at either end of the name # to check for partial matches. [Alias('Name', '__DeviceGroupID')] [Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [String]$ID, # Specifies one or more property names that ordinarily contain a link # to a related object to automatically retrieve and place in the # returned object. [Parameter()] [String[]]$ExpandProperty, # Returns only the Device URIs, and does not retrieve the device details. [Switch]$Quick, # If specified, device groups that are members of this device group will # be recursively expanded as well. [Parameter()] [Switch]$Recurse, # If specified, the cache will be checked for device group membership data before # making a call to the server for a given device group id. # This is primarily designed to be used internally, when functions are being called # repeatedly, such as in recursion scenarios, which may result in duplicated lookups. [Parameter()] [Hashtable]$Cache = @{} ) begin { EnsureConnected -ErrorAction Stop } process { $DeviceGroups = @(Get-EM7DeviceGroup -ID:$ID -Cache:$Cache) foreach ($DeviceGroup in $DeviceGroups) { $URI = CreateUri "device_group/$($DeviceGroup.__ID)/expanded_devices" $DeviceIDs = $Cache[$URI.AbsolutePath] if (!$DeviceIDs) { $DeviceIDs = HttpInvoke $URI $Cache[$URI.AbsolutePath] = $DeviceIDs } if ($DeviceIDs.Length) { if ($Quick) { Write-Output $DeviceIDs } else { Get-EM7Object -URI:$DeviceIDs -ExpandProperty:$ExpandProperty } } if ($Recurse) { foreach ($GroupID in $DeviceGroup.groups) { Get-EM7DeviceGroupMember -ID:$GroupID -ExpandProperty:$ExpandProperty -Quick:$Quick -Recurse:$Recurse -Cache:$Cache } } } } } ############################################################################## #.SYNOPSIS # Adds a device as a static member of the specified device group. ############################################################################## function Add-EM7DeviceGroupMember { [CmdletBinding(SupportsShouldProcess=$true)] param( # The name of an existing device group. # This must match one and only one device group, otherwise use ID. [Alias('ID', 'Name')] [Parameter(Position=0, Mandatory=$true)] [String]$Group, # A device piped from the output of another command (such as Get-EM7Device). # This property must be a device and have a corresponding device URI. [Parameter(ValueFromPipeline=$true)] [PSObject[]]$Device ) begin { EnsureConnected -ErrorAction Stop $AddedIDs = @() $AddedDevices = @() $DeviceGroup = Get-EM7DeviceGroup -ID:$Group -Limit 2 $DeviceGroupName = $Null $DeviceGroupID = $Null $DeviceGroupURI = $Null if ($DeviceGroup -eq $Null -or $DeviceGroup.Count -eq 0) { # They specified a device group id or name and no matching # device group was found. $DeviceGroup = $Null Write-Error "No matching device groups." Return } if ($DeviceGroup.Count -gt 1) { # They specified a device group name and more than one matched $DeviceGroup = $Null Write-Error "More than one matching device group." Return } $DeviceGroupName = $DeviceGroup.Name $DeviceGroupID = $DeviceGroup.__ID $DeviceGroupURI = $DeviceGroup.__URI Write-Verbose "Device Group: $DeviceGroupName ($DeviceGroupURI)" if ($DeviceGroup.devices) { Write-Verbose "Initial Devices: $(($DeviceGroup.devices | Split-Path -Leaf) -join ', ')" } else { Write-Verbose "Initial Devices: (none)" } } process { if ($DeviceGroup) { foreach ($D in $Device) { $DID = $D # If a device object was used as input instead of a URI or ID, # make sure we extract its __URI if ($DID.__URI) { $DID = $DID.__URI } # Normalize integer IDs to URIs if ($DID -as [Int32]) { $DID = "/api/device/$DID" } # Make sure URI represents a device if ($DID -notmatch '^/api/device/\d+$') { Write-Error "Expected input: $D" continue } # Check if device group already contains device id if ($DeviceGroup.devices -contains $DID) { Write-Verbose " Device Group $DeviceGroupURI already contains $DID" } else { Write-Verbose " Add Device: $DID" $AddedDevices += $D $AddedIDs += $DID } } } } end { # Changes are collected throughout the pipeline invocation and # only pushed up to the server at the end. # Double check we have a device group and changes were actually made. # Only push the devices property, rather than the entire object. if ($DeviceGroup -and $AddedIDs.Count) { # Fetch the device group again # Since we have to specify all members, it's possible that the member list # has changed before we started. This will minimize issues due to concurrency. if ($DeviceGroup = Get-EM7Object -Resource device_group -ID:$DeviceGroupID) { $DeviceGroup.devices += $AddedIDs Write-Verbose "Final Devices: $(($DeviceGroup.devices | Split-Path -Leaf) -join ', ')" if ($PSCmdlet.ShouldProcess($DeviceGroupURI, "Update Device Group")) { Set-EM7Object -URI:$DeviceGroupURI @{devices=$DeviceGroup.devices} } Write-Output $AddedDevices } } } } <# .SYNOPSIS Gets the devices groups that the specified device is a member of. .DESCRIPTION There is no API operation that returns the device group membership for a given device, so this function must get every device group in the system and index the devices. If there are a large number of devices or device groups in the system, this could potentially be a long process. #> function Get-EM7DeviceGroupMembership { [CmdletBinding()] [OutputType([PSObject])] param ( # The ID, URL, or object representing the device whose membership to check. [Alias('__DeviceID')] [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [PSObject]$Device ) begin { $ProgID = Get-Random -Minimum 100000 Write-Progress "Loading device groups..." -Id:$ProgID $Cache = @{} $DeviceGroupCache = @(Get-Em7DeviceGroup -Limit 999999 -Cache:$Cache) foreach ($DeviceGroup in $DeviceGroupCache) { $DeviceGroup.devices = @(Get-EM7DeviceGroupMember -ID $DeviceGroup.__URI -Recurse -Quick -Cache:$Cache) $DeviceGroup.deviceCount = $DeviceGroup.devices.Count } Write-Progress "Done" -Id:$ProgID -Completed } process { $DID = $Null # We need a URI. # If Device object was passed in, get its URI. if ($Device.__URI) { $DID = $Device.__URI } elseif ($Device -as [Int32]) { $DID = "/api/device/$Device" } elseif ($Device -match $Globals.UriPattern -and $Matches.t -eq 'device') { $DID = $Device } else { Write-Error "Unsupported Device parameter: $Device" } if ($DID) { $DeviceGroupCache | Where-Object { $_.devices -contains $DID } } } end { } } |