Auto-commit: 2025-10-31 08:59:02
This commit is contained in:
219
API/graphql - ListComputerGroup.ps1
Normal file
219
API/graphql - ListComputerGroup.ps1
Normal file
@@ -0,0 +1,219 @@
|
||||
# List-ComputerGroups.ps1 — robust fallback + HTTP 400 handling + introspection
|
||||
|
||||
param(
|
||||
[string]$Url,
|
||||
[string]$Token,
|
||||
[switch]$SkipCertCheck,
|
||||
[int]$PageSize = 200,
|
||||
[string]$NameLike,
|
||||
[switch]$Raw,
|
||||
[switch]$Grid,
|
||||
[string]$ExportCsv,
|
||||
[switch]$Introspect
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {}
|
||||
|
||||
function Get-GatewayUri {
|
||||
param([Parameter(Mandatory)][string]$HostLike)
|
||||
$h = $HostLike.Trim()
|
||||
if ($h -match '^https?://') { $h = $h -replace '^https?://','' }
|
||||
$h = $h.TrimEnd('/')
|
||||
if ([string]::IsNullOrWhiteSpace($h)) { throw 'TaniumUrl empty after normalization.' }
|
||||
"https://$h/plugin/products/gateway/graphql"
|
||||
}
|
||||
|
||||
# TLS bypass for Windows PowerShell 5.1
|
||||
$script:__oldCb = $null
|
||||
function Enter-InsecureTls { param([switch]$Enable)
|
||||
if (-not $Enable) { return }
|
||||
$script:__oldCb = [System.Net.ServicePointManager]::ServerCertificateValidationCallback
|
||||
$cb = [System.Net.Security.RemoteCertificateValidationCallback]{ param($s,$c,$ch,$e) $true }
|
||||
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $cb
|
||||
}
|
||||
function Exit-InsecureTls {
|
||||
if ($script:__oldCb -ne $null) {
|
||||
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $script:__oldCb
|
||||
$script:__oldCb = $null
|
||||
}
|
||||
}
|
||||
|
||||
# Load config if needed
|
||||
$BaseDir = if ($PSScriptRoot) { $PSScriptRoot } else { $pwd.Path }
|
||||
$configPath = Join-Path $BaseDir 'config.json'
|
||||
if (-not $Url -or -not $Token) {
|
||||
if (-not (Test-Path $configPath)) { throw "Missing -Url/-Token and config.json not found: $configPath" }
|
||||
$cfg = Get-Content -Path $configPath -Raw | ConvertFrom-Json
|
||||
if (-not $Url) { $Url = [string]$cfg.TaniumUrl }
|
||||
if (-not $Token) { $Token = [string]$cfg.TaniumApiToken }
|
||||
if (-not $PSBoundParameters.ContainsKey('SkipCertCheck')) { $SkipCertCheck = [bool]$cfg.SkipCertificateCheck }
|
||||
}
|
||||
|
||||
$gateway = Get-GatewayUri -HostLike $Url
|
||||
$headers = @{ 'Content-Type' = 'application/json'; session = $Token }
|
||||
|
||||
# ---- Introspection (optional)
|
||||
if ($Introspect) {
|
||||
$IntrospectQuery = @'
|
||||
query {
|
||||
__type(name: "ComputerGroup") {
|
||||
name
|
||||
fields { name type { kind name ofType { kind name } } }
|
||||
}
|
||||
}
|
||||
'@
|
||||
$body = @{ query = $IntrospectQuery } | ConvertTo-Json -Depth 6
|
||||
$resp = $null
|
||||
$ps7 = ($PSVersionTable.PSVersion.Major -ge 7)
|
||||
if ($ps7 -and $SkipCertCheck) {
|
||||
$resp = Invoke-RestMethod -SkipCertificateCheck -Method Post -Uri $gateway -Headers $headers -Body $body
|
||||
} else {
|
||||
try { Enter-InsecureTls -Enable:$SkipCertCheck; $resp = Invoke-RestMethod -Method Post -Uri $gateway -Headers $headers -Body $body }
|
||||
finally { Exit-InsecureTls }
|
||||
}
|
||||
if ($resp.errors) { Write-Error (($resp.errors | ForEach-Object message) -join '; '); return }
|
||||
$resp.data.__type.fields | Select-Object name,@{n='type';e={$_.type.name ?? $_.type.ofType.name}} | Format-Table -AutoSize
|
||||
return
|
||||
}
|
||||
|
||||
# ---- Queries (order = safest → richer)
|
||||
$Q_Basic = @'
|
||||
query listComputerGroups($first: Int, $after: Cursor) {
|
||||
computerGroups(first: $first, after: $after) {
|
||||
edges { node { id name managementRightsEnabled } }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$Q_ContentSet = @'
|
||||
query listComputerGroups($first: Int, $after: Cursor) {
|
||||
computerGroups(first: $first, after: $after) {
|
||||
edges { node { id name managementRightsEnabled contentSet { name } } }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$Q_ContentSetV2 = @'
|
||||
query listComputerGroups($first: Int, $after: Cursor) {
|
||||
computerGroups(first: $first, after: $after) {
|
||||
edges { node { id name managementRightsEnabled contentSetV2 { name } } }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$Q_Filter = @'
|
||||
query listComputerGroups($first: Int, $after: Cursor) {
|
||||
computerGroups(first: $first, after: $after) {
|
||||
edges { node {
|
||||
id name managementRightsEnabled
|
||||
filter { memberOf { name } sensor { name } op value }
|
||||
} }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$queries = @($Q_Basic, $Q_ContentSet, $Q_ContentSetV2, $Q_Filter)
|
||||
|
||||
function Invoke-Gql {
|
||||
param([string]$Query, [hashtable]$Vars, [string]$OpName)
|
||||
$body = @{ query = $Query; variables = $Vars; operationName = $OpName } | ConvertTo-Json -Depth 10
|
||||
$ps7 = ($PSVersionTable.PSVersion.Major -ge 7)
|
||||
try {
|
||||
if ($ps7 -and $SkipCertCheck) {
|
||||
return Invoke-RestMethod -SkipCertificateCheck -Method Post -Uri $gateway -Headers $headers -ContentType 'application/json' -Body $body
|
||||
} else {
|
||||
Enter-InsecureTls -Enable:$SkipCertCheck
|
||||
return Invoke-RestMethod -Method Post -Uri $gateway -Headers $headers -ContentType 'application/json' -Body $body
|
||||
}
|
||||
}
|
||||
catch {
|
||||
# If server returned HTTP 400 with a JSON GraphQL error, parse it and return so we can fallback.
|
||||
$we = $_.Exception
|
||||
if ($we.Response) {
|
||||
try {
|
||||
$sr = New-Object IO.StreamReader($we.Response.GetResponseStream())
|
||||
$txt = $sr.ReadToEnd()
|
||||
if ($txt) { return ($txt | ConvertFrom-Json) }
|
||||
} catch { }
|
||||
}
|
||||
throw # non-GraphQL error -> bubble up
|
||||
}
|
||||
finally { Exit-InsecureTls }
|
||||
}
|
||||
|
||||
# ---- Fetch with fallback chain
|
||||
$allEdges = New-Object System.Collections.Generic.List[object]
|
||||
$cursor = $null
|
||||
$qIndex = 0
|
||||
$opName = 'listComputerGroups'
|
||||
|
||||
while ($true) {
|
||||
$vars = @{ first = $PageSize; after = $cursor }
|
||||
$resp = Invoke-Gql -Query $queries[$qIndex] -Vars $vars -OpName $opName
|
||||
|
||||
if ($resp.errors) {
|
||||
if ($qIndex -lt ($queries.Count - 1)) {
|
||||
# restart from page 1 with next query
|
||||
$qIndex++; $cursor = $null; $allEdges.Clear()
|
||||
continue
|
||||
} else {
|
||||
throw ("GraphQL errors: " + (($resp.errors | ForEach-Object { $_.message }) -join '; '))
|
||||
}
|
||||
}
|
||||
|
||||
$edges = $resp.data.computerGroups.edges
|
||||
if ($edges) { $allEdges.AddRange($edges) }
|
||||
|
||||
$pi = $resp.data.computerGroups.pageInfo
|
||||
if (-not $pi -or -not $pi.hasNextPage) { break }
|
||||
$cursor = $pi.endCursor
|
||||
}
|
||||
|
||||
if ($Raw) { [pscustomobject]@{ total = $allEdges.Count; edges = $allEdges } | ConvertTo-Json -Depth 14; return }
|
||||
if ($allEdges.Count -eq 0) { Write-Output 'No computer groups returned.'; return }
|
||||
|
||||
function Get-ContentSetName($n) {
|
||||
if ($n.PSObject.Properties.Match('contentSet').Count -gt 0 -and $n.contentSet) { return $n.contentSet.name }
|
||||
if ($n.PSObject.Properties.Match('contentSetV2').Count -gt 0 -and $n.contentSetV2) { return $n.contentSetV2.name }
|
||||
return ''
|
||||
}
|
||||
function Get-FilterSummary($n) {
|
||||
if (-not $n.PSObject.Properties.Match('filter')) { return '' }
|
||||
$f = $n.filter; if ($null -eq $f) { return '' }
|
||||
if ($f.memberOf -and $f.memberOf.name) { return ("memberOf: " + $f.memberOf.name) }
|
||||
if ($f.sensor -and $f.sensor.name) { return ("sensor '" + $f.sensor.name + "' " + $f.op + " '" + $f.value + "'") }
|
||||
return ''
|
||||
}
|
||||
|
||||
$rows = foreach ($edge in $allEdges) {
|
||||
$n = $edge.node
|
||||
if ($NameLike -and -not ($n.name -match $NameLike)) { continue }
|
||||
[pscustomobject]@{
|
||||
Id = $n.id
|
||||
Name = $n.name
|
||||
ManagementRights = $n.managementRightsEnabled
|
||||
ContentSet = (Get-ContentSetName $n)
|
||||
Filter = (Get-FilterSummary $n)
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $rows) { Write-Output 'No matching groups.'; return }
|
||||
$rowsSorted = $rows | Sort-Object Name
|
||||
|
||||
if ($ExportCsv) {
|
||||
try { $rowsSorted | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $ExportCsv; Write-Host "Exported to: $ExportCsv" -ForegroundColor Green }
|
||||
catch { Write-Warning "CSV export failed: $($_.Exception.Message)" }
|
||||
}
|
||||
|
||||
if ($Grid) {
|
||||
$title = "Computer Groups — {0} item(s)" -f ($rowsSorted.Count)
|
||||
if (Get-Command Out-GridView -ErrorAction SilentlyContinue) { $rowsSorted | Out-GridView -Title $title; return }
|
||||
else { Write-Warning "Out-GridView not available. On PS7+: Install-Module Microsoft.PowerShell.GraphicalTools" }
|
||||
}
|
||||
|
||||
$rowsSorted | Format-Table -AutoSize
|
||||
Reference in New Issue
Block a user