Automation standard
Standard scope
Applies to: all HCS-governed repos, base rules; scope overlays noted inline where applicable Domain: automation Status: Active
All automation across HCS projects follows a pipeline-first model. If you run it more than once, it belongs in a pipeline.
Pipeline-first principle
Ad-hoc scripts run locally are fine for one-off operations. Any operation that:
- Needs to run on a schedule
- Needs to run on a trigger (push, PR, merge)
- Touches Azure resources in a repeatable way
- Produces artifacts that other systems consume
- Needs an audit trail
...belongs in a pipeline. Script it, pipeline it, version it.
CI compute — which standard governs what
Three standards divide CI/CD compute along a strict boundary, and no tier table or runner label appears in more than one of them:
- This standard (
automation.md) governs pipeline design — when a pipeline runs, what triggers it, how it handles secrets, and what gates it needs. It does not name specific compute. - CI Runners governs the registered CI runner fleet — the GitHub Actions self-hosted runners and GitLab CI runners that pipelines defined here actually execute on.
- Build Environments governs build hosts you invoke directly — WSL,
bld-01— for work that is not CI-registered at all.
If you're asking "what agent label do I put in my YAML," the answer is in CI Runners, not here.
ADO Pipelines vs. GitHub Actions
Use the right tool for the job:
| Use ADO Pipelines for | Use GitHub Actions for |
|---|---|
| Anything touching Azure (deploy, provision, configure) | Repo-level CI: build, test, lint, publish |
| ADO-integrated workflows (work item updates, test plans) | Publishing packages to NuGet, PyPI, PSGallery |
| Windows-agent tasks that need domain-joined runners | GitHub-native actions (dependabot, code scanning) |
| Pipelines that consume ADO Variable Groups | Open-source repo workflows |
| Anything requiring approval gates | PR validation on public repos |
In mixed scenarios (ADO project with a GitHub mirror), ADO runs the deployment pipeline and GitHub Actions runs CI. They do not overlap.
GitLab integration
This section is TierPoint-scope only — see the platform-boundary note in Governance. HCS-owned projects (CloudGrange, Turner Legacy, Azure Local, etc.) are GitHub/ADO; they never use GitLab, and nothing below applies to them.
GitLab repos live under tierpoint/prodtech (tp-* and others) for TierPoint client-facing work. GitLab CI pipelines target the HCS-tenant shared runner. See CI Runners for what that runner is, how it's registered, and its labels/tags. This avoids any GitLab premium tier dependency for CI.
For event-driven GitLab→ADO integration (when ADO deployment pipelines must be triggered by a GitLab push):
- GitLab webhook fires on push/merge to configured repos
- Webhook target is an Azure Function (consumption plan, free tier) in the HCS subscription
- The Azure Function translates the GitLab event into an ADO pipeline trigger via the ADO REST API
- ADO pipeline runs with the appropriate inputs
Idempotency
Every pipeline and every script called from a pipeline must be idempotent. That means:
- Safe to re-run with the same inputs and produce the same outcome
- No side effects that accumulate on re-run (no duplicate resources, no duplicate entries)
- Existence checks before creates: check if the resource exists before attempting to create it
- Upsert patterns over create-or-fail patterns
If a pipeline fails halfway through, re-running it should finish the job — not create a mess that requires manual cleanup.
Cloud authentication in pipelines
Use credentialless workload identity before retrieving a secret:
- Azure: use a managed identity or an approved federated service connection.
- AWS: the pipeline assumes a least-privilege IAM role through OIDC or another approved workload-federation mechanism. Do not create an IAM user's access key for a pipeline.
- EC2 workloads: attach an IAM instance profile; never install AWS access keys on the host.
- Human AWS administration: use IAM Identity Center through
aws sso login. Human SSO sessions never run unattended inside a pipeline.
The AWS IAM Identity Center directory Region and the workload deployment Region are separate settings. Do not choose a deployment Region merely because it hosts the SSO directory.
Secret handling in pipelines
When a workload cannot use managed identity or federation, the accepted pipeline pattern is:
- The secret lives in the vault owned by the workload's scope.
- A KV-linked ADO Variable Group maps the secret to a pipeline variable name.
- The pipeline references the Variable Group.
- The pipeline consumes the variable as an environment variable or task input.
variables:
- group: platform-prd-secrets
steps:
- task: PowerShell@2
inputs:
filePath: scripts/Invoke-AuthenticatedOperation.ps1
pwsh: true
env:
GITHUB_TOKEN: $(hcs-github-org-pat)Never:
- Inline secrets in pipeline YAML
- Store secret values in pipeline variable definitions (non-KV-linked)
- Echo or log secret values
- Pass secrets as positional arguments to scripts (they appear in process lists)
- Export temporary AWS SSO or assumed-role credentials into Key Vault or a variable group
- Use a human IAM Identity Center session as a pipeline credential
Use named parameters or environment variables to pass secrets to scripts:
- task: PowerShell@2
inputs:
script: scripts/Deploy-Something.ps1
env:
GITHUB_TOKEN: $(hcs-github-org-pat)Pipeline YAML location
ADO pipeline YAML lives under .ado/ in the repo root. This is the single correct location — there is no alternative directory. The live public documentation pipeline (.ado/docs-deploy.yml) is wired to this path, and every ADO pipeline definition in every HCS repo uses it.
- ADO pipeline YAML lives under
.ado/in the repo root - GitHub Actions workflow YAML lives under
.github/workflows/ - Pipeline files are committed to the repo and version-controlled — no pipelines defined only in the UI
repo-root/
├── .ado/
│ ├── build.yml
│ └── deploy.yml
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── publish.ymlManual trigger requirement
Every pipeline must have a manual trigger option in addition to any automatic triggers. In ADO YAML pipelines, workflow_dispatch equivalent is trigger: none + manual run capability, or a scheduled trigger with a manual override. In GitHub Actions, add workflow_dispatch: to every workflow.
# GitHub Actions
on:
push:
branches: [main]
workflow_dispatch: # always include this# ADO
trigger:
branches:
include:
- main
# Manual runs are available via the ADO UI on any pipeline — no extra config neededApproval gates
Any pipeline that deploys to prd must have an approval gate before the deployment stage. Configure in ADO under Environments → Approvals and checks. Self-approval is acceptable for solo projects.
Multi-tool interoperability
Complex deployments combine multiple IaC tools. The rule: all tools read from the same config source. A single infrastructure.yml (or equivalent) drives Bicep parameter files, Terraform variable files, PowerShell scripts, and Ansible playbooks — no value is defined in more than one place.
infrastructure.yml ← single source of truth
│
├── Bicep parameters (generated by Export-BicepParams.ps1)
├── Terraform tfvars (generated by Export-TerraformVars.ps1)
├── Ansible inventory (generated by Export-AnsibleInventory.ps1)
└── PowerShell config (read directly at runtime)Tool selection guidelines:
| Scenario | Preferred tool |
|---|---|
| Azure resource provisioning (VMs, VNets, RGs, KVs) | Bicep |
| Multi-cloud or state-heavy deployments | Terraform |
| OS configuration, role installation, domain join | PowerShell (DSC or Invoke- scripts) |
| Configuration management at scale | Ansible |
| Hybrid: Azure + OS configuration | Bicep (Azure) + PowerShell (OS) — orchestrated by ADO pipeline |
When combining tools, the ADO pipeline is the orchestrator. It runs Bicep first (Azure resources), then PowerShell (OS configuration), using outputs from Bicep (resource IDs, IPs) as inputs to PowerShell.
# ADO pipeline — hybrid deployment
stages:
- stage: Provision
jobs:
- job: BicepDeploy
steps:
- task: AzureCLI@2
inputs:
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
az deployment group create `
--resource-group rg-hcs-platform-prd-eus-01 `
--template-file infra/main.bicep `
--parameters @infra/parameters.json
- stage: Configure
dependsOn: Provision
jobs:
- job: OSConfig
steps:
- task: PowerShell@2
inputs:
filePath: scripts/Invoke-NodeConfiguration.ps1
arguments: -ConfigPath config/variables.ymlNever split what belongs together. If two tools deploy to the same resource, there is an ownership conflict — resolve it by giving one tool ownership of that resource entirely.
Pipeline Notifications
When a pipeline needs to notify a user via email (such as a nightly report or discovery alert):
- Do not use third-party email providers or hardcode SMTP credentials.
- Deploy an Azure Logic App (using an HTTP Request trigger) with an Office 365 Outlook API connection.
- Use Bicep to deploy/update the Logic App from within the pipeline run, outputting its trigger URL (Webhook URL).
- Pass the Webhook URL to the script performing the work, and use
Invoke-RestMethodto POST the payload. - Note: The API Connection must be authorized manually in the Azure Portal the first time it is deployed.
Scope overlays
The rules above are the base (hcs) automation standard: ADO Pipelines for anything touching Azure, GitHub Actions for repo-level CI, and the HCS-tenant shared runner for both. Two scopes run a different primary CI platform and carry overlays that describe it. Overlays add platform-specific detail on top of the base pipeline-first, idempotency, and secret-handling rules — those rules are universal. Overlay content is authored in each scope's own repo and resolved at read time (see docs/standards/_scopes/README.md).
| Scope | CI platform overlay | Where it lives |
|---|---|---|
azurelocal | GitHub Actions as the primary CI surface (release-please flow, GitHub-native workflows), still using the HCS self-hosted runner | AzureLocal platform repo, staged into _scopes/azurelocal/automation.md |
tierpoint-prodtech | GitLab CI as the primary pipeline surface — a 4-stage pipeline (validate / plan / deploy / test) on the HCS-tenant shared Linux runner (see CI Runners for the tag) | prodtech docs repo, staged into _scopes/tierpoint-prodtech/automation.md |
Do not reproduce overlay content here. The base secret-handling model (Key Vault → KV-linked variable group → pipeline variable) and the manual-trigger and idempotency requirements apply in every scope regardless of CI platform.