265 lines
10 KiB
PowerShell
265 lines
10 KiB
PowerShell
#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
|
|
}
|
|
}
|
|
}
|