# GeoNetAgent - Install atau update ke %ProgramData%\GeoNetAgent # Unduh dari https://agent.gisportal.id/install atau -SourcePath lokal [CmdletBinding()] param( [string]$BaseUrl = 'https://agent.gisportal.id/install', [string]$InstallRoot = (Join-Path $env:ProgramData 'GeoNetAgent'), [string]$AgentToken, [string]$SourcePath, [switch]$SkipScheduledTask, [switch]$SkipInitialRun, [int]$ScheduleHours = 6 ) $ErrorActionPreference = 'Stop' function Test-GeoNetAdmin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object Security.Principal.WindowsPrincipal($id) return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Get-GeoNetInstallBaseUrl { param([string]$Url) return $Url.TrimEnd('/') } function Get-GeoNetRemoteJson { param([string]$Url) return (Invoke-WebRequest -Uri $Url -UseBasicParsing).Content | ConvertFrom-Json } function Get-GeoNetRemoteFile { param([string]$Url, [string]$Destination) $parent = Split-Path -Parent $Destination if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $lastErr = $null for ($try = 1; $try -le 3; $try++) { try { Invoke-WebRequest -Uri $Url -OutFile $Destination -UseBasicParsing -TimeoutSec 90 if ((Get-Item $Destination).Length -gt 0) { return } } catch { $lastErr = $_ Start-Sleep -Seconds 2 } } if (Get-Command curl.exe -ErrorAction SilentlyContinue) { & curl.exe -fsSL --tlsv1.2 $Url -o $Destination if ($LASTEXITCODE -eq 0 -and (Test-Path $Destination)) { return } } throw $lastErr } function Copy-GeoNetAgentPayload { param( [string]$SourceRoot, [string]$DestinationRoot, [object]$Manifest ) foreach ($rel in $Manifest.files) { $src = Join-Path $SourceRoot ($rel -replace '/', [IO.Path]::DirectorySeparatorChar) $dst = Join-Path $DestinationRoot ($rel -replace '/', [IO.Path]::DirectorySeparatorChar) if (-not (Test-Path $src)) { throw "File sumber tidak ada: $src" } $dir = Split-Path -Parent $dst if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } Copy-Item -Path $src -Destination $dst -Force } } function Install-GeoNetAgentPayload { param( [string]$Base, [string]$DestinationRoot, [object]$Manifest ) foreach ($rel in $Manifest.files) { $url = "$Base/$($rel -replace '\\', '/')" $dst = Join-Path $DestinationRoot ($rel -replace '/', [IO.Path]::DirectorySeparatorChar) Write-Host " -> $rel" Get-GeoNetRemoteFile -Url $url -Destination $dst } } function Set-GeoNetAgentConfig { param( [string]$InstallRoot, [string]$Base, [string]$Token, [bool]$UseRemoteConfig ) $configPath = Join-Path $InstallRoot 'config.json' if ($Token) { $cfg = [ordered]@{ collector_url = 'https://agent.gisportal.id/api/v1/agent/report' agent_token = $Token include_software = $true max_software = 500 timeout_seconds = 120 } if (Test-Path $configPath) { try { $existing = Get-Content $configPath -Raw | ConvertFrom-Json if ($existing.collector_url) { $cfg.collector_url = $existing.collector_url } if ($null -ne $existing.include_software) { $cfg.include_software = [bool]$existing.include_software } if ($existing.max_software) { $cfg.max_software = [int]$existing.max_software } if ($existing.timeout_seconds) { $cfg.timeout_seconds = [int]$existing.timeout_seconds } } catch { } } $cfg | ConvertTo-Json | Set-Content -Path $configPath -Encoding UTF8 return } if ((Test-Path $configPath) -and -not $UseRemoteConfig) { Write-Host 'config.json lokal dipertahankan.' -ForegroundColor DarkGray return } if ($UseRemoteConfig) { Get-GeoNetRemoteFile -Url "$Base/config.json" -Destination $configPath return } throw 'config.json tidak ada. Sediakan -AgentToken atau config.json di URL install.' } function Invoke-GeoNetSchtasks { param( [Parameter(Mandatory)][string[]]$ArgumentList, [switch]$IgnoreError ) $prev = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' $output = & schtasks.exe @ArgumentList 2>&1 $code = $LASTEXITCODE $ErrorActionPreference = $prev if ($code -ne 0 -and -not $IgnoreError) { $msg = ($output | Out-String).Trim() if ($msg) { throw $msg } throw "schtasks gagal (exit $code): schtasks.exe $($ArgumentList -join ' ')" } return $code } function Register-GeoNetAgentTask { param( [string]$InstallRoot, [int]$Hours ) $userScript = Join-Path $InstallRoot 'UserAgent.ps1' if (-not (Test-Path $userScript)) { throw "UserAgent.ps1 tidak ditemukan di $InstallRoot" } $taskArgs = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$userScript`"" $taskCmd = "powershell.exe $taskArgs" $taskNames = @('GeoNetAgent-Report', 'GeoNetAgent-Report-Startup') foreach ($name in $taskNames) { Unregister-ScheduledTask -TaskName $name -Confirm:$false -ErrorAction SilentlyContinue Invoke-GeoNetSchtasks -ArgumentList @('/Delete', '/TN', $name, '/F') -IgnoreError | Out-Null } Invoke-GeoNetSchtasks -ArgumentList @( '/Create', '/TN', 'GeoNetAgent-Report-Startup', '/TR', $taskCmd, '/SC', 'ONSTART', '/RU', 'SYSTEM', '/RL', 'HIGHEST', '/F' ) | Out-Null Invoke-GeoNetSchtasks -ArgumentList @( '/Create', '/TN', 'GeoNetAgent-Report', '/TR', $taskCmd, '/SC', 'HOURLY', '/MO', "$Hours", '/RU', 'SYSTEM', '/RL', 'HIGHEST', '/F' ) | Out-Null } $base = Get-GeoNetInstallBaseUrl -Url $BaseUrl Write-Host "GeoNetAgent install/update" -ForegroundColor Cyan Write-Host " Target: $InstallRoot" if (-not (Test-Path $InstallRoot)) { New-Item -ItemType Directory -Path $InstallRoot -Force | Out-Null } if ($SourcePath) { $sourceRoot = Resolve-Path $SourcePath $manifestPath = Join-Path $sourceRoot 'install\manifest.json' if (-not (Test-Path $manifestPath)) { $manifestPath = Join-Path $sourceRoot 'manifest.json' } if (-not (Test-Path $manifestPath)) { throw "manifest.json tidak ditemukan di $SourcePath" } $manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json Write-Host "Salin dari lokal v$($manifest.version) ..." Copy-GeoNetAgentPayload -SourceRoot $sourceRoot -DestinationRoot $InstallRoot -Manifest $manifest Set-GeoNetAgentConfig -InstallRoot $InstallRoot -Base $base -Token $AgentToken -UseRemoteConfig:$false } else { Write-Host "Unduh dari $base ..." $manifest = Get-GeoNetRemoteJson -Url "$base/manifest.json" Write-Host "Paket v$($manifest.version) ($($manifest.files.Count) file)" Install-GeoNetAgentPayload -Base $base -DestinationRoot $InstallRoot -Manifest $manifest $remoteConfig = $false try { $null = Invoke-WebRequest -Uri "$base/config.json" -Method Head -UseBasicParsing $remoteConfig = $true } catch { } Set-GeoNetAgentConfig -InstallRoot $InstallRoot -Base $base -Token $AgentToken -UseRemoteConfig:$remoteConfig } $manifest.version | Set-Content -Path (Join-Path $InstallRoot 'VERSION') -Encoding ASCII -NoNewline if (-not $SkipScheduledTask) { if (-not (Test-GeoNetAdmin)) { Write-Warning 'Bukan Administrator - file terpasang, tetapi Scheduled Task dilewati. Jalankan ulang sebagai Admin.' } else { Write-Host 'Mendaftarkan Scheduled Task (SYSTEM: startup + tiap' $ScheduleHours 'jam) ...' Register-GeoNetAgentTask -InstallRoot $InstallRoot -Hours $ScheduleHours Write-Host 'Scheduled Task OK: GeoNetAgent-Report, GeoNetAgent-Report-Startup' -ForegroundColor Green } } if (-not $SkipInitialRun) { Write-Host 'Menjalankan laporan awal ...' -ForegroundColor Cyan & (Join-Path $InstallRoot 'UserAgent.ps1') exit $LASTEXITCODE } Write-Host 'Selesai.' -ForegroundColor Green exit 0