← All Posts

Ansible YAML Inventory Builder

Code & Tools
Ansible YAML Inventory Builder cover

Project Overview

This interactive Python script simplifies creating valid Ansible YAML inventories, especially in airgapped environments. It guides you through defining groups, hosts (supporting formats like hostname=ip, hostname-only, or IP-only), and optional child groups. The output is clean, Ansible-compliant YAML ready for immediate use with ansible or ansible-playbook commands.

Built quickly with GenAI assistance—perfect for labs, automation pipelines, or teaching Ansible basics. It eliminates manual YAML errors and enforces best practices.

Full Code

Security Note: Always review code before executing. SHA256 Hash: 51ea38c9041b22416e305b7abeb27ccf8d0ebc13c2d71b424a6cc7dd6c039a30/p>


    import argparse
    import sys
    import yaml

    def main():
        parser = argparse.ArgumentParser(
            description="Interactively build an Ansible YAML inventory (official format)."
        )
        parser.add_argument(
            "--output",
            default=None,
            help="Output file for the inventory (skips filename prompt)"
        )
        args = parser.parse_args()

        # Internal structure:
        # groupname: {
        #   'hosts': {hostname: {vars}},
        #   'children': set()
        # }
        groups = {}

        print("\nInteractive Ansible YAML Inventory Builder")
        print("Rules:")
        print("- Top-level keys are group names")
        print("- Hosts may be 'hostname=ip', hostname-only, or ip-only")
        print("- Child groups are optional\n")

        while True:
            group_name = input("Enter group name (or 'done' to finish): ").strip()
            if group_name.lower() == "done":
                break
            if not group_name:
                print("Group name cannot be empty.\n")
                continue

            group = groups.setdefault(
                group_name,
                {"hosts": {}, "children": set()}
            )

            hosts_input = input(
                f"Hosts for '{group_name}' "
                "(comma-separated, empty for none): "
            ).strip()

            if hosts_input:
                for item in hosts_input.split(","):
                    item = item.strip()
                    if not item:
                        continue

                    # hostname=ip format
                    if "=" in item:
                        hostname, addr = item.split("=", 1)
                        hostname = hostname.strip()
                        addr = addr.strip()

                        if not hostname or not addr:
                            print(f"Ignoring invalid host entry: {item}")
                            continue

                        group["hosts"][hostname] = {
                            "ansible_host": addr
                        }
                    else:
                        # hostname-only OR ip-only
                        group["hosts"][item] = {}

            children_input = input(
                f"Child groups for '{group_name}' "
                "(comma-separated, empty for none): "
            ).strip()

            if children_input:
                for child in children_input.split(","):
                    child = child.strip()
                    if not child:
                        continue
                    if child == group_name:
                        print("A group cannot be its own child — ignored.")
                    else:
                        group["children"].add(child)

            print()

        if not groups:
            print("No groups defined. Exiting.")
            sys.exit(0)

        # Validate referenced children
        defined_groups = set(groups.keys())
        undefined_children = {
            child
            for g in groups.values()
            for child in g["children"]
            if child not in defined_groups
        }

        if undefined_children:
            print("\nWarning: These child groups are referenced but not defined:")
            for name in sorted(undefined_children):
                print(f"  - {name}")
            if input("Proceed anyway? (y/n): ").strip().lower() != "y":
                print("Aborted.")
                sys.exit(1)

        # Build final inventory
        inventory = {}

        for group_name in sorted(groups):
            data = groups[group_name]
            entry = {}

            # Hosts (always emitted)
            entry["hosts"] = dict(sorted(data["hosts"].items()))

            # Children (only if defined)
            if data["children"]:
                entry["children"] = {child: {} for child in sorted(data["children"])}

            inventory[group_name] = entry

        # Output filename
        if args.output:
            output_file = args.output
        else:
            while True:
                output_file = input("Output filename (inventory.yml): ").strip()
                if output_file:
                    break

        # Write YAML
        try:
            with open(output_file, "w") as f:
                yaml.safe_dump(
                    inventory,
                    f,
                    indent=2,
                    sort_keys=False,
                    default_flow_style=False
                )
        except Exception as e:
            print(f"Error writing file: {e}")
            sys.exit(1)

        print(f"\nInventory written to {output_file}\n")

        print("Preview:")
        print("-" * 40)
        with open(output_file) as f:
            print(f.read())

    if __name__ == "__main__":
        main()
                

Execution Demo

Running the script and generating an inventory:

Script execution in terminal

Verifying the generated inventory with Ansible:

ansible-inventory --list output

Conclusion

This lightweight tool makes Ansible inventory creation fast, error-free, and repeatable—ideal for airgapped setups or quick prototyping. Save as yml-create.py, make executable if desired, and start building inventories effortlessly.