UpdateContainersWithDefinitionFile.psm1

## Namespaces.
using namespace System;
using namespace System.Collections.Generic;
using namespace System.IO;
using namespace System.Text.Json;
using namespace System.Text.Json.Serialization;
using namespace System.ComponentModel.DataAnnotations;

## Module Functions
using module '.\Function\Get-ContainerDefinitionFilePath.Function.psm1';
using module '.\Function\Get-ContainerDefinitionJsonSerializerOptions.Function.psm1';
using module '.\Function\Update-ContainersWithDefinitionFile.Function.psm1';

## Engine Enum.
enum UpdateContainersEngineEnum
{
    <#
    .NAME
        UpdateContainersEngineEnum

    .SYNOPSIS
        Enum defining supported container engines.

    .DESCRIPTION
        This enum specifies the container engines that the script can manage,
        allowing for differentiation between Docker and Podman commands.
    #>


    ## Docker Command Enum Value.
    Docker

    ## Podman Command Enum Value.
    Podman
};

## IP Address Method Enum.
enum UpdateContainersIpAddressMethodEnum
{
    <#
    .NAME
        UpdateContainersIpAddressingMethodEnum

    .SYNOPSIS
        Enum defining IP addressing methods for container networks.

    .DESCRIPTION
        This enum specifies the methods available for assigning IP addresses
        to containers within a network, either automatically or manually.
    #>


    ## Auto Addressing Enum Value.
    Auto

    ## Manual Addressing Enum Value.
    Manual
};

## Container Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerDefinition
{
    <#
    .NAME
        UpdateContainersContainerDefinition

    .SYNOPSIS
        Class defining a container configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a container that can be
        created and managed by container engines like Docker and Podman.

    .PROPERTIES
        Aliases
            A list of aliases for the container within its networks.

        Capabilities
            A list of Linux capabilities to add to the container.

        Command
            The container engine command to use (Docker or Podman).

        Devices
            A list of device mappings for the container.

        DnsServers
            A list of DNS server IP addresses to use for the container.

        Environment
            A dictionary of environment variables to set in the container.

        HealthCheck
            The health check configuration for the container.

        Hostname
            The hostname to assign to the container.

        Image
            The container image to use.

        MacAddress
            The MAC address to assign to the container's network interface.

        Memory
            The memory limit for the container (e.g., "512m" for 512 megabytes).

        Mounts
            A list of mount configurations for the container.

        Name
            The name of the container.

        Networks
            A list of network configurations for the container.

        Privileged
            A boolean indicating whether the container should run in privileged mode.

        PublishedPorts
            A list of port publishing configurations for the container.

        RestartPolicy
            The restart policy for the container (e.g., "no", "on-failure", "always", "unless-stopped").

        Runtime
            The runtime to use for the container (e.g., "runc", "nvidia").
    #>


    ## Aliases Property.
    [JsonPropertyName('alias')]
    [List[String]] $Aliases = [List[String]]::new();

    ## Capabilities Property.
    [JsonPropertyName('capability')]
    [List[String]] $Capabilities = [List[String]]::new();

    ## Command Property.
    [AllowNull()]
    [JsonPropertyName('command')]
    [Nullable[UpdateContainersEngineEnum]] $Command = $null;

    ## Devices Property.
    [JsonPropertyName('device')]
    [List[UpdateContainersContainerDeviceDefinition]] $Devices = [List[UpdateContainersContainerDeviceDefinition]]::new();

    ## DNS Servers Property.
    [JsonPropertyName('dns')]
    [List[String]] $DnsServers = [List[String]]::new();

    ## Environment Property.
    [JsonPropertyName('environment')]
    [Dictionary[String, String]] $Environment = [Dictionary[String, String]]::new();

    ## Health Check Property.
    [JsonPropertyName('healthCheck')]
    [UpdateContainersContainerHealthCheckDefinition] $HealthCheck = $null;

    ## Hostname Property.
    [JsonPropertyName('hostname')]
    [String] $Hostname = $null;

    ## Image Property.
    [JsonPropertyName('image')]
    [JsonRequired()]
    [String] $Image;

    ## MacAddress Property.
    [AllowNull()]
    [JsonPropertyName('mac')]
    [String] $MacAddress = $null;

    ## Memory Property.
    [JsonPropertyName('memory')]
    [String] $Memory = $null;

    ## Mounts Property.
    [JsonPropertyName('mount')]
    [List[UpdateContainersContainerMountDefinition]] $Mounts = [List[UpdateContainersContainerMountDefinition]]::new();

    ## Name Property.
    [JsonPropertyName('name')]
    [JsonRequired()]
    [String] $Name;

    ## Networks Property.
    [JsonPropertyName('network')]
    [List[UpdateContainersContainerNetworkDefinition]] $Networks = [List[UpdateContainersContainerNetworkDefinition]]::new();

    ## Privileged Property.
    [JsonPropertyName('privileged')]
    [Boolean] $Privileged = $false;

    ## Published Ports Property.
    [JsonPropertyName('publish')]
    [List[UpdateContainersContainerPublishDefinition]] $PublishedPorts = [List[UpdateContainersContainerPublishDefinition]]::new();

    ## Restart Policy Property.
    [JsonPropertyName('restart')]
    [String] $RestartPolicy = "unless-stopped";

    ## Runtime Property.
    [JsonPropertyName('runtime')]
    [String] $Runtime = $null;

    ## ToCommandLine Method.
    [String] ToCommandLine([UpdateContainersEngineEnum] $engine, [List[String]] $dns = [List[String]]::new(), [List[UpdateContainersReplacementDefinition]] $replacements = [List[UpdateContainersReplacementDefinition]]::new(), [Boolean] $dryRun = $false)
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string to create the container.

        .DESCRIPTION
            This method constructs the appropriate command line string to create
            a container using the specified container engine, based on the properties
            defined in the ContainerDefinition class. It also applies any provided
            DNS servers and path replacements.

        .PARAMETERS
            engine
                The container engine to use (Docker or Podman).

                Required? true

            dns
                A list of DNS server IP addresses to use for the container.

                Required? false
                Default @()

            replacements
                A list of DefinitionReplacement objects to apply to paths in the
                container definition.

                Required? false
                Default @()

            dryRun
                A boolean indicating whether this is a dry run (no actual execution).

                Required? false
                Default $false

        .OUTPUTS
            String
                The command line string to create the container.
        #>


        ## Reconcile the engine command between the definition and the container.
        $engine = ($null -ne $this.Command -and "" -ne $this.Command.Trim() ? $this.Command.ToString() : $engine.ToString()).ToLower().Trim();

        ## Define our command line string.
        [String] $commandLine = "container create";

        ## Iterate over the capabilities if there are any.
        foreach ($capability in $this.Capabilities)
        {
            ## Append the capability to the command string.
            $commandLine = "${commandLine} ```n`t--cap-add '$($capability.Trim())'";
        }

        ## Iterate over the devices if there are any.
        foreach ($device in $this.Devices)
        {
            ## Append the device to the command string.
            $commandLine = "${commandLine} ```n`t$($device.ToCommandLine())";
        }

        ## Iterate over the provided DNS servers if there are any.
        foreach ($server in $this.DnsServers)
        {
            ## Append the DNS server to the command string.
            $commandLine = "${commandLine} ```n`t--dns '$($server.Trim())'";
        }

        ## Iterate over the provided DNS servers if there are any.
        foreach ($server in $dns)
        {
            ## Append the DNS server to the command string.
            $commandLine = "${commandLine} ```n`t--dns '$($server.Trim())'";
        }

        ## Iterate over the environment variables if there are any.
        foreach ($key in $this.Environment.Keys)
        {
            ## Localize the value.
            $value = $this.Environment[$key].Trim();

            ## Iterate over the replacements and replace them in the value.
            foreach ($replacement in $replacements) { $value = $replacement.Replace($value); }

            ## Update the command line string.
            $commandLine = "${commandLine} ```n`t--env '$($key.Trim())=$($value.Trim())'";
        }

        ## Check for a health check on the container.
        if ($null -ne $this.HealthCheck)
        {
            ## Append the health check to the command line string.
            $commandLine = "${commandLine} ```n`t$($this.HealthCheck.ToCommandLine())";
        }

        ## Check for a provided hostname in the container definition.
        if ($null -eq $this.Hostname -or "" -eq $this.Hostname.Trim())
        {
            ## Reset the hostname in the container definition to the container name.
            $this.Hostname = $this.Name.Trim();
        }

        ## Add the hostname to the command string.
        $commandLine = "${commandLine} ```n`t--hostname '$($this.Hostname.Trim())'";

        ## Check for a provided MAC address in the container definition.
        if ($null -ne $this.MacAddress -and "" -ne $this.MacAddress.Trim())
        {
            ## Append the MAC address to the command string.
            $commandLine = "${commandLine} ```n`t--mac-address '$($this.MacAddress.Trim())'";
        }

        ## Check for a memory limit on the container.
        if ($null -ne $this.Memory -and "" -ne $this.Memory.Trim())
        {
            ## Append the memory limit to the command string.
            $commandLine = "${commandLine} ```n`t--memory '$($this.Memory.Trim())'";
        }

        ## Iterate over the mounts if there are any.
        foreach ($mount in $this.Mounts)
        {
            ## Append the mount to the command string.
            $commandLine = "${commandLine} ```n`t$($mount.ToCommandLine($replacements))";
        }

        ## Add the name to the command string.
        $commandLine = "${commandLine} ```n`t--name '$($this.Name.Trim())'";

        ## Localize the first network as the primary network.
        [UpdateContainersContainerNetworkDefinition] $network = $this.Networks | Select-Object -First 1;

        ## Check for a primary network.
        if ($null -ne $network)
        {
            ## Append the network to the command string.
            $commandLine = "${commandLine} ```n`t$($network.ToCommandLine($engine, $false, $this, $dryRun))";
        }

        ## Iterate over the aliases if there are any.
        foreach ($alias in $this.Aliases)
        {
            ## Append the alias to the command string.
            $commandLine = "${commandLine} ```n`t--network-alias '$($alias.Trim())'";
        }

        ## Check for a privileged container.
        if ($this.Privileged)
        {
            ## Append the privileged flag to the command string.
            $commandLine = "${commandLine} ```n`t--privileged";
        }

        ## Iterate over the published ports if there are any.
        foreach ($port in $this.PublishedPorts)
        {
            ## Append the published port to the command string.
            $commandLine = "${commandLine} ```n`t$($port.ToCommandLine())";
        }

        ## Add the restart-policy to the command string.
        if ($null -ne $this.RestartPolicy -and "" -ne $this.RestartPolicy.Trim())
        {
            ## Append the restart policy to the command string.
            $commandLine = "${commandLine} ```n`t--restart '$($this.RestartPolicy.Trim())'";
        }
        else
        {
            ## Default the restart policy to unless-stopped in the command string.
            $commandLine = "${commandLine} ```n`t--restart 'unless-stopped'";
        }

        ## Check for a provided runtime.
        if ($null -ne $this.Runtime -and "" -ne $this.Runtime.Trim())
        {
            ## Append the runtime to the command string.
            $commandLine = "${commandLine} ```n`t--runtime '$($this.Runtime.Trim())'";
        }

        ## We're done, return the command.
        return "${commandLine} ```n`t'$($this.Image.Trim())'";
    }
};

## Container Device Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerDeviceDefinition
{
    <#
    .NAME
        UpdateContainersContainerDeviceDefinition

    .SYNOPSIS
        Class defining a device mapping for container engines.

    .DESCRIPTION
        This class represents a device mapping that can be applied to containers
        managed by container engines like Docker and Podman.

    .PROPERTIES
        Destination
            The path inside the container where the device will be mapped.

        Source
            The path on the host system of the device to be mapped.
    #>


    ## Destination Property.
    [AllowNull()]
    [JsonPropertyName('destination')]
    [String] $Destination = $null;

    ## Source Property.
    [JsonPropertyName('source')]
    [JsonRequired()]
    [String] $Source;

    ## ToCommandLine Method.
    [String] ToCommandLine()
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string for the device mapping.

        .DESCRIPTION
            This method constructs the appropriate command line string to map
            a device into a container using the specified source and destination
            paths defined in the ContainerDeviceDefinition class.

        .OUTPUTS
            String
                The command line string for the device mapping.
        #>


        ## Define our command line string.
        [String] $command = "--device '$($this.Source.Trim())";

        ## Check for a provided source.
        if ($null -ne $this.Destination -and "" -ne $this.Destination.Trim())
        {
            ## Append the source to the command line string.
            $command = "${command}:$($this.Source.Trim())";
        }

        ## We're done, return the command line string.
        return "${command}'".Trim();
    }
};

## Container Health Check Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerHealthCheckDefinition
{
    <#
    .NAME
        UpdateContainersContainerHealthCheckDefinition

    .SYNOPSIS
        Class defining a health check configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a health check that can be
        applied to containers managed by container engines like Docker and Podman.

    .PROPERTIES
        Command
            The command to run to check the health of the container.

        Interval
            The time interval between health checks (e.g., "30s" for 30 seconds).

        Retries
            The number of consecutive failures required before marking the container as unhealthy.

        StartPeriod
            The initial delay before starting health checks after the container starts (e.g., "3s" for 3 seconds).

        Timeout
            The maximum time to wait for the health check command to complete (e.g., "30s" for 30 seconds).
    #>


    ## Command Property.
    [JsonPropertyName('command')]
    [JsonRequired()]
    [String] $Command;

    ## Interval Property.
    [AllowNull()]
    [JsonPropertyName('interval')]
    [String] $Interval = "30s";

    ## Retries Property.
    [AllowNull()]
    [JsonPropertyName('retries')]
    [Range(1, 10)]
    [Int32] $Retries = 3;

    ## Start Period Property.
    [AllowNull()]
    [JsonPropertyName('start')]
    [String] $StartPeriod = "3s";

    ## Timeout Property.
    [AllowNull()]
    [JsonPropertyName('timeout')]
    [String] $Timeout = "30s";

    ## ToCommandLine Method.
    [String] ToCommandLine()
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string for the health check configuration.

        .DESCRIPTION
            This method constructs the appropriate command line string to define
            a health check for a container using the properties defined in the
            ContainerHealthCheckDefinition class.

        .OUTPUTS
            String
                The command line string for the health check configuration.
        #>


        ## Define our command line string.
        [String] $commandLine = "--health-cmd='$($this.Command.Trim())'";

        ## Check for a provided interval.
        if ($null -ne $this.Interval -and "" -ne $this.Interval.Trim())
        {
            ## Append the interval to the command line string.
            $commandLine = "${commandLine} ```n`t--health-interval='$($this.Interval.Trim())'";
        }

        ## Check for a provided retries.
        if ($null -ne $this.Retries)
        {
            ## Append the retries to the command line string.
            $commandLine = "${commandLine} ```n`t--health-retries=$($this.Retries)";
        }

        ## Check for a provided start period.
        if ($null -ne $this.StartPeriod -and "" -ne $this.StartPeriod.Trim())
        {
            ## Append the start period to the command line string.
            $commandLine = "${commandLine} ```n`t--health-start-period='$($this.StartPeriod.Trim())'";
        }

        ## Check for a provided timeout.
        if ($null -ne $this.Timeout -and "" -ne $this.Timeout.Trim())
        {
            ## Append the timeout to the command line string.
            $commandLine = "${commandLine} ```n`t--health-timeout='$($this.Timeout.Trim())'";
        }

        ## We're done, return the command line string.
        return $commandLine.Trim();
    }
};

## Container Mount Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerMountDefinition
{
    <#
    .NAME
        UpdateContainersContainerMountDefinition

    .SYNOPSIS
        Class defining a mount configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a mount that can be
        applied to containers managed by container engines like Docker and Podman.

    .PROPERTIES
        Destination
            The path inside the container where the mount will be located.

        Source
            The path on the host system to be mounted into the container.

        ReadOnly
            A boolean indicating whether the mount should be read-only.

        Type
            The type of mount (e.g., bind, volume, tmpfs).
    #>


    ## Destination Property.
    [JsonPropertyName('destination')]
    [String] $Destination = $null;

    ## Source Property.
    [JsonPropertyName('source')]
    [JsonRequired()]
    [String] $Source;

    ## Read-Only Property.
    [JsonPropertyName('ro')]
    [Boolean] $ReadOnly = $false;

    ## Type Property.
    [JsonPropertyName('type')]
    [String] $Type = "bind";

    ## ToCommandLine Method.
    [String] ToCommandLine([List[UpdateContainersReplacementDefinition]] $replacements = [List[UpdateContainersReplacementDefinition]]::new())
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string for the mount configuration.

        .DESCRIPTION
            This method constructs the appropriate command line string to define
            a mount for a container using the properties defined in the
            ContainerMountDefinition class. It also applies any provided path
            replacements to the source and destination paths.

        .PARAMETERS
            replacements
                A list of DefinitionReplacement objects to apply to the source
                and destination paths.

                Required? false
                Default @()

        .OUTPUTS
            String
                The command line string for the mount configuration.
        #>


        ## Ensure we have a destination.
        if ($null -eq $this.Destination -or "" -eq $this.Destination.Trim())
        {
            ## Reset the destination to the source path.
            $this.Destination = $this.Source.Trim();
        }

        ## Iterate over the replacements.
        foreach ($replacement in $replacements)
        {
            ## Make the replacement in the destination path.
            $this.Destination = $replacement.Replace($this.Destination);

            ## Make the replacement in the source path.
            $this.Source = $replacement.Replace($this.Source);
        }

        ## Define our command line string.
        [String] $command = "--mount 'destination=$($this.Destination.Trim())";

        ## Check the read-only flag.
        if ($this.ReadOnly)
        {
            ## Append the read-only flag to the command line string.
            $command = "${command},ro=true";
        }

        ## Add the source to the command line string.
        $command = "${command},source=$($this.Source.Trim())";

        ## Check for a provided type.
        if ($null -ne $this.Type -and "" -ne $this.Type.Trim())
        {
            ## Append the type to the command line string.
            $command = "${command},type=$($this.Type.ToLower().Trim())";
        }
        else
        {
            ## Default the type to bind in the command line string.
            $command = "${command},type=bind";
        }

        ## We're done, return the command line string.
        return "${command}'".Trim();
    }
};

## Container Network Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerNetworkDefinition
{
    <#
    .NAME
        UpdateContainersContainerNetworkDefinition

    .SYNOPSIS
        Class defining a network configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a network that can be
        applied to containers managed by container engines like Docker and Podman.

    .PROPERTIES
        DefaultRoute
            A boolean indicating whether this network should be the default route for the container.

        IPv4Method
            The method for assigning an IPv4 address (Auto or Manual).

        IPv4Node
            The last octet of the IPv4 address to assign if using Manual method.

        IPv6Method
            The method for assigning an IPv6 address (Auto or Manual).

        IPv6Node
            The last segment of the IPv6 address to assign if using Manual method.

        Name
            The name of the network to connect the container to.
    #>


    ## IPv4 Property.
    [AllowNull()]
    [JsonPropertyName('method')]
    [UpdateContainersIpAddressMethodEnum] $IPv4Method = [UpdateContainersIpAddressMethodEnum]::Auto;

    ## IPv4 Node Property.
    [AllowNull()]
    [JsonPropertyName('node')]
    [Range(2, 254)]
    [Int32] $IPv4Node = $null;

    ## IPv6 Property.
    [JsonPropertyName('method6')]
    [UpdateContainersIpAddressMethodEnum] $IPv6Method = [UpdateContainersIpAddressMethodEnum]::Auto;

    ## IPv6 Node Property.
    [AllowNull()]
    [JsonPropertyName('node6')]
    [Range(2, 254)]
    [Int32] $IPv6Node = $null;

    ## Name Property.
    [JsonPropertyName('name')]
    [JsonRequired()]
    [String] $Name;

    ## Routes Property.
    [AllowNull()]
    [JsonPropertyName('route')]
    [List[UpdateContainersContainerNetworkRouteDefinition]] $Routes = [List[UpdateContainersContainerNetworkRouteDefinition]]::new();

    ## To Command Line Method.
    [String] ToCommandLine([String] $engine, [Boolean] $link, [UpdateContainersContainerDefinition] $container, [Boolean] $dryRun)
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string to connect the container to the network.

        .DESCRIPTION
            This method constructs the appropriate command line string to connect
            a container to a specified network using the given container engine.
            It also handles IP address assignments and DNS server configurations.

        .PARAMETERS
            engine
                The container engine to use (Docker or Podman).

                Required? true

            link
                A boolean indicating whether this is a network link operation.

                Required? true

            container
                The container definition to which the network will be connected.

                Required? true

            dryRun
                A boolean indicating whether this is a dry run (no actual execution).

                Required? true

        .OUTPUTS
            String
                The command line string to connect the container to the network.
        #>


        ## Reconcile the IPv4 addressing method based on the inputs.
        $this.IPv4Method = ($this.IPv4Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv4Node `
                -and $this.IPv4Node -ge 2 -and $this.IPv4Node -le 254 ? `
                    [UpdateContainersIpAddressMethodEnum]::Manual : [UpdateContainersIpAddressMethodEnum]::Auto);

        ## Reconcile the IPv4 Node based on the IPv6 Node if necessary.
        $this.IPv4Node = ($this.IPv4Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and ($this.IPv4Node -lt 2 `
                -or $this.IPv4Node -gt 254) ? $null : $this.IPv4Node);

        ## Reconcile the IPv6 addressing method based on the inputs.
        $this.IPv6Method = ($this.IPv6Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and (($null -ne $this.IPv6Node `
            -and $this.IPv6Node -ge 2 -and $this.IPv6Node -le 254) -or ($null -ne $this.IPv4Node `
            -and $this.IPv4Node -ge 2 -and $this.IPv4Node -le 254)) ? `
                [UpdateContainersIpAddressMethodEnum]::Manual : [UpdateContainersIpAddressMethodEnum]::Auto);

        ## Reconcile the IPv6 Node based on the IPv4 Node if necessary.
        $this.IPv6Node = ($this.IPv6Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and ($null -eq $this.IPv6Node `
            -or $this.IPv6Node -le 2 -or $this.IPv6Node -ge 254) -and ($this.IPv4Method `
            -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv4Node -and $this.IPv4Node -ge 2 `
            -and $this.IPv4Node -le 254) ? $this.IPv4Node : $this.IPv6Node);

        ## Localize our command results.
        $commandResults = (& "$($engine.ToString().ToLower())" network ls `
            --filter "Name=$($this.Name.Trim())" `
            --format "{{.Name}}");

        ## Localize the existence of the network.
        [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
            ($commandResults.Trim() | Where-Object { $_.Trim() -eq $this.Name.Trim() }).Length } else { 0 };

        ## Check if the network exists.
        if (0 -eq $exists -and -not $dryRun)
        {
            ## We don't have a valid network for this container.
            throw "Network [$($this.Name.Trim())] Not Found";
        }

        ## Localize our command results.
        $commandResults = (& "$($engine.ToString().ToLower())" network inspect "$($this.Name.Trim())" `
            --format "{{(index .IPAM.Config 0).Gateway}},{{(index .IPAM.Config 1).Gateway}}");

        ## Localize the gateway addresses for the network.
        [String[]] $gateways = if ($null -ne $commandResults -and "" -ne $commandResults.Trim())
        {
            ($commandResults.Trim() | Where-Object { $null -ne $_ -and "" -ne $_.Trim() -and $_ -ne "," }).Split(",") `
            | Where-Object { $null -ne $_ -and "" -ne $_.Trim() } `
            | ForEach-Object { $_.Trim() } `
            | Select-Object -Unique
        } else { @() };

        ## Define our assignments list.
        [List[String]] $assignments = [List[String]]::new();

        ## Iterate over the gateways.
        foreach ($gateway in $gateways)
        {
            ## Check for an IPv4 gateway.
            if ($gateway.Contains(".") -and $this.IPv4Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv4Node)
            {
                ## Add the IPv4 assignment to the list.
                $assignments.Add("--ip '$($gateway.Substring(0, $gateway.LastIndexOf(".")).Trim()).$($this.IPv4Node)'");
            }

            ## Check for an IPv6 gateway.
            if ($gateway.Contains("::") -and $this.IPv6Method -eq [UpdateContainersIpAddressMethodEnum]::Manual -and $null -ne $this.IPv6Node)
            {
                ## Add the IPv6 assignment to the list.
                $assignments.Add("--ip6 '$($gateway.Substring(0, $gateway.LastIndexOf("::")).Trim())::$($this.IPv6Node)'");
            }
        }

        ## Check for a container assignment.
        if ($false -eq $link)
        {
            ## Define our command line string.
            [String] $commandLineString = "--network '$($this.Name.Trim())'";

            ## Check for any assignments.
            if ($assignments.Count -gt 0)
            {
                ## Localize the assignments.
                [String] $assignmentsString = ($assignments -Join " ```n`t");

                ## Append the assignments to the command line string.
                $commandLineString = "${commandLineString} ```n`t${assignmentsString}";
            }

            ## We're done, generate and return our container network definition.
            return $commandLineString.Trim();
        }

        ## Localize the command line string.
        [String] $commandLine = "network connect";

        ## Check for a container name.
        if ($null -ne $container)
        {
            ## Iterate over the aliases if there are any.
            foreach ($alias in $container.Aliases)
            {
                ## Append the alias to the command line string.
                $commandLine = "${commandLine} ```n`t--alias '$($alias.Trim())'";
            }
        }

        ## Check for a default route.
        if ($this.DefaultRoute)
        {
            ## Append the default route to the command line string.
            $commandLine = "${commandLine} ```n`t--gw-priority 1";
        }

        ## Check for any assignments.
        if ($assignments.Count -ge 0)
        {
            ## Append the assignments to the command line string.
            $commandLine = "${commandLine} ```n`t$($assignments -Join " ```n`t")";
        }

        ## We're done, generate and return our container network linking definition.
        return "${commandLine} ```n`t'$($this.Name.Trim())' '$($container.Name.Trim())'";
    }
};

## Container Network Route Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerNetworkRouteDefinition
{
    <#
    .NAME
        UpdateContainersContainerNetworkRouteDefinition

    .SYNOPSIS
        Class defining a network route configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a network route that can be
        applied to containers managed by container engines like Docker and Podman.

    .PROPERTIES
        Destination
            The destination CIDR for the route (e.g., "192.168.3.0/24").

        Gateway
            The gateway IP address for the route (e.g., "192.168.3.1").
    #>


    ## Destination Property.
    [JsonPropertyName('destination')]
    [JsonRequired()]
    [String] $Destination;

    ## Gateway Property.
    [JsonPropertyName('gateway')]
    [JsonRequired()]
    [String] $Gateway;

    ## ToCommandLine Method.
    [String] ToCommandLine([String] $interface)
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string for the network route configuration.

        .DESCRIPTION
            This method constructs the appropriate command line string to define
            a network route for a container using the properties defined in the
            ContainerNetworkRouteDefinition class.

        .PARAMETERS
            interface
                The network interface to which the route applies.

                Required? true

        .OUTPUTS
            String
                The command line string for the network route configuration.
        #>


        ## Define and return our command line string.
        return "ip route add '$($this.Destination.Trim())' via '$($this.Gateway.Trim())' dev '$($interface.Trim())'";
    }
};

## Container Publish Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersContainerPublishDefinition
{
    <#
    .NAME
        UpdateContainersContainerPublishDefinition

    .SYNOPSIS
        Class defining a port publishing configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for publishing a port
        from a container to the host system, which can be applied to containers
        managed by container engines like Docker and Podman.

    .PROPERTIES
        Container
            The port inside the container to be published.

        Host
            The port on the host system to which the container port will be mapped.

        Protocol
            The protocol to use for the port mapping (e.g., tcp, udp).
    #>


    ## Container Property.
    [JsonPropertyName('container')]
    [JsonRequired()]
    [String] $Container;

    ## Host Property.
    [AllowNull()]
    [JsonPropertyName('host')]
    [String] $Host = $null;

    ## Protocol Property.
    [AllowNull()]
    [JsonPropertyName('protocol')]
    [String] $Protocol = $null;

    ## ToCommandLine Method.
    [String] ToCommandLine()
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string for the port publishing configuration.

        .DESCRIPTION
            This method constructs the appropriate command line string to publish
            a port from a container to the host system using the properties defined
            in the ContainerPublishDefinition class.

        .OUTPUTS
            String
                The command line string for the port publishing configuration.
        #>


        ## Reconcile the host port.
        $this.Host = ($null -eq $this.Host -or "" -eq $this.Host.Trim() ? $this.Container : $this.Host.Trim());

        ## Define our command line string.
        [String] $command = "--publish '$($this.Host.Trim()):$($this.Container.Trim())";

        ## Check for a provided protocol.
        if ($null -ne $this.Protocol -and "" -ne $this.Protocol.Trim())
        {
            ## Append the protocol to the command line string.
            $command = "${command}/$($this.Protocol.ToLower().Trim())";
        }

        ## We're done, return the command line string.
        return "${command}'".Trim();
    }
};

## Definition Class.
[NoRunspaceAffinity()]
class UpdateContainersDefinition
{
    <#
    .NAME
        UpdateContainersDefinition

    .SYNOPSIS
        Class defining a container engine definition.

    .DESCRIPTION
        This class represents a container engine definition that includes configurations
        for containers, networks, and other related settings. It supports both Docker and
        Podman as container engines.

    .PROPERTIES
        AnyRemoved
            A boolean indicating whether any containers were removed during the operation.

        Command
            The container engine command to use (Docker or Podman).

        Containers
            A list of container definitions to be created and managed.

        DebugEnabled
            A boolean indicating whether debug mode is enabled.

        DnsServers
            A list of DNS server IP addresses to use for the containers.

        DryRunEnabled
            A boolean indicating whether dry-run mode is enabled.

        Networks
            A list of network definitions to be created and managed.

        NoStartContainers
            A boolean indicating whether to create containers without starting them.

        OnlyCreateContainers
            An array of container names to create; if empty, all containers are created.

        PurgeImagesEnabled
            A boolean indicating whether to purge unused images after operations.

        Replacements
            A list of definition replacements to apply to paths and strings.
    #>


    ## AnyRemoved Property.
    [JsonIgnore()]
    [Boolean] $AnyRemoved = $false;

    ## Command Property.
    [JsonPropertyName('command')]
    [JsonRequired()]
    [UpdateContainersEngineEnum] $Command;

    ## Containers Property.
    [JsonPropertyName('container')]
    [JsonRequired()]
    [List[UpdateContainersContainerDefinition]] $Containers = [List[UpdateContainersContainerDefinition]]::new();

    ## Debug Property.
    [JsonPropertyName('debug')]
    [Boolean] $DebugEnabled = $false;

    ## DNS Servers Property.
    [JsonPropertyName('dns')]
    [List[String]] $DnsServers = [List[String]]::new();

    ## Dry-Run Property.
    [JsonPropertyName('dryRun')]
    [Boolean] $DryRunEnabled = $false;

    ## Networks Property.
    [JsonPropertyName('network')]
    [List[UpdateContainersNetworkDefinition]] $Networks = [List[UpdateContainersNetworkDefinition]]::new();

    ## No Start Containers Property.
    [JsonPropertyName('noStart')]
    [Boolean] $NoStartContainers = $false;

    ## Only Create Containers Property.
    [JsonPropertyName('only')]
    [String[]] $OnlyCreateContainers = @();

    ## Purge Images Property.
    [JsonPropertyName('purgeImages')]
    [Boolean] $PurgeImagesEnabled = $false;

    ## Replacements Property
    [JsonPropertyName('replacement')]
    [List[UpdateContainersReplacementDefinition]] $Replacements = [List[UpdateContainersReplacementDefinition]]::new();

    ## Create Containers Method.
    [Void] CreateContainers()
    {
        <#
        .NAME
            CreateContainers

        .SYNOPSIS
            Creates containers based on the definitions provided.

        .DESCRIPTION
            This method iterates over the container definitions and creates each container
            if it does not already exist. It uses the specified container engine command
            (Docker or Podman) to perform the creation. If dry-run mode is enabled, it will
            only simulate the creation without actually executing the commands.
        #>


        ## Iterate over the containers.
        foreach ($container in $this.Containers)
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? `
                $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim();

            ## Execute our existence command results.
            $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}");

            ## Localize the existence of the container.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length
            } else { 0 };

            ## Check if the container exists.
            if (0 -eq $exists -or $true -eq $this.DryRunEnabled)
            {
                ## Create the container.
                Write-Host "Creating container '$($container.Name.Trim())'." -ForegroundColor Green;

                ## Create the container in the local registry.
                $this.ExecuteCommand("${engine} $($container.ToCommandLine($this.Command, $this.DnsServers, `
                    $this.Replacements, $this.DryRunEnabled))"
);
            }
        }
    }

    ## Create Networks Method.
    [Void] CreateNetworks()
    {
        <#
        .NAME
            CreateNetworks

        .SYNOPSIS
            Creates networks based on the definitions provided.

        .DESCRIPTION
            This method iterates over the network definitions and creates each network
            if it does not already exist. It uses the specified container engine command
            (Docker or Podman) to perform the creation. If dry-run mode is enabled, it will
            only simulate the creation without actually executing the commands.
        #>


        ## Iterate over the distinct networks.
        foreach ($network in ($this.Networks | Group-Object -Property 'Name' | ForEach-Object {
            $_.Group | Select-Object -First 1
        }))
        {
            ## Reconcile the engine command between the definition and the network.
            [String] $engine = $($null -ne $network.Command -and "" -ne $network.Command.Trim() ? `
                $network.Command : $this.Command).ToString().ToLower().Trim();

            ## Localize our command results.
            $commandResults = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}");

            ## Localize the existence of the network.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $network.Name.Trim() }).Length } else { 0 };

            ## Check if the network exists.
            if (0 -eq $exists -or $true -eq $this.DryRunEnabled)
            {
                ## Create the network.
                Write-Host "Creating network '$($network.Name.Trim())'." -ForegroundColor Green;

                ## Create the network in the local registry.
                $this.ExecuteCommand("${engine} $($network.ToCommandLine())");
            }
        }
    }

    ## Execute Command Method.
    [Void] ExecuteCommand([String] $command)
    {
        <#
        .NAME
            ExecuteCommand

        .SYNOPSIS
            Executes a command line string using Invoke-Expression.

        .DESCRIPTION
            This method takes a command line string as input and executes it using
            the Invoke-Expression cmdlet. It supports dry-run and debug modes, where
            the command is only displayed without execution.

        .PARAMETERS
            command
                The command line string to be executed.
        #>


        ## Check for a dry-run.
        if ($this.DebugEnabled -or $this.DryRunEnabled)
        {
            ## Output the command that would be executed.
            Write-Host "Invoke-Expression -Command `"$($command.Trim());`" | Out-Null;" -ForegroundColor Yellow;

            ## Write our buffer line.
            Write-Host "" -ForegroundColor Yellow;
        }

        ## Check to see if we should execute the command then execute it.
        if ($false -eq $this.DryRunEnabled) { Invoke-Expression -Command "${command};" | Out-Null; }
    }

    ## Link Containers to Routes Method.
    [Void] LinkContainersToRoutes()
    {
        <#
        .NAME
            LinkContainersToRoutes

        .SYNOPSIS
            Links containers to their defined networks based on the definitions provided.

        .DESCRIPTION
            This method iterates over the network definitions and links each container
            to its defined networks if it is not already linked. It uses the specified
            container engine command (Docker or Podman) to perform the linking. If dry-run
            mode is enabled, it will only simulate the linking without actually executing
            the commands.
        #>


        ## Iterate over the containers whose network(s) contain routes.
        foreach ($container in ($this.Containers | Where-Object {
            $null -ne $_.Networks -and $_.Networks.Count -gt 0 -and ($_.Networks | Where-Object {
                 $null -ne $_.Routes -and $_.Routes.Count -gt 0
            }).Count -gt 0
        }))
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = ($null -ne $container.Command ? `
                $container.Command : $this.Command).ToString().ToLower().Trim();

            ## Localize our command results.
            $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}");

            ## Check for the existence of the container.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length
            } else { 0 };

            ## Ensure the container exists.
            if (0 -eq $exists)
            {
                ## Write the message to the host, letting them know we're skipping it.
                Write-Host "Container '$($container.Name.Trim())' does not exist, skipping route link(s)." -ForegroundColor Gray;

                ## We're done with this iteration.
                continue;
            }

            ## Define our interface iterator.
            [Int32] $interface = 0;

            ## Iterate over the networks for the container that contain routes.
            foreach ($network in ($container.Networks | Where-Object {
                $null -ne $_.Routes -and $_.Routes.Count -gt 0
            }))
            {
                ## Localize our command results.
                $commandResults = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}");

                ## Check for the existence of the network.
                $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                    ($commandResults.Trim() | Where-Object { $_.Trim() -eq $network.Name.Trim() }).Length
                } else { 0 };

                ## Ensure the network exists.
                if (0 -eq $exists)
                {
                    ## Write the message to the host, letting them know we're skipping it.
                    Write-Host "Network '$($network.Name.Trim())' does not exist, skipping route link(s) for container '$($container.Name.Trim())'." -ForegroundColor Gray;

                    ## Increment the interface iterator.
                    ++$interface;

                    ## We're done with this iteration.
                    if (-not $this.DebugEnabled) { continue; };
                }

                ## Iterate over the routes for the network.
                foreach ($route in $network.Routes)
                {
                    ## Write the message to the host, letting them know we're linking it.
                    Write-Host -ForegroundColor Green `
                        "Routing container '$($container.Name.Trim())' to '$($route.Destination.Trim())' via '$($route.Gateway.Trim())' on 'eth$($interface)'.";

                    ## Execute the command to link the container to the physical network if necessary.
                    $this.ExecuteCommand("${engine} exec --privileged '$($container.Name.Trim())' ```n`t$($route.ToCommandLine("eth${interface}"))");
                }
            }
        }
    }

    ## Link Containers to Physical Network Method.
    [Void] LinkContainersToSecondaryNetworks()
    {
        <#
        .NAME
            LinkContainersToSecondaryNetworks

        .SYNOPSIS
            Links containers to their secondary networks based on the definitions provided.

        .DESCRIPTION
            This method iterates over the container definitions and links each container
            to its secondary networks if it is not already linked. It uses the specified
            container engine command (Docker or Podman) to perform the linking. If dry-run
            mode is enabled, it will only simulate the linking without actually executing
            the commands.
        #>


        ## Iterate over the containers.
        foreach ($container in $this.Containers)
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = ($null -ne $container.Command ? `
                $container.Command : $this.Command).ToString().ToLower().Trim();

            ## Iterate over the networks for the container, skipping the first one as it's the primary network.
            foreach ($network in ($container.Networks | Select-Object -Skip 1))
            {
                ## Localize our command results.
                $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}");

                ## Check to see if the container is already linked to the network.
                [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                    ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length
                } else { 0 };

                ## Check if the container is already linked to the network.
                if (0 -ne $exists)
                {
                    ## Write the message to the host, letting them know we're skipping it.
                    Write-Host "Container '$($container.Name.Trim())' is already linked to network '$($network.Name.Trim())', skipping." -ForegroundColor Gray;

                    ## We're done with this iteration.
                    if ($false -eq $this.DryRunEnabled) { continue; };
                }

                ## Write the message to the host, letting them know we're linking it.
                Write-Host "Linking container '$($container.Name.Trim())' to network '$($network.Name.Trim())'." -ForegroundColor Green;

                ## Generate and execute the command to link the container to the physical network if necessary.
                $this.ExecuteCommand("${engine} $($network.ToCommandLine($engine, $true, $container, $this.DryRunEnabled))");
            }
        }
    }

    ## Prune Method.
    [Void] Prune()
    {
        <#
        .NAME
            Prune

        .SYNOPSIS
            Prunes unused containers, images, networks, and volumes.

        .DESCRIPTION
            This method prunes unused containers, images, networks, and volumes
            from the container engine's local registry. It uses the specified container
            engine command (Docker or Podman) to perform the pruning. If dry-run mode is enabled,
            it will only simulate the pruning without actually executing the commands.
        #>


        ## Write the message to the host, letting them know we're pruning containers.
        Write-Host "Pruning containers." -ForegroundColor Gray;

        ## Prune any stopped containers.
        $this.ExecuteCommand("$($this.Command.ToString().ToLower()) container prune -f");

        ## Write the message to the host, letting them know we're pruning images.
        Write-Host "Pruning images." -ForegroundColor Gray;

        ## Prune any dangling images.
        $this.ExecuteCommand("$($this.Command.ToString().ToLower()) image prune -a -f");

        ## Write the message to the host, letting them know we're pruning networks.
        Write-Host "Pruning networks." -ForegroundColor Gray;

        ## Prune any unused networks.
        $this.ExecuteCommand("$($this.Command.ToString().ToLower()) network prune -f");

        ## Write the message to the host, letting them know we're pruning volumes.
        Write-Host "Pruning volumes." -ForegroundColor Gray;

        ## Prune any unused volumes.
        $this.ExecuteCommand("$($this.Command.ToString().ToLower()) volume prune -f");
    }

    ## Pull Images Method.
    [Void] PullImages()
    {
        <#
        .NAME
            PullImages

        .SYNOPSIS
            Pulls the latest images for the defined containers.

        .DESCRIPTION
            This method iterates over the unique container images defined in the
            container definitions and pulls the latest version of each image from
            the remote registry. It uses the specified container engine command
            (Docker or Podman) to perform the pull operation. If dry-run mode is enabled,
            it will only simulate the pull without actually executing the commands.
        #>


        ## Iterate over the unique container images.
        foreach ($container in ($this.Containers | Select-Object -Property Image -Unique))
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? `
                $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim();

            ## Pull the image from the remote registry.
            Write-Host "Pulling image '$($container.Image.Trim())'." -ForegroundColor Cyan;

            ## Pull the latest image into the local registry.
            $this.ExecuteCommand("${engine} pull --quiet '$($container.Image.Trim())'");
        }
    }

    ## Purge Containers Method.
    [Void] PurgeContainers()
    {
        <#
        .NAME
            PurgeContainers

        .SYNOPSIS
            Purges containers based on the definitions provided.

        .DESCRIPTION
            This method iterates over the container definitions and removes each container
            if it exists. It uses the specified container engine command (Docker or Podman)
            to perform the removal. If dry-run mode is enabled, it will only simulate the
            removal without actually executing the commands.
        #>


        ## Iterate over the containers.
        foreach ($container in $this.Containers)
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? `
                $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim();

            ## Localize our command results.
            $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}");

            ## Localize the existence of the container.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length
            } else { 0 };

            ## Check if the container exists.
            if (0 -ne $exists -or $true -eq $this.DryRunEnabled)
            {
                ## Stop the container.
                Write-Host "Stopping container '$($container.Name.Trim())'." -ForegroundColor Gray;

                ## Stop the container if it's running.
                $this.ExecuteCommand("${engine} container stop '$($container.Name.Trim())'");

                ## Remove the container.
                Write-Host "Removing container '$($container.Name.Trim())'." -ForegroundColor Gray;

                ## Remove the container from the local registry.
                $this.ExecuteCommand("${engine} container rm -f '$($container.Name.Trim())'");

                ## Reset the AnyRemoved flag.
                $this.AnyRemoved = $true;
            }
        }
    }

    ## Purge Images Method.
    [Void] PurgeImages()
    {
        <#
        .NAME
            PurgeImages

        .SYNOPSIS
            Purges images based on the definitions provided.

        .DESCRIPTION
            This method iterates over the unique container images defined in the
            container definitions and removes each image if it exists. It uses the
            specified container engine command (Docker or Podman) to perform the removal.
        #>


        ## Iterate over the distinct container images.
        foreach ($container in ($this.Containers | Select-Object -Property Image -Unique))
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? `
                $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim();

            ## Localize our command results.
            $commandResults = (& "${engine}" image ls --filter "Reference=$($container.Image.Trim())" `
                --format "{{.Repository}}:{{.Tag}}");

            ## Localize the existence of the image.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Image.Trim() }).Length
            } else { 0 };

            ## Check if the image exists.
            if (0 -ne $exists -or $true -eq $this.DryRunEnabled)
            {
                ## Remove the image.
                Write-Host "Removing image '$($container.Image.Trim())'." -ForegroundColor Gray;

                ## Remove the image from the local registry.
                $this.ExecuteCommand("${engine} image rm -f '$($container.Image.Trim())'");

                ## Reset the AnyRemoved flag.
                $this.AnyRemoved = $true;
            }
        }
    }

    ## Purge Networks Method.
    [Void] PurgeNetworks()
    {
        <#
        .NAME
            PurgeNetworks

        .SYNOPSIS
            Purges networks based on the definitions provided.

        .DESCRIPTION
            This method iterates over the network definitions and removes each network
            if it exists. It uses the specified container engine command (Docker or Podman)
            to perform the removal. If dry-run mode is enabled, it will only simulate the
            removal without actually executing the commands.
        #>


        ## Iterate over the networks.
        foreach ($network in $this.Networks | Group-Object -Property 'Name' | ForEach-Object {
            $_.Group | Select-Object -First 1
        })
        {
            ## Reconcile the engine command between the definition and the network.
            [String] $engine = $($null -ne $network.Command -and "" -ne $network.Command.Trim() ? `
                $network.Command.ToString() : $this.Command.ToString()).ToLower().Trim();

            ## Localize our command results.
            $commandResults = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}");

            ## Localize the existence of the network.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $network.Name.Trim() }).Length } else { 0 };

            ## Check if the network exists.
            if (0 -ne $exists -or $true -eq $this.DryRunEnabled)
            {
                ## Remove the network.
                Write-Host "Removing network '$($network.Name.Trim())'." -ForegroundColor Gray;

                ## Remove the network from the local registry.
                $this.ExecuteCommand("${engine} network rm '$($network.Name.Trim())'");

                ## Reset the AnyRemoved flag.
                $this.AnyRemoved = $true;
            }
        }
    }

    ## Run Method.
    [Void] Run([Boolean] $dryRun, [Boolean] $noStart, [String[]] $only, [Boolean] $printCommands,
               [Boolean] $purgeImages, [HashTable] $replacements)
    {
        <#
        .NAME
            Run

        .SYNOPSIS
            Executes the container engine definition to manage containers and networks.

        .DESCRIPTION
            This method orchestrates the entire process of managing containers and networks
            based on the definitions provided. It handles purging existing resources, pulling
            images, creating networks and containers, linking containers to networks, and starting
            containers. It also supports dry-run and debug modes, as well as path replacements.

        .PARAMETERS
            dryRun
                A boolean indicating whether to perform a dry-run (no actual changes).

            noStart
                A boolean indicating whether to skip starting the containers after creation.

            only
                An array of container names to limit the operation to specific containers.

            printCommands
                A boolean indicating whether to print the commands that would be executed.

            purgeImages
                A boolean indicating whether to purge unused images after operations.

            replacements
                A hashtable of key-value pairs for path and string replacements.
        #>


        ## Reconcile the debug flag between the definition file and the command line parameter.
        $this.DebugEnabled = ($printCommands -or $this.DebugEnabled);

        ## Reconcile the dry-run flag between the definition file and the command line parameter.
        $this.DryRunEnabled = ($dryRun -or $this.DryRunEnabled);

        ## Reconcile the no-start flag between the definition file and the command line parameter.
        $this.NoStartContainers = ($noStart -or $this.NoStartContainers);

        ## Reconcile the only-create flag between the definition file and the command line parameter.
        $this.OnlyCreateContainers += $only;

        ## Reconcile the purge-images flag between the definition file and the command line parameter.
        $this.PurgeImagesEnabled = ($purgeImages -or $this.PurgeImagesEnabled);

        ## Sort and trim the only list and ensure uniqueness.
        if ($this.OnlyCreateContainers.Count -gt 0)
        {
            ## Reset the containers in the instance to the subset of only containers.
            $this.Containers = $this.Containers | Where-Object {
                $this.OnlyCreateContainers -contains $_.Name.Trim()
            };
        }

        ## Check for dry-run or debug mode.
        if ($this.DryRunEnabled -or ($this.OnlyCreateContainers.Count -gt 0) -or $this.DebugEnabled -or $this.PurgeImagesEnabled)
        {
            ## Write the buffer space.
            Write-Host "`n" -ForegroundColor Magenta;
        }

        ## Check for a dry run then write the message to the host.
        if ($this.DryRunEnabled) {
            Write-Host "[DRY-RUN]: No changes will be made." -ForegroundColor Magenta; }

        ## Check for no container start then write the message to the host.
        if ($this.NoStartContainers) {
            Write-Host "[NO-START]: Containers will not be started after creation nor will routes be linked to them." `
            -ForegroundColor Magenta; }

        ## Check for only creating specific containers then write the message to the host.
        if ($this.OnlyCreateContainers.Count -gt 0) {
            Write-Host "[ONLY]: Only the following containers will be processed: `n`t`t`t - $(($this.OnlyCreateContainers | Sort-Object -Unique) -join "`n`t`t`t - ")`n" `
            -ForegroundColor Magenta; }

        ## Check for debug mode then write the message to the host.
        if ($this.DebugEnabled) {
            Write-Host "[PRINT-CMD]: Commands will be printed before execution." -ForegroundColor Magenta; }

        ## Check for image purging then write the message to the host.
        if ($this.PurgeImagesEnabled) {
            Write-Host "[PURGE-IMG]: All images will be removed before pulling new ones." -ForegroundColor Magenta; }

        ## Check for dry-run or debug mode.
        if ($this.DryRunEnabled -or ($this.OnlyCreateContainers.Count -gt 0) -or $this.DebugEnabled -or $this.PurgeImagesEnabled)
        {
            ## Write the buffer space.
            Write-Host "`n" -ForegroundColor Magenta;
        }

        ## Iterate over the replacements from the command line parameter.
        foreach ($key in $Replacements.Keys)
        {
            ## Add the replacement to the definition replacements.
            $this.Replacements[$key.Trim()] = $replacements[$key].Trim();
        }

        ## Ensure the containers are unique by name.
        $this.Containers = $this.Containers | Group-Object -Property 'Name' | ForEach-Object {
            $_.Group | Select-Object -First 1
        };

        ## Purge the containers.
        $this.PurgeContainers();

        ## Purge the networks.
        $this.PurgeNetworks();

        ## Check for image purging.
        if ($this.PurgeImagesEnabled -or $this.DryRunEnabled)
        {
            ## Purge the images.
            $this.PurgeImages();
        }

        ## Pull the images.
        $this.PullImages();

        ## Create the networks.
        $this.CreateNetworks();

        ## Create the containers.
        $this.CreateContainers();

        ## Link the containers to their secondary networks.
        $this.LinkContainersToSecondaryNetworks();

        ## Check to see if we should start the containers.
        if (-not $this.NoStartContainers -or $this.DryRunEnabled)
        {
            ## Start the containers.
            $this.StartContainers();

            ## Link the containers to their routes.
            $this.LinkContainersToRoutes();
        }

        ## Cleanup any dangling resources.
        $this.Prune();
    }

    ## StartContainers Method.
    [Void] StartContainers()
    {
        <#
        .NAME
            StartContainers

        .SYNOPSIS
            Starts containers based on the definitions provided.

        .DESCRIPTION
            This method iterates over the container definitions and starts each container
            if it exists. It uses the specified container engine command (Docker or Podman)
            to perform the start operation. If dry-run mode is enabled, it will only simulate
            the start without actually executing the commands.
        #>


        ## Iterate over the containers.
        foreach ($container in $this.Containers)
        {
            ## Reconcile the engine command between the definition and the container.
            [String] $engine = $($null -ne $container.Command -and "" -ne $container.Command.Trim() ? `
                $container.Command.ToString() : $this.Command.ToString()).ToLower().Trim();

            ## Localize our command results.
            $commandResults = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}");

            ## Localize the existence of the container.
            [Int32] $exists = if ($null -ne $commandResults -and "" -ne $commandResults.Trim()) {
                ($commandResults.Trim() | Where-Object { $_.Trim() -eq $container.Name.Trim() }).Length } else { 0 };

            ## Check if the container exists.
            if (0 -ne $exists -or $true -eq $this.DryRunEnabled)
            {
                ## Start the container.
                Write-Host "Starting container '$($container.Name.Trim())'." -ForegroundColor Green;

                ## Start the container in the local registry.
                $this.ExecuteCommand("${engine} container start '$($container.Name.Trim())'");
            }
        }
    }
};

## Definition Replacement Class.
[NoRunspaceAffinity()]
class UpdateContainersNetworkDefinition
{
    <#
    .NAME
        UpdateContainersNetworkDefinition

    .SYNOPSIS
        Class defining a network configuration for container engines.

    .DESCRIPTION
        This class represents the configuration details for a network that can be
        created and managed by container engines like Docker and Podman.

    .PROPERTIES
        Bridge
            The name of the bridge interface to use for the network.

        Command
            The container engine command to use (Docker or Podman).

        Driver
            The network driver to use (e.g., bridge, macvlan).

        Interface
            The name of the network interface to bind to (if applicable).

        IPv4Prefix
            The IPv4 subnet prefix for the network (e.g., 192.168.1).

        IPv6Prefix
            The IPv6 subnet prefix for the network (e.g., fd00:abcd:1234:1).

        Name
            The name of the network.
    #>


    ## Bridge Property.
    [AllowNull()]
    [JsonPropertyName('bridge')]
    [String] $Bridge = $null;

    ## Command Property.
    [AllowNull()]
    [JsonPropertyName('command')]
    [Nullable[UpdateContainersEngineEnum]] $Command = $null;

    ## Driver Property.
    [AllowNull()]
    [JsonPropertyName('driver')]
    [String] $Driver = "bridge";

    ## Interface Name Property.
    [AllowNull()]
    [JsonPropertyName('interface')]
    [String] $Interface = $null;

    ## IPv4Prefix Property.
    [JsonPropertyName('prefix')]
    [JsonRequired()]
    [String] $IPv4Prefix;

    ## IPv6 Property.
    [AllowNull()]
    [JsonPropertyName('prefix6')]
    [String] $IPv6Prefix;

    ## Name Property.
    [JsonPropertyName('name')]
    [JsonRequired()]
    [String] $Name;

    ## ToCommandLine Method.
    [String] ToCommandLine()
    {
        <#
        .NAME
            ToCommandLine

        .SYNOPSIS
            Generates the command line string to create the network.

        .DESCRIPTION
            This method constructs the appropriate command line string to create
            a network using the specified container engine, based on the properties
            defined in the NetworkDefinition class.

        .OUTPUTS
            String
                The command line string to create the network.
        #>


        ## Define our list of non-namable interface drivers.
        [String[]] $unnamedDrivers = @("macvlan", "ipvlan");

        ## Define our command line string.
        [String] $commandLine = "network create ```n`t--driver '$($this.Driver.ToLower().Trim())'";

        ## Check for an IPv4 prefix.
        if ($null -ne $this.IPv4Prefix -and "" -ne $this.IPv4Prefix.Trim())
        {
            ## Add the IPv6 gateway and subnet to the command line string.
            $commandLine = "${commandLine} ```n`t--gateway '$($this.IPv4Prefix.Trim()).1' ```n`t--ipv4 ```n`t--subnet '$($this.IPv4Prefix.Trim()).0/24'";
        }

        ## Check for an IPv6 prefix
        if ($null -ne $this.IPv6Prefix -and "" -ne $this.IPv6Prefix.Trim())
        {
            ## Append the IPv6 to the command line string.
            $commandLine = "${commandLine} ```n`t--gateway '$($this.IPv6Prefix.Trim())::1' ```n`t--ipv6 ```n`t--subnet '$($this.IPv6Prefix.Trim())::/64'";
        }

        ## Check for a bridge name.
        if ($null -ne $this.Bridge -and "" -ne $this.Bridge.Trim() -and ($unnamedDrivers -Contains $this.Driver.ToLower().Trim()))
        {
            ## Append the bridge name to the command line string.
            $commandLine = "${commandLine} ```n`t--opt 'parent=$($this.Bridge.Trim() )'";
        }

        ## Check for an interface name.
        if ($null -ne $this.Interface -and "" -ne $this.Interface.Trim() -and -not ($unnamedDrivers -Contains $this.Driver.ToLower().Trim()))
        {
            ## Append the interface name to the command line string.
            $commandLine = "${commandLine} ```n`t--opt 'com.docker.network.bridge.name=$($this.Interface.Trim() )'";
        }

        ## We're done, return the command line string.
        return "${commandLine} ```n`t'$($this.Name.Trim())'".Trim();
    }
};

## Definition Replacement Class.
[NoRunspaceAffinity()]
class UpdateContainersReplacementDefinition
{
    <#
    .NAME
        UpdateContainersReplacementDefinition

    .SYNOPSIS
        Class defining a key-value replacement for paths and strings.

    .DESCRIPTION
        This class represents a key-value pair used for replacing placeholders
        in strings or paths, with an option to treat the value as a file system path.

    .PROPERTIES
        Key
            The placeholder key to be replaced in the target string.

        Path
            A boolean indicating whether the value should be treated as a file system path.

        Value
            The value to replace the key with, which can be a string or a path.
    #>


    ## Key Property.
    [JsonPropertyName('key')]
    [JsonRequired()]
    [String] $Key;

    ## The Path Property.
    [JsonPropertyName('path')]
    [Boolean] $Path = $false;

    ## Value Property.
    [JsonPropertyName('value')]
    [JsonRequired()]
    [String] $Value;

    ## Replace Method.
    [String] Replace([String] $value)
    {
        <#
        .NAME
            Replace

        .SYNOPSIS
            Replaces occurrences of the key in the provided value with the defined value.

        .DESCRIPTION
            This method searches for occurrences of the key, formatted as `%{key}%`,
            in the provided string and replaces them with the defined value. If the
            Path property is set to true, it treats the value as a file system path
            and resolves it accordingly.

        .PARAMETERS
            value
                The string in which to perform the replacement.
        #>


        ## Ensure we have a value to replace.
        if ($null -eq $value -or "" -eq $value.Trim()) { return $value; }

        ## Check for a path replacement.
        if ($this.Path)
        {
            ## Check check the operation system.
            if ([Environment]::OSVersion.Platform -eq [PlatformID]::Unix)
            {
                ## Replace any drive letters with empty in the value.
                $this.Value = $($this.Value.Trim() -replace "^[a-zA-Z]:", "").Trim();

                ## Replace any Windows-style backslashes with slashes in the value.
                $this.Value = $($this.Value.Trim() -replace "\\", "/").Trim();

                ## Replace any double slashes with a single slash in the value.
                $this.Value = $($this.Value.Trim() -replace "//", "/").Trim();
            }
            else
            {
                ## Replace any Unix-style slashes with backslashes in the value.
                $this.Value = $($this.Value.Trim() -replace "/", "\\").Trim();

                ## Replace any double backslashes with a single backslash in the value.
                $this.Value = $($this.Value.Trim() -replace "\\\\", "\\").Trim();

                ## Check for a leading backslash in the value.
                if ($this.Value.StartsWith("\"))
                {
                    ## Prepend the value with the current drive letter.
                    $this.Value = "$($([Path]::GetPathRoot((Get-Location).Path).Trim()))$($this.Value.Trim())".Trim();
                }
            }

            ## Resolve the path to ensure it exists.
            $resolvedPath = (Resolve-Path -Path $this.Value.Trim() -ErrorAction SilentlyContinue);

            ## Resolve the full path from the value.
            $this.Value = ($null -ne $resolvedPath ? $resolvedPath.Path.Trim() : $this.Value.Trim());
        }

        ## Return the replaced value.
        return ($value.Trim() -replace "%{$($this.Key.Trim())}%", $this.Value.Trim());
    }
};

## Define the types we want to export.
[Type[]] $exportableTypes = @(
    [UpdateContainersEngineEnum]
    [UpdateContainersIpAddressMethodEnum]
    [UpdateContainersContainerDefinition]
    [UpdateContainersContainerDeviceDefinition]
    [UpdateContainersContainerHealthCheckDefinition]
    [UpdateContainersContainerMountDefinition]
    [UpdateContainersContainerNetworkDefinition]
    [UpdateContainersContainerNetworkRouteDefinition]
    [UpdateContainersContainerPublishDefinition]
    [UpdateContainersDefinition]
    [UpdateContainersNetworkDefinition]
    [UpdateContainersReplacementDefinition]
);

## Get the non-public TypeAccelerators class for defining new accelerators.
$typeAcceleratorsClass = [PSObject].Assembly.GetType('System.Management.Automation.TypeAccelerators')

## Add type accelerators for every exportable type.
$existingTypeAccelerators = $typeAcceleratorsClass::Get

## Iterate over the types we want to export.
foreach ($type in $exportableTypes) {

    # !! $TypeAcceleratorsClass::Add() quietly ignores attempts to redefine existing
    # !! accelerators with different target types, so we check explicitly.
    $existing = $existingTypeAccelerators[$type.FullName]

    ## Check for an existing type accelerator with a different type.
    if ($null -ne $existing -and $existing -ne $type) {

        ## We're done, throw an error.
        throw "Unable to register type accelerator [$($type.FullName)], because it is already defined with a different type ([$existing])."
    }

    ## Add the type accelerator for the type.
    $typeAcceleratorsClass::Add($type.FullName, $type)
}

## Export our module functions.
Export-ModuleMember -Function 'Get-ContainerDefinitionFilePath';
Export-ModuleMember -Function 'Get-ContainerDefinitionJsonSerializerOptions';
Export-ModuleMember -Function 'Update-ContainersWithDefinitionFile';