<# .SYNOPSIS Create or delete a Tanium Computer Group via Gateway GraphQL. .DESCRIPTION - CREATE: filter "Computer Name STARTS_WITH " (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 -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." } }