AWX/AAP credential plugin for Delinea (Thycotic) Secret Server
Project description
Delinea Secret Server — AWX/AAP Credential Plugin
Custom AWX/AAP managed credential plugin for Delinea (Thycotic) Secret Server. Authenticates via OAuth2 at job launch, retrieves a short-lived token, and injects it into the runtime — the raw password is never exposed to the running job.
Architecture
┌──────────────────────┐
│ AAP / AWX │
│ │
│ ┌────────────────┐ │ POST /oauth2/token
│ │ Credential │──│──────────────────────────────┐
│ │ Plugin │ │ │
│ │ (Python) │◄─│──────────────────────────────┤
│ └───────┬────────┘ │ { "access_token": ... } │
│ │ │ │
│ ▼ │ ┌─────────┴──────────┐
│ ┌────────────────┐ │ │ Delinea Secret │
│ │ Injector │ │ │ Server │
│ │ (env + extras) │ │ │ (OAuth2 endpoint) │
│ └───────┬────────┘ │ └────────────────────┘
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Ansible Job │ │
│ │ (playbook) │ │
│ │ │ │
│ │ TSS_TOKEN ✔ │ │
│ │ PASSWORD ✘ │ │
│ └────────────────┘ │
└──────────────────────┘
Table of Contents
- Quick Start
- Development
- Plugin Details
- Testing
- CI/CD Pipeline
- Release Process
- Deployment to AAP/AWX
- Usage in Playbooks
- Repository Hardening
- Contributing
Quick Start
Install
pip install awx-delinea-secret-server-credential-plugin
Then on every AWX/AAP node:
awx-manage setup_managed_credential_types
Do I need the credential_type/ YAML files?
No. This package uses the AWX managed credential plugin pattern:
- It exposes a
CredentialPluginobject through Python entry points - Input schema is defined in Python (
inputs) - AWX discovers it via
awx-manage setup_managed_credential_types
The credential_type/ YAML files are kept as reference only.
Development
Prerequisites
- Python 3.8+
- GNU Make
- Git
Setup
git clone https://github.com/acedya/tss-credential-plugin.git
cd tss-credential-plugin
make install-dev # creates .venv, installs package + dev deps
Makefile Reference
The Makefile is the single source of truth — CI workflows call make targets, so local and CI behavior stay perfectly aligned.
| Target | Description |
|---|---|
make help |
Show all available targets |
make install-dev |
Install package with dev dependencies in .venv |
make format |
Auto-format code (black + isort) |
make lint |
CI-equivalent lint checks (black, isort, flake8, mypy) |
make test |
Run unit tests |
make test-ci |
CI-equivalent tests with coverage XML |
make build |
Build source + wheel distributions |
make release-check |
Build + twine check |
make ci |
Full CI-equivalent run: lint + test-ci + build |
make release-tag TAG=v0.2.1 [PUSH=1] |
Safe validated release tag creation |
make clean |
Remove caches, bytecode, build artifacts |
Project Structure
.
├── credential_plugins/
│ ├── __init__.py
│ └── delinea_secret_server.py # Main plugin module
├── tests/
│ ├── __init__.py
│ └── test_delinea_credential_plugin.py
├── credential_type/ # Reference YAML (not used at runtime)
├── examples/
│ └── example_playbook.yaml
├── scripts/
│ └── release.sh # Safe tag helper
├── .github/workflows/
│ ├── ci.yml # Test / lint / build
│ └── release.yml # PyPI publish / GitHub Release
├── pyproject.toml # Package metadata + tool config
├── Makefile # Single source of truth for CI
├── CHANGELOG.md # Release notes
└── README.md
Plugin Details
Credential Input Fields
| Field | Type | Required | Secret | Description |
|---|---|---|---|---|
server_url |
string | Yes | No | Base URL (e.g. https://myserver/SecretServer) |
username |
string | Yes | No | Application user name |
password |
string | Yes | Yes | Password (encrypted at rest by AAP) |
domain |
string | No | No | Application user domain |
Injector Output
The plugin injects only the OAuth2 token and server URL — never the raw password.
| Type | Variable | Value |
|---|---|---|
| Environment | TSS_SERVER_URL |
Secret Server URL |
| Environment | TSS_TOKEN |
OAuth2 access token |
| Extra var | tss_server_url |
Secret Server URL |
| Extra var | tss_token |
OAuth2 access token |
Implementation
-
_get_access_token(server_url, username, password, domain, verify_ssl)POSTs to{server_url}/oauth2/tokenwithgrant_type=password. Returns theaccess_tokenstring. Raisesrequests.HTTPErroron failure,KeyErrorif token missing. -
backend(credential_params)AWX entry point called at job launch. Calls_get_access_token(), returns{"tss_token": ..., "tss_server_url": ...}.
OAuth2 Token Flow
POST {server_url}/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=password&username={username}&password={password}&domain={domain}
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "bearer",
"expires_in": 1200
}
Testing
Run Tests
make ci # full CI parity: lint + test + build
make test # unit tests only
make test-ci # tests with coverage XML
make test-verbose # verbose output
make lint # lint checks only
Test Matrix
| Test | Description |
|---|---|
test_get_access_token_success |
Token returned on successful OAuth2 call |
test_get_access_token_without_domain |
Domain is optional, absent from request |
test_get_access_token_http_error |
HTTPError raised on non-2xx |
test_get_access_token_missing_key |
KeyError raised when access_token missing |
test_backend_returns_token_and_url |
backend() returns expected dict |
test_backend_password_not_in_output |
Raw password never in plugin output |
Dependencies
pytest, pytest-cov, responses (HTTP mocking), black, isort, flake8, mypy — all installed via make install-dev.
CI/CD Pipeline
Design Principles
- Makefile = source of truth: workflows call
maketargets, never duplicate shell commands - Local reproducibility:
make ci≡ci.yml,make release-check≡release.ymlbuild step - OIDC Trusted Publishing: no API token secrets stored in GitHub
Workflows
| Workflow | Trigger | Jobs |
|---|---|---|
ci.yml |
Push to main, pull requests |
Test (matrix: 3.10, 3.11), Lint, Build |
release.yml |
Tag v*.*.* |
PyPI publish + GitHub Release |
Trusted Publishing Setup
This repository uses PyPI OIDC Trusted Publishing — no API token secrets required.
Create a trusted publisher configuration on pypi.org:
| Setting | Value |
|---|---|
| Owner | Your GitHub org/user |
| Repository | tss-credential-plugin |
| Workflow | release.yml |
| Environment | pypi |
Publish trigger: strict vX.Y.Z tags, only if the tagged commit is on main.
Release notes are populated from CHANGELOG.md.
Local Publish Fallback
Token-based publishing is available for emergencies:
make publish-pypi-token PYPI_API_TOKEN=pypi-...
Release Process
Branching Model — GitHub Flow
This project follows GitHub Flow, the simplest branching model:
mainis always deployable- Create a branch from
mainwith a descriptive name (e.g.add-ssl-toggle,fix-token-parsing) - Commit your changes and push early for visibility
- Open a pull request to start discussion and trigger CI
- Review & approve — CI must pass, at least one approval required
- Merge to
main— branch is deleted after merge - Tag & release when ready:
make release-tag TAG=vX.Y.Z PUSH=1
Creating a Release
# 1. Update CHANGELOG.md with the new version notes
# 2. Create and validate the tag
make release-tag TAG=v0.2.1
# 3. Push when ready (triggers PyPI publish + GitHub Release)
make release-tag TAG=v0.2.1 PUSH=1
Safety Checks (scripts/release.sh)
The release helper enforces:
- Strict
vX.Y.Zsemver format - Must be on
mainbranch - Clean git working tree
- Tag must not exist locally or on
origin make cimust pass before tag creation
Server-side guard: release.yml verifies the tagged commit is an ancestor of origin/main.
Deployment to AAP/AWX
-
Install the plugin on AWX/AAP nodes (or build a custom Execution Environment)
pip install awx-delinea-secret-server-credential-plugin
-
Register credential types
awx-manage setup_managed_credential_types -
Create a Credential using the Delinea Secret Server type — fill in
server_url,username,password, and optionallydomain -
Attach to a Job Template — the token is injected at launch time as env vars and extra vars
Usage in Playbooks
Via extra vars (recommended)
- name: Retrieve a secret from Delinea Secret Server
ansible.builtin.debug:
msg: >-
{{ lookup('delinea.ss.tss', 42,
server_url=tss_server_url,
token=tss_token) }}
Via environment variables
- name: Use environment variables
ansible.builtin.debug:
msg: >-
Server: {{ lookup('env', 'TSS_SERVER_URL') }}
Token: {{ lookup('env', 'TSS_TOKEN') }}
Repository Hardening
Apply these in GitHub UI: Settings → Rules → Rulesets.
Branch Protection
main:
- Require pull request with at least 1 approval
- Dismiss stale approvals on new commits
- Require status checks: CI jobs from
ci.yml - Require conversation resolution
- Block force pushes and branch deletion
Tag Protection
v*.*.* tags:
- Restrict creation/update/deletion to maintainers only
- Works with local guard (
scripts/release.sh) and workflow guard (release.yml)
Environment Protection
| Environment | Configuration |
|---|---|
pypi |
Required reviewers (recommended), limit to protected branches/tags |
Contributing
Workflow
- Create a branch from
mainwith a descriptive name - Make changes, run
make formatbefore committing - Push and open a pull request — CI runs automatically
- Get review, iterate, then merge to
main
Roadmap
- Client credentials grant (SDK-based auth)
- Configurable
verify_ssltoggle in credential input - Token caching for rapid successive lookups
- Custom Execution Environment image with plugin pre-installed
- Integration tests against a real Secret Server instance
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file awx_delinea_secret_server_credential_plugin-0.2.0.tar.gz.
File metadata
- Download URL: awx_delinea_secret_server_credential_plugin-0.2.0.tar.gz
- Upload date:
- Size: 9.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad3ee6a7921e863ee29f0bd6e6388eda81e30402681e3161b4c165ec34571784
|
|
| MD5 |
143b353d3f59774789c9d99b46fa18ba
|
|
| BLAKE2b-256 |
b6cf4eb0bc8bbc8116d21cfac3b6ae14cee536665ae2080ad8678a859711598c
|
Provenance
The following attestation bundles were made for awx_delinea_secret_server_credential_plugin-0.2.0.tar.gz:
Publisher:
release.yml on acedya/tss-credential-plugin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
awx_delinea_secret_server_credential_plugin-0.2.0.tar.gz -
Subject digest:
ad3ee6a7921e863ee29f0bd6e6388eda81e30402681e3161b4c165ec34571784 - Sigstore transparency entry: 970174847
- Sigstore integration time:
-
Permalink:
acedya/tss-credential-plugin@0153eb64836a70885daabd06d97993b40f72a458 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/acedya
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0153eb64836a70885daabd06d97993b40f72a458 -
Trigger Event:
push
-
Statement type:
File details
Details for the file awx_delinea_secret_server_credential_plugin-0.2.0-py3-none-any.whl.
File metadata
- Download URL: awx_delinea_secret_server_credential_plugin-0.2.0-py3-none-any.whl
- Upload date:
- Size: 8.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e74bf4c5ac44d6a59d4c7e769871cd818e87df9da895fb3f0b6e0d6f56b0d063
|
|
| MD5 |
6b7fb9ab2bbf422713a02befcf5bd163
|
|
| BLAKE2b-256 |
da68e56a3b24e5679a1288b495d92b54e6438c9c6c8cf18fbf101ecbd0fc714f
|
Provenance
The following attestation bundles were made for awx_delinea_secret_server_credential_plugin-0.2.0-py3-none-any.whl:
Publisher:
release.yml on acedya/tss-credential-plugin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
awx_delinea_secret_server_credential_plugin-0.2.0-py3-none-any.whl -
Subject digest:
e74bf4c5ac44d6a59d4c7e769871cd818e87df9da895fb3f0b6e0d6f56b0d063 - Sigstore transparency entry: 970174904
- Sigstore integration time:
-
Permalink:
acedya/tss-credential-plugin@0153eb64836a70885daabd06d97993b40f72a458 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/acedya
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0153eb64836a70885daabd06d97993b40f72a458 -
Trigger Event:
push
-
Statement type: