Created
April 24, 2026 17:04
-
-
Save amalagaura/d5fcd1b24b2b0fe3446c35e1d1acbbb3 to your computer and use it in GitHub Desktop.
tf-attr-editor - Edit attributes within a list of HCL objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| tf-attr-editor - Edit attributes within a list of HCL objects | |
| """ | |
| import argparse | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| import hcl2 | |
| __version__ = "0.4.3-debug" | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("-file", required=True) | |
| parser.add_argument("-path", required=True) | |
| parser.add_argument("-pos", type=int, required=True) | |
| parser.add_argument("-field", required=True) | |
| parser.add_argument("-value", required=True) | |
| parser.add_argument("--debug", action="store_true", help="Print parsed HCL structure") | |
| parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}") | |
| args = parser.parse_args() | |
| file_path = Path(args.file) | |
| if not file_path.exists(): | |
| print(f"Error: file {file_path} does not exist", file=sys.stderr) | |
| sys.exit(1) | |
| # Parse HCL | |
| try: | |
| with open(file_path, "r", encoding="utf-8") as f: | |
| data = hcl2.load(f) | |
| except Exception as e: | |
| print(f"Error: invalid HCL: {e}", file=sys.stderr) | |
| sys.exit(1) | |
| if args.debug: | |
| print("=== DEBUG: Parsed HCL structure ===") | |
| import pprint | |
| pprint.pprint(data, indent=2) | |
| print("=================================") | |
| # Try to find the list | |
| target_list = None | |
| # Case 1: Top-level list | |
| if args.path in data and isinstance(data[args.path], list): | |
| target_list = data[args.path] | |
| # Case 2: locals.my_list | |
| elif args.path.startswith("locals."): | |
| list_name = args.path.split(".")[-1] | |
| for loc in data.get("locals", []): | |
| if isinstance(loc, dict) and list_name in loc and isinstance(loc[list_name], list): | |
| target_list = loc[list_name] | |
| break | |
| # Case 3: Just "my_list" inside locals | |
| elif args.path == "my_list": | |
| for loc in data.get("locals", []): | |
| if isinstance(loc, dict) and "my_list" in loc and isinstance(loc["my_list"], list): | |
| target_list = loc["my_list"] | |
| break | |
| if target_list is None: | |
| print(f"Error: list '{args.path}' not found", file=sys.stderr) | |
| print("Tip: Run with --debug to see the parsed structure", file=sys.stderr) | |
| sys.exit(1) | |
| # Rest of the logic (unchanged) | |
| if not isinstance(target_list, list) or args.pos < 0 or args.pos >= len(target_list): | |
| print(f"Error: position {args.pos} is out of bounds", file=sys.stderr) | |
| sys.exit(1) | |
| target_obj = target_list[args.pos] | |
| if not isinstance(target_obj, dict): | |
| print(f"Error: element at position {args.pos} is not an object", file=sys.stderr) | |
| sys.exit(1) | |
| if args.field in target_obj: | |
| print(f"Error: field '{args.field}' already exists", file=sys.stderr) | |
| sys.exit(1) | |
| target_obj[args.field] = args.value | |
| try: | |
| with open(file_path, "w", encoding="utf-8") as f: | |
| hcl2.dump(data, f) | |
| except Exception as e: | |
| print(f"Error: failed to write file: {e}", file=sys.stderr) | |
| sys.exit(1) | |
| try: | |
| subprocess.run(["terraform", "fmt", "-write=true", str(file_path)], check=True, capture_output=True) | |
| except Exception: | |
| pass | |
| print(f"✅ Successfully added '{args.field}' to object at position {args.pos} in {args.path}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment