Auto-commit: 2025-10-31 08:59:02

This commit is contained in:
David Wuibaille
2025-10-31 08:59:02 +01:00
parent d3b18d8b45
commit 851c85ec3d
30 changed files with 3734 additions and 6 deletions

View File

@@ -0,0 +1,264 @@
#requires -Version 7.0
<#
.SYNOPSIS
Ensure a Tanium role by NAME: update if it exists, else create, then update from JSON.
.DESCRIPTION
- Accepts two JSON shapes:
(A) Custom: { "Description": "...", "Permissions": { "<Content Set>": { "privileges": { "#x":[ "...", ... ] } } }, "Display Name": "..." }
(B) Tanium export: { "object_list": { "content_set_roles": [ { name, description, content_set_role_privileges: [...] } ] } }
- Forces role name to -RoleName.
- Creates role if missing, then calls Update-TaniumRole.
.PARAMETER RoleName
Target role name to ensure (forced into JSON before update).
.PARAMETER PathToRoleJSON
Path to the role JSON file (custom or Tanium export).
.PARAMETER ContentSetOverride
Optional content set ID to force in Update-TaniumRole.
.PARAMETER ConfigPath
Path to config.json containing TaniumUrl and TaniumApiToken (default: alongside script).
.EXAMPLE
.\Ensure-TaniumRole.ps1 -RoleName "test - T2_TANIUM - N2 - Patch Operator" -PathToRoleJSON .\test.json
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$RoleName,
[Parameter(Mandatory = $true, Position = 1)]
[ValidateScript({
if (-not (Test-Path $_ -PathType Leaf)) { throw "File not found: $_" }
if ([IO.Path]::GetExtension($_) -notin '.json','.JSON') { throw "Not a .json file: $_" }
$true
})]
[string]$PathToRoleJSON,
[Parameter(Position = 2)]
[int]$ContentSetOverride = 0,
[string]$ConfigPath = (Join-Path $PSScriptRoot 'config.json')
)
begin {
$ErrorActionPreference = 'Stop'
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {}
if (-not (Get-Command -Name Update-TaniumRole -ErrorAction SilentlyContinue)) {
throw "Update-TaniumRole not found. Import the module that defines it before running this script."
}
# Load input JSON (any shape)
try {
$inObj = Get-Content -Path $PathToRoleJSON -Raw | ConvertFrom-Json -ErrorAction Stop
} catch {
throw "Invalid JSON in '$PathToRoleJSON': $($_.Exception.Message)"
}
function Convert-CustomRoleJsonToTaniumExport {
<#
Input (custom):
{
"Description": "...",
"Permissions": {
"ContentSetA": { "privileges": { "#patch": [ "patch X", "patch Y" ], "#admin":[ "read sensor" ] } },
"ContentSetB": { ... }
},
"Display Name": "..."
}
Output (export):
{
"comment":"Generated for Update-TaniumRole",
"version":2,
"object_list":{
"content_set_roles":[
{
"name":"<RoleName>",
"description":"<Description>",
"deny_flag":0,
"all_content_sets_flag":0,
"reserved_name":"",
"category":"custom",
"content_set_role_privileges":[
{ "content_set":{"name":"ContentSetA"}, "content_set_privilege":{"name":"patch X"}, "json_metadata":"" },
...
],
"metadata":[]
}
]
}
}
#>
param(
[Parameter(Mandatory)][object]$InputObject,
[Parameter(Mandatory)][string]$NameToForce
)
$desc = ''
if ($InputObject.'Description') { $desc = [string]$InputObject.'Description' }
elseif ($InputObject.description) { $desc = [string]$InputObject.description }
$privRows = New-Object System.Collections.Generic.List[object]
if ($InputObject.Permissions -is [object]) {
foreach ($csName in $InputObject.Permissions.PSObject.Properties.Name) {
$csBlock = $InputObject.Permissions.$csName
if ($csBlock -and $csBlock.privileges) {
foreach ($cat in $csBlock.privileges.PSObject.Properties.Name) {
$vals = $csBlock.privileges.$cat
if ($vals -is [System.Collections.IEnumerable]) {
foreach ($p in $vals) {
if ([string]::IsNullOrWhiteSpace($p)) { continue }
$privRows.Add([pscustomobject]@{
content_set = @{ name = $csName }
content_set_privilege = @{ name = [string]$p }
json_metadata = ""
})
}
}
}
}
}
}
# De-duplicate
$unique = $privRows |
Group-Object { "$($_.content_set.name)|$($_.content_set_privilege.name)" } |
ForEach-Object { $_.Group[0] }
return [pscustomobject]@{
comment = "Generated for Update-TaniumRole"
version = 2
object_list = @{
content_set_roles = @(
[pscustomobject]@{
name = $NameToForce
description = $desc
deny_flag = 0
all_content_sets_flag = 0
reserved_name = ""
category = "custom"
content_set_role_privileges = $unique
metadata = @()
}
)
}
}
}
# Normalize to Tanium export shape
$exportObj = $null
if ($inObj.object_list -and $inObj.object_list.content_set_roles) {
# Already export shape
$exportObj = $inObj
# enforce RoleName on the single role (or first)
$rolesArr = @($exportObj.object_list.content_set_roles)
if ($rolesArr.Count -eq 0) { throw "Export JSON contains no roles." }
if ($rolesArr.Count -gt 1) {
# Force only the first for safety; or throw if you want strict single-role files
throw "JSON contains multiple roles. Provide a single-role file or split it."
}
$exportObj.object_list.content_set_roles[0].name = $RoleName
}
else {
# Custom shape -> convert
if (-not $inObj.Permissions) {
throw "No content_set_roles[] found AND no Permissions{} block found in JSON."
}
$exportObj = Convert-CustomRoleJsonToTaniumExport -InputObject $inObj -NameToForce $RoleName
}
# Write temp JSON (don't touch original)
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
$tempJson = Join-Path $env:TEMP ("tanium-role-{0}-{1}.json" -f ($RoleName -replace '[^\w\-\. ]','_'), $ts)
$exportObj | ConvertTo-Json -Depth 100 | Set-Content -Path $tempJson -Encoding utf8
# Tanium API bootstrap
if (-not (Test-Path $ConfigPath -PathType Leaf)) {
throw "Config file not found: $ConfigPath"
}
$cfg = Get-Content -Path $ConfigPath -Raw | ConvertFrom-Json
$baseUrl = ($cfg.TaniumUrl -replace '/+$','')
if ($baseUrl -notmatch '^https?://') { $baseUrl = "https://$baseUrl" }
$headers = @{
'Accept' = 'application/json'
'Content-Type' = 'application/json'
}
if ($cfg.TaniumApiToken) {
$headers['X-Api-Token'] = $cfg.TaniumApiToken
$headers['session'] = $cfg.TaniumApiToken
}
function Invoke-TaniumApi {
param(
[Parameter(Mandatory)][ValidateSet('GET','POST','PATCH','PUT','DELETE')] [string]$Method,
[Parameter(Mandatory)][string]$Path,
[object]$Body = $null
)
$uri = '{0}{1}' -f $baseUrl, $Path
if ($Body -ne $null) {
Invoke-RestMethod -Method $Method -Uri $uri -Headers $headers -Body ($Body | ConvertTo-Json -Depth 20) -ErrorAction Stop
} else {
Invoke-RestMethod -Method $Method -Uri $uri -Headers $headers -ErrorAction Stop
}
}
function Get-TaniumUserRolesAll {
$resp = Invoke-TaniumApi -Method GET -Path '/api/v2/user_roles'
if ($resp.items) { return @($resp.items) }
if ($resp.data) { return @($resp.data) }
if ($resp.user_roles) { return @($resp.user_roles) }
if ($resp -is [System.Collections.IEnumerable]) { return @($resp) }
return @()
}
function Ensure-TaniumRoleIdByName {
param([Parameter(Mandatory)][string]$Name,[string]$Description = '')
$all = Get-TaniumUserRolesAll
$existing = $all | Where-Object { $_.name -eq $Name } | Select-Object -First 1
if ($existing -and $existing.id) { return [int]$existing.id }
# create
$body = @{
name = $Name
description = $Description
category = 'custom'
}
$resp = Invoke-TaniumApi -Method POST -Path '/api/v2/user_roles' -Body $body
if ($resp.id) { return [int]$resp.id }
if ($resp.item -and $resp.item.id) { return [int]$resp.item.id }
if ($resp.data -and $resp.data.id) { return [int]$resp.data.id }
$all2 = Get-TaniumUserRolesAll
$match = $all2 | Where-Object { $_.name -eq $Name } | Select-Object -First 1
if ($match -and $match.id) { return [int]$match.id }
throw "Role '$Name' created but ID could not be determined."
}
# Description from export for creation metadata
$desc = ''
try { $desc = [string]$exportObj.object_list.content_set_roles[0].description } catch {}
$script:TargetRoleId = Ensure-TaniumRoleIdByName -Name $RoleName -Description $desc
}
process {
try {
$params = @{
RoleIDs = @($script:TargetRoleId)
PathToRoleJSON = $tempJson
}
if ($ContentSetOverride -gt 0) { $params['ContentSetOverride'] = $ContentSetOverride }
Write-Host "Ensured role '$RoleName' (ID: $($script:TargetRoleId)). Applying JSON..." -ForegroundColor Cyan
$result = Update-TaniumRole @params
$result
}
finally {
if (Test-Path $tempJson) {
Remove-Item -Path $tempJson -Force -ErrorAction SilentlyContinue
}
}
}