# POLISH LANGUAGE [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 chcp 65001 | Out-Null # LOG $logDir = Join-Path $env:ProgramData "PSITAgent" $logPath = Join-Path $logDir "assets.log" function Write-Log { param([string]$Message, [string]$Level = "INFO") $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Write-Host $Message try { if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } if ((Test-Path $logPath) -and (Get-Item $logPath).Length -gt 1MB) { Move-Item -Path $logPath -Destination (Join-Path $logDir "assets.log.bak") -Force } Add-Content -Path $logPath -Value "[$timestamp] [$Level] $Message" -Encoding UTF8 } catch { } } # LOAD CONFIG $configPath = Join-Path $PSScriptRoot "config.json" $config = Get-Content $configPath -Raw | ConvertFrom-Json $apiUrl = $config."snipe-it".url.TrimEnd('/') $apiKey = $config."snipe-it".apikey $verifySSL = $config."snipe-it"."verify-ssl" # SSL (opcjonalne - self-signed cert wewnetrznego Snipe-IT) if (-not $verifySSL) { add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy } # HEADERS $headers = @{ "Authorization" = "Bearer $apiKey" "Accept" = "application/json" "Content-Type" = "application/json" } # GET SEARCH VALUE try { $searchValue = Invoke-Expression $config."search-term".value } catch { Write-Log "Blad pobierania wartosci wyszukiwania (search-term)" "ERROR" exit 1 } if (-not $searchValue) { Write-Log "Nie udalo sie uzyskac wartosci wyszukiwania" "ERROR" exit 1 } Write-Log "======== Wyszukiwanie assetu ========" Write-Log "Szukana wartosc: $searchValue" # FIND ASSET $searchUrl = "$apiUrl/hardware?search=$searchValue" try { $response = Invoke-RestMethod -Uri $searchUrl -Headers $headers -Method GET } catch { Write-Log "Blad komunikacji z API Snipe-IT (wyszukiwanie)" "ERROR" exit 1 } if ($response.total -eq 0) { Write-Log "Nie znaleziono assetu w Snipe-IT" "ERROR" exit 0 } # WERYFIKACJA DOPASOWANIA $searchType = $config."search-term".type $matched = $response.rows | Where-Object { "$($_.($searchType))".Trim() -eq "$searchValue".Trim() } $matchCount = ($matched | Measure-Object).Count if ($matchCount -eq 0) { Write-Log "Brak dokladnego dopasowania assetu ($searchType = $searchValue)" "ERROR" exit 1 } if ($matchCount -gt 1) { Write-Log "Znaleziono wiecej niz jeden pasujacy asset ($searchType = $searchValue) - przerwano aktualizacje" "ERROR" exit 1 } $assetId = $matched[0].id Write-Log "ID assetu: $assetId" # COLLECT DATA $customFields = @{} foreach ($field in $config.fields.custom_fields.PSObject.Properties) { $key = $field.Name $fieldConfig = $field.Value if (-not $fieldConfig.enabled) { continue } try { $value = Invoke-Expression $fieldConfig.ps_command if ($value -is [int] -or $value -is [double]) { $finalValue = $value } else { $finalValue = "$value" } $customFields[$key] = $finalValue } catch { Write-Log "Blad odczytu pola: $key" "ERROR" } } # MINIMALIZACJA RUCHU API - POROWNANIE Z OSTATNIO WYSLANYMI WARTOSCIAMI $cachePath = Join-Path $logDir "last_sent.json" $previous = @{} if (Test-Path $cachePath) { try { $obj = Get-Content $cachePath -Raw | ConvertFrom-Json foreach ($p in $obj.PSObject.Properties) { $previous[$p.Name] = $p.Value } } catch { $previous = @{} } } $changedFields = @{} foreach ($key in $customFields.Keys) { if (-not $previous.ContainsKey($key) -or "$($previous[$key])" -ne "$($customFields[$key])") { $changedFields[$key] = $customFields[$key] } } if ($changedFields.Count -eq 0) { Write-Log "Brak zmian danych - pominieto aktualizacje API" exit 0 } # PREVIEW Write-Log "=== Podglad zmienionych danych ===" foreach ($field in $config.fields.custom_fields.PSObject.Properties) { $key = $field.Name $fieldConfig = $field.Value if ($changedFields.ContainsKey($key)) { $name = if ($fieldConfig.name) { $fieldConfig.name } else { $key } $value = $changedFields[$key] $namePadded = $name.PadRight(20) Write-Log "- $namePadded : $value" } } # BUILD BODY $body = $changedFields | ConvertTo-Json -Depth 5 # UPDATE ASSET $updateUrl = "$apiUrl/hardware/$assetId" Write-Log "======== Aktualizacja assetu ========" try { Invoke-RestMethod -Uri $updateUrl -Headers $headers -Method PATCH -Body $body | Out-Null Write-Log "Aktualizacja Snipe-IT zakonczona powodzeniem" } catch { Write-Log "Blad aktualizacji assetu ID: $assetId - $($_.Exception.Message)" "ERROR" exit 1 } foreach ($key in $changedFields.Keys) { $previous[$key] = $changedFields[$key] } $previous | ConvertTo-Json -Depth 5 | Set-Content -Path $cachePath -Encoding UTF8