AWX/AAP credential plugin for Delinea (Thycotic) Secret Server
Project description
Delinea Secret Server — AWX/AAP Credential Plugin
Custom AWX/AAP credential plugin for Delinea (Thycotic) Secret Server. Uses the official Delinea Python SDK (
python-tss-sdk) to authenticate via OAuth2 at job launch, retrieves a short-lived access token, and provides it through AWX credential linking — the raw password is never exposed to the running job.
Architecture
┌───────────────────────────────┐
│ AAP / AWX │
│ │
│ ┌─────────────────────────┐ │ python-tss-sdk (OAuth2)
│ │ Delinea SS Credential │──│──────────────────────────────┐
│ │ (External – Plugin) │ │ │
│ │ base_url, user, pass │◄─│──────────────────────────────┤
│ └────────────┬────────────┘ │ { "access_token": ... } │
│ │ credential │ │
│ │ linking │ ┌─────────┴──────────┐
│ ▼ │ │ Delinea Secret │
│ ┌─────────────────────────┐ │ │ Server │
│ │ Target Credential │ │ │ (OAuth2 endpoint) │
│ │ (fields linked via │ │ └────────────────────┘
│ │ identifier dropdown) │ │
│ └────────────┬────────────┘ │
│ │ injected by │
│ │ target type │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Ansible Job (playbook) │ │
│ │ │ │
│ │ TSS_TOKEN ✔ │ │
│ │ PASSWORD ✘ │ │
│ └─────────────────────────┘ │
└───────────────────────────────┘
Table of Contents
- Quick Start
- Development
- Plugin Details
- Testing
- CI/CD Pipeline
- Release Process
- Deployment to AAP/AWX
- Credential Linking
- 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 a separate credential type in AWX?
Yes — for injection. This plugin is an external credential source (it resolves values). To inject those values into your Ansible jobs as environment variables or extra vars, you need a target credential type with injectors. See Credential Linking for the recommended setup.
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
├── 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 |
|---|---|---|---|---|
base_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
This plugin is an external credential source — it does not define its own injectors.
AWX calls backend(**kwargs) and uses the returned value to populate a linked credential field.
The identifier metadata dropdown selects what the plugin returns:
| Identifier | Returns |
|---|---|
token (default) |
OAuth2 access token |
base_url |
Secret Server base URL (pass-through) |
To inject values as environment variables or extra vars, create a target credential type with those injectors, then link its fields to this plugin (see Credential Linking below).
Implementation
-
_get_authorizer(base_url, username, password, domain)Creates aPasswordGrantAuthorizerorDomainPasswordGrantAuthorizerfrompython-tss-sdk. The SDK handles the OAuth2passwordgrant internally. -
backend(**kwargs)AWX entry point called at job launch. Receives allfieldsandmetadataas keyword arguments. Returns a single string based on theidentifiermetadata dropdown value (tokenorbase_url).
Self-Signed Certificates
When using a self-signed certificate for SSL, the REQUESTS_CA_BUNDLE environment variable should be set to the path of the certificate (in .pem format). This will negate the need to ignore SSL certificate verification, which makes your application vulnerable.
export REQUESTS_CA_BUNDLE=/path/to/your/ca-bundle.pem
Please reference the requests documentation for further details on the REQUESTS_CA_BUNDLE environment variable, should you require it.
Note: On RHEL / CentOS systems the system CA bundle is typically located at
/etc/pki/tls/cert.pem.
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_authorizer_without_domain |
Uses PasswordGrantAuthorizer when domain absent |
test_get_authorizer_with_domain |
Uses DomainPasswordGrantAuthorizer when domain set |
test_backend_returns_token |
Returns token when identifier is token |
test_backend_defaults_to_token |
Defaults to token when identifier omitted |
test_backend_token_with_domain |
Uses domain authorizer and returns token |
test_backend_returns_base_url |
Returns base URL when identifier is base_url |
test_backend_raises_on_unknown_identifier |
ValueError raised for unknown identifier |
test_backend_password_not_in_output |
Raw password never in plugin output |
test_backend_sdk_error_propagates |
SDK authentication errors propagate to AWX |
test_inputs_has_required_fields |
INPUTS declares expected authentication fields |
test_inputs_password_is_secret |
Password field is marked as secret |
test_inputs_metadata_has_identifier |
Metadata includes identifier dropdown |
test_inputs_identifier_has_choices |
Identifier has token / base_url choices |
test_inputs_identifier_has_default |
Identifier defaults to token |
test_inputs_required_includes_identifier |
identifier is listed as required |
test_credential_plugin_structure |
CredentialPlugin has exactly 3 fields |
test_credential_plugin_no_injectors |
Plugin does not include injectors |
test_credential_plugin_name |
Plugin name matches AWX UI display |
test_credential_plugin_inputs_is_inputs |
Plugin references module-level INPUTS |
test_credential_plugin_backend_is_callable |
Plugin backend is callable |
Dependencies
pytest, pytest-cov, black, isort, flake8, mypy — all installed via make install-dev. Tests mock the SDK with unittest.mock.
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
Containerised AAP (single-node / podman)
The plugin must be installed inside both controller containers (automation-controller-web and automation-controller-task).
Install from GitHub (quickest):
podman exec -it -u 0 automation-controller-web awx-python -m pip install git+https://github.com/acedya/tss-credential-plugin.git --force-reinstall
podman exec -it -u 0 automation-controller-task awx-python -m pip install git+https://github.com/acedya/tss-credential-plugin.git --force-reinstall
podman exec -it -u 0 automation-controller-web awx-manage setup_managed_credential_types
podman exec -it -u 0 automation-controller-task awx-manage setup_managed_credential_types
podman restart automation-controller-web
podman restart automation-controller-task
Install from a local wheel:
# Build on your dev machine
make build
# Copy the wheel to the AAP host
scp dist/awx_delinea_secret_server_credential_plugin-*.whl admin@<aap-host>:/tmp/
# Copy into the containers
podman cp /tmp/awx_delinea_secret_server_credential_plugin-*.whl automation-controller-web:/tmp/
podman cp /tmp/awx_delinea_secret_server_credential_plugin-*.whl automation-controller-task:/tmp/
# Install
podman exec -it -u 0 automation-controller-web awx-python -m pip install /tmp/awx_delinea_secret_server_credential_plugin-*.whl
podman exec -it -u 0 automation-controller-task awx-python -m pip install /tmp/awx_delinea_secret_server_credential_plugin-*.whl
# Register
podman exec -it -u 0 automation-controller-web awx-manage setup_managed_credential_types
podman exec -it -u 0 automation-controller-task awx-manage setup_managed_credential_types
podman restart automation-controller-web
podman restart automation-controller-task
Note:
pip installinside containers is ephemeral — reinstall after container restarts, or build a custom controller image for persistence.
Standard (non-containerised) install
-
Install the plugin
awx-python -m pip install awx-delinea-secret-server-credential-plugin
-
Register credential types
awx-manage setup_managed_credential_types
After installation
-
Create a "Delinea Secret Server" credential — fill in
base_url,username,password, and optionallydomain -
Link to a target credential — see Credential Linking below
Credential Linking
This plugin is an external credential source. It authenticates to Secret Server and returns a value (token or base URL) that AWX injects into a linked credential field.
To use the plugin you need two things in AWX:
- A custom credential type (defines the fields + injectors for your jobs)
- A Delinea Secret Server credential (the source — authenticates to Secret Server)
Then you create a credential of your custom type and link its fields to the Delinea credential.
Step 1 — Create the Target Credential Type
Go to Administration → Credential Types → Add.
| Setting | Value |
|---|---|
| Name | Delinea Secret Server Token (or any name you prefer) |
| Description | Injects a Delinea SS OAuth2 token and base URL |
Input Configuration (paste as YAML):
fields:
- id: tss_token
label: TSS Token
type: string
secret: true
- id: tss_base_url
label: TSS Base URL
type: string
required:
- tss_token
- tss_base_url
Injector Configuration (paste as YAML):
env:
TSS_TOKEN: '{{ tss_token }}'
TSS_BASE_URL: '{{ tss_base_url }}'
extra_vars:
tss_token: '{{ tss_token }}'
tss_base_url: '{{ tss_base_url }}'
Tip: Adjust the injectors to your needs — if you only need env vars, remove the
extra_varsblock (and vice versa).
Click Save.
Step 2 — Create the Source Credential (Delinea Secret Server)
Go to Resources → Credentials → Add.
| Setting | Value |
|---|---|
| Name | Delinea SS - Production (or any name) |
| Credential Type | Delinea Secret Server (the plugin type — appears after installing the plugin and running awx-manage setup_managed_credential_types) |
| Secret Server URL | https://myserver/SecretServer or https://mytenant.secretservercloud.com |
| Username | Your application user username |
| Password | The corresponding password |
| Domain | (optional) Your AD domain if using domain auth |
Click Save.
Step 3 — Create the Target Credential and Link Fields
Go to Resources → Credentials → Add.
| Setting | Value |
|---|---|
| Name | Delinea SS Token - Production (or any name) |
| Credential Type | Delinea Secret Server Token (the custom type from Step 1) |
Now link each field to the source credential:
- TSS Token field — click the key icon (🔑) next to the field:
- Credential → select
Delinea SS - Production - Output value → select
token
- Credential → select
- TSS Base URL field — click the key icon (🔑) next to the field:
- Credential → select
Delinea SS - Production - Output value → select
base_url
- Credential → select
Click Save.
Step 4 — Attach to a Job Template
Go to Resources → Templates → edit your Job Template.
In the Credentials section, add the Delinea SS Token - Production credential (the target from Step 3).
At launch, AWX will:
- Call the Delinea plugin to authenticate and get a fresh OAuth2 token
- Inject
TSS_TOKENandTSS_BASE_URLas environment variables - Inject
tss_tokenandtss_base_urlas extra vars - Your playbook can use either method to access the values
Usage in Playbooks
Via extra vars (recommended)
- name: Retrieve a secret from Delinea Secret Server
ansible.builtin.debug:
msg: >-
{{ lookup('delinea.ss.tss', 42,
base_url=tss_base_url,
token=tss_token) }}
Via environment variables
- name: Use environment variables
ansible.builtin.debug:
msg: >-
Server: {{ lookup('env', 'TSS_BASE_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
- Custom Execution Environment image with plugin pre-installed
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.4.tar.gz.
File metadata
- Download URL: awx_delinea_secret_server_credential_plugin-0.2.4.tar.gz
- Upload date:
- Size: 14.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1204e69a1ad95877dedd67d3ca96e130335873136101ad870ed6d173f46ccb4
|
|
| MD5 |
3cea6c242676b0fe962476123eb3434f
|
|
| BLAKE2b-256 |
5aa0f9b1cfb7a31b7ca86a094d1cd1ca141b5da1deafe0843fe19191028c0da7
|
Provenance
The following attestation bundles were made for awx_delinea_secret_server_credential_plugin-0.2.4.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.4.tar.gz -
Subject digest:
d1204e69a1ad95877dedd67d3ca96e130335873136101ad870ed6d173f46ccb4 - Sigstore transparency entry: 975803767
- Sigstore integration time:
-
Permalink:
acedya/tss-credential-plugin@887ea13045afd1df93f104da748a3a0ad1262f06 -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/acedya
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@887ea13045afd1df93f104da748a3a0ad1262f06 -
Trigger Event:
push
-
Statement type:
File details
Details for the file awx_delinea_secret_server_credential_plugin-0.2.4-py3-none-any.whl.
File metadata
- Download URL: awx_delinea_secret_server_credential_plugin-0.2.4-py3-none-any.whl
- Upload date:
- Size: 10.5 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 |
479fad48f342eb2cd01b7baacc311131fcd029e55016cd0d5f3de46df7d34129
|
|
| MD5 |
964ef16c26bc9149d050fb4a7528f0b1
|
|
| BLAKE2b-256 |
631a02fd4a23a53c5a14433e2b1c710ac3e70733273002417909d3ee75e520c0
|
Provenance
The following attestation bundles were made for awx_delinea_secret_server_credential_plugin-0.2.4-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.4-py3-none-any.whl -
Subject digest:
479fad48f342eb2cd01b7baacc311131fcd029e55016cd0d5f3de46df7d34129 - Sigstore transparency entry: 975803779
- Sigstore integration time:
-
Permalink:
acedya/tss-credential-plugin@887ea13045afd1df93f104da748a3a0ad1262f06 -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/acedya
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@887ea13045afd1df93f104da748a3a0ad1262f06 -
Trigger Event:
push
-
Statement type: