src/public/Remote/Save-AitherPSSession.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Save PSSession configuration for later reconnection

.DESCRIPTION
    Saves PSSession configuration (connection details) to configuration storage so you can
    reconnect to the same server later without re-entering credentials. Useful for frequently
    accessed servers and automation scenarios.

.PARAMETER Session
    PSSession object to save configuration from. This parameter is REQUIRED.
    The session's connection details will be extracted and saved.

.PARAMETER Name
    Name identifier for the saved session configuration. This parameter is REQUIRED.
    Use this name later with Connect-AitherPSSession to reconnect.

    Examples:
    - "Production-Server"
    - "Dev-Environment"
    - "Database-Cluster"

.PARAMETER Description
    Optional description of the saved session. Useful for documenting what the session is used for.

.PARAMETER Force
    Overwrite existing saved session configuration with the same name.

.INPUTS
    System.Management.Automation.Runspaces.PSSession
    You can pipe PSSession objects to Save-AitherPSSession.

.OUTPUTS
    PSCustomObject
    Returns saved session configuration object with Name, ComputerName, Port, UseSSH, UseSSL properties.

.EXAMPLE
    $session = New-AitherPSSession -ComputerName "server01" -Credential (Get-Credential)
    Save-AitherPSSession -Session $session -Name "Production"

    Creates a session and saves its configuration as "Production".

.EXAMPLE
    Get-AitherPSSession | Save-AitherPSSession -Name "CurrentSessions"

    Saves all current sessions' configurations.

.EXAMPLE
    Save-AitherPSSession -Session $session -Name "Dev" -Description "Development environment"

    Saves session with a descriptive name and description.

.NOTES
    Saved session configurations are stored in the AitherZero configuration system.
    Credentials are NOT saved - you'll need to provide them again when reconnecting.

    Saved configurations include:
    - ComputerName
    - Port
    - UseSSH flag
    - UseSSL flag
    - ConfigurationName
    - Session name

.LINK
    Connect-AitherPSSession
    Get-AitherPSSession
    New-AitherPSSession
#>

function Save-AitherPSSession {
[OutputType([PSCustomObject])]
[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(Mandatory=$false, ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [System.Management.Automation.Runspaces.PSSession[]]$Session,

    [Parameter(Mandatory=$false)]
    [ValidateNotNullOrEmpty()]
    [string]$Name,

    [Parameter()]
    [string]$Description,

    [switch]$Force,

    [switch]$ShowOutput
)

begin {
    # Save original log targets
    $originalLogTargets = $script:AitherLogTargets

    # Set log targets based on ShowOutput parameter
    if ($ShowOutput) {
        # Ensure Console is in the log targets
        if ($script:AitherLogTargets -notcontains 'Console') {
            $script:AitherLogTargets += 'Console'
        }
    }
    else {
        # Remove Console from log targets if present (default behavior)
        if ($script:AitherLogTargets -contains 'Console') {
            $script:AitherLogTargets = $script:AitherLogTargets | Where-Object { $_ -ne 'Console' }
        }
    }

    $moduleRoot = Get-AitherModuleRoot
    $sessionsConfigPath = Join-Path $moduleRoot 'library' 'saved-sessions'

    if (-not (Test-Path $sessionsConfigPath)) {
        New-Item -Path $sessionsConfigPath -ItemType Directory -Force | Out-Null
    }
}

process {
    try {
        foreach ($sessionToSave in $Session) {
        try {
            $sessionConfig = @{
                Name = $Name
                ComputerName = $sessionToSave.ComputerName
                Port = if ($sessionToSave.Options.Port) { $sessionToSave.Options.Port } else { if ($sessionToSave.Options.UseSSL) { 5986 } else { 5985 } }
                UseSSH = $sessionToSave.Transport -eq 'SSH'
                UseSSL = $sessionToSave.Options.UseSSL
                ConfigurationName = $sessionToSave.ConfigurationName
                Description = $Description
                Created = Get-Date
                SessionId = $sessionToSave.Id
            }

            $configFile = Join-Path $sessionsConfigPath "${Name}.json"

            if ((Test-Path $configFile) -and -not $Force) {
                throw "Session configuration '$Name' already exists. Use -Force to overwrite."
            }

            if ($PSCmdlet.ShouldProcess($Name, "Save PSSession configuration")) {
                $sessionConfig | ConvertTo-Json -Depth 10 | Out-File -FilePath $configFile -Encoding UTF8 -Force

                Write-AitherLog -Level Information -Message "Saved PSSession configuration: $Name" -Source $PSCmdlet.MyInvocation.MyCommand.Name -Data $sessionConfig
                return [PSCustomObject]$sessionConfig
            }
        }
        catch {
            # Use centralized error handling
            $errorScript = Join-Path $PSScriptRoot '..' 'Private' 'Write-AitherError.ps1'
            if (Test-Path $errorScript) {
                . $errorScript -ErrorRecord $_ -CmdletName $PSCmdlet.MyInvocation.MyCommand.Name -Operation "Saving PSSession configuration: $Name" -Parameters $PSBoundParameters -ThrowOnError
            }
            else {
                $errorObject = [PSCustomObject]@{
                    PSTypeName = 'AitherZero.Error'
                    Success = $false
                    ErrorId = [System.Guid]::NewGuid().ToString()
                    Cmdlet = $PSCmdlet.MyInvocation.MyCommand.Name
                    Operation = "Saving PSSession configuration: $Name"
                    Error = $_.Exception.Message
                    Timestamp = Get-Date
                }
                Write-Output $errorObject

                Write-AitherLog -Level Error -Message "Failed to save PSSession configuration $Name : $($_.Exception.Message)" -Source $PSCmdlet.MyInvocation.MyCommand.Name -Exception $_
            }
            throw
        }
    }
    }
    finally {
        # Restore original log targets
        $script:AitherLogTargets = $originalLogTargets
    }
}

}