Skip to content

Instantly share code, notes, and snippets.

@thoroc
Created April 14, 2023 07:02
Show Gist options
  • Select an option

  • Save thoroc/7bcc5bce2dc49f5ec7cbe662f2aeda10 to your computer and use it in GitHub Desktop.

Select an option

Save thoroc/7bcc5bce2dc49f5ec7cbe662f2aeda10 to your computer and use it in GitHub Desktop.
cloudformation template to terraform template
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()
[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