Created
April 14, 2023 07:02
-
-
Save thoroc/7bcc5bce2dc49f5ec7cbe662f2aeda10 to your computer and use it in GitHub Desktop.
cloudformation template to terraform template
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
| import sys | |
| import json | |
| from pathlib import Path | |
| from collections import OrderedDict | |
| from typing import Optional | |
| import boto3 | |
| from botocore.exceptions import ClientError | |
| import click | |
| from loguru import logger | |
| import oyaml as yaml | |
| import cf2tf.save | |
| from cf2tf.cloudformation import Template | |
| from cf2tf.convert import TemplateConverter | |
| from cf2tf.terraform import code | |
| DATA_DIR = Path(Path.cwd(), "data") | |
| # lifted directly from https://github.com/DontShaveTheYak/cf2tf/blob/master/src/cf2tf/app.py | |
| def convert_template(output: Optional[str], template_path: str): | |
| """Convert Cloudformation template into Terraform. | |
| Args: | |
| template_path (str): The path to the cloudformation template | |
| """ | |
| # Need to take this path and parse the cloudformation file | |
| tmpl_path = Path(template_path) | |
| # Where/how we will write the results | |
| output_writer = cf2tf.save.create_writer(output) | |
| logger.info("Converting {} to Terraform!", tmpl_path.name) | |
| logger.debug("Template location is {}", tmpl_path) | |
| cf_template = Template.from_yaml(tmpl_path).template | |
| # Need to get the code from the repo | |
| search_manger = code.search_manager() | |
| # Turn Cloudformation template into a Terraform configuration | |
| config = TemplateConverter(tmpl_path.stem, cf_template, search_manger).convert() | |
| # Save this configuration to disc | |
| config.save(output_writer) | |
| def get_template(stack, save: bool = False, output_dir: Path = None): | |
| """Fetch the CF template for the given Stack.""" | |
| logger.info("Get CF template for Stack={}", stack.name) | |
| session = boto3.session.Session() | |
| client = session.client("cloudformation") | |
| try: | |
| response = client.get_template(StackName=stack.name) | |
| logger.debug("Response: {} {}", type(response), response) | |
| template = response.get("TemplateBody") | |
| # in case we have to deal with OrderedDict | |
| if isinstance(template, OrderedDict): | |
| template = yaml.dump(template) | |
| logger.debug("Template: {} {}", type(template), template) | |
| if save: | |
| logger.info("Saving CF Template") | |
| output_dir = output_dir or Path(DATA_DIR, "cf_templates") | |
| output_file = Path(output_dir, f"cf-{stack.name}.yaml") | |
| output_file.parent.mkdir(parents=True, exist_ok=True) | |
| logger.info("Saving CF Template to: {}", output_file) | |
| output_file.write_text(template, encoding="utf-8") | |
| return output_file | |
| return template | |
| except ClientError as exc: | |
| logger.error(exc) | |
| def list_stacks(match: str = None, save: bool = False): | |
| """Get a list for all the stacks in the account.""" | |
| logger.info("Listing all stacks matching the pattern: '{}'", match) | |
| cfn = boto3.resource("cloudformation") | |
| statuses = ["ROLLBACK_COMPLETE", "CREATE_COMPLETE", "UPDATE_COMPLETE"] | |
| stacks = [stack for stack in cfn.stacks.all() if stack.stack_status in statuses] | |
| if match: | |
| stacks = list(filter(lambda s: match in s.name, stacks)) | |
| logger.debug("Stacks: {}", stacks) | |
| if save: | |
| logger.info("Saving all stacks") | |
| data = {"Resources": [stack.name for stack in stacks]} | |
| output_file = Path(DATA_DIR, "cf-stacks.json") | |
| output_file.parent.mkdir(parents=True, exist_ok=True) | |
| logger.debug("Saving stacks to {}", output_file) | |
| output_file.write_text(json.dumps(data, indent=4), encoding="utf-8") | |
| return stacks | |
| @click.command() | |
| @click.option( | |
| "-m", | |
| "--match", | |
| required=False, | |
| type=str, | |
| help="String matching for the stack's name(s)", | |
| ) | |
| @click.option( | |
| "-d", | |
| "--debug", | |
| is_flag=True, | |
| help="Enable debug mode. Prints debug messages to the console.", | |
| ) | |
| def run(match: str, debug: bool): | |
| """Main function""" | |
| logger.info("Running Main thread") | |
| if not debug: | |
| # default loguru level is DEBUG | |
| logger.remove() | |
| logger.add(sys.stderr, level="INFO") | |
| stacks = list_stacks(match=match, save=True) | |
| for stack in stacks: | |
| template_path = get_template(stack, save=True) | |
| output_dir = Path(DATA_DIR, "terraform", template_path.stem) | |
| convert_template(str(output_dir), str(template_path)) | |
| if __name__ == "__main__": | |
| run() |
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
| [tool.poetry] | |
| name = "cf_to_tf_converted" | |
| version = "0.1.0" | |
| description = "QnD script to convert cf template to terraform template" | |
| [tool.poetry.dependencies] | |
| python = "^3.10" | |
| boto3 = "^1.26.103" | |
| cf2tf = "^0.5.0" | |
| loguru = "^0.7.0" | |
| click = "^8.1.3" | |
| tqdm = "^4.65.0" | |
| oyaml = "^1.0" | |
| [tool.poetry.group.dev.dependencies] | |
| black = "^23.3.0" | |
| pylint = "^2.17.2" | |
| ruff = "^0.0.261" | |
| [build-system] | |
| requires = ["poetry-core"] | |
| build-backend = "poetry.core.masonry.api" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment