Class/UpdateContainersDefinition.Class.psm1

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

## Custom Modules.
using module '..\Enum\UpdateContainersEngine.Enum.psm1';
using module '.\UpdateContainersContainerDefinition.Class.psm1';
using module '.\UpdateContainersNetworkDefinition.Class.psm1';
using module '.\UpdateContainersReplacementDefinition.Class.psm1';

## Definition Class.
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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()
        })
        {
            ## 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 the existence of the container.
            [Int32] $exists = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}" `
                | Where-Object { $_ -eq "$($container.Name.Trim())" }).Length;

            ## 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or ($this.Containers | Where-Object {
                $this.OnlyCreateContainers -contains $_.Name.Trim() -and ($_.Networks | Where-Object {
                    $_.Name.ToLower().Trim() -eq $network.Name.ToLower().Trim()
                }).Count -gt 0
            }).Count -gt 0
        } | 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 the existence of the network.
            [Int32] $exists = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}" `
                | Where-Object { $_ -eq "$($network.Name.Trim())" }).Length;

            ## 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 {
                ($this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()) `
                    -and $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();

            ## Check for the existence of the container.
            [Int32] $exists = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}" `
                | Where-Object { $_ -eq "$($container.Name.Trim())" }).Length;

            ## 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
            }))
            {
                ## Check for the existence of the network.
                $exists = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}" `
                    | Where-Object { $_ -eq "$($network.Name.Trim())" }).Length;

                ## 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()
        })
        {
            ## 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))
            {
                ## Check to see if the container is already linked to the network.
                [Int32] $exists = (& "${engine}" network inspect "$($network.Name.Trim())" `
                    --format "{{range .Containers}}{{.Name}} {{end}}" | Where-Object {
                    $_ -eq "$($container.Name.Trim())"
                }).Length;

                ## 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()
        } | 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()
        })
        {
            ## 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 the existence of the container.
            [Int32] $exists = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}" `
                | Where-Object { $_ -eq "$($container.Name.Trim())" }).Length;

            ## 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()
        } | 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 the existence of the image.
            [Int32] $exists = (& "${engine}" image ls --filter "Reference=$($container.Image.Trim())" `
                --format "{{.Repository}}:{{.Tag}}" | Where-Object { $_ -eq "$($container.Image.Trim())" }).Length;

            ## 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or ($this.Containers | Where-Object {
                $this.OnlyCreateContainers -contains $_.Name.Trim() -and ($_.Networks | Where-Object {
                    $_.Name.ToLower().Trim() -eq $network.Name.ToLower().Trim()
                }).Count -gt 0
            }).Count -gt 0
        } | 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 the existence of the network.
            [Int32] $exists = (& "${engine}" network ls --filter "Name=$($network.Name.Trim())" --format "{{.Name}}" `
                | Where-Object { $_ -eq "$($network.Name.Trim())" }).Length;

            ## 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);

        ## Check the only list for a single element with a csv.
        if ($this.OnlyCreateContainers.Count -eq 1 -and $this.OnlyCreateContainers[0].Contains(","))
        {
            ## Split the single element into multiple elements.
            $this.OnlyCreateContainers = $this.OnlyCreateContainers[0].Split(",") | Where-Object {
                $null -ne $_ -and "" -ne $_.Trim() };
        }

        ## Sort and trim the only list and ensure uniqueness.
        if ($Only.Count -gt 0)
        {
            ## Sort and trim the only list and ensure uniqueness.
            $Only = $Only | ForEach-Object { $_.Trim() } | Sort-Object -Unique;
        }

        ## 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 -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();
        }

        ## 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();
        }

        ## Check for any removed resources.
        #if ($this.AnyRemoved)
        #{
        # ## Write the message to the host, letting them know we're waiting for the engine to stabilize.
        # Write-Host "" -ForegroundColor Gray;
        # Write-Host "Waiting [15s] for the engine(s) to stabilize." -ForegroundColor Gray;
        # Write-Host "" -ForegroundColor Gray;

        # ## Wait for a few seconds to allow the engine to stabilize.
        # Start-Sleep -Seconds 15;
        #}

        ## 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 | Where-Object {
            $this.OnlyCreateContainers.Count -eq 0 -or $this.OnlyCreateContainers -contains $_.Name.Trim()
        })
        {
            ## 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 the existence of the container.
            [Int32] $exists = (& "${engine}" ps -a --filter "Name=$($container.Name.Trim())" --format "{{.Names}}" `
                | Where-Object { $_ -eq "$($container.Name.Trim())" }).Length;

            ## 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())'");
            }
        }
    }
};