<# .SYNOPSIS Install a SecurityInsight Pro licence into the ONE SI Key Vault (#173). .DESCRIPTION SecurityInsight Pro reads its licence at run time from the Key Vault secret 'SI-License' in the vault Get-SIVaultName resolves (SI_KeyVaultName, or SpnKeyVaultName on an install that predates that name). Until now nothing put it there -- it was stored by hand, and a paying customer without it silently ran the free edition. This is how it gets there: 1. VERIFY with the real verifier, before anything is written: the signature and dates must verify (Valid, Grace or NotYetValid), every feature it grants must be one SecurityInsight has, and when the licence is bound to tenants this tenant must be one of them. 2. WRITE the secret (the licence file's JSON, unchanged). 3. READ IT BACK and verify the stored value again, then print the edition, features and dates. The licence file's content is never printed. -WhatIf shows the vault, the secret name and the verdict, and writes nothing. .PARAMETER Path The licence file you received (.silicense / .aitlicense). .PARAMETER VaultName Override the vault. Normally resolved from config\SecurityInsight.custom.ps1. .PARAMETER TenantId The tenant this install runs in, for the tenant-binding check. Default: the tenant of the signed-in account. .EXAMPLE .\Set-SILicense.ps1 -Path .\contoso.silicense .EXAMPLE .\Set-SILicense.ps1 -Path .\contoso.silicense -WhatIf .NOTES Requirements: PowerShell 7 (pwsh). Windows PowerShell 5.1 is refused at once (#Requires -Version 7.0). Role: Key Vault Secrets Officer -- on the SecurityInsight Key Vault Hosts: *.blob.core.windows.net, *.queue.core.windows.net, *.table.core.windows.net, *.vault.azure.net, 169.254.169.254, api.loganalytics.io, api.msrc.microsoft.com, api.security.microsoft.com, api.securitycenter.microsoft.com, api.securityplatform.microsoft.com, database.windows.net, entra.microsoft.com, graph.microsoft.com, invardia.com, licenses.nuget.org, login.microsoftonline.com, management.azure.com, monitor.azure.com, nvd.nist.gov, outlook.office.com, outlook.office365.com, portal.invardia.com, storage.azure.com, vault.azure.net Without a licence SecurityInsight runs the Community edition: every free engine works, and each Pro engine prints one line and publishes nothing. A licence can also be placed as config\*.silicense instead of in Key Vault; the vault is preferred because it survives a rebuilt machine and is read by container hosting. .LINK https://invardia.com/docs/si/scripts/Set-SILicense/ #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)][string]$Path, [Parameter()][string]$VaultName, [Parameter()][string]$TenantId ) #Requires -Version 7.0 # SecurityInsight 3.0.79 -- Set-SILicense.ps1, standalone (Pro edition). Built 2026-11-09T20:28:39Z from commit c1472e2f4c240c971c9559f07eef7e794b322001, source SOLUTIONS/SecurityInsight/setup/Set-SILicense.ps1, # by release/Build-SISupportScripts.ps1 (framework SUPPORT-DL-1). Download: Invoke-WebRequest https://invardia.com/support/si/Set-SILicense.ps1 -OutFile Set-SILicense.ps1 # It carries the 107 SecurityInsight file(s) the script loads (listed in $__siSupportFiles below, readable, each line # prefixed with "|"); at run time it writes them to a private temporary folder, checks each SHA-256, runs the original # script from there with your parameters, and removes the folder. No PowerShell modules are needed. $script:SIScriptVersion = '3.0.79' # SI #547: the version this copy reports first (stamped into its embedded setup/_shared/SI-ScriptRun.ps1 too) $__siSupportVersion = '3.0.79' $__siSupportEntry = 'setup/Set-SILicense.ps1' $__siSupportFiles = [ordered]@{} $__siSupportFiles['setup/Set-SILicense.ps1'] = @{ Sha256 = 'b5b935b20c2997644cc9da2f34ebd12d1c4fa6b91201bba380d263cef62fcae6'; Text = @' |<# |.SYNOPSIS | Install a SecurityInsight Pro licence into the ONE SI Key Vault (#173). | |.DESCRIPTION | SecurityInsight Pro reads its licence at run time from the Key Vault secret 'SI-License' in the vault | Get-SIVaultName resolves (SI_KeyVaultName, or SpnKeyVaultName on an install that predates that name). Until | now nothing put it there -- it was stored by hand, and a paying customer without it silently ran the free | edition. This is how it gets there: | | 1. VERIFY with the real verifier, before anything is written: the signature and dates must verify | (Valid, Grace or NotYetValid), every feature it grants must be one SecurityInsight has, and when | the licence is bound to tenants this tenant must be one of them. | 2. WRITE the secret (the licence file's JSON, unchanged). | 3. READ IT BACK and verify the stored value again, then print the edition, features and dates. | | The licence file's content is never printed. -WhatIf shows the vault, the secret name and the verdict, | and writes nothing. | |.PARAMETER Path | The licence file you received (.silicense / .aitlicense). |.PARAMETER VaultName | Override the vault. Normally resolved from config\SecurityInsight.custom.ps1. |.PARAMETER TenantId | The tenant this install runs in, for the tenant-binding check. Default: the tenant of the signed-in account. |.EXAMPLE | .\Set-SILicense.ps1 -Path .\contoso.silicense |.EXAMPLE | .\Set-SILicense.ps1 -Path .\contoso.silicense -WhatIf |.NOTES | Without a licence SecurityInsight runs the Community edition: every free engine works, and each Pro engine | prints one line and publishes nothing. A licence can also be placed as config\*.silicense instead of in | Key Vault; the vault is preferred because it survives a rebuilt machine and is read by container hosting. | |.LINK | https://invardia.com/docs/si/scripts/Set-SILicense/ |#> |[CmdletBinding(SupportsShouldProcess)] |param( | [Parameter(Mandatory)][string]$Path, | [Parameter()][string]$VaultName, | [Parameter()][string]$TenantId |) | |#Requires -Version 7.0 # after the help and the param block: before the help, it hides the help from Get-Help |# framework section 12.7 / 12.10: the documentation link first, a transcript of the run, -WhatIf writes nothing |. ([IO.Path]::Combine($PSScriptRoot, '_shared', 'SI-ScriptRun.ps1')) |$siRun = Start-SIScriptRun -Script 'Set-SILicense' -Quiet:($false) |try { |$ErrorActionPreference = 'Stop' |$siRoot = Split-Path -Parent $PSScriptRoot |. (Join-Path $siRoot 'auth\Get-SIVaultName.ps1') |. (Join-Path $siRoot 'engine\SI-FeatureCatalog.ps1') |function Info([string]$m) { Write-Host "[INFO] $m" -ForegroundColor Gray } |function Ok ([string]$m) { Write-Host "[OK] $m" -ForegroundColor Green } | |# Load the customer's config for the vault name, the way every launcher does -- unless one was given. |if (-not $VaultName) { | $cfg = Join-Path $siRoot 'config\SecurityInsight.custom.ps1' | if (Test-Path -LiteralPath $cfg) { try { . $cfg 3>$null } catch { Write-Warning ("config\SecurityInsight.custom.ps1 did not load cleanly: {0}" -f $_.Exception.Message) } } |} |$vault = Get-SIVaultName -VaultName $VaultName |Info ("vault : {0} secret: SI-License" -f $vault) | |if (-not (Test-Path -LiteralPath $Path)) { throw "Licence file not found: $Path" } |$text = [IO.File]::ReadAllText((Resolve-Path -LiteralPath $Path).Path).Trim() |# Azure over native REST, no PowerShell module (#305), no az CLI (#521). An existing sign-in in this process (the guided |# install, the licence request of the updater job) is reused; otherwise a person signs in here (Azure Cloud Shell, or the browser). |if (-not (Get-Command -Name Get-SISetupAzureContext -ErrorAction SilentlyContinue)) { . ([IO.Path]::Combine($PSScriptRoot, '_shared', 'SI-SetupAzure.ps1')) } |if (-not "$TenantId".Trim()) { | $azCtx = Get-SISetupAzureContext | if (-not $azCtx) { try { $azCtx = Connect-SISetupAzure } catch { Write-Warning ("not signed in to Azure: {0}" -f $_.Exception.Message) } } | if ($azCtx) { $TenantId = [string]$azCtx.Tenant.Id } |} | |# ---- 1. verify BEFORE writing ---------------------------------------------------------------------- |$lic = Get-SILicense -SecretText $text |$known = @(Get-SIFeatureCatalog | ForEach-Object { $_.Name }) |$check = Test-SILicenseForInstall -License $lic -KnownFeatures $known -TenantId $TenantId |if (-not $check.Ok) { | $check.Problems | ForEach-Object { Write-Host "[ERROR] $_" -ForegroundColor Red } | throw 'The licence was NOT installed -- nothing was written. Contact Invardia support (support@invardia.com) with the lines above.' |} |Ok ("verified : {0}" -f $check.Summary) |@($check.Notes) | Where-Object { $_ } | ForEach-Object { Write-Host "[INFO] $_" } |if ($lic.Status -eq 'Grace') { Write-Warning ("the licence is in its grace period: {0}" -f $lic.Reason) } |if ($lic.Status -eq 'NotYetValid') { Write-Warning ("the licence is not active yet: {0}" -f $lic.Reason) } | |if (-not $PSCmdlet.ShouldProcess("$vault/SI-License", 'write the SecurityInsight Pro licence')) { return } | |# ---- 2. write ---------------------------------------------------------------------------------------- |if (-not (Get-SISetupAzureContext)) { $null = Connect-SISetupAzure -TenantId $TenantId } |$sec = ConvertTo-SecureString -String $text -AsPlainText -Force |$null = Set-SIAzKeyVaultSecret -VaultName $vault -Name 'SI-License' -SecretValue $sec -ContentType 'application/json' -ErrorAction Stop | |# ---- 3. read back and verify the STORED value --------------------------------------------------------- |$back = Get-SIAzKeyVaultSecret -VaultName $vault -Name 'SI-License' -AsPlainText -ErrorAction Stop |$again = Get-SILicense -SecretText $back |$check2 = Test-SILicenseForInstall -License $again -KnownFeatures $known -TenantId $TenantId |if (-not $check2.Ok -or "$back".Trim() -ne $text) { throw 'The stored licence did not read back identical and valid -- check the vault and re-run.' } |Ok ("installed and read back: SecurityInsight Pro for {0}; {1}" -f $again.Customer, $again.Reason) |Info 'Pro engines pick it up on their next run. Nothing else to do.' | |# ---- 4. the audit trail (#260): when this install has a Pro database, the install is recorded there ----------------- |# Never the licence content -- the customer, the edition, the dates and the vault. Best effort: the licence is installed |# whether or not the row can be written (it says so). |if ([string]$global:SI_ProSqlServer -and [string]$global:SI_ProSqlDatabase) { | try { | . (Join-Path $siRoot 'engine\pro-settings\SI-ProSettings.ps1') | $first = { param([object[]]$v) foreach ($x in $v) { if (-not [string]::IsNullOrWhiteSpace([string]$x)) { return [string]$x } }; '' } | $store = New-SIProSettingsStore -Server ([string]$global:SI_ProSqlServer) -Database ([string]$global:SI_ProSqlDatabase) ` | -AppId (& $first @($global:SI_SPN_AppId)) -TenantId (& $first @($global:SI_SPN_TenantId, $TenantId)) ` | -CertThumbprint (& $first @($global:SI_SPN_CertThumbprint)) # #524: no fallback to the retired platform globals | $okRow = Write-SIAuditEvent -Store $store -Action 'licence.licence.install' -Actor (Get-SIAuditActor) -ActorSource 'setup' -Target ("{0}/SI-License" -f $vault) ` | -After ([ordered]@{ customer = [string]$again.Customer; status = [string]$again.Status; summary = [string]$check2.Summary }) | if ($okRow) { Info 'recorded on the audit trail of the Pro database.' } | } catch { Write-Warning ("the licence is installed, but it could not be recorded on the audit trail: {0}" -f $_.Exception.Message) } |} |} finally { Stop-SIScriptRun -Run $siRun } '@ } $__siSupportFiles['setup/_shared/SI-ScriptRun.ps1'] = @{ Sha256 = 'a5c4d788d516521639611af43e1b88458216ce58bdb455885cac8e2a568afbd2'; Text = @' |#Requires -Version 5.1 |<# | EVERY SCRIPT A PERSON RUNS (framework DOCS/REQUIREMENTS.md section 12.7 SCRIPT-DOC-1 + section 12.10 SECURITY-PACK-1; SI #481 / #484). | One helper, called first by each such script: | | $siRun = Start-SIScriptRun -Script '