Skip to content

iamteedoh/aapCredsDecrypt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWX/AAP Credential Decrypt & Import Script

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.


Table of Contents


Introduction

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.


Use Case

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.


Features

  • 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.

Prerequisites

  • 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)

Flowchart of aapCreds.py

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
Loading

Installation and Setup


Installation and Setup - Option 1 (Outside of Playbooks)

Running Script Outside of an Ansible Playbook

  1. Environment Verification: Confirm you have access to AWX models by running an interactive shell:
awx-manage shell_plus

If you do have access, proceed. If you don't, make sure you are on a server, typically a controller node, that does have access.

  1. 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).

  1. 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


Usage Instructions

1. Start the Script:

Become a user with elevated priveleges, typically root:

sudo su -

Drop into the Django ORM:

awx-manage shell_plus

Execute 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.py

2. Interactive Main Menu:

Upon 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

3. Output Options (for Decryption):

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.

4. Import Process:

When importing, the script checks for duplicates (credentials with the same name, type, and organization) and logs any that are skipped.


Non-Interactive

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 import and export flags
  • 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

Export Example:

./aapCreds.py --quiet --export --export-file=creds.json

Import Example:

./aapCreds.py --quiet --import --import-file=creds.json

Installation and Setup - Option 2 (Within Playbooks)

Running Script within an Ansible Playbook

  1. Environment Verification:

Confirm you have access to AWX modesl by running an interactive shell:

awx-manage shell_plus

If you do have access, proceed. If you don't, make sure you are on a server, typically a controller node, that does have access.

  1. 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-manage plugin|module instead of running it separately. This simplifies things when using the command module in an Ansible playbook
  1. 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
  1. 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: absent

If 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: absent

Then, 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 ..."

Function Details

list_used_credential_types

  • Purpose: Retrieve a list of CredentialType objects that are actively used by at least one Credential.

  • Implementation: Collects distinct credential type IDs from the Credential objects and filters the CredentialType queryset accordingly.

get_teams_from_role

  • Purpose: Given a Role object, returns the associated list of Team objects.

  • Implementation: Handles differences across AWX/AAP versions by checking for attributes like team_set or teams, iterating over related objects, if necessary.

decrypt_single_credential

  • 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_field while preserving original values for non-secret fields.

decrypt_credentials_by_ids

  • 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_credential for each.

decrypt_all_credentials

  • Purpose: Decrypts every credential stored in the AWX system.

  • Implementation: Retrieves all credentials via the ORM and processes each eith decrypt_single_credential.

ouput_results

  • 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.

import_credential

  • 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 Credential object and restores role memberships and associations with job templates.

import_credentials_from_file

  • 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.

interactive_memu (In interactive mode only)

  • 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.


Troubleshooting

  • 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.


Additional Notes

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-manage command or within an interactive shell. In these contexts, the script's top-level code, which includes the call to interactive_menu(), is executed immediately. This direct execution pattern makes an explicit main() 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-manage tool 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. The awx-manage tool will strip the script name and only use the basename.

Contributing

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.

About

A Python script to decrypt and export Credential Types in AWX and AAP

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages