---
name: azure-devops-pipeline
description: Generates Azure DevOps pipeline YAML using EKS-Pool with nonprod auto-deploy and prod manual approval gate. Always load this skill first, then load the type-specific skill before generating any YAML.
---

## What I do

Guide the generation of a complete `azure-pipelines.yml` file for a self-hosted EKS-Pool Azure DevOps agent pool. I define all shared standards. You MUST also load the appropriate type skill before generating YAML:

- Lambda deployments → load `azure-pipeline-lambda`
- Ansible playbooks → load `azure-pipeline-ansible`
- Docker builds → load `azure-pipeline-docker`

## IMPORTANT — do not generate YAML without loading a type skill

STOP. Before generating any pipeline YAML, you MUST load the type skill that matches the requested pipeline type:
- `azure-pipeline-lambda` for Lambda
- `azure-pipeline-ansible` for Ansible
- `azure-pipeline-docker` for Docker

Generate nothing until that skill is loaded.

## Required inputs — ask the user for these before generating

1. **Service/repo name** — used in display names and tags
2. **Pipeline type** — `lambda` | `ansible` | `docker`
3. **Target tier** — `nonprod` | `prod`
4. **Trigger branch** — branch that triggers auto-deploy (default: `main`)
5. **Secret sources** — which are in use: `ADO variable groups` | `AWS SSM/Secrets Manager` | `Vault/OpenBao` (can be multiple)
6. **ADO variable group name(s)** — if ADO variable groups selected

## Pipeline skeleton — always use this structure

```yaml
trigger:
  branches:
    include:
      - <trigger-branch>

pool: EKS-Pool

stages:
  - stage: Lint
    displayName: "Lint"
    jobs:
      - job: Lint
        pool: EKS-Pool
        timeoutInMinutes: 30
        continueOnError: false
        steps: []  # type skill fills this in

  - stage: SecurityScan
    displayName: "Security Scan"
    dependsOn: Lint
    condition: succeeded()
    jobs:
      - job: SecurityScan
        pool: EKS-Pool
        timeoutInMinutes: 30
        continueOnError: false
        steps: []  # type skill fills this in

  - stage: Build
    displayName: "Build"
    dependsOn: SecurityScan
    condition: succeeded()
    jobs:
      - job: Build
        pool: EKS-Pool
        timeoutInMinutes: 30
        continueOnError: false
        steps: []  # type skill fills this in

  - stage: DeployNonprod
    displayName: "Deploy — Nonprod"
    dependsOn: Build
    condition: succeeded()
    jobs:
      - deployment: DeployNonprod
        displayName: "Deploy to Nonprod"
        pool: EKS-Pool
        timeoutInMinutes: 30
        environment: nonprod
        strategy:
          runOnce:
            deploy:
              steps: []  # type skill fills this in

  - stage: DeployProd
    displayName: "Deploy — Prod"
    dependsOn: DeployNonprod
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/<trigger-branch>'))
    jobs:
      - deployment: DeployProd
        displayName: "Deploy to Prod"
        pool: EKS-Pool
        timeoutInMinutes: 30
        environment: prod   # manual approval gate configured in ADO environment settings
        strategy:
          runOnce:
            deploy:
              steps: []  # type skill fills this in + git tag step below
```

## Prod tier pipelines

When `target tier` is `prod`, omit `DeployNonprod` entirely. The pipeline contains only `Lint` → `SecurityScan` → `Build` → `DeployProd` with the manual approval gate.

When `target tier` is `nonprod`, omit `DeployProd` entirely.

## Git tagging on prod deploy

Add this as the final step inside `DeployProd`'s steps (prod tier only):

```yaml
- script: |
    git config user.email "azdo-pipeline@$(System.TeamProject)"
    git config user.name "Azure DevOps Pipeline"
    git remote set-url origin "https://x-token:$(System.AccessToken)@$(echo $BUILD_REPOSITORY_URI | sed 's|https://||')"
    git tag $(Build.BuildNumber) $(Build.SourceVersion)
    git push origin $(Build.BuildNumber)
  displayName: "Tag commit with build number"
  env:
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)
    BUILD_REPOSITORY_URI: $(Build.Repository.Uri)
```

## Secret handling patterns

Emit the correct block(s) based on declared secret sources:

### ADO variable groups
```yaml
variables:
  - group: <variable-group-name>
```
Reference values as `$(VAR_NAME)` throughout the pipeline.

### AWS SSM Parameter Store
```yaml
- script: |
    VALUE=$(aws ssm get-parameter \
      --name "/myapp/mykey" \
      --with-decryption \
      --query "Parameter.Value" \
      --output text)
    echo "##vso[task.setvariable variable=MY_VAR;issecret=true]$VALUE"
  displayName: "Fetch secret from SSM"
```

### AWS Secrets Manager
```yaml
- script: |
    VALUE=$(aws secretsmanager get-secret-value \
      --secret-id "myapp/mykey" \
      --query "SecretString" \
      --output text)
    echo "##vso[task.setvariable variable=MY_VAR;issecret=true]$VALUE"
  displayName: "Fetch secret from Secrets Manager"
```

### Vault / OpenBao
```yaml
- script: |
    VALUE=$(vault kv get -field=mykey secret/myapp/mykey)
    echo "##vso[task.setvariable variable=MY_VAR;issecret=true]$VALUE"
  displayName: "Fetch secret from Vault"
  env:
    VAULT_ADDR: $(VAULT_ADDR)
    VAULT_TOKEN: $(VAULT_TOKEN)
```

## Hard rules — always follow these

- `pool: EKS-Pool` on every job — no exceptions
- `timeoutInMinutes: 30` on every job
- `continueOnError: false` at **job level** on every job (not step level). Step-level `continueOnError` may be omitted.
- No secrets hardcoded in YAML — all via variable groups or runtime fetch
- Every stage and job has a `displayName:` set
- `pool: EKS-Pool` must appear at job level, not stage level, to ensure it applies correctly
