Public/Start-LMSessionSyncServer.ps1

<#
.SYNOPSIS
    This function starts a session synchronization server for Logic Monitor.

.DESCRIPTION
    The Start-LMSessionSyncServer function starts a Pode server that is used to synchronize sessions for Logic Monitor.
    It uses a secret vault to store session details. If the vault does not exist, it creates one.
    The function also provides options to enable request logging and error logging.

.PARAMETER EnableRequestLogging
    A switch to control whether request logging is enabled.

.PARAMETER EnableErrorLogging
    A switch to control whether error logging is enabled.

.EXAMPLE
    Start-LMSessionSyncServer -EnableRequestLogging -EnableErrorLogging

    This command starts the session synchronization server with request logging and error logging enabled.

.INPUTS
    None. You cannot pipe objects to Start-LMSessionSyncServer.

.OUTPUTS
    The function does not return any output. It writes messages to the host to indicate the status of the server and the secret vault.

.NOTES
    The function throws an error if it fails to unlock the secret vault with the provided credentials.
#>

Function Start-LMSessionSyncServer {
    Param(
        [Switch]$EnableRequestLogging,
        [Switch]$EnableErrorLogging
    )
    Begin {
        #Ensure we have a vault to use for storing session details
        $VaultName = "Logic.Monitor"

        Try {
            Get-SecretVault -Name $VaultName -ErrorAction Stop | Out-Null
            Write-Host "Existing vault $VaultName already exists, skipping creation" -ForegroundColor Yellow
        }
        Catch {
            If($_.Exception.Message -like "*Vault $VaultName does not exist in registry*") {
                Write-Host "Credential vault for cached accounts does not currently exist, creating credential vault: $VaultName" -ForegroundColor Yellow
                Register-SecretVault -Name $VaultName -ModuleName Microsoft.PowerShell.SecretStore
                Get-SecretStoreConfiguration | Out-Null
            }
        }

    }
    Process{
        #Assuming we got here we can start our session server
        Start-PodeServer {
            #Ensure we have a vault to use for storing session details
            $VaultName = "Logic.Monitor"
            $VaultKeyPrefix = "LMSessionSync"

            #Capture creds so we can use them
            Try{
                Unlock-SecretVault -Name $VaultName -Password $(Read-Host "Enter vault credentials" -AsSecureString -OutVariable VaultCred) -ErrorAction Stop
                Set-PodeState -Name 'VaultUnlock' -Value $VaultCred | Out-Null
            }
            Catch {
                Write-Error "Unable to start SessionSync server without valid vault unlock credentials: $_"
                Return
            }

            #Rotate APIKey in vault
            $ApiKey = (1..64| ForEach-Object {[byte](Get-Random -Max 256)} | ForEach-Object ToString X2) -join ''
            Set-Secret -Name $VaultKeyPrefix-RESTAPIKey -Vault $VaultName -Secret $ApiKey -Metadata @{Modified="$(Get-Date)";Portal="SessionSync-ApiKey"}

            #Sete our web server state
            Set-PodeState -Name 'VaultName' -Value $VaultName | Out-Null
            Set-PodeState -Name 'VaultKeyPrefix' -Value $VaultKeyPrefix | Out-Null
            Set-PodeState -Name 'VaultApiKey' -Value $ApiKey | Out-Null

            Add-PodeEndpoint -Address 127.0.0.1 -Port 8072 -Protocol Http

            If($EnableRequestLogging){New-PodeLoggingMethod -Terminal | Enable-PodeRequestLogging}
            If($EnableErrorLogging){New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging}

            # setup apiKey authentication to validate a user
            New-PodeAuthScheme -ApiKey | Add-PodeAuth -Name 'Auth' -Sessionless -ScriptBlock {
                param($key)

                #Grab current key
                $ApiKey = Get-PodeState -Name 'VaultApiKey'
                
                #Check if user is authenticated
                if ($key.toString() -eq $ApiKey.toString()) {
                    return @{
                        User = @{'ID' ='1'}
                    }
                }

                # authentication failed
                return $null
            }
        
            # check the request on this route against the authentication
            Add-PodeRoute -Method Get -Path '/api/v1/portal/:AccountName' -Authentication 'Auth' -ScriptBlock {
                #Store Session Info in Secret Vault
                $VaultName = Get-PodeState -Name 'VaultName'
                $VaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                $VaultUnlock = Get-PodeState -Name 'VaultUnlock'

                Unlock-SecretVault -Name  $VaultName -Password $VaultUnlock

                #Get Session details in vault, return response data
                $AccountName = $WebEvent.Parameters['AccountName']
                Try{
                    $SecretData = Get-Secret -Name $VaultKeyPrefix-$AccountName -Vault  $VaultName -AsPlainText -ErrorAction Stop
                    Write-PodeJsonResponse -Value $SecretData
                }
                Catch{
                    Write-PodeTextResponse -Value "Unexpected error: $($_.Exception.Message)" -StatusCode 500
                    #Set-PodeResponseStatus -Code 500 -Exception "Unexpected error: $($_.Exception.Message)" -NoErrorPage
                }
            }
        
            # this route will not be validated against the authentication
            Add-PodeRoute -Method Post -Path '/api/v1/portal/:AccountName' -ScriptBlock {
                #Store Session Info in Secret Vault
                $VaultName = Get-PodeState -Name 'VaultName'
                $VaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                $VaultUnlock = Get-PodeState -Name 'VaultUnlock'

                #Convert request data into JSON and unlock vault
                $SecretData = $($WebEvent.Data | ConvertTo-Json)
                Unlock-SecretVault -Name  $VaultName -Password $VaultUnlock

                #Add/Update Session details in vault, return response data
                $AccountName = $WebEvent.Parameters['AccountName']
                Try{
                    Set-Secret -Name $VaultKeyPrefix-$AccountName -Vault  $VaultName -Secret $SecretData -Metadata @{Modified="$(Get-Date)";Type="SessionSync";Portal=$AccountName}
                    Write-PodeJsonResponse -Value $SecretData
                }
                Catch{
                    Write-PodeTextResponse -Value "Unexpected error: $($_.Exception.Message)" -StatusCode 500
                    #Set-PodeResponseStatus -Code 500 -Exception "Unexpected error: $($_.Exception.Message)" -NoErrorPage
                }
            }

            Register-PodeEvent -Type Terminate -Name 'CleanupSessions' -ScriptBlock {
                $VaultName = Get-PodeState -Name 'VaultName'
                $VaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                $VaultUnlock = Get-PodeState -Name 'VaultUnlock'

                Unlock-SecretVault -Name  $VaultName -Password $VaultUnlock
                $Sessions = Get-SecretInfo -Vault $VaultName | Where-Object {$_.Name -like "*$VaultKeyPrefix*"}
                Foreach ($Session in $Sessions){
                    Try{
                        Remove-Secret -Vault $VaultName -Name $Session.Name -ErrorAction Stop
                        Write-Host "Successfully cleared session details for $($Session.Metadata["Portal"])." -ForegroundColor Green
                    }
                    Catch{
                        Write-Error "Unable to clear session details for $($Session.Metadata["Portal"]): $_"
                    }
                }
                Disconnect-LMAccount
            }
        }
    }
    End{}
}