Public/New-CIEMDatabase.ps1
|
function New-CIEMDatabase { <# .SYNOPSIS Creates and initializes the CIEM SQLite database. .DESCRIPTION Creates the CIEM database file, applies provider schemas, and syncs static database catalogs. Safe to call multiple times because schema creation uses idempotent SQL and catalog sync upserts current data. The database is stored inside the psu-app package root at data/ciem.db. .PARAMETER Path Explicit path to the database file. If omitted, auto-resolved based on the runtime context (PSU or local dev). .PARAMETER SchemaPath Explicit path to the schema SQL file. If omitted, resolved from Get-CIEMDefaultConfig relative to the module root. .PARAMETER PassThru Returns the database file path. .EXAMPLE New-CIEMDatabase .EXAMPLE New-CIEMDatabase -Path '/home/data/ciem.db' -PassThru #> [CmdletBinding()] param( [string]$Path, [string]$SchemaPath, [switch]$PassThru ) $ErrorActionPreference = 'Stop' $config = Get-CIEMDefaultConfig # Resolve database path — uses $script:DataRoot (set in psm1) which is already # resolved to be outside the module version directory so data survives upgrades. if (-not $Path) { if (-not $script:DataRoot) { throw 'New-CIEMDatabase: $script:DataRoot is not set. Module not loaded correctly.' } $Path = Join-Path $script:DataRoot 'ciem.db' } # Resolve schema path if (-not $SchemaPath) { if (-not $script:ModuleRoot) { throw 'New-CIEMDatabase: Cannot resolve schema path — $script:ModuleRoot is not set. Provide -SchemaPath explicitly.' } $SchemaPath = Join-Path $script:ModuleRoot $config.database.schemaPath } # Validate schema file exists if (-not (Test-Path $SchemaPath)) { throw "New-CIEMDatabase: Schema file not found at '$SchemaPath'" } # Ensure the parent directory exists $dir = Split-Path $Path -Parent if (-not (Test-Path $dir)) { New-Item -Path $dir -ItemType Directory -Force | Out-Null Write-Verbose "CIEM DB: Created directory $dir" } $isNew = -not (Test-Path $Path) # Read and execute schema SQL. The schema MUST be fully idempotent — every # CREATE TABLE / CREATE INDEX uses IF NOT EXISTS, and ALTER TABLE migrations # that have been baked into the CREATE TABLE definitions must be removed # from the file. If a statement throws, that is a real bug — fail fast. $schemaSql = Get-Content -Path $SchemaPath -Raw $conn = Open-PSUSQLiteConnection -Database $Path try { foreach ($statement in ($schemaSql -split ';\s*\n' | Where-Object { $_.Trim() })) { Invoke-PSUSQLiteQuery -Connection $conn -Query $statement.Trim() -AsNonQuery | Out-Null } } finally { $conn.Dispose() } if ($isNew) { Write-Verbose "CIEM DB: Created new database at $Path" } else { Write-Verbose "CIEM DB: Database at $Path is up to date" } # Store path in module scope for other functions $script:DatabasePath = $Path foreach ($schema in @( @{ Path = Join-Path $script:AzureRoot 'Data/azure_schema.sql'; Label = 'Azure' } @{ Path = Join-Path $script:AzureDiscoveryRoot 'Data/discovery_schema.sql'; Label = 'AzureDiscovery' } @{ Path = Join-Path $script:GraphRoot 'Data/graph_schema.sql'; Label = 'Graph' } )) { $dbPath = Get-CIEMDatabasePath if (-not $dbPath) { throw "Database path not resolved for $($schema.Label) schema." } if (-not (Test-Path $schema.Path)) { throw "Schema file not found: $($schema.Path)" } $providerSchemaSql = Get-Content -Path $schema.Path -Raw $providerSchemaConnection = Open-PSUSQLiteConnection -Database $dbPath try { foreach ($statement in ($providerSchemaSql -split ';\s*\n' | Where-Object { $_.Trim() })) { Invoke-PSUSQLiteQuery -Connection $providerSchemaConnection -Query $statement.Trim() -AsNonQuery | Out-Null } } finally { $providerSchemaConnection.Dispose() } } UpdateCIEMAttackPathStorageSchema $attackPathRuleSync = Sync-CIEMAttackPathRuleCatalog Write-Verbose "CIEM DB: Synced $($attackPathRuleSync.RuleCount) attack path rules" SyncCIEMCheckCatalog -Provider 'Azure' if ($PassThru) { $Path } } |