Auto-commit: 2025-10-31 08:59:02
This commit is contained in:
237
API/graphql - CheckBiosVersion.ps1
Normal file
237
API/graphql - CheckBiosVersion.ps1
Normal file
@@ -0,0 +1,237 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Query Tanium Gateway (GraphQL) for endpoints and return Computer Name (NetBIOS), Model, and BIOS Version.
|
||||
Supports pagination, optional filtering from a simple list file (one name per line), Out-GridView, and CSV export.
|
||||
|
||||
.DESCRIPTION
|
||||
- Uses GraphQL operation exampleGetEndpoints with sensors "Model" and "BIOS Version".
|
||||
- Paginates using pageInfo.hasNextPage / endCursor until all endpoints are collected.
|
||||
- Optional filtering: provide -ListFile with one computer name per line (NetBIOS or FQDN).
|
||||
- Normalizes names by taking the part before the first dot and uppercasing (so NetBIOS and FQDN match).
|
||||
- Reads URL/token from a local config.json if not passed as parameters.
|
||||
- Optional TLS bypass for lab (-SkipCertCheck or via config.json flag).
|
||||
|
||||
.USAGE
|
||||
# With config.json (same folder): { "TaniumUrl": "tanium.pp.dktinfra.io", "TaniumApiToken": "token-xxxx", "SkipCertificateCheck": true }
|
||||
./APICheckBiosVersion.ps1 -Count 200 -Time 30 -MaxAge 900 -PageSize 200
|
||||
|
||||
# Filter from a list (one name per line, no header)
|
||||
./APICheckBiosVersion.ps1 -ListFile .\machines.txt
|
||||
|
||||
# Grid view and CSV export
|
||||
./APICheckBiosVersion.ps1 -ListFile .\machines.txt -Grid -ExportCsv C:\Temp\bios.csv
|
||||
|
||||
# Raw JSON of aggregated edges
|
||||
./APICheckBiosVersion.ps1 -Raw
|
||||
|
||||
.PARAMETERS
|
||||
-Url, -Token Override Tanium URL and API token (otherwise read from config.json).
|
||||
-Count, -Time, -MaxAge GraphQL collection knobs (expectedCount, stableWaitTime, maxAge seconds).
|
||||
-PageSize Page size for pagination (GraphQL 'first').
|
||||
-ListFile Optional path to a simple list (one hostname per line) to filter results.
|
||||
-Raw Output aggregated JSON instead of table/grid.
|
||||
-Grid Show results in Out-GridView (if available).
|
||||
-Pick With -Grid, allow selection and output only the selection.
|
||||
-ExportCsv Path to write CSV export.
|
||||
-SkipCertCheck Ignore TLS cert validation (lab/dev only).
|
||||
|
||||
.NOTES
|
||||
Requires: PowerShell 5.1+ (Out-GridView on PS7 via Microsoft.PowerShell.GraphicalTools).
|
||||
#>
|
||||
|
||||
param(
|
||||
[string]$Url,
|
||||
[string]$Token,
|
||||
[switch]$SkipCertCheck,
|
||||
|
||||
[int]$Count = 200,
|
||||
[int]$Time = 30,
|
||||
[int]$MaxAge = 900,
|
||||
[int]$PageSize = 200,
|
||||
|
||||
[string]$ListFile,
|
||||
[switch]$Raw,
|
||||
[switch]$Grid,
|
||||
[switch]$Pick,
|
||||
[string]$ExportCsv
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {}
|
||||
|
||||
# ---------- 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
|
||||
}
|
||||
}
|
||||
|
||||
function Get-HostKey {
|
||||
param([string]$Name)
|
||||
if ([string]::IsNullOrWhiteSpace($Name)) { return '' }
|
||||
$n = $Name.Trim()
|
||||
$nb = ($n -split '\.', 2)[0] # take part before first dot if FQDN
|
||||
return $nb.ToUpperInvariant()
|
||||
}
|
||||
|
||||
function Read-TargetsFromFile {
|
||||
param([Parameter(Mandatory)][string]$Path)
|
||||
if (-not (Test-Path -LiteralPath $Path)) { throw "List file not found: $Path" }
|
||||
$set = New-Object System.Collections.Generic.HashSet[string]
|
||||
Get-Content -LiteralPath $Path -Encoding UTF8 | ForEach-Object {
|
||||
$line = $_.Trim()
|
||||
if (-not [string]::IsNullOrWhiteSpace($line)) { [void]$set.Add((Get-HostKey $line)) }
|
||||
}
|
||||
if ($set.Count -eq 0) { throw "List file is empty: $Path" }
|
||||
return $set
|
||||
}
|
||||
|
||||
function Get-ValuesForSensor {
|
||||
param([array]$Columns, [string]$SensorName)
|
||||
$match = $Columns | Where-Object { $_.sensor.name -eq $SensorName }
|
||||
if (-not $match) { return '' }
|
||||
$vals = @()
|
||||
foreach ($c in $match) {
|
||||
foreach ($v in $c.values) {
|
||||
if ($null -ne $v -and ("$v").Trim()) { $vals += ("$v") }
|
||||
}
|
||||
}
|
||||
# de-dup while preserving order
|
||||
$uniq = @()
|
||||
foreach ($v in $vals) { if (-not $uniq.Contains($v)) { $uniq += $v } }
|
||||
return ($uniq -join ' | ')
|
||||
}
|
||||
|
||||
# ---------- 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 }
|
||||
}
|
||||
|
||||
# ---------- Prepare ----------
|
||||
$gateway = Get-GatewayUri -HostLike $Url
|
||||
$headers = @{ 'Content-Type' = 'application/json'; session = $Token }
|
||||
$targetSet = $null
|
||||
if ($ListFile) { $targetSet = Read-TargetsFromFile -Path $ListFile }
|
||||
|
||||
# ---------- GraphQL (paginated) ----------
|
||||
$Query = @'
|
||||
query exampleGetEndpoints($count: Int, $time: Int, $maxAge: Int, $first: Int, $after: Cursor) {
|
||||
endpoints(source: { ts: { expectedCount: $count, stableWaitTime: $time, maxAge: $maxAge } }, first: $first, after: $after) {
|
||||
edges { node {
|
||||
computerID
|
||||
name
|
||||
serialNumber
|
||||
ipAddress
|
||||
sensorReadings(sensors: [{name: "Model"}, {name: "BIOS Version"}]) {
|
||||
columns { name values sensor { name } }
|
||||
}
|
||||
}}
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$allEdges = New-Object System.Collections.Generic.List[object]
|
||||
$cursor = $null
|
||||
|
||||
while ($true) {
|
||||
$variables = @{ count = $Count; time = $Time; maxAge = $MaxAge; first = $PageSize; after = $cursor }
|
||||
$bodyObj = @{ query = $Query; variables = $variables; operationName = 'exampleGetEndpoints' }
|
||||
$bodyJson = $bodyObj | ConvertTo-Json -Depth 12
|
||||
|
||||
$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 }
|
||||
}
|
||||
|
||||
if ($resp.errors) { throw ("GraphQL errors: " + (($resp.errors | ForEach-Object { $_.message }) -join '; ')) }
|
||||
|
||||
$edges = $resp.data.endpoints.edges
|
||||
if ($edges) { $allEdges.AddRange($edges) }
|
||||
|
||||
$pi = $resp.data.endpoints.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 'Aucun endpoint retourné.'; return }
|
||||
|
||||
# ---------- Filter (optional) + Output ----------
|
||||
$rows = foreach ($edge in $allEdges) {
|
||||
$n = $edge.node
|
||||
$nb = Get-HostKey $n.name
|
||||
if ($targetSet -and -not $targetSet.Contains($nb)) { continue }
|
||||
$cols = $n.sensorReadings.columns
|
||||
[pscustomobject]@{
|
||||
Nom = $nb
|
||||
Modèle = (Get-ValuesForSensor -Columns $cols -SensorName 'Model')
|
||||
VersionBIOS = (Get-ValuesForSensor -Columns $cols -SensorName 'BIOS Version')
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $rows) { Write-Output 'Aucun poste correspondant.'; return }
|
||||
$rowsSorted = $rows | Sort-Object Nom
|
||||
|
||||
# Optional CSV export
|
||||
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)" }
|
||||
}
|
||||
|
||||
# Grid mode
|
||||
if ($Grid) {
|
||||
$title = "BIOS Versions — {0} poste(s)" -f ($rowsSorted.Count)
|
||||
if (Get-Command Out-GridView -ErrorAction SilentlyContinue) {
|
||||
if ($Pick) {
|
||||
$sel = $rowsSorted | Out-GridView -Title $title -PassThru
|
||||
if ($sel) { $sel | Format-Table -AutoSize }
|
||||
return
|
||||
} else {
|
||||
$rowsSorted | Out-GridView -Title $title
|
||||
return
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Out-GridView n'est pas disponible. Sous PowerShell 7+, installe: Install-Module Microsoft.PowerShell.GraphicalTools"
|
||||
}
|
||||
}
|
||||
|
||||
# Default console table
|
||||
$rowsSorted | Format-Table -AutoSize
|
||||
Reference in New Issue
Block a user