From bc64478101d45db0ddb476e2835205a53e67ea4c Mon Sep 17 00:00:00 2001 From: Gianluigi Mucciolo Date: Thu, 21 May 2020 14:47:50 +0200 Subject: [PATCH 1/3] add macro --- macro/README.md | 75 +++++++++++++++++++++++++++++++++++ macro/example.py | 25 ++++++++++++ macro/source/macro.py | 29 ++++++++++++++ macro/source/requirements.txt | 1 + macro/template.yaml | 40 +++++++++++++++++++ 5 files changed, 170 insertions(+) create mode 100644 macro/README.md create mode 100644 macro/example.py create mode 100755 macro/source/macro.py create mode 100644 macro/source/requirements.txt create mode 100644 macro/template.yaml diff --git a/macro/README.md b/macro/README.md new file mode 100644 index 000000000..b28c3a56b --- /dev/null +++ b/macro/README.md @@ -0,0 +1,75 @@ +# Troposphere Macro + +The `Troposphere` macro adds the ability to create CloudFormation resources from troposphere stacks. + +# How to install and use the Macro in your AWS account + +## Deploying + +1. You will need an S3 bucket to store the CloudFormation artifacts: + * If you don't have one already, create one with `aws s3 mb s3://` + +2. Install all python requirements + + ```shell + pip install -r source/requirements.txt -t source + ``` + +3. Package the CloudFormation template. The provided template uses [the AWS Serverless Application Model](https://aws.amazon.com/about-aws/whats-new/2016/11/introducing-the-aws-serverless-application-model/) so must be transformed before you can deploy it. + + ```shell + aws cloudformation package \ + --template-file template.yaml \ + --s3-bucket \ + --output-template-file template.output + ``` + +4. Deploy the packaged CloudFormation template to a CloudFormation stack: + + ```shell + aws cloudformation deploy \ + --stack-name troposphere-macro \ + --template-file template.output \ + --capabilities CAPABILITY_IAM + ``` + +5. To test out the macro's capabilities, try launching the provided example template: + + ```shell + aws cloudformation deploy \ + --stack-name template-macro-example \ + --template-file example.py + ``` + +## Usate + +Just add your resources to macro_template object, the template object is created by macro itself. +You can provide your troposphere code in Troposphere tag. + +``` +Transform: [Troposhere] +Description: Example Macro Troposhere. + +Parameters: + InstanceName: + Type: String + Default: "MyInstance" + ImageId: + Type: String + Default: "ami-951945d0" + InstanceType: + Type: String + Default: "t1.micro" + +Troposhere: | + from troposphere import Ref + import troposphere.ec2 as ec2 + + instance = ec2.Instance('MyInstance') + + instance.ImageId = Ref('ImageId') + instance.InstanceType = Ref('InstanceType') + instance.Tags = ec2.Tags(Name = Ref('InstanceName')) + + macro_template.add_resource(instance) +``` \ No newline at end of file diff --git a/macro/example.py b/macro/example.py new file mode 100644 index 000000000..b252bf447 --- /dev/null +++ b/macro/example.py @@ -0,0 +1,25 @@ +Transform: [Troposhere] +Description: Example Macro Troposhere. + +Parameters: + InstanceName: + Type: String + Default: "MyInstance" + ImageId: + Type: String + Default: "ami-eb0c7791" + InstanceType: + Type: String + Default: "t1.micro" + +Troposhere: | + from troposphere import Ref + import troposphere.ec2 as ec2 + + instance = ec2.Instance('MyInstance') + + instance.ImageId = Ref('ImageId') + instance.InstanceType = Ref('InstanceType') + instance.Tags = ec2.Tags(Name = Ref('InstanceName')) + + macro_template.add_resource(instance) \ No newline at end of file diff --git a/macro/source/macro.py b/macro/source/macro.py new file mode 100755 index 000000000..a341847c3 --- /dev/null +++ b/macro/source/macro.py @@ -0,0 +1,29 @@ +import sys, os, io, traceback +from troposphere.template_generator import TemplateGenerator + +def handle_template(template): + troposphere_code = template["Troposhere"] + del template["Troposhere"] + + macro_template = TemplateGenerator(template) + exec(troposphere_code) + + return macro_template.to_json() + +def handler(event, context): + request_id = event['requestId'] + parameters = event['templateParameterValues'] + + macro_response = { + 'status': 'success', + 'requestId': request_id + } + + try: + macro_response['fragment'] = handle_template(event['fragment']) + except Exception as e: + traceback.print_exc() + macro_response['status'] = 'failure' + macro_response['errorMessage'] = str(e) + + return macro_response diff --git a/macro/source/requirements.txt b/macro/source/requirements.txt new file mode 100644 index 000000000..2adba948d --- /dev/null +++ b/macro/source/requirements.txt @@ -0,0 +1 @@ +troposphere[policy] \ No newline at end of file diff --git a/macro/template.yaml b/macro/template.yaml new file mode 100644 index 000000000..4ea01a8ae --- /dev/null +++ b/macro/template.yaml @@ -0,0 +1,40 @@ +Transform: AWS::Serverless-2016-10-31 + +Resources: + TransformExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - 'sts:AssumeRole' + Path: / + Policies: + - PolicyName: root + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 'logs:*' + Resource: 'arn:aws:logs:*:*:*' + + MacroFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: python3.6 + CodeUri: source/ + Handler: macro.handler + Role: !GetAtt TransformExecutionRole.Arn + Timeout: 300 + + Macro: + Type: AWS::CloudFormation::Macro + Properties: + Name: Troposhere + FunctionName: !GetAtt MacroFunction.Arn From a9a3456ee7e3f8877e263ffab51ca8be61b5d390 Mon Sep 17 00:00:00 2001 From: Gianluigi Mucciolo Date: Thu, 21 May 2020 15:04:17 +0200 Subject: [PATCH 2/3] Update README.md --- macro/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/macro/README.md b/macro/README.md index b28c3a56b..23d91f077 100644 --- a/macro/README.md +++ b/macro/README.md @@ -41,7 +41,7 @@ The `Troposphere` macro adds the ability to create CloudFormation resources from --template-file example.py ``` -## Usate +## Usage Just add your resources to macro_template object, the template object is created by macro itself. You can provide your troposphere code in Troposphere tag. @@ -72,4 +72,5 @@ Troposhere: | instance.Tags = ec2.Tags(Name = Ref('InstanceName')) macro_template.add_resource(instance) -``` \ No newline at end of file +``` + From 8bd2f81ea69b365dc53f7f4717d02d5df6ec6afd Mon Sep 17 00:00:00 2001 From: Gianluigi Mucciolo Date: Thu, 21 May 2020 22:10:12 +0200 Subject: [PATCH 3/3] add Makefile, update example with macro_parameters example --- macro/Makefile | 10 ++++++++++ macro/example.py | 15 ++++++++++----- macro/source/macro.py | 14 +++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 macro/Makefile diff --git a/macro/Makefile b/macro/Makefile new file mode 100644 index 000000000..893872025 --- /dev/null +++ b/macro/Makefile @@ -0,0 +1,10 @@ +BUCKET=macro-template-default-831650818513-us-east-1 + +macro: + pip install -r source/requirements.txt -t source + aws cloudformation package --template-file template.yaml --s3-bucket $(BUCKET) --output-template-file template.output + aws cloudformation deploy --template-file template.output --stack-name TroposphereMacro --capabilities CAPABILITY_IAM + rm -rf template.output + +sample: + aws cloudformation deploy --stack-name template-macro-example --template-file example.py --parameter-overrides InstanceName=NewName \ No newline at end of file diff --git a/macro/example.py b/macro/example.py index b252bf447..0baff7d97 100644 --- a/macro/example.py +++ b/macro/example.py @@ -16,10 +16,15 @@ from troposphere import Ref import troposphere.ec2 as ec2 - instance = ec2.Instance('MyInstance') + list_instances = ["One", "Two", "Three", "Four"] - instance.ImageId = Ref('ImageId') - instance.InstanceType = Ref('InstanceType') - instance.Tags = ec2.Tags(Name = Ref('InstanceName')) + instance_name = macro_parameters["InstanceName"] - macro_template.add_resource(instance) \ No newline at end of file + for el in list_instances: + instance = ec2.Instance('MyInstance{}'.format(el)) + + instance.ImageId = Ref('ImageId') + instance.InstanceType = Ref('InstanceType') + instance.Tags = ec2.Tags(Name = instance_name + el) + + macro_template.add_resource(instance) \ No newline at end of file diff --git a/macro/source/macro.py b/macro/source/macro.py index a341847c3..b55a464b0 100755 --- a/macro/source/macro.py +++ b/macro/source/macro.py @@ -1,18 +1,20 @@ -import sys, os, io, traceback from troposphere.template_generator import TemplateGenerator +import traceback + +def handle_template(event): + template = event['fragment'] + macro_parameters = event['templateParameterValues'] -def handle_template(template): troposphere_code = template["Troposhere"] del template["Troposhere"] macro_template = TemplateGenerator(template) exec(troposphere_code) - return macro_template.to_json() + return macro_template.to_dict() def handler(event, context): request_id = event['requestId'] - parameters = event['templateParameterValues'] macro_response = { 'status': 'success', @@ -20,10 +22,12 @@ def handler(event, context): } try: - macro_response['fragment'] = handle_template(event['fragment']) + macro_response['fragment'] = handle_template(event) except Exception as e: traceback.print_exc() macro_response['status'] = 'failure' macro_response['errorMessage'] = str(e) return macro_response + +