autojanet/skills/azure-pipeline-ansible/SKILL.md
Zoë cc74ad0bd0
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
fix: use library/ Harbor project, add skills, fix pipeline secrets
- .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/
2026-05-30 15:43:14 -07:00

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`