Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- .woodpecker.yaml: image paths -> library/autojanet-{agent,dispatcher}
- .woodpecker.yaml: secret names RS_HARBOR_USER / RS_HARBOR_PASS (global)
- container/Dockerfile: restore COPY skills/, skills/ populated from opencode config
- skills/: 84 opencode skills bundled into image
- k8s/manifests: update image refs to library/
145 lines
4.8 KiB
Markdown
145 lines
4.8 KiB
Markdown
---
|
|
name: azure-pipeline-ansible
|
|
description: Extends azure-devops-pipeline for Ansible playbook runs. Handles syntax check, galaxy install, vault passwords, SSH key injection, check mode on nonprod, and dynamic AWS EC2 inventory. Always load azure-devops-pipeline first.
|
|
---
|
|
|
|
## What I add
|
|
|
|
Type-specific steps for Ansible pipelines. Merge these into the skeleton from `azure-devops-pipeline`.
|
|
|
|
## Additional required inputs — ask the user
|
|
|
|
1. **Playbook path** — e.g. `playbooks/site.yml`
|
|
2. **Inventory source** — `static` | `dynamic-aws-ec2`
|
|
3. **Ansible Vault in use** — `yes` | `no`
|
|
4. **ADO secret variable name for vault password** — if vault in use, e.g. `ANSIBLE_VAULT_PASSWORD`
|
|
5. **ADO secret variable name for SSH private key** — e.g. `ANSIBLE_SSH_KEY`
|
|
6. **Ansible version to pin** — e.g. `9.2.0`
|
|
7. **Run --check mode on nonprod before real apply** — `yes` (default) | `no`
|
|
|
|
## Lint stage steps
|
|
|
|
```yaml
|
|
- script: |
|
|
pip install "ansible==$(ANSIBLE_VERSION)" ansible-lint
|
|
ansible-lint <playbook-path> --profile production
|
|
displayName: "Lint — ansible-lint"
|
|
env:
|
|
ANSIBLE_VERSION: <ansible-version>
|
|
```
|
|
|
|
## Security scan stage steps
|
|
|
|
```yaml
|
|
- script: |
|
|
pip install "ansible==$(ANSIBLE_VERSION)" ansible-lint
|
|
ansible-lint <playbook-path> --profile security \
|
|
--sarif-file ansible-lint-security.sarif || true
|
|
ansible-galaxy install -r requirements.yml --force
|
|
displayName: "Security scan — ansible-lint security profile"
|
|
env:
|
|
ANSIBLE_VERSION: <ansible-version>
|
|
- task: PublishBuildArtifacts@1
|
|
inputs:
|
|
pathToPublish: ansible-lint-security.sarif
|
|
artifactName: security-scan
|
|
displayName: "Publish scan results"
|
|
```
|
|
|
|
## Build stage steps
|
|
|
|
```yaml
|
|
- script: |
|
|
pip install "ansible==$(ANSIBLE_VERSION)"
|
|
[ -f requirements.yml ] && ansible-galaxy install -r requirements.yml || true
|
|
ansible-playbook <playbook-path> --syntax-check -i <inventory-file>
|
|
displayName: "Validate — syntax check and galaxy install"
|
|
env:
|
|
ANSIBLE_VERSION: <ansible-version>
|
|
```
|
|
|
|
Note: for dynamic-aws-ec2 inventory, replace `-i <inventory-file>` with `-i aws_ec2.yml` and ensure `aws_ec2.yml` exists in the repo with the `amazon.aws.aws_ec2` plugin configured.
|
|
|
|
## Deploy stage steps
|
|
|
|
### Step order — always emit in this order
|
|
|
|
1. Write SSH key to temp file
|
|
2. Write vault password to temp file (if vault in use)
|
|
3. Check mode run (nonprod only, if enabled)
|
|
4. Real playbook run
|
|
5. Clean up SSH key (condition: always)
|
|
6. Clean up vault password (condition: always)
|
|
|
|
### SSH key injection (always include)
|
|
|
|
```yaml
|
|
- script: |
|
|
echo "$(ANSIBLE_SSH_KEY)" > /tmp/ansible_ssh_key
|
|
chmod 600 /tmp/ansible_ssh_key
|
|
displayName: "Inject SSH key"
|
|
env:
|
|
ANSIBLE_SSH_KEY: $(ANSIBLE_SSH_KEY)
|
|
```
|
|
|
|
### Vault password file (include only if vault in use)
|
|
|
|
```yaml
|
|
- script: |
|
|
echo "$(ANSIBLE_VAULT_PASSWORD)" > /tmp/vault_pass
|
|
chmod 600 /tmp/vault_pass
|
|
displayName: "Write vault password file"
|
|
env:
|
|
ANSIBLE_VAULT_PASSWORD: $(ANSIBLE_VAULT_PASSWORD)
|
|
```
|
|
|
|
### Check mode run (nonprod only, if enabled)
|
|
|
|
```yaml
|
|
- script: |
|
|
VAULT_ARGS=""
|
|
[ -f /tmp/vault_pass ] && VAULT_ARGS="--vault-password-file /tmp/vault_pass"
|
|
ansible-playbook <playbook-path> \
|
|
-i <inventory> \
|
|
--check \
|
|
--diff \
|
|
--private-key /tmp/ansible_ssh_key \
|
|
$VAULT_ARGS
|
|
displayName: "Dry run — check mode"
|
|
```
|
|
|
|
### Real run
|
|
|
|
```yaml
|
|
- script: |
|
|
VAULT_ARGS=""
|
|
[ -f /tmp/vault_pass ] && VAULT_ARGS="--vault-password-file /tmp/vault_pass"
|
|
ansible-playbook <playbook-path> \
|
|
-i <inventory> \
|
|
--diff \
|
|
--private-key /tmp/ansible_ssh_key \
|
|
$VAULT_ARGS
|
|
displayName: "Apply playbook"
|
|
```
|
|
|
|
### Cleanup (always at end of deploy steps — condition: always())
|
|
|
|
```yaml
|
|
- script: rm -f /tmp/ansible_ssh_key
|
|
displayName: "Clean up SSH key"
|
|
condition: always()
|
|
|
|
- script: rm -f /tmp/vault_pass
|
|
displayName: "Clean up vault password file"
|
|
condition: always()
|
|
```
|
|
|
|
## Hard rules for Ansible
|
|
|
|
- Always pin Ansible version with quoted pip specifier `"ansible==$(ANSIBLE_VERSION)"` — never use `latest`, unquoted `==` may fail in some shells
|
|
- Always clean up SSH key and vault password files with `condition: always()` — they must be removed even if the playbook fails
|
|
- Always include `--diff` on real runs so changes are visible in pipeline logs
|
|
- SSH key file permissions must be `600` — Ansible refuses keys with broader permissions
|
|
- Use shell variable expansion (`VAULT_ARGS=""`) rather than subshell substitution in the step script to avoid bash syntax issues in ADO agents
|
|
- For dynamic inventory, AWS credentials come from the OIDC service connection environment — same pattern as Lambda
|
|
- `requirements.yml` must exist in the repo if galaxy install step is included; if uncertain, wrap with `[ -f requirements.yml ] && ansible-galaxy install -r requirements.yml || true`
|