# Overview

The deployment scripts are structured in **three tiers**, loosely analogous to a presentation/domain/data layered architecture in software. This separation exists to:

  - Keep ESP-specific knowledge isolated to higher tiers
  - Allow Tier 3 functions to be tested in isolation
  - Allow the scripts to run independently of AzDO
  - Make incremental automation easier (replace manual prompts one function at a time)

# Entry Point — How to Call the Scripts

``` powershell
Invoke-Deployment.ps1 -Customer ROMAC -Environment DEV -Stages PREDEPLOY,DEPLOY,POSTDEPLOY
```

You can pass any combination of stages. For example, to only validate:

``` powershell
Invoke-Deployment.ps1 -Customer ROMAC -Environment DEV -Stages VALIDATE
```

# Tier 1 — Entry Point (Presentation Layer)

## Responsibility

  - The **single entry point** to the whole deployment system
  - Loads and validates the manifest (see [[ESP Manifest]])
  - Loads all required PowerShell modules (Tier 2, Tier 3, helpers)
  - Calls the appropriate Tier 2 functions for the requested stages
  - Passes the manifest object through to Tier 2

## Key Characteristics

  - One script: `Invoke-Deployment.ps1`
  - Does NOT contain deployment logic itself
  - Acts as a wiring layer only

## Pseudocode

``` powershell
param(
    [string]$Customer,
    [string]$Environment,
    [string[]]$Stages
)

$manifest = Load-Manifest -Customer $Customer -Environment $Environment
Assert-ManifestValid -Manifest $manifest

Import-Module ./Tier2/PreDeployment.psm1
Import-Module ./Tier2/Deployment.psm1
Import-Module ./Tier2/PostDeployment.psm1
Import-Module ./Helpers/Logging.psm1

foreach ($stage in $Stages) {
    switch ($stage) {
        "PREDEPLOY"   { Invoke-PreDeployment  -Manifest $manifest }
        "DEPLOY"      { Invoke-Deployment     -Manifest $manifest }
        "POSTDEPLOY"  { Invoke-PostDeployment -Manifest $manifest }
    }
}
```

# Tier 2 — Orchestration Layer (Domain Layer)

## Responsibility

  - One script per deployment stage (e.g. `PreDeployment.psm1`, `Deployment.psm1`)
  - Contains the **logic and sequencing** of what needs to happen
  - Decides **which** Tier 3 functions to call and in what order
  - Reacts to return values from Tier 3 (e.g. if a function fails, abort or retry)

## Key Constraints

  - **Does NOT** pass the manifest to Tier 3 — all required data is extracted and passed as explicit parameters
  - **Does NOT** directly perform any action itself — delegates entirely to Tier 3
  - **Does** have knowledge of ESP and what a deployment involves

## Why "Does Not Pass Manifest to Tier 3"?

Tier 3 functions are designed to be generic and reusable. If they accepted a manifest object, they'd need to know about its structure — breaking isolation. Instead, Tier 2 extracts what Tier 3 needs:

``` powershell
# BAD — passes the whole manifest (couples Tier 3 to manifest structure)
Stop-WindowsService -Manifest $manifest

# GOOD — extracts what's needed and passes explicitly
Stop-WindowsService -ServiceName $manifest.Services.DLService.Name `
                    -ServerName  $manifest.Servers.AppServer
```

## Scripts in This Tier

| Script                | Stage it covers     |
| --------------------- | ------------------- |
| `Validation.psm1`     | VALIDATE stage      |
| `Prerequisites.psm1`  | PREREQUISITES stage |
| `PreDeployment.psm1`  | PREDEPLOY stage     |
| `Deployment.psm1`     | DEPLOY stage        |
| `PostDeployment.psm1` | POSTDEPLOY stage    |

# Tier 3 — Functional Layer (Workers)

## Responsibility

  - Small, **single-purpose** functions
  - No knowledge of ESP, the manifest, or deployment context
  - Accept all required data as **explicit parameters**
  - Can be tested in isolation with mock data

## Key Characteristics

  - Examples: `Stop-WindowsService`, `Invoke-SqlScript`, `Copy-Files`, `Get-RemoteSession`, `Send-CitrixNotification`
  - Initially implemented as **manual prompt wrappers** — the function prints instructions to a human and waits for confirmation
  - Will be replaced with real automation incrementally

## The Manual Prompt Pattern (Current State)

``` powershell
function Stop-WindowsService {
    param([string]$ServiceName, [string]$ServerName)

    # Current implementation — prompts a human
    $response = Invoke-ManualPrompt -Message "Please stop service '$ServiceName' on '$ServerName', then press Enter."
    return $response
}
```

## Target Implementation (Automated)

``` powershell
function Stop-WindowsService {
    param([string]$ServiceName, [string]$ServerName)

    $session = Get-RemoteSession -ServerName $ServerName
    Invoke-Command -Session $session -ScriptBlock {
        Stop-Service -Name $using:ServiceName -Force
        (Get-Service -Name $using:ServiceName).WaitForStatus('Stopped', '00:01:00')
    }
}
```

## Exceptions — Functions That Cannot Be Manual Prompts

A small number of Tier 3 functions **must** be implemented for real from the start because they return data the scripts need to function:

| Function            | Why it can't be a manual prompt                       |
| ------------------- | ----------------------------------------------------- |
| `Load-Manifest`     | Returns the manifest object — must actually read file |
| `Get-RemoteSession` | Returns a PS session — must actually connect          |
| `Read-File`         | Returns file contents — must actually read            |

# Helper Modules

In addition to the three tiers, **general-purpose helper modules** exist that can be called from anywhere (Tier 1, 2, or 3):

| Module         | Purpose                                    |
| -------------- | ------------------------------------------ |
| `Logging.psm1` | Write structured log output                |
| `Prompt.psm1`  | The manual prompt mechanism used by Tier 3 |
|                |                                            |

Helpers follow the same rule as Tier 3: **the manifest is never passed to them**.

# Testing Strategy

Because Tier 3 functions are isolated, they can be unit tested without any connection to a real ESP environment:

``` powershell
# Example Pester test for Stop-WindowsService
Describe "Stop-WindowsService" {
    It "calls Invoke-Command with correct service name" {
        Mock Invoke-Command {}
        Mock Get-RemoteSession { return [PSCustomObject]@{ Session = "MockSession" } }

        Stop-WindowsService -ServiceName "DLService" -ServerName "SERVER01"

        Assert-MockCalled Invoke-Command -Times 1
    }
}
```

# Incremental Automation Plan

The architecture is designed so that automation can be added **one function at a time**, without restructuring anything:

1.  Deploy with all Tier 3 functions as manual prompts (guided checklist)
2.  Identify the lowest-risk, simplest functions to automate first
3.  Replace manual prompts with real implementations one by one
4.  Each replacement can be independently tested before deployment

Suggested automation order (rough):

1.  `Stop-WindowsService` / `Start-WindowsService` — well-understood, low risk
2.  `Copy-Files` — straightforward file operations
3.  `Invoke-SqlScript` — once databases are in dacpac-ready state
4.  Citrix-related functions — last, most complex

# Architecture Diagram (Text)

    AzDO Pipeline (YAML)
    │
    └─► Invoke-Deployment.ps1         [TIER 1]
            │  Loads manifest
            │  Loads all modules
            │
            ├─► PreDeployment.psm1    [TIER 2]
            │       ├─► Copy-Files    [TIER 3]
            │       └─► ...
            │
            ├─► Deployment.psm1       [TIER 2]
            │       ├─► Stop-WindowsService    [TIER 3]
            │       ├─► Invoke-SqlScript       [TIER 3]
            │       ├─► Start-WindowsService   [TIER 3]
            │       └─► ...
            │
            └─► PostDeployment.psm1   [TIER 2]
                    ├─► Get-ServiceStatus      [TIER 3]
                    └─► ...
    
            [Helpers: Logging, Prompt — available at any tier]

## Mermaid Diagram (Text)

``` mermaid
graph TD

    %% Tier 1
    subgraph "Tier 1 - Entry Point"
        A[Invoke-Deployment.ps1]
    end

    %% Tier 2
    subgraph "Tier 2 - Deployment Phases"
        B[PreDeployment.psm1]
        C[Deployment.psm1]
        D[PostDeployment.psm1]
    end

    %% Tier 3
    subgraph "Tier 3 - Actions"
        E[Copy-Files]
        F[Stop-WindowsService]
        G[Invoke-SqlScript]
        H[Start-WindowsService]
        I[Get-ServiceStatus]
    end

    %% Helpers
    subgraph "Helpers (All Tiers)"
        J[Logging Helper]
        K[Prompt Helper]
    end

    %% Relationships
    A --> B
    A --> C
    A --> D

    B --> E

    C --> F
    C --> G
    C --> H

    D --> I

    A --> J
    A --> K
```
