219 lines
7.0 KiB
PowerShell
219 lines
7.0 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Create or delete a Tanium Computer Group via Gateway GraphQL.
|
|
|
|
.DESCRIPTION
|
|
- CREATE: filter "Computer Name STARTS_WITH <Prefix>" (usable as Computer Management Group; optional Content Set).
|
|
- DELETE: delete a Computer Group by its Tanium ID.
|
|
- Reads URL/token from config.json (same folder) unless -Url/-Token are provided.
|
|
- Optional TLS bypass for lab with -SkipCertCheck.
|
|
|
|
.USAGE (examples)
|
|
# Create a group (no defaults; you MUST pass -GroupName and -Prefix)
|
|
.\Create-ComputerGroup.ps1 -GroupName "LAB - starts with LAB" -Prefix "LAB" -MrEnabled:$true
|
|
.\Create-ComputerGroup.ps1 -GroupName "LAB - starts with LAB" -Prefix "LAB" -ContentSetName "My Content Set"
|
|
|
|
# Delete by ID
|
|
.\Create-ComputerGroup.ps1 -Delete -Id "12345"
|
|
|
|
# Override URL/token
|
|
.\Create-ComputerGroup.ps1 -Url tanium.pp.dktinfra.io -Token 'token-xxxx' -GroupName "LAB ..." -Prefix "LAB"
|
|
|
|
# Raw JSON (debug)
|
|
.\Create-ComputerGroup.ps1 -GroupName "LAB ..." -Prefix "LAB" -Raw
|
|
#>
|
|
|
|
param(
|
|
[string]$Url,
|
|
[string]$Token,
|
|
[switch]$SkipCertCheck,
|
|
|
|
# CREATE params (no defaults; both required for creation)
|
|
[string]$GroupName,
|
|
[string]$Prefix,
|
|
[bool] $MrEnabled = $true,
|
|
[string]$ContentSetName,
|
|
|
|
# DELETE params
|
|
[switch]$Delete,
|
|
[string]$Id,
|
|
|
|
[switch]$Raw
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {}
|
|
|
|
function Show-ShortHelp {
|
|
Write-Host ""
|
|
Write-Host "Create mode (requires -GroupName and -Prefix):" -ForegroundColor Cyan
|
|
Write-Host " .\Create-ComputerGroup.ps1 -GroupName 'LAB - starts with LAB' -Prefix 'LAB' [-MrEnabled:`$true] [-ContentSetName 'My Content Set']" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host "Delete mode (requires -Delete and -Id):" -ForegroundColor Cyan
|
|
Write-Host " .\Create-ComputerGroup.ps1 -Delete -Id '12345'" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host "Common options: -Url <host> -Token <token> -SkipCertCheck -Raw" -ForegroundColor DarkGray
|
|
}
|
|
|
|
# ---------- Helpers ----------
|
|
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 (temporary, restored after call)
|
|
$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 }
|
|
}
|
|
|
|
# ---------- Endpoint & headers ----------
|
|
$gateway = Get-GatewayUri -HostLike $Url
|
|
$headers = @{ 'Content-Type' = 'application/json'; session = $Token }
|
|
|
|
# ---------- GraphQL mutations ----------
|
|
$Create_WithContentSet = @'
|
|
mutation createGroup($name: String!, $mrEnabled: Boolean!, $prefix: String!, $contentSetName: String!) {
|
|
computerGroupCreate(
|
|
input: {
|
|
name: $name
|
|
managementRightsEnabled: $mrEnabled
|
|
contentSetRef: { name: $contentSetName }
|
|
filter: {
|
|
sensor: { name: "Computer Name" }
|
|
op: STARTS_WITH
|
|
value: $prefix
|
|
}
|
|
}
|
|
) {
|
|
group { id name }
|
|
error { message }
|
|
}
|
|
}
|
|
'@
|
|
|
|
$Create_NoContentSet = @'
|
|
mutation createGroup($name: String!, $mrEnabled: Boolean!, $prefix: String!) {
|
|
computerGroupCreate(
|
|
input: {
|
|
name: $name
|
|
managementRightsEnabled: $mrEnabled
|
|
filter: {
|
|
sensor: { name: "Computer Name" }
|
|
op: STARTS_WITH
|
|
value: $prefix
|
|
}
|
|
}
|
|
) {
|
|
group { id name }
|
|
error { message }
|
|
}
|
|
}
|
|
'@
|
|
|
|
$Delete_ById = @'
|
|
mutation deleteComputerGroup($id: ID!) {
|
|
computerGroupDelete(ref: { id: $id }) {
|
|
id
|
|
error { message }
|
|
}
|
|
}
|
|
'@
|
|
|
|
# ---------- Mode selection & validation ----------
|
|
if ($Delete) {
|
|
if ([string]::IsNullOrWhiteSpace($Id)) {
|
|
Write-Warning "Missing -Id for deletion."
|
|
Show-ShortHelp
|
|
return
|
|
}
|
|
$Query = $Delete_ById
|
|
$Variables = @{ id = $Id }
|
|
$OpName = 'deleteComputerGroup'
|
|
}
|
|
else {
|
|
if ([string]::IsNullOrWhiteSpace($GroupName) -or [string]::IsNullOrWhiteSpace($Prefix)) {
|
|
Write-Warning "Missing -GroupName and/or -Prefix for creation."
|
|
Show-ShortHelp
|
|
return
|
|
}
|
|
$useCS = -not [string]::IsNullOrWhiteSpace($ContentSetName)
|
|
$Query = if ($useCS) { $Create_WithContentSet } else { $Create_NoContentSet }
|
|
$Variables = @{ name = $GroupName; mrEnabled = $MrEnabled; prefix = $Prefix }
|
|
if ($useCS) { $Variables.contentSetName = $ContentSetName }
|
|
$OpName = 'createGroup'
|
|
}
|
|
|
|
# ---------- Execute ----------
|
|
$bodyObj = @{ query = $Query; variables = $Variables; operationName = $OpName }
|
|
$bodyJson = $bodyObj | ConvertTo-Json -Depth 10
|
|
|
|
$resp = $null
|
|
$ps7 = ($PSVersionTable.PSVersion.Major -ge 7)
|
|
if ($ps7 -and $SkipCertCheck) {
|
|
$resp = Invoke-RestMethod -SkipCertificateCheck -Method Post -Uri $gateway -Headers $headers -ContentType 'application/json' -Body $bodyJson
|
|
}
|
|
else {
|
|
try {
|
|
Enter-InsecureTls -Enable:$SkipCertCheck
|
|
$resp = Invoke-RestMethod -Method Post -Uri $gateway -Headers $headers -ContentType 'application/json' -Body $bodyJson
|
|
}
|
|
finally { Exit-InsecureTls }
|
|
}
|
|
|
|
# ---------- Output ----------
|
|
if ($Raw) { $resp | ConvertTo-Json -Depth 12; return }
|
|
|
|
if ($resp.errors) {
|
|
Write-Error ("GraphQL errors: " + (($resp.errors | ForEach-Object { $_.message }) -join '; '))
|
|
return
|
|
}
|
|
|
|
if ($Delete) {
|
|
$del = $resp.data.computerGroupDelete
|
|
if ($del.error -and $del.error.message) {
|
|
Write-Error ("Delete failed: {0}" -f $del.error.message)
|
|
} elseif ($del.id) {
|
|
Write-Host ("Deleted group ID: {0}" -f $del.id) -ForegroundColor Green
|
|
} else {
|
|
Write-Warning "No confirmation returned. Use -Raw to inspect the full response."
|
|
}
|
|
}
|
|
else {
|
|
$crt = $resp.data.computerGroupCreate
|
|
if ($crt.error -and $crt.error.message) {
|
|
Write-Error ("Create failed: {0}" -f $crt.error.message)
|
|
} elseif ($crt.group) {
|
|
Write-Host ("Created group: {0} (ID: {1})" -f $crt.group.name, $crt.group.id) -ForegroundColor Green
|
|
} else {
|
|
Write-Warning "No group returned. Use -Raw to inspect the full response."
|
|
}
|
|
}
|