Update configuration management cmdlets

- Added Get-OPNSenseConfig cmdlet that retrieves the OPNSense configuration as an XML document
- 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
- Removed Backup-OPNSenseConfig cmdlet (use Get-OPNSenseConfig instead)
- Updated documentation and examples
This commit is contained in:
GraceSolutions
2025-04-15 08:53:09 -04:00
parent c423cca25a
commit 68e6819131
16 changed files with 654 additions and 108 deletions
@@ -1,53 +0,0 @@
using System;
using System.Management.Automation;
using System.Threading.Tasks;
using PSOPNSenseAPI.Services;
namespace PSOPNSenseAPI.Cmdlets
{
/// <summary>
/// <para type="synopsis">Creates a backup of the OPNSense firewall configuration.</para>
/// <para type="description">The Backup-OPNSenseConfig cmdlet creates a backup of the OPNSense firewall configuration.</para>
/// <example>
/// <para>Example 1: Create a backup</para>
/// <code>Backup-OPNSenseConfig</code>
/// <para>This example creates a backup of the OPNSense firewall configuration with an automatically generated filename.</para>
/// </example>
/// <example>
/// <para>Example 2: Create a backup with a specific filename</para>
/// <code>Backup-OPNSenseConfig -Filename "pre-upgrade-backup"</code>
/// <para>This example creates a backup of the OPNSense firewall configuration with a specific filename.</para>
/// </example>
/// </summary>
[Cmdlet(VerbsData.Backup, "OPNSenseConfig")]
[OutputType(typeof(string))]
public class BackupOPNSenseConfigCmdlet : OPNSenseBaseCmdlet
{
/// <summary>
/// <para type="description">The filename for the backup.</para>
/// </summary>
[Parameter(Mandatory = false, Position = 0)]
public string Filename { get; set; }
/// <summary>
/// Processes the cmdlet
/// </summary>
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);
}
}
}
}
@@ -23,8 +23,8 @@ namespace PSOPNSenseAPI.Cmdlets
/// <para type="description">The path to save the configuration file.</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0)]
[ValidateNotNullOrEmpty]
public string Path { get; set; }
[ValidateNotNull]
public FileInfo Path { get; set; }
/// <summary>
/// <para type="description">Overwrites the file if it exists.</para>
@@ -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)
{
@@ -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
{
/// <summary>
/// <para type="synopsis">Gets the OPNSense firewall configuration as an XML document.</para>
/// <para type="description">The Get-OPNSenseConfig cmdlet retrieves the OPNSense firewall configuration and returns it as an XML document.</para>
/// <example>
/// <para>Example 1: Get the configuration as an XML document</para>
/// <code>$config = Get-OPNSenseConfig</code>
/// <para>This example retrieves the OPNSense firewall configuration as an XML document.</para>
/// </example>
/// <example>
/// <para>Example 2: Get the configuration and pipe it to Restore-OPNSenseConfig</para>
/// <code>Get-OPNSenseConfig | Restore-OPNSenseConfig</code>
/// <para>This example retrieves the OPNSense firewall configuration and restores it.</para>
/// </example>
/// </summary>
[Cmdlet(VerbsCommon.Get, "OPNSenseConfig")]
[OutputType(typeof(XmlDocument))]
public class GetOPNSenseConfigCmdlet : OPNSenseBaseCmdlet
{
/// <summary>
/// Processes the cmdlet
/// </summary>
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);
}
}
}
}
@@ -23,8 +23,8 @@ namespace PSOPNSenseAPI.Cmdlets
/// <para type="description">The path to the configuration file.</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0)]
[ValidateNotNullOrEmpty]
public string Path { get; set; }
[ValidateNotNull]
public FileInfo Path { get; set; }
/// <summary>
/// <para type="description">Suppresses the confirmation prompt.</para>
@@ -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)
@@ -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
{
/// <summary>
/// <para type="synopsis">Restores an OPNSense firewall configuration from a backup.</para>
/// <para type="description">The Restore-OPNSenseConfig cmdlet restores an OPNSense firewall configuration from a backup.</para>
/// <para type="synopsis">Restores an OPNSense firewall configuration.</para>
/// <para type="description">The Restore-OPNSenseConfig cmdlet restores an OPNSense firewall configuration from a backup or an XML document.</para>
/// <example>
/// <para>Example 1: Restore a configuration backup</para>
/// <code>Restore-OPNSenseConfig -Filename "config-backup-20250414-1234.xml"</code>
/// <para>This example restores an OPNSense firewall configuration from a backup file.</para>
/// </example>
/// <example>
/// <para>Example 2: Restore a configuration from an XML document</para>
/// <code>$config = Get-OPNSenseConfig; Restore-OPNSenseConfig -XmlDocument $config</code>
/// <para>This example restores an OPNSense firewall configuration from an XML document.</para>
/// </example>
/// <example>
/// <para>Example 3: Restore a configuration from the pipeline</para>
/// <code>Get-OPNSenseConfig | Restore-OPNSenseConfig</code>
/// <para>This example restores an OPNSense firewall configuration from the pipeline.</para>
/// </example>
/// </summary>
[Cmdlet(VerbsData.Restore, "OPNSenseConfig", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
[OutputType(typeof(void))]
@@ -21,10 +33,17 @@ namespace PSOPNSenseAPI.Cmdlets
/// <summary>
/// <para type="description">The filename of the backup to restore.</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0)]
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "Filename")]
[ValidateNotNullOrEmpty]
public string Filename { get; set; }
/// <summary>
/// <para type="description">The XML document containing the configuration to restore.</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "XmlDocument", ValueFromPipeline = true)]
[ValidateNotNull]
public XmlDocument XmlDocument { get; set; }
/// <summary>
/// <para type="description">Suppresses the confirmation prompt.</para>
/// </summary>
@@ -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)
+3 -3
View File
@@ -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',