diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e57dd7..c6cd8ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,20 @@ All notable changes to the PSOPNSenseAPI module will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [2025.04.15.0739] - 2025-04-15 +## [2025.04.15.1143] - 2025-04-15 + +### Added +- Added `Get-OPNSenseConfig` cmdlet that retrieves the OPNSense configuration as an XML document ### Changed - Renamed `Upgrade-OPNSenseFirmware` to `Start-OPNSenseFirmwareUpgrade` to resolve cmdlet name conflict +- Updated `Restore-OPNSenseConfig` to accept an XML document from the pipeline or as a parameter +- Changed `Export-OPNSenseConfig` and `Import-OPNSenseConfig` to use System.IO.FileInfo for the Path parameter - Added unit tests for firmware management cmdlets +### Removed +- Removed `Backup-OPNSenseConfig` cmdlet (use `Get-OPNSenseConfig` instead) + ## [0.6.0] - 2025-04-14 ### Added diff --git a/README.md b/README.md index d9fe29f..e4e97b5 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,11 @@ Start-OPNSenseFirmwareUpgrade -Wait -Timeout 1200 # Reboot firewall Restart-OPNSenseFirewall -Wait -Timeout 300 -# Backup and restore configuration -Backup-OPNSenseConfig -Filename "pre-upgrade-backup" -Export-OPNSenseConfig -Path "C:\Backups\opnsense-config.xml" +# Configuration management +Get-OPNSenseConfig | Restore-OPNSenseConfig -Force # Get and restore configuration in one line +$config = Get-OPNSenseConfig # Get configuration as XML document +Export-OPNSenseConfig -Path (New-Object System.IO.FileInfo "C:\Backups\opnsense-config.xml") +Import-OPNSenseConfig -Path (New-Object System.IO.FileInfo "C:\Backups\opnsense-config.xml") -Force ``` ## Documentation diff --git a/docs/cmdlets/Export-OPNSenseConfig.md b/docs/cmdlets/Export-OPNSenseConfig.md new file mode 100644 index 0000000..61c8c4e --- /dev/null +++ b/docs/cmdlets/Export-OPNSenseConfig.md @@ -0,0 +1,76 @@ +# Export-OPNSenseConfig + +## SYNOPSIS +Exports the OPNSense firewall configuration. + +## SYNTAX + +``` +Export-OPNSenseConfig -Path [-Force] +``` + +## DESCRIPTION +The Export-OPNSenseConfig cmdlet exports the OPNSense firewall configuration to a file. + +## EXAMPLES + +### Example 1: Export the configuration to a file +```powershell +$path = New-Object System.IO.FileInfo "C:\Backups\opnsense-config.xml" +Export-OPNSenseConfig -Path $path +``` + +This example exports the OPNSense firewall configuration to a file. + +## PARAMETERS + +### -Path +The path to save the configuration file. Must be a FileInfo object with an .xml extension. + +```yaml +Type: FileInfo +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Overwrites the file if it exists. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES +This cmdlet requires a connection to an OPNSense firewall. Use Connect-OPNSense to establish a connection. +If the directory specified in the Path parameter does not exist, it will be created. + +## RELATED LINKS + +[Get-OPNSenseConfig](Get-OPNSenseConfig.md) +[Import-OPNSenseConfig](Import-OPNSenseConfig.md) +[Restore-OPNSenseConfig](Restore-OPNSenseConfig.md) diff --git a/docs/cmdlets/Get-OPNSenseConfig.md b/docs/cmdlets/Get-OPNSenseConfig.md new file mode 100644 index 0000000..acf2a0c --- /dev/null +++ b/docs/cmdlets/Get-OPNSenseConfig.md @@ -0,0 +1,52 @@ +# Get-OPNSenseConfig + +## SYNOPSIS +Gets the OPNSense firewall configuration as an XML document. + +## SYNTAX + +``` +Get-OPNSenseConfig +``` + +## DESCRIPTION +The Get-OPNSenseConfig cmdlet retrieves the OPNSense firewall configuration and returns it as an XML document. + +## EXAMPLES + +### Example 1: Get the configuration as an XML document +```powershell +$config = Get-OPNSenseConfig +``` + +This example retrieves the OPNSense firewall configuration as an XML document. + +### Example 2: Get the configuration and pipe it to Restore-OPNSenseConfig +```powershell +Get-OPNSenseConfig | Restore-OPNSenseConfig +``` + +This example retrieves the OPNSense firewall configuration and restores it. + +## PARAMETERS + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Xml.XmlDocument +The OPNSense configuration as an XML document. + +## NOTES +This cmdlet requires a connection to an OPNSense firewall. Use Connect-OPNSense to establish a connection. + +## RELATED LINKS + +[Restore-OPNSenseConfig](Restore-OPNSenseConfig.md) +[Export-OPNSenseConfig](Export-OPNSenseConfig.md) +[Import-OPNSenseConfig](Import-OPNSenseConfig.md) diff --git a/docs/cmdlets/Import-OPNSenseConfig.md b/docs/cmdlets/Import-OPNSenseConfig.md new file mode 100644 index 0000000..bf4eace --- /dev/null +++ b/docs/cmdlets/Import-OPNSenseConfig.md @@ -0,0 +1,106 @@ +# Import-OPNSenseConfig + +## SYNOPSIS +Imports an OPNSense firewall configuration. + +## SYNTAX + +``` +Import-OPNSenseConfig -Path [-Force] [-WhatIf] [-Confirm] +``` + +## DESCRIPTION +The Import-OPNSenseConfig cmdlet imports an OPNSense firewall configuration from a file. + +## EXAMPLES + +### Example 1: Import a configuration from a file +```powershell +$path = New-Object System.IO.FileInfo "C:\Backups\opnsense-config.xml" +Import-OPNSenseConfig -Path $path +``` + +This example imports an OPNSense firewall configuration from a file. + +## PARAMETERS + +### -Path +The path to the configuration file. Must be a FileInfo object with an .xml extension. + +```yaml +Type: FileInfo +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Suppresses the confirmation prompt. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES +This cmdlet requires a connection to an OPNSense firewall. Use Connect-OPNSense to establish a connection. +The firewall will restart after the configuration is imported. + +## RELATED LINKS + +[Get-OPNSenseConfig](Get-OPNSenseConfig.md) +[Export-OPNSenseConfig](Export-OPNSenseConfig.md) +[Restore-OPNSenseConfig](Restore-OPNSenseConfig.md) diff --git a/docs/cmdlets/Restore-OPNSenseConfig.md b/docs/cmdlets/Restore-OPNSenseConfig.md new file mode 100644 index 0000000..8d45f9c --- /dev/null +++ b/docs/cmdlets/Restore-OPNSenseConfig.md @@ -0,0 +1,142 @@ +# Restore-OPNSenseConfig + +## SYNOPSIS +Restores an OPNSense firewall configuration. + +## SYNTAX + +### Filename +``` +Restore-OPNSenseConfig -Filename [-Force] [-WhatIf] [-Confirm] +``` + +### XmlDocument +``` +Restore-OPNSenseConfig -XmlDocument [-Force] [-WhatIf] [-Confirm] +``` + +## DESCRIPTION +The Restore-OPNSenseConfig cmdlet restores an OPNSense firewall configuration from a backup or an XML document. + +## EXAMPLES + +### Example 1: Restore a configuration backup +```powershell +Restore-OPNSenseConfig -Filename "config-backup-20250414-1234.xml" +``` + +This example restores an OPNSense firewall configuration from a backup file. + +### Example 2: Restore a configuration from an XML document +```powershell +$config = Get-OPNSenseConfig +Restore-OPNSenseConfig -XmlDocument $config +``` + +This example restores an OPNSense firewall configuration from an XML document. + +### Example 3: Restore a configuration from the pipeline +```powershell +Get-OPNSenseConfig | Restore-OPNSenseConfig +``` + +This example restores an OPNSense firewall configuration from the pipeline. + +## PARAMETERS + +### -Filename +The filename of the backup to restore. + +```yaml +Type: String +Parameter Sets: Filename +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -XmlDocument +The XML document containing the configuration to restore. + +```yaml +Type: XmlDocument +Parameter Sets: XmlDocument +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Force +Suppresses the confirmation prompt. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### System.Xml.XmlDocument +An XML document containing the OPNSense configuration. + +## OUTPUTS + +### None + +## NOTES +This cmdlet requires a connection to an OPNSense firewall. Use Connect-OPNSense to establish a connection. +The firewall will restart after the configuration is restored. + +## RELATED LINKS + +[Get-OPNSenseConfig](Get-OPNSenseConfig.md) +[Export-OPNSenseConfig](Export-OPNSenseConfig.md) +[Import-OPNSenseConfig](Import-OPNSenseConfig.md) diff --git a/docs/examples/Configuration-Management.ps1 b/docs/examples/Configuration-Management.ps1 index aacdfa3..5dac784 100644 --- a/docs/examples/Configuration-Management.ps1 +++ b/docs/examples/Configuration-Management.ps1 @@ -8,22 +8,29 @@ Connect-OPNSense -Server "https://firewall.example.com" -ApiKey "your_api_key" - $backups = Get-OPNSenseConfigBackup $backups | Format-Table -Property Filename, Description, FileSize, Date, Version -# Create a new configuration backup -$backupFilename = Backup-OPNSenseConfig -Filename "pre-upgrade-backup" -Write-Output "Created backup: $backupFilename" +# Get the current configuration as an XML document +$config = Get-OPNSenseConfig +Write-Output "Retrieved configuration with root element: $($config.DocumentElement.Name)" # Export the configuration to a file -$exportPath = "C:\Backups\opnsense-config.xml" +$exportPath = New-Object System.IO.FileInfo "C:\Backups\opnsense-config.xml" Export-OPNSenseConfig -Path $exportPath -Force -Write-Output "Exported configuration to: $exportPath" +Write-Output "Exported configuration to: $($exportPath.FullName)" # Import a configuration from a file # Note: This will restart the firewall -# Import-OPNSenseConfig -Path $exportPath -Confirm +# Import-OPNSenseConfig -Path $exportPath -Force -# Restore a configuration backup +# Restore a configuration backup by filename # Note: This will restart the firewall -# Restore-OPNSenseConfig -Filename $backupFilename -Confirm +# Restore-OPNSenseConfig -Filename "config-backup-20250415-1234.xml" -Force + +# Restore a configuration from an XML document +# Note: This will restart the firewall +# Restore-OPNSenseConfig -XmlDocument $config -Force + +# Pipeline example: Get and restore configuration in one line +# Get-OPNSenseConfig | Restore-OPNSenseConfig -Force # Disconnect from the firewall Disconnect-OPNSense diff --git a/output/PSOPNSenseAPI/PSOPNSenseAPI.psd1 b/output/PSOPNSenseAPI/PSOPNSenseAPI.psd1 index 305cbd6..e729d66 100644 --- a/output/PSOPNSenseAPI/PSOPNSenseAPI.psd1 +++ b/output/PSOPNSenseAPI/PSOPNSenseAPI.psd1 @@ -1,19 +1,70 @@ @{ + # Script module or binary module file associated with this manifest. RootModule = 'PSOPNSenseAPI.psm1' - ModuleVersion = '2025.04.15.0739' - GUID = '1f0e4b77-cc7c-4a1e-b45a-d7c51a3c562e' - Author = 'PSOPNSenseAPI Contributors' - CompanyName = 'PSOPNSenseAPI' - Copyright = 'Copyright (c) 2025 PSOPNSenseAPI Contributors' - Description = 'PowerShell module for interacting with the OPNSense API to configure firewalls' - PowerShellVersion = '5.1' + + # Version number of this module. + ModuleVersion = '2025.04.15.1143' + + # Supported PSEditions CompatiblePSEditions = @('Desktop', 'Core') - #DotNetFrameworkVersion = '4.7.2' - CLRVersion = '4.0.0' + + # ID used to uniquely identify this module + GUID = '9a3b4c55-5f9a-4b8c-87d9-9a7a5cdd5c9f' + + # Author of this module + Author = 'PSOPNSenseAPI Contributors' + + # Company or vendor of this module + CompanyName = 'PSOPNSenseAPI' + + # Copyright statement for this module + Copyright = '(c) 2025 PSOPNSenseAPI Contributors. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'PowerShell module for interacting with the OPNSense API to configure firewalls' + + # Minimum version of the PowerShell engine required by this module + PowerShellVersion = '5.1' + + # Name of the PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + DotNetFrameworkVersion = '4.7.2' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + ClrVersion = '4.0' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + NestedModules = @('lib\PSOPNSenseAPI.dll') + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @() + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @( 'Apply-OPNSenseFirewallChanges', - 'Backup-OPNSenseConfig', 'Connect-OPNSense', 'Connect-OPNSenseTailscale', 'ConvertTo-OPNSenseNetworkNotation', @@ -29,6 +80,7 @@ 'Enable-OPNSenseTailscale', 'Export-OPNSenseConfig', 'Get-OPNSenseAlias', + 'Get-OPNSenseConfig', 'Get-OPNSenseConfigBackup', 'Get-OPNSenseConnection', 'Get-OPNSenseCronJob', @@ -104,16 +156,58 @@ 'Update-OPNSenseFirmware', 'Start-OPNSenseFirmwareUpgrade' ) + + # Variables to export from this module VariablesToExport = @() + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. AliasesToExport = @() - RequiredAssemblies = @('lib\System.Net.IPNetwork.dll', 'lib\Newtonsoft.Json.dll') - NestedModules = @('lib\PSOPNSenseAPI.dll') + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. Tags = @('PowerShell', 'OPNSense', 'Firewall', 'API') + + # A URL to the license for this module. LicenseUri = 'https://github.com/freedbygrace/PSOPNSenseAPI/blob/main/LICENSE' + + # A URL to the main website for this project. ProjectUri = 'https://github.com/freedbygrace/PSOPNSenseAPI' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module ReleaseNotes = 'https://github.com/freedbygrace/PSOPNSenseAPI/blob/main/CHANGELOG.md' - } - } + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfoURI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' } diff --git a/output/PSOPNSenseAPI/PSOPNSenseAPI.psm1 b/output/PSOPNSenseAPI/PSOPNSenseAPI.psm1 index 0fe4fb0..d4b84da 100644 --- a/output/PSOPNSenseAPI/PSOPNSenseAPI.psm1 +++ b/output/PSOPNSenseAPI/PSOPNSenseAPI.psm1 @@ -1,2 +1,13 @@ -# This file is intentionally empty. -# The module uses NestedModules in the PSD1 file to load the assemblies. +# Load the main assembly directly +$mainDllPath = Join-Path $PSScriptRoot "lib\PSOPNSenseAPI.dll" + +Write-Verbose "Loading assembly from $mainDllPath" + +try { + Add-Type -Path $mainDllPath + Write-Verbose "Successfully loaded assembly from $mainDllPath" +} catch { + $errorMessage = $_.Exception.Message + Write-Error "Failed to load assembly from $($mainDllPath): $($errorMessage)" + throw +} diff --git a/output/PSOPNSenseAPI/lib/PSOPNSenseAPI.dll b/output/PSOPNSenseAPI/lib/PSOPNSenseAPI.dll index 9fa133f..d0ae65d 100644 Binary files a/output/PSOPNSenseAPI/lib/PSOPNSenseAPI.dll and b/output/PSOPNSenseAPI/lib/PSOPNSenseAPI.dll differ diff --git a/src/PSOPNSenseAPI/Cmdlets/BackupOPNSenseConfigCmdlet.cs b/src/PSOPNSenseAPI/Cmdlets/BackupOPNSenseConfigCmdlet.cs deleted file mode 100644 index 5c4d527..0000000 --- a/src/PSOPNSenseAPI/Cmdlets/BackupOPNSenseConfigCmdlet.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Management.Automation; -using System.Threading.Tasks; -using PSOPNSenseAPI.Services; - -namespace PSOPNSenseAPI.Cmdlets -{ - /// - /// Creates a backup of the OPNSense firewall configuration. - /// The Backup-OPNSenseConfig cmdlet creates a backup of the OPNSense firewall configuration. - /// - /// Example 1: Create a backup - /// Backup-OPNSenseConfig - /// This example creates a backup of the OPNSense firewall configuration with an automatically generated filename. - /// - /// - /// Example 2: Create a backup with a specific filename - /// Backup-OPNSenseConfig -Filename "pre-upgrade-backup" - /// This example creates a backup of the OPNSense firewall configuration with a specific filename. - /// - /// - [Cmdlet(VerbsData.Backup, "OPNSenseConfig")] - [OutputType(typeof(string))] - public class BackupOPNSenseConfigCmdlet : OPNSenseBaseCmdlet - { - /// - /// The filename for the backup. - /// - [Parameter(Mandatory = false, Position = 0)] - public string Filename { get; set; } - - /// - /// Processes the cmdlet - /// - protected override void ProcessRecord() - { - try - { - var configService = new ConfigService(ApiClient, Logger); - - var task = Task.Run(async () => await configService.CreateConfigBackupAsync(Filename)); - var result = task.GetAwaiter().GetResult(); - - WriteVerbose($"Created configuration backup: {result.Filename}"); - WriteObject(result.Filename); - } - catch (Exception ex) - { - HandleException(ex); - } - } - } -} diff --git a/src/PSOPNSenseAPI/Cmdlets/ExportOPNSenseConfigCmdlet.cs b/src/PSOPNSenseAPI/Cmdlets/ExportOPNSenseConfigCmdlet.cs index 81f0083..5213205 100644 --- a/src/PSOPNSenseAPI/Cmdlets/ExportOPNSenseConfigCmdlet.cs +++ b/src/PSOPNSenseAPI/Cmdlets/ExportOPNSenseConfigCmdlet.cs @@ -23,8 +23,8 @@ namespace PSOPNSenseAPI.Cmdlets /// The path to save the configuration file. /// [Parameter(Mandatory = true, Position = 0)] - [ValidateNotNullOrEmpty] - public string Path { get; set; } + [ValidateNotNull] + public FileInfo Path { get; set; } /// /// Overwrites the file if it exists. @@ -39,14 +39,16 @@ namespace PSOPNSenseAPI.Cmdlets { try { + string fullPath = Path.FullName; + // Check if the file exists - if (File.Exists(Path) && !Force.IsPresent) + if (File.Exists(fullPath) && !Force.IsPresent) { WriteError(new ErrorRecord( - new IOException($"The file '{Path}' already exists. Use -Force to overwrite."), + new IOException($"The file '{fullPath}' already exists. Use -Force to overwrite."), "FileExists", ErrorCategory.ResourceExists, - Path)); + fullPath)); return; } @@ -56,16 +58,16 @@ namespace PSOPNSenseAPI.Cmdlets var configContent = task.GetAwaiter().GetResult(); // Create the directory if it doesn't exist - var directory = System.IO.Path.GetDirectoryName(Path); + var directory = System.IO.Path.GetDirectoryName(fullPath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // Write the configuration to the file - File.WriteAllBytes(Path, configContent); + File.WriteAllBytes(fullPath, configContent); - WriteVerbose($"Exported configuration to {Path}"); + WriteVerbose($"Exported configuration to {fullPath}"); } catch (Exception ex) { diff --git a/src/PSOPNSenseAPI/Cmdlets/GetOPNSenseConfigCmdlet.cs b/src/PSOPNSenseAPI/Cmdlets/GetOPNSenseConfigCmdlet.cs new file mode 100644 index 0000000..9b62fd2 --- /dev/null +++ b/src/PSOPNSenseAPI/Cmdlets/GetOPNSenseConfigCmdlet.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Management.Automation; +using System.Threading.Tasks; +using System.Xml; +using PSOPNSenseAPI.Services; + +namespace PSOPNSenseAPI.Cmdlets +{ + /// + /// Gets the OPNSense firewall configuration as an XML document. + /// The Get-OPNSenseConfig cmdlet retrieves the OPNSense firewall configuration and returns it as an XML document. + /// + /// Example 1: Get the configuration as an XML document + /// $config = Get-OPNSenseConfig + /// This example retrieves the OPNSense firewall configuration as an XML document. + /// + /// + /// Example 2: Get the configuration and pipe it to Restore-OPNSenseConfig + /// Get-OPNSenseConfig | Restore-OPNSenseConfig + /// This example retrieves the OPNSense firewall configuration and restores it. + /// + /// + [Cmdlet(VerbsCommon.Get, "OPNSenseConfig")] + [OutputType(typeof(XmlDocument))] + public class GetOPNSenseConfigCmdlet : OPNSenseBaseCmdlet + { + /// + /// Processes the cmdlet + /// + protected override void ProcessRecord() + { + try + { + var configService = new ConfigService(ApiClient, Logger); + + var task = Task.Run(async () => await configService.ExportConfigAsync()); + var configContent = task.GetAwaiter().GetResult(); + + // Convert the byte array to an XML document + var xmlDoc = new XmlDocument(); + using (var memoryStream = new MemoryStream(configContent)) + { + xmlDoc.Load(memoryStream); + } + + WriteVerbose("Retrieved OPNSense configuration as XML document"); + WriteObject(xmlDoc); + } + catch (Exception ex) + { + HandleException(ex); + } + } + } +} diff --git a/src/PSOPNSenseAPI/Cmdlets/ImportOPNSenseConfigCmdlet.cs b/src/PSOPNSenseAPI/Cmdlets/ImportOPNSenseConfigCmdlet.cs index 2a9804e..00912e6 100644 --- a/src/PSOPNSenseAPI/Cmdlets/ImportOPNSenseConfigCmdlet.cs +++ b/src/PSOPNSenseAPI/Cmdlets/ImportOPNSenseConfigCmdlet.cs @@ -23,8 +23,8 @@ namespace PSOPNSenseAPI.Cmdlets /// The path to the configuration file. /// [Parameter(Mandatory = true, Position = 0)] - [ValidateNotNullOrEmpty] - public string Path { get; set; } + [ValidateNotNull] + public FileInfo Path { get; set; } /// /// Suppresses the confirmation prompt. @@ -39,18 +39,20 @@ namespace PSOPNSenseAPI.Cmdlets { try { + string fullPath = Path.FullName; + // Check if the file exists - if (!File.Exists(Path)) + if (!File.Exists(fullPath)) { WriteError(new ErrorRecord( - new FileNotFoundException($"The file '{Path}' does not exist."), + new FileNotFoundException($"The file '{fullPath}' does not exist."), "FileNotFound", ErrorCategory.ObjectNotFound, - Path)); + fullPath)); return; } - if (!Force.IsPresent && !ShouldProcess(Path, "Import configuration")) + if (!Force.IsPresent && !ShouldProcess(fullPath, "Import configuration")) { return; } @@ -58,12 +60,12 @@ namespace PSOPNSenseAPI.Cmdlets var configService = new ConfigService(ApiClient, Logger); // Read the configuration file - var configContent = File.ReadAllBytes(Path); + var configContent = File.ReadAllBytes(fullPath); var task = Task.Run(async () => await configService.ImportConfigAsync(configContent)); var result = task.GetAwaiter().GetResult(); - WriteVerbose($"Imported configuration from {Path}: {result.Status}"); + WriteVerbose($"Imported configuration from {fullPath}: {result.Status}"); WriteWarning("The firewall is restarting. You may need to reconnect after it comes back online."); } catch (Exception ex) diff --git a/src/PSOPNSenseAPI/Cmdlets/RestoreOPNSenseConfigCmdlet.cs b/src/PSOPNSenseAPI/Cmdlets/RestoreOPNSenseConfigCmdlet.cs index 497597c..64322b0 100644 --- a/src/PSOPNSenseAPI/Cmdlets/RestoreOPNSenseConfigCmdlet.cs +++ b/src/PSOPNSenseAPI/Cmdlets/RestoreOPNSenseConfigCmdlet.cs @@ -1,18 +1,30 @@ using System; +using System.IO; using System.Management.Automation; using System.Threading.Tasks; +using System.Xml; using PSOPNSenseAPI.Services; namespace PSOPNSenseAPI.Cmdlets { /// - /// Restores an OPNSense firewall configuration from a backup. - /// The Restore-OPNSenseConfig cmdlet restores an OPNSense firewall configuration from a backup. + /// Restores an OPNSense firewall configuration. + /// The Restore-OPNSenseConfig cmdlet restores an OPNSense firewall configuration from a backup or an XML document. /// /// Example 1: Restore a configuration backup /// Restore-OPNSenseConfig -Filename "config-backup-20250414-1234.xml" /// This example restores an OPNSense firewall configuration from a backup file. /// + /// + /// Example 2: Restore a configuration from an XML document + /// $config = Get-OPNSenseConfig; Restore-OPNSenseConfig -XmlDocument $config + /// This example restores an OPNSense firewall configuration from an XML document. + /// + /// + /// Example 3: Restore a configuration from the pipeline + /// Get-OPNSenseConfig | Restore-OPNSenseConfig + /// This example restores an OPNSense firewall configuration from the pipeline. + /// /// [Cmdlet(VerbsData.Restore, "OPNSenseConfig", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] [OutputType(typeof(void))] @@ -21,10 +33,17 @@ namespace PSOPNSenseAPI.Cmdlets /// /// The filename of the backup to restore. /// - [Parameter(Mandatory = true, Position = 0)] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Filename")] [ValidateNotNullOrEmpty] public string Filename { get; set; } + /// + /// The XML document containing the configuration to restore. + /// + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "XmlDocument", ValueFromPipeline = true)] + [ValidateNotNull] + public XmlDocument XmlDocument { get; set; } + /// /// Suppresses the confirmation prompt. /// @@ -38,17 +57,39 @@ namespace PSOPNSenseAPI.Cmdlets { try { - if (!Force.IsPresent && !ShouldProcess(Filename, "Restore configuration backup")) + var configService = new ConfigService(ApiClient, Logger); + string actionDescription = "Restore configuration"; + string targetName = ParameterSetName == "Filename" ? Filename : "from XML document"; + + if (!Force.IsPresent && !ShouldProcess(targetName, actionDescription)) { return; } - var configService = new ConfigService(ApiClient, Logger); + if (ParameterSetName == "Filename") + { + // Restore from backup file + var task = Task.Run(async () => await configService.RestoreConfigBackupAsync(Filename)); + var result = task.GetAwaiter().GetResult(); - var task = Task.Run(async () => await configService.RestoreConfigBackupAsync(Filename)); - var result = task.GetAwaiter().GetResult(); + WriteVerbose($"Restored configuration from backup: {result.Status}"); + } + else + { + // Restore from XML document + byte[] configContent; + using (var memoryStream = new MemoryStream()) + { + XmlDocument.Save(memoryStream); + configContent = memoryStream.ToArray(); + } + + var task = Task.Run(async () => await configService.ImportConfigAsync(configContent)); + var result = task.GetAwaiter().GetResult(); + + WriteVerbose($"Restored configuration from XML document: {result.Status}"); + } - WriteVerbose($"Restored configuration backup: {result.Status}"); WriteWarning("The firewall is restarting. You may need to reconnect after it comes back online."); } catch (Exception ex) diff --git a/src/PSOPNSenseAPI/PSOPNSenseAPI.psd1 b/src/PSOPNSenseAPI/PSOPNSenseAPI.psd1 index d554578..e729d66 100644 --- a/src/PSOPNSenseAPI/PSOPNSenseAPI.psd1 +++ b/src/PSOPNSenseAPI/PSOPNSenseAPI.psd1 @@ -3,7 +3,7 @@ RootModule = 'PSOPNSenseAPI.psm1' # Version number of this module. - ModuleVersion = '2025.04.15.0739' + ModuleVersion = '2025.04.15.1143' # Supported PSEditions CompatiblePSEditions = @('Desktop', 'Core') @@ -57,7 +57,7 @@ # FormatsToProcess = @() # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = @() + NestedModules = @('lib\PSOPNSenseAPI.dll') # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @() @@ -65,7 +65,6 @@ # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @( 'Apply-OPNSenseFirewallChanges', - 'Backup-OPNSenseConfig', 'Connect-OPNSense', 'Connect-OPNSenseTailscale', 'ConvertTo-OPNSenseNetworkNotation', @@ -81,6 +80,7 @@ 'Enable-OPNSenseTailscale', 'Export-OPNSenseConfig', 'Get-OPNSenseAlias', + 'Get-OPNSenseConfig', 'Get-OPNSenseConfigBackup', 'Get-OPNSenseConnection', 'Get-OPNSenseCronJob',