Private/Vault/Initialize-GuerrillaVault.ps1

# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0
# https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/
# AI/LLM use: see AI-USAGE.md for required attribution
function Initialize-GuerrillaVault {
    <#
    .SYNOPSIS
        Ensures the PSGuerrilla SecretManagement vault exists and is configured.
    .DESCRIPTION
        Checks for required modules (SecretManagement + SecretStore), offers to
        install them if missing, registers the PSGuerrilla vault, and configures
        it for non-interactive use (DPAPI on Windows, encrypted file on Linux/macOS).
    #>

    [CmdletBinding()]
    param(
        [string]$VaultName = 'PSGuerrilla',
        [switch]$Force
    )

    # --- Check for required modules ---
    $smInstalled = Get-Module -ListAvailable -Name 'Microsoft.PowerShell.SecretManagement' -ErrorAction SilentlyContinue
    $ssInstalled = Get-Module -ListAvailable -Name 'Microsoft.PowerShell.SecretStore' -ErrorAction SilentlyContinue

    $missing = @()
    if (-not $smInstalled) { $missing += 'Microsoft.PowerShell.SecretManagement' }
    if (-not $ssInstalled) { $missing += 'Microsoft.PowerShell.SecretStore' }

    if ($missing.Count -gt 0) {
        $border = '=' * 60
        Write-Host ''
        Write-Host " $($script:Palette.Amber)$border$($PSStyle.Reset)"
        Write-Host " $($script:Palette.Parchment) SAFEHOUSE PREREQUISITES$($PSStyle.Reset)"
        Write-Host " $($script:Palette.Amber)$border$($PSStyle.Reset)"
        Write-Host ''
        Write-Host " $($script:Palette.Khaki) The following modules are required for secure credential$($PSStyle.Reset)"
        Write-Host " $($script:Palette.Khaki) storage and are not currently installed:$($PSStyle.Reset)"
        Write-Host ''
        foreach ($mod in $missing) {
            Write-Host " $($script:Palette.Amber)* $mod$($PSStyle.Reset)"
        }
        Write-Host ''
        Write-Host " $($script:Palette.Gray) These are Microsoft's official credential management$($PSStyle.Reset)"
        Write-Host " $($script:Palette.Gray) modules from the PowerShell Gallery.$($PSStyle.Reset)"
        Write-Host " $($script:Palette.Amber)$border$($PSStyle.Reset)"
        Write-Host ''

        if (-not $Force) {
            $response = Read-Host ' Install now? [Y/n]'
            if ($response -and $response -notmatch '^[Yy]') {
                throw 'SecretManagement modules are required. Install them manually or re-run with -Force.'
            }
        }

        foreach ($mod in $missing) {
            Write-Host " Installing $mod..." -NoNewline
            try {
                Install-Module -Name $mod -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
                Write-Host " $($script:Palette.Sage)DONE$($PSStyle.Reset)"
            } catch {
                Write-Host " $($script:Palette.Amber)FAILED$($PSStyle.Reset)"
                throw "Failed to install $mod`: $_"
            }
        }

        # Import after install
        Import-Module Microsoft.PowerShell.SecretManagement -Force -ErrorAction Stop
        Import-Module Microsoft.PowerShell.SecretStore -Force -ErrorAction Stop
    } else {
        # Ensure modules are imported
        if (-not (Get-Module -Name 'Microsoft.PowerShell.SecretManagement')) {
            Import-Module Microsoft.PowerShell.SecretManagement -ErrorAction Stop
        }
        if (-not (Get-Module -Name 'Microsoft.PowerShell.SecretStore')) {
            Import-Module Microsoft.PowerShell.SecretStore -ErrorAction Stop
        }
    }

    # --- Register vault if not exists ---
    $existingVault = Get-SecretVault -Name $VaultName -ErrorAction SilentlyContinue
    if (-not $existingVault) {
        # Configure SecretStore BEFORE registering the vault so the store is created
        # with Authentication=None from the start, rather than defaulting to Password
        # and then needing the user to enter a password twice just to remove it.
        $needsConfig = $true
        try {
            $ssConfig = Get-SecretStoreConfiguration -ErrorAction Stop
            if ($ssConfig.Authentication -eq 'None') { $needsConfig = $false }
        } catch {
            # No store configured yet — Set-SecretStoreConfiguration below will create one.
        }
        if ($needsConfig) {
            try {
                Set-SecretStoreConfiguration -Authentication None -Interaction None `
                    -Password $null -Confirm:$false -Force -ErrorAction Stop
            } catch {
                Write-Warning "Could not auto-configure SecretStore for non-interactive use: $_"
                Write-Warning 'If prompted for a password next, it is from the Microsoft SecretStore module.'
            }
        }

        Write-Verbose "Registering vault '$VaultName' with SecretStore backend"
        Register-SecretVault -Name $VaultName -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault -ErrorAction Stop
        Write-Verbose "Vault '$VaultName' registered for non-interactive access"
    } else {
        Write-Verbose "Vault '$VaultName' already exists"
    }

    return @{
        VaultName = $VaultName
        Status    = 'Ready'
        Backend   = 'Microsoft.PowerShell.SecretStore'
        Protection = if ($IsWindows -or (-not (Test-Path variable:IsWindows))) { 'DPAPI (current user)' } else { 'Encrypted file (password-protected)' }
    }
}