# 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 ```