Items Field Inspection & Bulk Updates
PowerShell utilities for inspecting Items API fields and performing bulk updates via HaloPSA API.
Overview
These scripts provide field-level inspection and bulk update capabilities for the Items (Inventory) module. Useful for troubleshooting, data cleanup, and mass modifications.
Field Inspection Script
Query all fields available on an Item record to identify field names, types, and current values.
Use Cases
- Discover field names before writing update scripts
- Troubleshoot missing or unexpected data
- Verify field types for API payload construction
- Document available fields for integration work
Script
<#
.SYNOPSIS
HaloPSA Items - Field Inspector
.DESCRIPTION
Queries Items API and displays all available fields with types and values
#>
# Configuration
$tenant = 'your_tenant'
$clientId = 'your_client_id'
$clientSecret = 'your_client_secret'
$authUrl = "https://$tenant.halopsa.com/auth/token"
$apiUrl = "https://$tenant.halopsa.com/api"
# Authenticate
$tokenBody = @{
grant_type = 'client_credentials'
client_id = $clientId
client_secret = $clientSecret
scope = 'read:items'
}
$token = Invoke-RestMethod -Uri $authUrl -Method Post -Body $tokenBody
$headers = @{
Authorization = "Bearer $($token.access_token)"
'Content-Type' = 'application/json'
}
# Query Items with detailed fields
$itemsUrl = "$apiUrl/Item?page_size=5&includedetails=true"
$response = Invoke-RestMethod -Uri $itemsUrl -Headers $headers -Method Get
# Display field information
foreach ($item in $response.items) {
Write-Host "`n=== Item ID: $($item.id) - $($item.name) ===" -ForegroundColor Cyan
$item.PSObject.Properties | ForEach-Object {
$fieldName = $_.Name
$fieldValue = $_.Value
$fieldType = $_.TypeNameOfValue
Write-Host "$fieldName" -NoNewline -ForegroundColor Yellow
Write-Host " [$fieldType]" -NoNewline -ForegroundColor Gray
Write-Host " = $fieldValue" -ForegroundColor White
}
}
# Save raw JSON for detailed inspection
$response | ConvertTo-Json -Depth 10 | Out-File "items_fields_raw.json"
Write-Host "`nRaw JSON saved to: items_fields_raw.json" -ForegroundColor Green
Key Parameters
| Parameter | Description |
|---|---|
includedetails=true |
Returns all fields including custom fields |
page_size |
Number of items to return (use small number for inspection) |
Example Output
=== Item ID: 363 - MNT-135 ===
id [Int32] = 363
name [String] = MNT-135
description [String] = Tilting Wall Mount
template_id [Int32] = 88
item_type_id [Int32] = 2
supplier_id [Int32] = 15
...
Bulk Update Script
Perform mass updates on Items records with dry-run mode, batch processing, and comprehensive logging.
Use Cases
- Clear template_id from all items
- Update pricing across inventory
- Reassign supplier relationships
- Bulk field modifications
Features
- Dry-run mode by default - Preview changes before execution
- Batch processing - Configurable batch sizes with rate limiting
- Progress tracking - Real-time percentage complete
- Error handling - Continues on failures, reports all errors
- Comprehensive logging - Full audit trail with timestamps
Script
<#
.SYNOPSIS
HaloPSA Items - Bulk Update Template
.DESCRIPTION
Bulk update Items with dry-run mode and comprehensive logging
Modify the $updatePayload section for your specific use case
#>
$ErrorActionPreference = 'Continue'
# ============================================
# CONFIGURATION
# ============================================
$tenant = 'your_tenant'
$clientId = 'your_client_id'
$clientSecret = 'your_client_secret'
$batchSize = 10
$delayBetweenBatches = 500 # milliseconds
$dryRun = $true # Set to $false to actually update
$authUrl = "https://$tenant.halopsa.com/auth/token"
$apiUrl = "https://$tenant.halopsa.com/api"
# ============================================
# LOGGING
# ============================================
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = "BulkItemUpdate_$timestamp.log"
function Write-Log {
param(
[Parameter(Mandatory=$false)]
[AllowEmptyString()]
[string]$Message = '',
[Parameter(Mandatory=$false)]
[ValidateSet('Black','DarkBlue','DarkGreen','DarkCyan','DarkRed','DarkMagenta','DarkYellow','Gray','DarkGray','Blue','Green','Cyan','Red','Magenta','Yellow','White')]
[string]$Color = 'White'
)
if ($Message -eq '') {
Write-Host ''
Add-Content -Path $logFile -Value ''
} else {
$logMessage = "$(Get-Date -Format 'HH:mm:ss') $Message"
Write-Host $logMessage -ForegroundColor $Color
Add-Content -Path $logFile -Value $logMessage
}
}
Write-Log -Message "=== HaloPSA Bulk Item Update - Started ===" -Color Cyan
Write-Log -Message "Log file: $logFile" -Color Cyan
Write-Log
# ============================================
# AUTHENTICATE
# ============================================
Write-Log -Message 'Authenticating...' -Color Cyan
try {
$tokenBody = @{
grant_type = 'client_credentials'
client_id = $clientId
client_secret = $clientSecret
scope = 'edit:items read:items'
}
$token = Invoke-RestMethod -Uri $authUrl -Method Post -Body $tokenBody -ErrorAction Stop
$headers = @{
Authorization = "Bearer $($token.access_token)"
'Content-Type' = 'application/json'
}
Write-Log -Message '✓ Authentication successful' -Color Green
Write-Log
} catch {
Write-Log -Message '✗ Authentication failed' -Color Red
Write-Log -Message "Error: $($_.Exception.Message)" -Color Red
exit 1
}
# ============================================
# RETRIEVE ALL ITEMS
# ============================================
Write-Log -Message 'Retrieving items...' -Color Cyan
$allItems = @()
$page = 1
$pageSize = 100
try {
do {
$itemsUrl = "$apiUrl/Item?page_size=$pageSize&page_no=$page"
$response = Invoke-RestMethod -Uri $itemsUrl -Headers $headers -Method Get -ErrorAction Stop
if ($response.items) {
$allItems += $response.items
}
$page++
Start-Sleep -Milliseconds 200
} while ($response.items -and $response.items.Count -eq $pageSize)
Write-Log -Message "✓ Retrieved $($allItems.Count) total items" -Color Green
Write-Log
} catch {
Write-Log -Message '✗ Failed to retrieve items' -Color Red
Write-Log -Message "Error: $($_.Exception.Message)" -Color Red
exit 1
}
# ============================================
# FILTER ITEMS TO UPDATE
# ============================================
Write-Log -Message 'Filtering items...' -Color Cyan
# MODIFY THIS FILTER FOR YOUR USE CASE
$itemsToUpdate = $allItems | Where-Object {
$_.template_id -ne $null -and $_.template_id -ne 0
}
if ($itemsToUpdate.Count -eq 0) {
Write-Log -Message '✓ No items match filter criteria' -Color Green
exit 0
}
Write-Log -Message "✓ Found $($itemsToUpdate.Count) items to update" -Color Yellow
Write-Log
# ============================================
# PREVIEW
# ============================================
Write-Log -Message "Preview - First 10 items:" -Color Cyan
for ($i = 0; $i -lt [Math]::Min(10, $itemsToUpdate.Count); $i++) {
$item = $itemsToUpdate[$i]
Write-Log -Message " ID: $($item.id) | Name: $($item.name) | template_id: $($item.template_id)"
}
Write-Log
# ============================================
# CONFIRMATION
# ============================================
if ($dryRun) {
Write-Log -Message '*** DRY RUN MODE ***' -Color Magenta
$confirm = Read-Host 'Continue with dry run? (yes/no)'
} else {
Write-Log -Message '*** LIVE MODE - WILL UPDATE ITEMS ***' -Color Red
$confirm = Read-Host 'Type YES in capitals to confirm'
}
if ($confirm -ne 'yes' -and $confirm -ne 'YES') {
Write-Log -Message 'Operation cancelled' -Color Yellow
exit 0
}
# ============================================
# PROCESS UPDATES
# ============================================
Write-Log
Write-Log -Message "Processing updates..." -Color Cyan
$successCount = 0
$failCount = 0
$processedCount = 0
$totalCount = $itemsToUpdate.Count
for ($i = 0; $i -lt $totalCount; $i += $batchSize) {
$batch = $itemsToUpdate[$i..[Math]::Min($i + $batchSize - 1, $totalCount - 1)]
foreach ($item in $batch) {
$processedCount++
$percentComplete = [Math]::Round(($processedCount / $totalCount) * 100, 1)
if ($dryRun) {
Write-Log -Message " [$processedCount/$totalCount ($percentComplete%)] DRY RUN: Would update ID $($item.id)" -Color Gray
$successCount++
} else {
try {
# MODIFY THIS PAYLOAD FOR YOUR USE CASE
$updatePayload = @{
id = $item.id
template_id = 0
}
# Manual array wrapper - HaloPSA API requires array
$updateBody = "[$($updatePayload | ConvertTo-Json -Depth 5 -Compress)]"
$updateResult = Invoke-RestMethod -Uri "$apiUrl/Item" -Headers $headers -Method Post -Body $updateBody -ContentType 'application/json' -ErrorAction Stop
Write-Log -Message " [$processedCount/$totalCount ($percentComplete%)] ✓ Updated ID $($item.id)" -Color Green
$successCount++
} catch {
Write-Log -Message " [$processedCount/$totalCount ($percentComplete%)] ✗ FAILED ID $($item.id)" -Color Red
$failCount++
}
Start-Sleep -Milliseconds 100
}
}
if ($i + $batchSize -lt $totalCount) {
Start-Sleep -Milliseconds $delayBetweenBatches
}
}
# ============================================
# SUMMARY
# ============================================
Write-Log
Write-Log -Message "=== SUMMARY ===" -Color Cyan
if ($dryRun) {
Write-Log -Message "DRY RUN: Items that would be updated: $successCount" -Color Yellow
} else {
Write-Log -Message "Successfully updated: $successCount" -Color Green
Write-Log -Message "Failed: $failCount" -Color $(if ($failCount -gt 0) { 'Red' } else { 'White' })
}
Write-Log -Message "Log saved to: $logFile" -Color Cyan
Critical API Requirements
Array Format: The Items API requires an array payload even for single updates:
# CORRECT - Array format
$updateBody = "[{\"id\":363,\"template_id\":0}]"
# WRONG - Single object
$updateBody = "{\"id\":363,\"template_id\":0}"
PowerShell's ConvertTo-Json strips array wrappers for single elements, so manual wrapping is required:
$updatePayload = @{ id = 363; template_id = 0 }
$updateBody = "[$($updatePayload | ConvertTo-Json -Compress)]"
Workflow
- Test in dev environment first
- Run dry-run - Review log file
- Verify preview - Confirm correct items selected
- Set
$dryRun = $false - Run live update - Type
YESto confirm - Verify in UI - Spot-check random items
- Deploy to production - After dev verification
Authentication
All scripts require HaloPSA API credentials:
- Configuration → Integrations → HaloPSA API → View Applications
- Create application - Select "Services" authentication
- Set permissions -
read:items,edit:items - Copy Client ID and Client Secret - Secret shown only once
- Update script configuration:
$tenant= Your HaloPSA subdomain (e.g., 'dtcdev')$clientId= Application Client ID$clientSecret= Application Client Secret
API Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/Item |
GET | Retrieve items (supports pagination) |
/Item/{id} |
GET | Retrieve single item by ID |
/Item |
POST | Create or update items (array payload) |
Query Parameters
| Parameter | Description | Example |
|---|---|---|
page_size |
Items per page (max 100) | ?page_size=50 |
page_no |
Page number | ?page_no=2 |
includedetails |
Include all fields | ?includedetails=true |
Common Use Cases
Clear Project Template from All Items
$itemsToUpdate = $allItems | Where-Object {
$_.template_id -ne $null -and $_.template_id -ne 0
}
$updatePayload = @{
id = $item.id
template_id = 0
}
Update Supplier for Specific Item Type
$itemsToUpdate = $allItems | Where-Object {
$_.item_type_id -eq 5 # Hardware
}
$updatePayload = @{
id = $item.id
supplier_id = 42
}
Bulk Price Update
$itemsToUpdate = $allItems | Where-Object {
$_.name -like "MNT-*" # All monitor mounts
}
$updatePayload = @{
id = $item.id
sell_price = $item.sell_price * 1.10 # 10% increase
}
Troubleshooting
Error: "Cannot deserialize...into type 'Item[]'"
Cause: JSON payload is not an array
Fix: Manually wrap payload in brackets: "[$json]"
Error: "Cannot bind parameter 'ForegroundColor'"
Cause: Write-Log function parameter parsing issue
Fix: Use named parameters: Write-Log -Message "text" -Color Green
Error: "Authentication failed"
Check:
- Client ID and Secret are correct
- API application has required permissions (
read:items,edit:items) - Tenant name is correct (no
.halopsa.comsuffix) - API application is active in HaloPSA
Items not updating despite success message
Check:
- Verify in HaloPSA UI
- Check API response for errors
- Ensure field name is correct (use field inspector)
- Verify field type matches payload type
Best Practices
- Always run dry-run first - Preview changes before execution
- Test in dev environment - Before production deployment
- Use small batch sizes - Avoid API rate limiting
- Add delays between batches - 500ms recommended
- Log everything - Timestamped logs for audit trail
- Verify in UI - Spot-check after bulk updates
- Keep backups - Export data before mass modifications