EnhancedSecretsAO.psm1

#Region '.\Public\Add-KeePassAttachment.ps1' -1

function Add-KeePassAttachment {
    <#
    .SYNOPSIS
    Adds an attachment to a specific entry in a KeePass database.
 
    .PARAMETER DatabasePath
    The full path to the KeePass database file.
 
    .PARAMETER EntryName
    The name of the entry to which the attachment will be added.
 
    .PARAMETER AttachmentName
    The name of the attachment being added.
 
    .PARAMETER AttachmentPath
    The full path to the attachment file.
 
    .PARAMETER KeyFilePath
    The full path to the key file for the KeePass database.
 
    .EXAMPLE
    Add-KeePassAttachment -DatabasePath "C:\code\secrets\myDatabase.kdbx" -EntryName "example_entry" -AttachmentName "certificate" -AttachmentPath "C:\code\secrets\cert.cer" -KeyFilePath "C:\code\secrets\myKeyFile.keyx"
    Adds the certificate as an attachment to the "example_entry" in the KeePass database.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The full path to the KeePass database file.")]
        [ValidateNotNullOrEmpty()]
        [string]$DatabasePath,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the entry to which the attachment will be added.")]
        [ValidateNotNullOrEmpty()]
        [string]$EntryName,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the attachment being added.")]
        [ValidateNotNullOrEmpty()]
        [string]$AttachmentName,

        [Parameter(Mandatory = $true, HelpMessage = "The full path to the attachment file.")]
        [ValidateNotNullOrEmpty()]
        [string]$AttachmentPath,

        [Parameter(Mandatory = $true, HelpMessage = "The full path to the key file for the KeePass database.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeyFilePath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Add-KeePassAttachment function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Validate paths
            if (-not (Test-Path -Path $DatabasePath)) {
                Write-EnhancedLog -Message "The database file does not exist: $DatabasePath" -Level "ERROR"
                throw "The database file does not exist: $DatabasePath"
            }

            if (-not (Test-Path -Path $KeyFilePath)) {
                Write-EnhancedLog -Message "The key file does not exist: $KeyFilePath" -Level "ERROR"
                throw "The key file does not exist: $KeyFilePath"
            }

            if (-not (Test-Path -Path $AttachmentPath)) {
                Write-EnhancedLog -Message "The attachment file does not exist: $AttachmentPath" -Level "ERROR"
                throw "The attachment file does not exist: $AttachmentPath"
            }

            # Construct command for KeePassXC CLI
            $command = "keepassxc-cli attachment-import `"$DatabasePath`" `"$EntryName`" `"$AttachmentName`" `"$AttachmentPath`" --key-file `"$KeyFilePath`" --no-password"

            Write-EnhancedLog -Message "Running command: $command" -Level "INFO"

            # Execute the command
            Invoke-Expression $command

            Write-Host "Attachment '$AttachmentName' added successfully to the entry '$EntryName' in the database." -ForegroundColor Green
            Write-EnhancedLog -Message "Attachment '$AttachmentName' added successfully to '$EntryName'." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Failed to add attachment: $($_.Exception.Message)" -Level "ERROR"
            throw "Failed to add attachment to the entry: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Add-KeePassAttachment function" -Level "Notice"
    }
}




# $addAttachmentParams = @{
# DatabasePath = "C:\code\secrets\myDatabase.kdbx"
# EntryName = "example_entry"
# AttachmentName = "certificate"
# AttachmentPath = "C:\code\secrets\cert.pfx"
# KeyFilePath = "C:\code\secrets\myKeyFile.keyx"
# }

# Add-KeePassAttachment @addAttachmentParams
#EndRegion '.\Public\Add-KeePassAttachment.ps1' 106
#Region '.\Public\Add-KeePassEntry.ps1' -1

function Add-KeePassEntry {
    <#
    .SYNOPSIS
    Adds a new entry to the KeePass database.
 
    .PARAMETER DatabasePath
    The full path to the KeePass database file.
 
    .PARAMETER KeyFilePath
    The full path to the key file for the KeePass database.
 
    .PARAMETER Username
    The username for the entry being added.
 
    .PARAMETER EntryName
    The name of the entry being added.
 
    .EXAMPLE
    Add-KeePassEntry -DatabasePath "C:\code\secrets\myDatabase.kdbx" -KeyFilePath "C:\code\secrets\myKeyFile.keyx" -Username "john_doe" -EntryName "example_entry"
    Adds the username "john_doe" to the "example_entry" in the KeePass database.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The full path to the KeePass database file.")]
        [ValidateNotNullOrEmpty()]
        [string]$DatabasePath,

        [Parameter(Mandatory = $true, HelpMessage = "The full path to the key file for the KeePass database.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeyFilePath,

        [Parameter(Mandatory = $true, HelpMessage = "The username for the entry being added.")]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the entry being added.")]
        [ValidateNotNullOrEmpty()]
        [string]$EntryName
    )

    Begin {
        Write-EnhancedLog -Message "Starting Add-KeePassEntry function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Validate paths
            if (-not (Test-Path -Path $DatabasePath)) {
                Write-EnhancedLog -Message "The database file does not exist: $DatabasePath" -Level "ERROR"
                throw "The database file does not exist: $DatabasePath"
            }

            if (-not (Test-Path -Path $KeyFilePath)) {
                Write-EnhancedLog -Message "The key file does not exist: $KeyFilePath" -Level "ERROR"
                throw "The key file does not exist: $KeyFilePath"
            }

            # Construct command for KeePassXC CLI
            $command = "keepassxc-cli add `"$DatabasePath`" -u `"$Username`" -g `"$EntryName`" --key-file `"$KeyFilePath`" --no-password"

            Write-EnhancedLog -Message "Running command: $command" -Level "INFO"

            # Execute the command
            Invoke-Expression $command

            Write-EnhancedLog -Message "Entry '$EntryName' added successfully to the database." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Failed to add entry: $($_.Exception.Message)" -Level "ERROR"
            throw "Failed to add entry to the database: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Add-KeePassEntry function" -Level "Notice"
    }
}


# Add a new entry to the KeePass database
# Define the parameters for adding a new entry
# $entryParams = @{
# DatabasePath = "C:\code\secrets\myDatabase.kdbx"
# KeyFilePath = "C:\code\secrets\myKeyFile.keyx"
# Username = "john_doe"
# EntryName = "example_entry"
# }

# Add a new entry to the KeePass database
# Add-KeePassEntry @entryParams




#EndRegion '.\Public\Add-KeePassEntry.ps1' 97
#Region '.\Public\Decrypt-FileWithCert.ps1' -1

function Decrypt-FileWithCert {
    <#
    .SYNOPSIS
    Decrypts a file using a certificate and AES encryption by restoring the certificate, decrypting the AES key, and decrypting the original file.
 
    .PARAMETER CertBase64Path
    The file path to the Base64-encoded certificate.
 
    .PARAMETER CertPasswordPath
    The file path to the certificate password.
 
    .PARAMETER KeyBase64Path
    The file path to the Base64-encoded AES key.
 
    .PARAMETER EncryptedFilePath
    The path to the encrypted file to be decrypted.
 
    .PARAMETER DecryptedFilePath
    The path where the decrypted file will be saved.
 
    .PARAMETER CertsDir
    The directory where the certificate files will be stored temporarily.
 
    .EXAMPLE
    $params = @{
        CertBase64Path = "C:\temp\certs\cert.pfx.base64"
        CertPasswordPath = "C:\temp\certpassword.txt"
        KeyBase64Path = "C:\temp\certs\secret.key.encrypted.base64"
        EncryptedFilePath = "C:\temp\myDatabase.zip.encrypted"
        DecryptedFilePath = "C:\temp\myDatabase.zip"
        CertsDir = "C:\temp\certs"
    }
    Decrypt-FileWithCert @params
    Decrypts the file using the given certificate and paths.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The file path to the Base64-encoded certificate.")]
        [ValidateNotNullOrEmpty()]
        [string]$CertBase64Path,

        [Parameter(Mandatory = $true, HelpMessage = "The file path to the certificate password.")]
        [ValidateNotNullOrEmpty()]
        [string]$CertPasswordPath,

        [Parameter(Mandatory = $true, HelpMessage = "The file path to the Base64-encoded AES key.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeyBase64Path,

        [Parameter(Mandatory = $true, HelpMessage = "The path to the encrypted file.")]
        [ValidateNotNullOrEmpty()]
        [string]$EncryptedFilePath,

        [Parameter(Mandatory = $true, HelpMessage = "The path where the decrypted file will be saved.")]
        [ValidateNotNullOrEmpty()]
        [string]$DecryptedFilePath,

        [Parameter(Mandatory = $true, HelpMessage = "The directory where the certificate files will be temporarily stored.")]
        [ValidateNotNullOrEmpty()]
        [string]$CertsDir
    )

    Begin {
        Write-EnhancedLog -Message "Starting Decrypt-FileWithCert function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Step 1: Create necessary directories if not exist
        if (-not (Test-Path -Path $CertsDir)) {
            New-Item -Path $CertsDir -ItemType Directory | Out-Null
            Write-EnhancedLog -Message "Created directory: $CertsDir" -Level "INFO"
        }
    }

    Process {
        try {
            # Step 2: Decode Base64-encoded certificate and save as .pfx
            $base64Cert = Get-Content -Path $CertBase64Path
            $certBytes = [Convert]::FromBase64String($base64Cert)
            $certPath = Join-Path $CertsDir "cert.pfx"
            [System.IO.File]::WriteAllBytes($certPath, $certBytes)
            Write-EnhancedLog -Message "Certificate restored at: $certPath" -Level "INFO"

            # Step 3: Get the certificate password from the file
            $certPassword = Get-Content -Path $CertPasswordPath

            # Step 4: Import the certificate with the private key
            $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
            $cert.Import($certPath, $certPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::UserKeySet)
            
            if ($cert.HasPrivateKey) {
                Write-EnhancedLog -Message "The certificate's private key is accessible." -Level "INFO"
            } else {
                Write-EnhancedLog -Message "The certificate's private key is not accessible." -Level "ERROR"
                throw "The certificate's private key is not accessible."
            }

            # Step 5: Decode Base64-encoded AES key and IV
            $base64Key = Get-Content -Path $KeyBase64Path
            $keyBytes = [Convert]::FromBase64String($base64Key)
            $aesKeyFilePath = Join-Path $CertsDir "secret.key.encrypted"
            [System.IO.File]::WriteAllBytes($aesKeyFilePath, $keyBytes)
            Write-EnhancedLog -Message "AES key and IV restored at: $aesKeyFilePath" -Level "INFO"

            # Step 6: Read encrypted AES key and IV
            $encryptedAESPackage = [System.IO.File]::ReadAllBytes($aesKeyFilePath)

            # Step 7: Extract IV (first 16 bytes) and encrypted AES key (remaining bytes)
            $iv = $encryptedAESPackage[0..15]
            $encryptedAESKey = $encryptedAESPackage[16..($encryptedAESPackage.Length - 1)]

            # Step 8: Decrypt the AES key using the certificate's private key with RSA-OAEP padding
            $rsaProvider = $cert.PrivateKey -as [System.Security.Cryptography.RSACryptoServiceProvider]
            if (-not $rsaProvider) {
                Write-EnhancedLog -Message "Unable to retrieve RSA private key from certificate." -Level "ERROR"
                throw "Unable to retrieve RSA private key from certificate."
            }

            $aesKey = $rsaProvider.Decrypt($encryptedAESKey, $true)  # Use $true for OAEP padding
            Write-EnhancedLog -Message "AES Key successfully decrypted." -Level "INFO"

            # Step 9: Decrypt the original file using AES
            $encryptedContent = [System.IO.File]::ReadAllBytes($EncryptedFilePath)

            $aes = [System.Security.Cryptography.AesCryptoServiceProvider]::Create()
            $aes.Key = $aesKey
            $aes.IV = $iv
            $decryptor = $aes.CreateDecryptor($aes.Key, $aes.IV)
            $decryptedBytes = $decryptor.TransformFinalBlock($encryptedContent, 0, $encryptedContent.Length)

            # Write the decrypted content to a new file
            [System.IO.File]::WriteAllBytes($DecryptedFilePath, $decryptedBytes)
            Write-EnhancedLog -Message "Decrypted file saved to: $DecryptedFilePath" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Decryption failed: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw "Decryption process failed: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Decrypt-FileWithCert function" -Level "Notice"
    }
}



#Example Usage


# # Define the parameters for the Decrypt-FileWithCert function using splatting
# $params = @{

# # Path to the Base64-encoded certificate file.
# # This file should contain the Base64-encoded contents of the .pfx certificate that you want to use for decryption.
# CertBase64Path = "C:\temp\certs\cert.pfx.base64"

# # Path to the text file that contains the password for the .pfx certificate.
# # This is the password that will be used to unlock the certificate and access the private key.
# CertPasswordPath = "C:\temp\certpassword.txt"

# # Path to the Base64-encoded AES key file.
# # This file contains the encrypted AES key that will be used to decrypt the target file.
# KeyBase64Path = "C:\temp\certs\secret.key.encrypted.base64"

# # Path to the file that is encrypted and needs to be decrypted.
# # This is the target file that was encrypted using the AES key, and it will be decrypted using the AES key and IV.
# EncryptedFilePath = "C:\temp\myDatabase.zip.encrypted"

# # Path where the decrypted file will be saved after decryption.
# # This is the output path where the function will store the decrypted version of the file.
# DecryptedFilePath = "C:\temp\myDatabase.zip"

# # Directory where temporary files such as the certificate and the AES key will be stored during the process.
# # This is a working directory where the function can safely write temporary files during the decryption.
# CertsDir = "C:\temp\certs"
# }

# # Call the Decrypt-FileWithCert function and pass the parameters via splatting.
# # This function will use the provided certificate, key, and encrypted file to perform the decryption and save the decrypted file.
# Decrypt-FileWithCert @params
#EndRegion '.\Public\Decrypt-FileWithCert.ps1' 183
#Region '.\Public\Download-GitHubReleaseAsset.ps1' -1

function Download-GitHubReleaseAsset {
    <#
    .SYNOPSIS
    Downloads a release asset from a GitHub repository based on the release tag and asset name.
 
    .PARAMETER Token
    The personal access token (PAT) for GitHub authentication.
 
    .PARAMETER RepoOwner
    The owner of the GitHub repository.
 
    .PARAMETER RepoName
    The name of the GitHub repository.
 
    .PARAMETER ReleaseTag
    The release tag to identify the specific release.
 
    .PARAMETER FileName
    The name of the asset file to download.
 
    .PARAMETER DestinationPath
    The path where the downloaded file will be saved.
 
    .EXAMPLE
    $params = @{
        Token = "mypat"
        RepoOwner = "aollivierre"
        RepoName = "Vault"
        ReleaseTag = "0.1"
        FileName = "ICTC_Project_2_Aug_29_2024.zip.aes.zip"
        DestinationPath = "C:\temp\ICTC_Project_2_Aug_29_2024.zip.aes.zip"
    }
    Download-GitHubReleaseAsset @params
    Downloads the specified asset from the GitHub release.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The personal access token (PAT) for GitHub authentication.")]
        [ValidateNotNullOrEmpty()]
        [string]$Token,

        [Parameter(Mandatory = $true, HelpMessage = "The owner of the GitHub repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$RepoOwner,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the GitHub repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$RepoName,

        [Parameter(Mandatory = $true, HelpMessage = "The release tag to identify the specific release.")]
        [ValidateNotNullOrEmpty()]
        [string]$ReleaseTag,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the asset file to download.")]
        [ValidateNotNullOrEmpty()]
        [string]$FileName,

        [Parameter(Mandatory = $true, HelpMessage = "The path where the downloaded file will be saved.")]
        [ValidateNotNullOrEmpty()]
        [string]$DestinationPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Download-GitHubReleaseAsset function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Check if the destination directory exists, create if it doesn't
        $destinationDirectory = Split-Path -Path $DestinationPath -Parent
        if (-not (Test-Path -Path $destinationDirectory)) {
            Write-EnhancedLog -Message "Destination directory does not exist. Creating directory: $destinationDirectory" -Level "INFO"
            New-Item -Path $destinationDirectory -ItemType Directory | Out-Null
        } else {
            Write-EnhancedLog -Message "Destination directory already exists: $destinationDirectory" -Level "INFO"
        }
    }

    Process {
        try {
            # Set headers for GitHub authentication
            $headers = @{
                Authorization = "token $Token"
                Accept        = "application/vnd.github+json"
            }

            # GitHub API URL to get release details
            $releaseUrl = "https://api.github.com/repos/$RepoOwner/$RepoName/releases"

            # Fetch the list of releases
            $releases = Invoke-RestMethod -Uri $releaseUrl -Headers $headers

            # Find the release with the specified tag
            $release = $releases | Where-Object { $_.tag_name -eq $ReleaseTag }

            if ($release) {
                # Find the asset by name
                $asset = $release.assets | Where-Object { $_.name -eq $FileName }

                if ($asset) {
                    $downloadUrl = $asset.url  # Get the asset's download URL
                    Write-EnhancedLog -Message "Asset found, starting download..." -Level "INFO"

                    # Set the download headers
                    $downloadHeaders = @{
                        Authorization = "token $Token"
                        Accept        = "application/octet-stream"
                    }

                    # Download the file
                    Invoke-WebRequest -Uri $downloadUrl -Headers $downloadHeaders -OutFile $DestinationPath
                    Write-EnhancedLog -Message "File downloaded successfully: $FileName to Destination $DestinationPath" -Level "INFO"
                } else {
                    Write-EnhancedLog -Message "Asset $FileName not found in the release." -Level "ERROR"
                    throw "Asset $FileName not found in the release."
                }
            } else {
                Write-EnhancedLog -Message "Release with tag $ReleaseTag not found." -Level "ERROR"
                throw "Release with tag $ReleaseTag not found."
            }
        }
        catch {
            Write-EnhancedLog -Message "Error during GitHub asset download: $($_.Exception.Message)" -Level "ERROR"
            throw "GitHub asset download failed: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Download-GitHubReleaseAsset function" -Level "Notice"
    }
}
#EndRegion '.\Public\Download-GitHubReleaseAsset.ps1' 131
#Region '.\Public\Encrypt-FileWithCert.ps1' -1

function Encrypt-FileWithCert {
    <#
    .SYNOPSIS
    Encrypts a file using a self-signed certificate and AES encryption, and stores the certificate, encrypted file, and AES key.
 
    .PARAMETER SecretFilePath
    The path to the file that will be encrypted.
 
    .PARAMETER CertsDir
    The directory where the certificate and related files will be stored.
 
    .PARAMETER EncryptedFilePath
    The path where the encrypted file will be saved.
 
    .PARAMETER EncryptedKeyFilePath
    The path where the encrypted AES key and IV will be saved.
 
    .EXAMPLE
    $params = @{
        SecretFilePath = "C:\code\secrets\myDatabase.zip"
        CertsDir = "C:\temp\certs"
        EncryptedFilePath = "C:\temp\myDatabase.zip.encrypted"
        EncryptedKeyFilePath = "C:\temp\secret.key.encrypted"
    }
    Encrypt-FileWithCert @params
    Encrypts the file and stores the necessary encryption artifacts.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The path to the file that will be encrypted.")]
        [ValidateNotNullOrEmpty()]
        [string]$SecretFilePath,

        [Parameter(Mandatory = $true, HelpMessage = "The directory where the certificate and related files will be stored.")]
        [ValidateNotNullOrEmpty()]
        [string]$CertsDir,

        [Parameter(Mandatory = $true, HelpMessage = "The path where the encrypted file will be saved.")]
        [ValidateNotNullOrEmpty()]
        [string]$EncryptedFilePath,

        [Parameter(Mandatory = $true, HelpMessage = "The path where the encrypted AES key and IV will be saved.")]
        [ValidateNotNullOrEmpty()]
        [string]$EncryptedKeyFilePath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Encrypt-FileWithCert function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Step 1: Create necessary directories if not exist
        if (-not (Test-Path -Path $CertsDir)) {
            New-Item -Path $CertsDir -ItemType Directory | Out-Null
            Write-EnhancedLog -Message "Created directory: $CertsDir" -Level "INFO"
        }
    }

    Process {
        try {
            # Step 2: Generate a random password for the certificate
            Add-Type -AssemblyName 'System.Web'
            $certPassword = [System.Web.Security.Membership]::GeneratePassword(128, 2)
            $passwordFilePath = Join-Path $CertsDir "certPassword.txt"
            $certPassword | Out-File -FilePath $passwordFilePath -Encoding UTF8
            Write-EnhancedLog -Message "Generated certificate password saved to: $passwordFilePath" -Level "INFO"

            # Step 3: Create a self-signed certificate and export it
            $cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Subject "CN=FileEncryptionCert" -KeyLength 2048 -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider"
            $certThumbprint = $cert.Thumbprint
            $certFile = Join-Path $CertsDir "cert.pfx"
            Export-PfxCertificate -Cert "Cert:\CurrentUser\My\$($certThumbprint)" -FilePath $certFile -Password (ConvertTo-SecureString $certPassword -AsPlainText -Force)
            Write-EnhancedLog -Message "Certificate generated and exported to: $certFile" -Level "INFO"

            # Step 4: Encrypt the file with AES
            $aes = [System.Security.Cryptography.AesCryptoServiceProvider]::Create()
            $aes.KeySize = 256
            $aes.GenerateKey()
            $aes.GenerateIV()

            # Read the file content and encrypt with AES
            $plainTextBytes = [System.IO.File]::ReadAllBytes($SecretFilePath)
            $encryptor = $aes.CreateEncryptor($aes.Key, $aes.IV)
            $encryptedBytes = $encryptor.TransformFinalBlock($plainTextBytes, 0, $plainTextBytes.Length)
            [System.IO.File]::WriteAllBytes($EncryptedFilePath, $encryptedBytes)
            Write-EnhancedLog -Message "File encrypted and saved to: $EncryptedFilePath" -Level "INFO"

            # Step 5: Encrypt the AES key with RSA using the certificate
            $rsa = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
            $rsa.ImportParameters($cert.PublicKey.Key.ExportParameters($false))
            $encryptedAESKey = $rsa.Encrypt($aes.Key, $true) # Encrypt the AES key with RSA

            # Save the encrypted AES key and IV to file
            $encryptedAESPackage = $aes.IV + $encryptedAESKey
            [System.IO.File]::WriteAllBytes($EncryptedKeyFilePath, $encryptedAESPackage)
            Write-EnhancedLog -Message "AES key encrypted and saved to: $EncryptedKeyFilePath" -Level "INFO"

            # Step 6: Convert certificate and encrypted AES key to Base64 for further use
            $certBytes = [System.IO.File]::ReadAllBytes($certFile)
            $base64Cert = [Convert]::ToBase64String($certBytes)
            $base64CertFile = Join-Path $CertsDir "cert.pfx.base64"
            [System.IO.File]::WriteAllText($base64CertFile, $base64Cert)
            Write-EnhancedLog -Message "Certificate Base64 encoded and saved to: $base64CertFile" -Level "INFO"

            $keyBytes = [System.IO.File]::ReadAllBytes($EncryptedKeyFilePath)
            $base64Key = [Convert]::ToBase64String($keyBytes)
            $base64KeyFile = Join-Path $CertsDir "secret.key.encrypted.base64"
            [System.IO.File]::WriteAllText($base64KeyFile, $base64Key)
            Write-EnhancedLog -Message "AES key and IV Base64 encoded and saved to: $base64KeyFile" -Level "INFO"

        }
        catch {
            Write-EnhancedLog -Message "Encryption process failed: $($_.Exception.Message)" -Level "ERROR"
            throw "Encryption process failed: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Encrypt-FileWithCert function" -Level "Notice"
    }
}





# # Define the parameters for the Encrypt-FileWithCert function using splatting
# $params = @{
# SecretFilePath = "C:\code\secrets\myDatabase.zip" # File to be encrypted
# CertsDir = "C:\temp\certs" # Directory to store certs and related files
# EncryptedFilePath = "C:\temp\myDatabase.zip.encrypted" # Output path for encrypted file
# EncryptedKeyFilePath = "C:\temp\secret.key.encrypted" # Output path for encrypted AES key and IV
# }

# # Call the function with splatted parameters
# Encrypt-FileWithCert @params
#EndRegion '.\Public\Encrypt-FileWithCert.ps1' 137
#Region '.\Public\Export-KeePassAttachment.ps1' -1

function Export-KeePassAttachment {
    <#
    .SYNOPSIS
    Exports an attachment from an entry in a KeePass database.
 
    .PARAMETER DatabasePath
    The full path to the KeePass database file.
 
    .PARAMETER EntryName
    The name of the entry containing the attachment.
 
    .PARAMETER AttachmentName
    The name of the attachment to be exported.
 
    .PARAMETER ExportPath
    The path where the attachment will be exported.
 
    .PARAMETER KeyFilePath
    The full path to the key file for the KeePass database.
 
    .EXAMPLE
    Export-KeePassAttachment -DatabasePath "C:\code\secrets\myDatabase.kdbx" -EntryName "example_entry" -AttachmentName "certificate" -ExportPath "C:\code\secrets\cert-fromdb.crt" -KeyFilePath "C:\code\secrets\myKeyFile.keyx"
    Exports the "certificate" attachment from the "example_entry" to the specified export path.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The full path to the KeePass database file.")]
        [ValidateNotNullOrEmpty()]
        [string]$DatabasePath,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the entry containing the attachment.")]
        [ValidateNotNullOrEmpty()]
        [string]$EntryName,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the attachment to be exported.")]
        [ValidateNotNullOrEmpty()]
        [string]$AttachmentName,

        [Parameter(Mandatory = $true, HelpMessage = "The path where the attachment will be exported.")]
        [ValidateNotNullOrEmpty()]
        [string]$ExportPath,

        [Parameter(Mandatory = $true, HelpMessage = "The full path to the key file for the KeePass database.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeyFilePath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Export-KeePassAttachment function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters


        Write-EnhancedLog -Message "Ensuring that KeePass is installed" -Level "Notice"
        Install-KeePass

    }

    Process {
        try {
            # Validate paths
            if (-not (Test-Path -Path $DatabasePath)) {
                Write-EnhancedLog -Message "The database file does not exist: $DatabasePath" -Level "ERROR"
                throw "The database file does not exist: $DatabasePath"
            }

            if (-not (Test-Path -Path $KeyFilePath)) {
                Write-EnhancedLog -Message "The key file does not exist: $KeyFilePath" -Level "ERROR"
                throw "The key file does not exist: $KeyFilePath"
            }

            # # Full path to the KeePass CLI executable
            # $keepassCliPath = "C:\Program Files\KeePassXC\keepassxc-cli.exe"

            # # Build the command with the full path
            # $command = "`"$keepassCliPath`" attachment-export `"$DatabasePath`" `"$EntryName`" `"$AttachmentName`" `"$ExportPath`" --key-file `"$KeyFilePath`" --no-password"


            # List of common paths where KeePassXC CLI might be installed
            $commonPaths = @(
                "C:\Program Files\KeePassXC\keepassxc-cli.exe",
                "C:\Program Files (x86)\KeePassXC\keepassxc-cli.exe"
            )

            # Find the KeePass CLI path
            $keepassCliPath = $commonPaths | Where-Object { Test-Path $_ }

            if (-not $keepassCliPath) {
                throw "KeePassXC CLI not found in common paths."
            }

            # Build the command with the dynamically located KeePassXC CLI path
            # $command = "`"$keepassCliPath`" attachment-export `"$DatabasePath`" `"$EntryName`" `"$AttachmentName`" `"$ExportPath`" --key-file `"$KeyFilePath`" --no-password"


            # Write-EnhancedLog -Message "Running command: $command" -Level "INFO"


         

            # Execute the command
            # Invoke-Expression $command




            # Build the arguments list for the KeePassXC CLI command
            # We're using Start-Process instead of Invoke-Expression, so we create a clean argument array
            $arguments = @(
                "attachment-export"               # Command to export an attachment from the KeePass database
                "`"$DatabasePath`""               # Path to the KeePass database file, wrapped in escaped quotes to handle spaces
                "`"$EntryName`""                  # Entry name within the KeePass database, escaped in quotes
                "`"$AttachmentName`""             # Name of the attachment to export from the specified entry
                "`"$ExportPath`""                 # Path where the exported attachment will be saved, escaped in quotes to handle spaces
                "--key-file"                      # Option to specify the key file for database access
                "`"$KeyFilePath`""                # Path to the key file used to unlock the KeePass database, escaped in quotes
                "--no-password"                   # Instruct the CLI to skip prompting for a password since only a key file is used
            )


            # Log the command (for debugging purposes)
            Write-EnhancedLog -Message "Running command: $keepassCliPath $($arguments -join ' ')" -Level "INFO"


            # Wait-Debugger

            # Use Start-Process to execute the command
            $process = Start-Process -FilePath $keepassCliPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru

            # Check the exit code
            if ($process.ExitCode -ne 0) {
                throw "KeePassXC CLI failed with exit code $($process.ExitCode)"
            }



            # Check if the file was actually exported
            if (Test-Path -Path $ExportPath) {
                Write-EnhancedLog -Message "Attachment '$AttachmentName' exported successfully to '$ExportPath'." -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "Attachment export failed: '$ExportPath' does not exist." -Level "ERROR"
                throw "Attachment export failed: '$AttachmentName' not found at '$ExportPath'."
            }



            # Write-EnhancedLog -Message "Attachment '$AttachmentName' exported successfully to '$ExportPath'." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Failed to export attachment: $($_.Exception.Message)" -Level "ERROR"
            throw "Failed to export attachment from the entry: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Export-KeePassAttachment function" -Level "Notice"
    }
}

# $exportAttachmentParams = @{
# DatabasePath = "C:\code\secrets\myDatabase.kdbx"
# EntryName = "example_entry"
# AttachmentName = "certificate"
# ExportPath = "C:\code\secrets\cert-fromdb.pfx"
# KeyFilePath = "C:\code\secrets\myKeyFile.keyx"
# }

# Export-KeePassAttachment @exportAttachmentParams
#EndRegion '.\Public\Export-KeePassAttachment.ps1' 170
#Region '.\Public\Install-Keepass.ps1' -1

function Install-KeePass {
    <#
    .SYNOPSIS
    Installs KeePass if it is not already installed and validates the installation before and after.
 
    .PARAMETER KeePassDownloadUrl
    The URL to download the KeePass installer.
 
    .PARAMETER KeePassInstallPath
    The installation path for KeePass.
 
    .PARAMETER MaxRetries
    The maximum number of retries for the file download if the first attempt fails.
 
    .EXAMPLE
    Install-KeePass
    Installs KeePass if it is not already installed.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, HelpMessage = "The URL to download the KeePass installer.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeePassDownloadUrl = "https://sourceforge.net/projects/keepass/files/KeePass%202.x/2.57/KeePass-2.57-Setup.exe/download",

        [Parameter(Mandatory = $false, HelpMessage = "The installation path for KeePass.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeePassInstallPath = "$env:ProgramFiles\KeePassPasswordSafe2",

        [Parameter(Mandatory = $false, HelpMessage = "Maximum number of retries for the download if it fails.")]
        [ValidateRange(1, 10)]
        [int]$MaxRetries = 3
    )

    Begin {
        Write-EnhancedLog -Message "Starting Install-KeePass function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Validate if KeePass is already installed
        $validateParams = @{
            SoftwareName  = "KeePass"
            RegistryPath  = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\KeePassPasswordSafe2_is1"
            ExePath       = "$KeePassInstallPath\KeePass.exe"
            MinVersion    = [version]"2.57"
            LatestVersion = [version]"2.57"
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Validating KeePass installation before attempting installation..." -Level "INFO"
            $validationResult = Validate-SoftwareInstallation @validateParams

            if ($validationResult.IsInstalled) {
                Write-EnhancedLog -Message "KeePass is already installed and meets the minimum version requirement." -Level "INFO"
                return $true
            }

            # KeePass is not installed, proceed with downloading and installing
            Write-EnhancedLog -Message "KeePass is not installed or does not meet the version requirement. Proceeding with installation." -Level "WARNING"
            Write-Host "Downloading KeePass installer..." -ForegroundColor Cyan
            $installerPath = "$env:TEMP\KeePassSetup.exe"

            # Use the Start-FileDownloadWithRetry function to download the installer
            $downloadParams = @{
                Source      = $KeePassDownloadUrl
                Destination = $installerPath
                MaxRetries  = $MaxRetries
            }
            Start-FileDownloadWithRetry @downloadParams

            Write-Host "Installing KeePass..." -ForegroundColor Cyan
            Start-Process -FilePath $installerPath -ArgumentList "/VERYSILENT", "/NORESTART" -Wait -NoNewWindow -ErrorAction Stop

            # Remove the installer after installation
            Remove-Item -Path $installerPath

            # Validate the installation again after the installation
            Write-EnhancedLog -Message "Validating KeePass installation after installation..." -Level "INFO"
            $postInstallValidation = Validate-SoftwareInstallation @validateParams

            if ($postInstallValidation.IsInstalled) {
                Write-Host "KeePass installed successfully." -ForegroundColor Green
                Write-EnhancedLog -Message "KeePass installed successfully. Version: $($postInstallValidation.InstalledVersion)" -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "KeePass installation failed or does not meet the version requirement after installation." -Level "ERROR"
                throw "KeePass installation validation failed."
            }
        }
        catch {
            Write-EnhancedLog -Message "Error during KeePass installation: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
        finally {
            Write-EnhancedLog -Message "Exiting Install-KeePass function" -Level "Notice"
        }
    }

    End {
        # No additional actions in the End block.
    }
}


# Install-KeePass
#EndRegion '.\Public\Install-Keepass.ps1' 108
#Region '.\Public\Install-KeePassXCCli.ps1' -1

function Install-KeePassXCCli {
    <#
    .SYNOPSIS
    Installs KeePassXC CLI based on the operating system and validates the installation before and after.
 
    .PARAMETER OS
    The operating system where KeePassXC CLI will be installed. Valid values are 'Windows' or 'Linux'.
 
    .PARAMETER MaxRetries
    The maximum number of retries for the file download if the first attempt fails.
 
    .EXAMPLE
    Install-KeePassXCCli -OS 'Windows'
    Installs KeePassXC CLI on a Windows system.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The operating system where KeePassXC CLI will be installed.")]
        [ValidateSet('Windows', 'Linux')]
        [string]$OS,

        [Parameter(Mandatory = $false, HelpMessage = "Maximum number of retries for the download if it fails.")]
        [ValidateRange(1, 10)]
        [int]$MaxRetries = 3
    )

    Begin {
        Write-EnhancedLog -Message "Starting Install-KeePassXCCli function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Validate parameters for the software based on OS
        if ($OS -eq 'Windows') {
            $validateParams = @{
                SoftwareName  = "KeePassXC CLI"
                RegistryPath  = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\KeePassXC_is1"
                ExePath       = "$env:ProgramFiles\KeePassXC\KeePassXC.exe"
                MinVersion    = [version]"2.7.9"
                LatestVersion = [version]"2.7.9"
            }
        } elseif ($OS -eq 'Linux') {
            $validateParams = @{
                SoftwareName  = "KeePassXC CLI"
                ExePath       = "/usr/local/bin/keepassxc-cli"
                MinVersion    = [version]"2.7.9"
                LatestVersion = [version]"2.7.9"
            }
        }
    }

    Process {
        try {
            Write-EnhancedLog -Message "Validating KeePassXC CLI installation before attempting installation..." -Level "INFO"
            $validationResult = Validate-SoftwareInstallation @validateParams

            if ($validationResult.IsInstalled) {
                Write-EnhancedLog -Message "KeePassXC CLI is already installed and meets the minimum version requirement." -Level "INFO"
                return $true
            }

            # KeePassXC CLI is not installed, proceed with downloading and installing
            Write-EnhancedLog -Message "KeePassXC CLI is not installed or does not meet the version requirement. Proceeding with installation." -Level "WARNING"

            if ($OS -eq 'Windows') {
                Write-Host "Downloading KeePassXC CLI installer for Windows..." -ForegroundColor Cyan
                $installerUrl = "https://github.com/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi"
                $installerPath = "$env:TEMP\KeePassXC.msi"

                # Use the Start-FileDownloadWithRetry function to download the installer
                $downloadParams = @{
                    Source      = $installerUrl
                    Destination = $installerPath
                    MaxRetries  = $MaxRetries
                }
                Start-FileDownloadWithRetry @downloadParams

                # Install KeePassXC CLI using msiexec
                Write-Host "Installing KeePassXC CLI..." -ForegroundColor Cyan
                Start-Process msiexec.exe -ArgumentList "/i $installerPath /quiet /norestart" -Wait -NoNewWindow -ErrorAction Stop

                # Remove the installer after installation
                Remove-Item -Path $installerPath

            } elseif ($OS -eq 'Linux') {
                Write-Host "Downloading KeePassXC CLI for Linux..." -ForegroundColor Cyan
                $appImageUrl = "https://github.com/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-x86_64.AppImage"
                $appImagePath = "/usr/local/bin/keepassxc-cli"

                # Use the Start-FileDownloadWithRetry function to download the AppImage
                $downloadParams = @{
                    Source      = $appImageUrl
                    Destination = $appImagePath
                    MaxRetries  = $MaxRetries
                }
                Start-FileDownloadWithRetry @downloadParams

                # Make the AppImage executable
                Write-Host "Setting permissions for KeePassXC CLI AppImage..." -ForegroundColor Cyan
                sudo chmod +x $appImagePath
            }

            # Validate the installation again after the installation
            Write-EnhancedLog -Message "Validating KeePassXC CLI installation after installation..." -Level "INFO"
            $postInstallValidation = Validate-SoftwareInstallation @validateParams

            if ($postInstallValidation.IsInstalled) {
                Write-Host "KeePassXC CLI installed successfully." -ForegroundColor Green
                Write-EnhancedLog -Message "KeePassXC CLI installed successfully. Version: $($postInstallValidation.InstalledVersion)" -Level "INFO"
            } else {
                Write-EnhancedLog -Message "KeePassXC CLI installation failed or does not meet the version requirement after installation." -Level "ERROR"
                throw "KeePassXC CLI installation validation failed."
            }
        }
        catch {
            Write-EnhancedLog -Message "Error during KeePassXC CLI installation: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw
        }
        finally {
            Write-EnhancedLog -Message "Exiting Install-KeePassXCCli function" -Level "Notice"
        }
    }

    End {
        # No additional actions in the End block.
    }
}








# if ($PSVersionTable.PSVersion.Major -ge 6) {
# # PowerShell 7+ (cross-platform)
# if ($IsWindows) {
# $OS = 'Windows'
# } elseif ($IsLinux) {
# $OS = 'Linux'
# } else {
# throw "Unsupported operating system."
# }
# } else {
# # PowerShell 5 (Windows-only)
# $OS = 'Windows'
# }

# Write-Host "Operating system detected: $OS"



# # Install KeePassXC CLI if it's not already installed
# if (-not (Get-Command keepassxc-cli -ErrorAction SilentlyContinue)) {
# Install-KeePassXCCli -OS $OS
# }
#EndRegion '.\Public\Install-KeePassXCCli.ps1' 158
#Region '.\Public\New-KeePassDatabase.ps1' -1

function New-KeePassDatabase {
    <#
    .SYNOPSIS
    Creates a new KeePass database with a specified path and key file.
 
    .PARAMETER DatabasePath
    The full path to the KeePass database file.
 
    .PARAMETER KeyFilePath
    The full path to the key file for the KeePass database.
 
    .EXAMPLE
    New-KeePassDatabase -DatabasePath "C:\code\secrets\myDatabase.kdbx" -KeyFilePath "C:\code\secrets\myKeyFile.keyx"
    Creates a new KeePass database at the specified path with the given key file.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The full path to the KeePass database file.")]
        [ValidateNotNullOrEmpty()]
        [string]$DatabasePath = "C:\code\secrets\myDatabase.kdbx",

        [Parameter(Mandatory = $true, HelpMessage = "The full path to the key file for the KeePass database.")]
        [ValidateNotNullOrEmpty()]
        [string]$KeyFilePath = "C:\code\secrets\myKeyFile.keyx"
    )

    Begin {
        Write-EnhancedLog -Message "Starting New-KeePassDatabase function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Extract directories from paths
        $databaseDirectory = Split-Path -Path $DatabasePath
        $keyFileDirectory = Split-Path -Path $KeyFilePath

        # Check if the directory exists, and create it if it doesn't
        if (-not (Test-Path -Path $databaseDirectory)) {
            Write-Host "Directory does not exist. Creating directory: $databaseDirectory" -Level "Warning"
            New-Item -Path $databaseDirectory -ItemType Directory | Out-Null
        }
        else {
            Write-EnhancedLog -Message "Directory already exists: $databaseDirectory" -Level "Notice"
        }

    }

    Process {
        # Check if the directory for the database exists
        if (-not (Test-Path -Path $databaseDirectory)) {
            Write-EnhancedLog -Message "The directory for the database path does not exist: $databaseDirectory" -Level "ERROR"
            throw "The directory for the database path does not exist."
        }

        # Check if the directory for the key file exists
        if (-not (Test-Path -Path $keyFileDirectory)) {
            Write-EnhancedLog -Message "The directory for the key file path does not exist: $keyFileDirectory" -Level "ERROR"
            throw "The directory for the key file path does not exist."
        }

        # Command to create KeePass database with the specified key file
        $command = "keepassxc-cli db-create `"$DatabasePath`" --set-key-file `"$KeyFilePath`""

        try {
            Invoke-Expression $command
            Write-EnhancedLog -Message "Database '$DatabasePath' created successfully with key file '$KeyFilePath'." -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Failed to create the KeePass database: $($_.Exception.Message)" -Level "ERROR"
            throw "Failed to create the database. $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting New-KeePassDatabase function" -Level "Notice"
    }
}

# # Define the path to the database and key file
# $databaseParams = @{
# DatabasePath = "C:\code\secrets\myDatabase.kdbx"
# KeyFilePath = "C:\code\secrets\myKeyFile.keyx"
# }

# # Now you can safely create the database
# New-KeePassDatabase @databaseParams

# Wait-Debugger
#EndRegion '.\Public\New-KeePassDatabase.ps1' 88
#Region '.\Public\Unzip-Directory.ps1' -1

function Unzip-Directory {
    <#
    .SYNOPSIS
    Extracts the contents of a zip archive to a specified directory.
 
    .PARAMETER ZipFilePath
    The full path to the zip file that you want to extract.
 
    .PARAMETER DestinationDirectory
    The directory where the contents of the zip file will be extracted.
 
    .EXAMPLE
    $params = @{
        ZipFilePath = "C:\code\secrets\vault.zip"
        DestinationDirectory = "C:\code\secrets\vault"
    }
    Unzip-Directory @params
    Unzips the archive to the specified directory.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The full path to the zip file.")]
        [ValidateNotNullOrEmpty()]
        [string]$ZipFilePath,

        [Parameter(Mandatory = $true, HelpMessage = "The directory where the contents will be extracted.")]
        [ValidateNotNullOrEmpty()]
        [string]$DestinationDirectory
    )

    Begin {
        Write-EnhancedLog -Message "Starting Unzip-Directory function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Check if the zip file exists
            if (-not (Test-Path -Path $ZipFilePath)) {
                Write-EnhancedLog -Message "Zip file does not exist: $ZipFilePath" -Level "ERROR"
                throw "Zip file does not exist: $ZipFilePath"
            }

            # Ensure the destination directory exists
            if (-not (Test-Path -Path $DestinationDirectory)) {
                New-Item -Path $DestinationDirectory -ItemType Directory | Out-Null
                Write-EnhancedLog -Message "Created directory: $DestinationDirectory" -Level "INFO"
            }

            # Extract the zip archive to the destination directory
            [System.IO.Compression.ZipFile]::ExtractToDirectory($ZipFilePath, $DestinationDirectory)
            Write-EnhancedLog -Message "Successfully unzipped the archive to: $DestinationDirectory" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Error during unzipping: $($_.Exception.Message)" -Level "ERROR"
            throw "Unzipping process failed: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Unzip-Directory function" -Level "Notice"
    }
}
#EndRegion '.\Public\Unzip-Directory.ps1' 65
#Region '.\Public\Upload-GitHubReleaseAsset.ps1' -1

function Upload-GitHubReleaseAsset {
    <#
    .SYNOPSIS
    Uploads an asset to a GitHub release using the GitHub CLI.
 
    .PARAMETER repoOwner
    The owner of the GitHub repository.
 
    .PARAMETER repoName
    The name of the GitHub repository.
 
    .PARAMETER releaseTag
    The tag of the release where the asset will be uploaded.
 
    .PARAMETER filePath
    The path of the file to be uploaded as an asset.
 
    .EXAMPLE
    $params = @{
        repoOwner = "aollivierre"
        repoName = "Vault"
        releaseTag = "0.1"
        filePath = "C:\temp2\vault.GH.Asset.zip"
    }
    Upload-GitHubReleaseAsset @params
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The owner of the GitHub repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$repoOwner,

        [Parameter(Mandatory = $true, HelpMessage = "The name of the GitHub repository.")]
        [ValidateNotNullOrEmpty()]
        [string]$repoName,

        [Parameter(Mandatory = $true, HelpMessage = "The tag of the release where the asset will be uploaded.")]
        [ValidateNotNullOrEmpty()]
        [string]$releaseTag,

        [Parameter(Mandatory = $true, HelpMessage = "The path of the file to be uploaded as an asset.")]
        [ValidateNotNullOrEmpty()]
        [string]$filePath
    )

    Begin {
        Write-EnhancedLog -Message "Starting GitHub Asset Upload Script" -Level "NOTICE"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters


        
        # Define the URL for the GitHub CLI releases page
        $githubCLIReleasesUrl = "https://api.github.com/repos/cli/cli/releases/latest"

        # Define the local path to save the installer
        $installerPath = "$env:TEMP\gh_cli_installer.msi"

        # Example invocation to install GitHub CLI:
        Install-GitHubCLI -releasesUrl $githubCLIReleasesUrl -installerPath $installerPath

    }

    Process {
        try {
            # Check if the file exists
            Write-EnhancedLog -Message "Checking if file exists: $filePath" -Level "INFO"
            if (-not (Test-Path -Path $filePath)) {
                Write-EnhancedLog -Message "File does not exist: $filePath" -Level "ERROR"
                throw "File does not exist: $filePath"
            }
            Write-EnhancedLog -Message "File exists: $filePath" -Level "INFO"

            # Upload the file using GitHub CLI
            Write-EnhancedLog -Message "Uploading asset $filePath to release $releaseTag..." -Level "INFO"
            $command = "gh release upload $releaseTag $filePath --repo $repoOwner/$repoName"
            Invoke-Expression $command

            Write-EnhancedLog -Message "File uploaded successfully: $filePath" -Level "INFO"
        }
        catch {
            Handle-Error -Message "Error during upload: $($_.Exception.Message)" -ErrorRecord $_
            throw
        }
    }

    End {
        Write-EnhancedLog -Message "Script finished." -Level "NOTICE"
    }
}
#EndRegion '.\Public\Upload-GitHubReleaseAsset.ps1' 91
#Region '.\Public\Zip-Directory.ps1' -1

# function Zip-Directory {
# <#
# .SYNOPSIS
# Zips the contents of a specified directory into a zip archive.

# .PARAMETER SourceDirectory
# The full path to the directory that you want to zip.

# .PARAMETER ZipFilePath
# The path where the zip archive will be saved.

# .EXAMPLE
# $params = @{
# SourceDirectory = "C:\code\secrets\vault"
# ZipFilePath = "C:\code\secrets\vault.zip"
# }
# Zip-Directory @params
# Zips the contents of the directory into an archive.
# #>

# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true, HelpMessage = "The full path to the directory that you want to zip.")]
# [ValidateNotNullOrEmpty()]
# [string]$SourceDirectory,

# [Parameter(Mandatory = $true, HelpMessage = "The path where the zip archive will be saved.")]
# [ValidateNotNullOrEmpty()]
# [string]$ZipFilePath
# )

# Begin {
# Write-EnhancedLog -Message "Starting Zip-Directory function" -Level "Notice"
# Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

# # Determine the destination directory and create it if necessary
# $destinationDir = Split-Path -Path $ZipFilePath -Parent
# if (-not (Test-Path -Path $destinationDir)) {
# New-Item -Path $destinationDir -ItemType Directory -Force | Out-Null
# Write-EnhancedLog -Message "Created destination directory: $destinationDir" -Level "INFO"
# }
# }

# Process {
# try {
# # Check if the source directory exists
# if (-not (Test-Path -Path $SourceDirectory)) {
# Write-EnhancedLog -Message "Source directory does not exist: $SourceDirectory" -Level "ERROR"
# throw "Source directory does not exist: $SourceDirectory"
# }

# # Retry logic to handle file locks or transient issues
# $maxRetries = 3
# $retryDelaySeconds = 2
# $attempt = 0
# $success = $false

# while ($attempt -lt $maxRetries -and -not $success) {
# try {
# # Attempt to compress the directory
# Compress-Archive -Path "$SourceDirectory\*" -DestinationPath $ZipFilePath -Force
# Write-EnhancedLog -Message "Successfully zipped the directory to: $ZipFilePath" -Level "INFO"
# $success = $true
# }
# catch {
# $attempt++
# Write-EnhancedLog -Message "Attempt $attempt Error during zipping: $($_.Exception.Message)" -Level "WARNING"
# if ($attempt -lt $maxRetries) {
# Write-EnhancedLog -Message "Retrying in $retryDelaySeconds seconds..." -Level "INFO"
# Start-Sleep -Seconds $retryDelaySeconds
# }
# else {
# Write-EnhancedLog -Message "Max retries reached. Zipping process failed." -Level "ERROR"
# throw "Zipping process failed after $maxRetries attempts: $($_.Exception.Message)"
# }
# }
# }
# }
# catch {
# # Additional error details for troubleshooting
# Write-EnhancedLog -Message "Error during zipping: $($_.Exception.Message)" -Level "ERROR"
# Write-EnhancedLog -Message "StackTrace: $($_.Exception.StackTrace)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw "Zipping process failed: $($_.Exception.Message)"
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Zip-Directory function" -Level "Notice"
# }
# }





# Enable long path support if configured
function Enable-LongPathSupport {
    param ([string]$Path)
    if ($Path -notlike "\\?*") {
        return "\\?\$Path"
    }
    return $Path
}

# Log each file path and check if it exceeds the 260-character path length
function Log-And-CheckPaths {
    param ([string]$Directory)
    $exceedsLimit = $false

    Get-ChildItem -Path $Directory -Recurse | ForEach-Object {
        $filePath = $_.FullName
        $fileLength = $filePath.Length

        # Log the file path and its length
        Write-EnhancedLog -Message "File path: $filePath, Length: $fileLength" -Level "INFO"

        # Check if the file path exceeds 260 characters
        if ($fileLength -gt 260) {
            Write-EnhancedLog -Message "File path exceeds 260 characters: $filePath, Length: $fileLength" -Level "WARNING"
            $exceedsLimit = $true
        }
    }

    return $exceedsLimit
}





function Zip-Directory {
    <#
    .SYNOPSIS
    Zips the contents of a specified directory into a zip archive.
 
    .PARAMETER SourceDirectory
    The full path to the directory that you want to zip.
 
    .PARAMETER ZipFilePath
    The path where the zip archive will be saved.
 
    .PARAMETER EnableLongPathSupport
    If set to $true, enables support for file paths longer than 260 characters.
 
    .EXAMPLE
    $params = @{
        SourceDirectory = "C:\code\secrets\vault"
        ZipFilePath = "C:\code\secrets\vault.zip"
        EnableLongPathSupport = $true
    }
    Zip-Directory @params
    Zips the contents of the directory into an archive, with support for long paths.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The full path to the directory that you want to zip.")]
        [ValidateNotNullOrEmpty()]
        [string]$SourceDirectory,

        [Parameter(Mandatory = $true, HelpMessage = "The path where the zip archive will be saved.")]
        [ValidateNotNullOrEmpty()]
        [string]$ZipFilePath,

        [Parameter(Mandatory = $false, HelpMessage = "Enables support for file paths longer than 260 characters.")]
        [bool]$EnableLongPathSupport = $false
    )

    Begin {
        Write-EnhancedLog -Message "Starting Zip-Directory function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters



        # Adjust paths if long path support is enabled
        if ($EnableLongPathSupport) {
            $SourceDirectory = Enable-LongPathSupport -Path $SourceDirectory
            $ZipFilePath = Enable-LongPathSupport -Path $ZipFilePath
        }

        # Check for long paths if long path support is not enabled
        if (-not $EnableLongPathSupport -and (Log-And-CheckPaths -Directory $SourceDirectory)) {
            Write-EnhancedLog -Message "Long paths detected but long path support is not enabled." -Level "ERROR"
            throw "Long paths detected. Enable long path support to proceed."
        }

        # Determine the destination directory and create it if necessary
        $destinationDir = Split-Path -Path $ZipFilePath -Parent
        if (-not (Test-Path -Path $destinationDir)) {
            New-Item -Path $destinationDir -ItemType Directory -Force | Out-Null
            Write-EnhancedLog -Message "Created destination directory: $destinationDir" -Level "INFO"
        }
    }

    Process {
        try {
            # Check if the source directory exists
            if (-not (Test-Path -Path $SourceDirectory)) {
                Write-EnhancedLog -Message "Source directory does not exist: $SourceDirectory" -Level "ERROR"
                throw "Source directory does not exist: $SourceDirectory"
            }

            # Retry logic to handle file locks or transient issues
            $maxRetries = 3
            $retryDelaySeconds = 2
            $attempt = 0
            $success = $false

            while ($attempt -lt $maxRetries -and -not $success) {
                try {
                    # Attempt to compress the directory
                    Compress-Archive -Path "$SourceDirectory\*" -DestinationPath $ZipFilePath -Force
                    Write-EnhancedLog -Message "Attempt $($attempt + 1): Successfully zipped the directory." -Level "INFO"
                    $success = $true
                }
                catch {
                    $attempt++
                    Write-EnhancedLog -Message "Attempt $attempt Error during zipping: $($_.Exception.Message)" -Level "WARNING"
                    if ($attempt -lt $maxRetries) {
                        Write-EnhancedLog -Message "Retrying in $retryDelaySeconds seconds..." -Level "INFO"
                        Start-Sleep -Seconds $retryDelaySeconds
                    }
                    else {
                        Write-EnhancedLog -Message "Max retries reached. Zipping process failed." -Level "ERROR"
                        throw "Zipping process failed after $maxRetries attempts: $($_.Exception.Message)"
                    }
                }
            }

            # Verify that the ZIP file was actually created
            if (Test-Path -Path $ZipFilePath) {
                Write-EnhancedLog -Message "ZIP file successfully created at: $ZipFilePath" -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "ZIP file was not found at the expected location: $ZipFilePath" -Level "ERROR"
                throw "ZIP file was not created successfully."
            }
        }
        catch {
            # Additional error details for troubleshooting
            Write-EnhancedLog -Message "Error during zipping: $($_.Exception.Message)" -Level "ERROR"
            Write-EnhancedLog -Message "StackTrace: $($_.Exception.StackTrace)" -Level "ERROR"
            throw "Zipping process failed: $($_.Exception.Message)"
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Zip-Directory function" -Level "Notice"
    }
}
#EndRegion '.\Public\Zip-Directory.ps1' 252