This script is designed for AWX/AAP environments to facilitate the decryption and import of credentials. It provides an interactive menu-based interface to:
- List all used Credential Types.
- Decrypt all credentials or selected ones.
- Import credentials from a JSON file.
This new version, aapCreds.py, includes enhanced flow control and a refined interactive menu (as illustrated in the flowchart below) to improve user experience.
- Introduction
- Use Case
- Features
- Prerequisites
- Flowchart of aapCreds.py
- Installation and Setup
- Usage Instructions
- Function Details
- Troubleshooting
- Additional Notes
- Contributing
- License
This script is a utility to manage AWX/AAP credentials by decrypting sensitive fields and importing credentials from a JSON file. It is meant to be executed within an AWX/AAP environment where all the necessary Django models and AWX-specific utilities are available.
This script is to facilitate the migration of credentials from Ansible Tower to AAP, where credentials are not part of the backup/export/import process.
- List Credential Types: Identify which credential types are currently in use.
- Decrypt Credentials: Decrypt secret fields (such as passwords and keys) for:
- All stored credentials.
- Specific credentials chosen by their IDs.
- Import Credentials: Import credentials from a JSON file with:
- Duplicate checks.
- Restoration of role memberships and job template associations.
- Enhanced Interactive Menu: The updated interactive menu offers a clearer, ordered selection of options, improving the overall flow control as visualized in the flowchart below.
-
AWX/AAP Environment:
This script must run within an AWX/AAP environment because it requires access to AWX-specific Django models and utilities.Tip: If running from within
awx-manage, start an interactive shell with:awx-manage shell_plus
Then execute the script within the environment as follows:
exec(open('/path/of/aapCreds.py').read()) -
Python: A compatible Python version (3.x+) installed within the AWX/AAP environment.
-
AWX/AAP Modules: Ensure that the following modules are available:
- awx.main.models (includes Credential, CredentialType, Organization, Project, JobTemplate, Team, Role, User)
- awx.main.utils (provides decrypt_field)
Below is the flowchart illustrating the overall flow of aapCreds.py. It demonstrates the two primary modes (non-interactive and interactive) along with the detailed menu options.
flowchart LR
%% Main Flow
A[Start Command] --> B{"Quiet Mode?"}
B -- "Yes" --> C[Non-Interactive Mode]
B -- "No" --> F[Interactive Mode]
C --> Z[End]
F --> Z[End]
%% Non-Interactive Subgraph
subgraph NonInteractive [Non-Interactive Mode]
direction LR
C --> D{"Export?"}
D -- "Yes" --> E[Decrypt All Credentials]
D -- "No" --> G{"Import?"}
E --> H[Write to File]
H --> I[Export End]
D -- "No Export File" --> J[Export Error: No File Specified]
J --> Z
G -- "Yes" --> K[Read Import File]
K --> L[Process Imports]
L --> M[Import End]
G -- "No" --> Z
G -- "No Import File" --> N[Import Error: No File Specified]
N --> Z
end
%% Interactive Subgraph
subgraph Interactive [Interactive Mode]
direction LR
F --> O{"Menu Options"}
O -- "Option 1" --> P[List Credential Types]
O -- "Option 2" --> Q[Decrypt All]
O -- "Option 3" --> R[Decrypt Specific]
O -- "Option 4" --> S[Import from File]
O -- "Option 5" --> T[Exit]
O -- "Invalid Option" --> U[Invalid Option]
P --> O
Q --> V[Output Results]
R --> V
V --> O
S --> W[Process Import File]
W --> O
U --> O
T --> Z
end
%% Styling nodes with darker font colors for better contrast
style A fill:#C8E6C9,stroke:#555,stroke-width:2px,color:#000
style B fill:#FFF9C4,stroke:#555,stroke-width:2px,color:#000
style C fill:#BBDEFB,stroke:#555,stroke-width:2px,color:#000
style F fill:#FFCCBC,stroke:#555,stroke-width:2px,color:#000
style Z fill:#F5F5F5,stroke:#555,stroke-width:2px,color:#000
style D fill:#FFF9C4,stroke:#555,stroke-width:2px,color:#000
style E fill:#C8E6C9,stroke:#555,stroke-width:2px,color:#000
style H fill:#BBDEFB,stroke:#555,stroke-width:2px,color:#000
style I fill:#F5F5F5,stroke:#555,stroke-width:2px,color:#000
style J fill:#FFCDD2,stroke:#555,stroke-width:2px,color:#000
style G fill:#FFF9C4,stroke:#555,stroke-width:2px,color:#000
style K fill:#BBDEFB,stroke:#555,stroke-width:2px,color:#000
style L fill:#C8E6C9,stroke:#555,stroke-width:2px,color:#000
style M fill:#F5F5F5,stroke:#555,stroke-width:2px,color:#000
style N fill:#FFCDD2,stroke:#555,stroke-width:2px,color:#000
style O fill:#FFF9C4,stroke:#555,stroke-width:2px,color:#000
style P fill:#BBDEFB,stroke:#555,stroke-width:2px,color:#000
style Q fill:#C8E6C9,stroke:#555,stroke-width:2px,color:#000
style R fill:#C8E6C9,stroke:#555,stroke-width:2px,color:#000
style S fill:#BBDEFB,stroke:#555,stroke-width:2px,color:#000
style T fill:#F5F5F5,stroke:#555,stroke-width:2px,color:#000
style U fill:#FFCDD2,stroke:#555,stroke-width:2px,color:#000
style V fill:#BBDEFB,stroke:#555,stroke-width:2px,color:#000
style W fill:#C8E6C9,stroke:#555,stroke-width:2px,color:#000
%% Force straight arrow lines
linkStyle default interpolation linear
- Environment Verification: Confirm you have access to AWX models by running an interactive shell:
awx-manage shell_plusIf you do have access, proceed. If you don't, make sure you are on a server, typically a controller node, that does have access.
- Import and Script:
Save the script as aapCreds.py onto your AWX/AAP server where the required Python environment is active (typically on your controller node).
- Apply Appropriate Permissions:
When executing the script manually, you'll need to ensure proper execute permissions. You can do so by running the following command:
shell chmod +x aapCreds.py
Become a user with elevated priveleges, typically root:
sudo su -Drop into the Django ORM:
awx-manage shell_plusExecute the script within an AWX shell:
exec(open("/path/to/aapCreds.py").read())Note: Alternatively, you can execute the script in the AWX/AAP environment (Not Recommended)
./aapCreds.pyUpon execution, the script displays a menu with the following options:
- Option 1: List all used Credential Types
- Option 2: Decrypt ALL Credentials.
- Option 3: Decrypt specific credentials by entering a comma-separated list of Credential IDs.
- Option 4: Import credentials from a JSON file.
- Option 4: Exit
After decryption, choose whether to:
- Print the decrypted data to the console.
- Save the decrypted data to a file.
- Both print and save the results.
When importing, the script checks for duplicates (credentials with the same name, type, and organization) and logs any that are skipped.
This script can can be executed non-interactively by using the --quiet option, which will require the following additional flag(s):
- For non-interactivity:
--quiet- This flag is mutually exclusive with the
importandexportflags
- This flag is mutually exclusive with the
- For exporting credentials:
--export --export-file={{ creds_file }}- This flag exports the decrypted credentials to a given filename
- For importing credentials:
--import --import-file={{ creds_file }}- This flag imports the credentials, which in-turn become decrypted, using a given filename
./aapCreds.py --quiet --export --export-file=creds.json./aapCreds.py --quiet --import --import-file=creds.json- Environment Verification:
Confirm you have access to AWX modesl by running an interactive shell:
awx-manage shell_plusIf you do have access, proceed. If you don't, make sure you are on a server, typically a controller node, that does have access.
- Import and Script:
- Save the script as aapCreds.py onto your AWX/AAP server, where the required Python environment is active (typically a controller node).
- The script should be saved to the following location on AAP:
/var/lib/awx/venv/awx/lib/python3.9/site-packages/awx/main/management/commands/aapCreds.py- This allows the script to be used as an
awx-manageplugin|module instead of running it separately. This simplifies things when using thecommandmodule in an Ansible playbook
- This allows the script to be used as an
- Apply Appropriate Permissions:
This is optional considering the Django framework will read in the file and execute the code. However, just in case you need to make it executable, here's how:
chmod +x aapCreds.py- Create Ansible Playbook for Execution: (Option 1)
If you would like to run this script as an Ansible Playbook, you'll need to create a playbook that offers the following tasks:
- A task for the export process (if necessary)
- A task for the import process (if necessary)
- A cleanup task to remove the decrypted file (if necessary)
Here is a sample playbook called credentials_tasks.yml:
---
- name: Manage AWX/AAP Credential Export/Import
hosts: devcontroller # Edit this!
gather_facts: false
vars:
creds_file: "{{ creds_file_path }}" # Edit this in your inventory.yml file (consider vaulting it)
tasks:
- name: Export credentials to JSON file
command: awx-manage aapCreds --quiet --export --export-file={{ creds_file }}
register: export_output
- name: Results of export
debug:
msg: "Export Output: {{ export_output.stdout }}"
- name: Import credentials from JSON file
command: awx-manage aapCreds-quiet --import --import-file={{ creds_file }}
register: import_output
- name: Results of import
debug:
msg: "Import Output: {{ import_output.stdout }}"
- name: Remove credentials JSON file
file:
path: "(( creds_file }}"
state: absentIf you prefer to run this playbook as a task from another playbook, you can create a task file that you can include in the bigger or separate playbook, as follows:
# credentials_tasks.yml
- name: Export credentials to JSON file
command: awx-manage aapCreds --quiet --export --export-file={{ creds_file }} # Edit this var within your inventory.yml file
register: export_output
- name: Results of export
debug:
msg: "Export Output: {{ export_output.stdout }}"
- name: Import credentials from JSON file
command: awx-manage aapCreds --quiet --import --import-file={{ creds_file }} # Edit this var within your inventory.yml file
register: import_output
- name: Results of import
debug:
msg: "Import Output: {{ import_output.stdout }}"
- name: Remove credentials JSON file
file:
path: {{ creds_file }} # Edit this var within your inventory.yml file
state: absentThen, in your main or bigger playbook, you include the playbook as a task, as follows:
---
- name: MAIN PLAYBOOK
hosts: localhost
tasks:
- name: Run credentials export/import tasks
include_tasks: credentials_tasks.yml
- name: Other tasks from main playbook continue ...
debug:
msg: "Other tasks running ..."-
Purpose: Retrieve a list of
CredentialTypeobjects that are actively used by at least oneCredential. -
Implementation: Collects distinct credential type IDs from the
Credentialobjects and filters theCredentialTypequeryset accordingly.
-
Purpose: Given a
Roleobject, returns the associated list ofTeamobjects. -
Implementation: Handles differences across AWX/AAP versions by checking for attributes like
team_setorteams, iterating over related objects, if necessary.
-
Purpose: Decrypts a single credential and builds a detailed dictionary including: * Credential metadata (ID, name, type, creation/modification dates) * Organization details * Access list (users and teams) * Related job templates * Decrypted input fields
-
Implementation: Iterates through each field, decrypting secret fields using
decrypt_fieldwhile preserving original values for non-secret fields.
-
Purpose: Fetches and decrypts credentials based on a list of provided Credential IDs.
-
Implementation: Uses Django's ORM to filter credentials by IDs, applying
decrypt_single_credentialfor each.
-
Purpose: Decrypts every credential stored in the AWX system.
-
Implementation: Retrieves all credentials via the ORM and processes each eith
decrypt_single_credential.
-
Purpose: Provides options for outputting decrypted credentials:
- Standard output (console)
- Saving to a JSON file
- Both
-
Implementation: Formats the output as JSON and prompts for the preferred method of display and storage.
-
Purpose: Imports a single credential from a dictionary (parsed from a JSON export).
-
Implementation: Validates the existence of the corresponding name, CredentialType, and Organization; checks for duplicates; then creates the new
Credentialobject and restores role memberships and associations with job templates.
-
Purpose: Imports multiple credentials from a JSON file.
-
Implementation: Reads JSON data from a specified file, iterating over each credential's data to invoke
import_credential. Summarizes the count of imported credentials and lists duplicates skipped.
-
Purpose:
- Serves as the primary control loop offering an interactive menu.
- It captures and validates the user's input, ensuring that only a valid selection proceeds.
- Depending on the user's choice, it calls the corresponding function (for example, it might call the decryption functions or the import function).
- It typically loops back to the menu after completing an action, allowing the user to perform multiple operations in one session until they choose to exit
- Essentially,
interactive_menu()orchestrates the interactive flow of the program and keeps the user engaged until they decide to stop.
-
Implemenation: Displays the main menu with options to list credential types, decrypt credentials (all or specific), import credentials, or exit; processes user input accordingly until exit is chosen.
-
Import Errors: If errors occur regarding AWX/AAP models, ensure the script is run within the AWX/AAP environment.
-
Decryption Failures: Should a decryption process fail for any field, the error is captured, and the field's value is set to
None. Check console output for details. -
File I/O Issues: Verify that file paths provided for saving or importing JSON data are accessible and that proper permissions are set.
Why isn't a main() function required in this script?
In many AWX/AAP management scripts, including this one, a separate main() function isn't strictly necessary because of the following
reasons:
- Direct Execution in AWX/AAP: The script is intended to be run within the AWP/AAP environment, often via the
awx-managecommand or within an interactive shell. In these contexts, the script's top-level code, which includes the call tointeractive_menu(), is executed immediately. This direct execution pattern makes an explicitmain()wrapper redundant. - Integration with AWX Management Commands: AWX management commands are typically structured so that the entry point is defined by the command registration. The AWX framework calls the script directly, so embedding all logic at the top level or via an immediate call to the interactive menu is both acceptable and common.
In short, the script is designed to run immediately when invoked in its intended environment, so a separate main() function is optional.
Why awx-manage aapCreds ... used vs awx-manage aapCreds.py ... considering the script name is aapCreds.py?
The usage of awx-manage aapCreds is based on how AWX registers and invokes management commands:
- Registered Command Name: The script has been integrated in AWX as a management command with the name aapCreds. When you call
awx-manage aapCreds, you are invoking that specific command. - Syntax Requirements: The
awx-managetool expects a clean command name. Adding extraneous characters, such as a trailing question mark or a field separator, such as a period (.), would not match the registered command name and would lead to errors or unexpected behavior. In other words, the command is designed to be called without any punctuation byond the command name. Theawx-managetool will strip the script name and only use the basename.
Contributions and improvements to the script are welcome. When submitting changes:
- Ensure compatibility with the AWX/AAP environment.
- Update this README accordingly.
- Provide clear commit messages and documentation for any new features.