Skip to main content

Delinea Secret Server — AWX/AAP Credential Plugin

CI Release PyPI version PyPI - Python Version Coverage License Code style: black Imports: isort

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

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 a PasswordGrantAuthorizer or DomainPasswordGrantAuthorizer from python-tss-sdk. The SDK handles the OAuth2 password grant internally.

  • backend(**kwargs) AWX entry point called at job launch. Receives all fields and metadata as keyword arguments. Returns a single string based on the identifier metadata dropdown value (token or base_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 make targets, never duplicate shell commands
  • Local reproducibility: make cici.yml, make release-checkrelease.yml build 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 Kevin Marquis
Repository awx-delinea-secret-server-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:

  1. main is always deployable
  2. Create a branch from main with a descriptive name (e.g. add-ssl-toggle, fix-token-parsing)
  3. Commit your changes and push early for visibility
  4. Open a pull request to start discussion and trigger CI
  5. Review & approve — CI must pass, at least one approval required
  6. Merge to main — branch is deleted after merge
  7. 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.Z semver format
  • Must be on main branch
  • Clean git working tree
  • Tag must not exist locally or on origin
  • make ci must 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 install inside containers is ephemeral — reinstall after container restarts, or build a custom controller image for persistence.

Standard (non-containerised) install

  1. Install the plugin

    awx-python -m pip install awx-delinea-secret-server-credential-plugin
    
  2. Register credential types

    awx-manage setup_managed_credential_types
    

After installation

  1. Create a "Delinea Secret Server" credential — fill in base_url, username, password, and optionally domain

  2. 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:

  1. A custom credential type (defines the fields + injectors for your jobs)
  2. 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_vars block (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:

  1. TSS Token field — click the key icon (🔑) next to the field:
    • Credential → select Delinea SS - Production
    • Output value → select token
  2. TSS Base URL field — click the key icon (🔑) next to the field:
    • Credential → select Delinea SS - Production
    • Output value → select base_url

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:

  1. Call the Delinea plugin to authenticate and get a fresh OAuth2 token
  2. Inject TSS_TOKEN and TSS_BASE_URL as environment variables
  3. Inject tss_token and tss_base_url as extra vars
  4. 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

  1. Create a branch from main with a descriptive name
  2. Make changes, run make format before committing
  3. Push and open a pull request — CI runs automatically
  4. Get review, iterate, then merge to main

Roadmap

  • Custom Execution Environment image with plugin pre-installed

License

Apache-2.0

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

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file awx_delinea_secret_server_credential_plugin-0.2.5.tar.gz.

File metadata

File hashes

Hashes for awx_delinea_secret_server_credential_plugin-0.2.5.tar.gz
Algorithm Hash digest
SHA256 6088f5767f8b696cb7a1b458bb9ceec6a74b20089c54a7daf4495cc0b8f0565a
MD5 882025ae18e26159f59f74eaf93a8756
BLAKE2b-256 c6af94bfe3eac6e76ae39b7a9f01ea1ff12ce077b80b9094a8058423f98ac90d

See more details on using hashes here.

Provenance

The following attestation bundles were made for awx_delinea_secret_server_credential_plugin-0.2.5.tar.gz:

Publisher: release.yml on acedya/awx-delinea-secret-server-credential-plugin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file awx_delinea_secret_server_credential_plugin-0.2.5-py3-none-any.whl.

File metadata

File hashes

Hashes for awx_delinea_secret_server_credential_plugin-0.2.5-py3-none-any.whl
Algorithm Hash digest
SHA256 60584de659a617489e291658ad72dae3c3c75e721e476252b8065b4bfed6c9c2
MD5 5c29e0984851ae5483f5f0a605235dda
BLAKE2b-256 9acb48672e21261fb65fa2c5a367f5fe2c0c89929b907dfe8f74e4631ddc1a00

See more details on using hashes here.

Provenance

The following attestation bundles were made for awx_delinea_secret_server_credential_plugin-0.2.5-py3-none-any.whl:

Publisher: release.yml on acedya/awx-delinea-secret-server-credential-plugin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.7

2 files

This release

0.2.5 This release

2 files

0.2.4

2 files

0.2.2

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page