Monday 20 May 2019

Create CloudFormation template using troposphere

Here step by a step python code to create IAM role.
This is not production ready code. It is just as a sample.


CreateRole.py

from awacs.aws import Allow, Deny, Statement, Principal, Policy, Action
from troposphere import Template, Ref, GetAtt, Output
from troposphere.iam import Role, PolicyType
from awacs.sts import AssumeRole
from libs.common import *


class CfTemplate(object):
    template = Template()

    @classmethod
    def resource_title(cls, name):
        camel_case_name = name.title().replace("_", "").replace("-", "")
        return camel_case_name


class Security(CfTemplate):
    @classmethod
    def create_admin_role(cls, name):
        admin_role = CfTemplate.template.add_resource(Role(
            "%sRole" % CfTemplate.resource_title(name),
            AssumeRolePolicyDocument=Policy(
                Statement=[
                    Statement(Effect=Allow,
                              Principal=Principal("Service", ["ec2.amazonaws.com"]),
                              Action=[AssumeRole], )
                ]
            ),
            ManagedPolicyArns=[
                "arn:aws:iam::aws:policy/AdministratorAccess"
            ]
        ))

        CfTemplate.template.add_output(
            Output(
                "%sOutput" % cls.resource_title(name),
                Description="RoleArn",
                Value=GetAtt(admin_role, "Arn")
            )
        )

        return admin_role

    @classmethod
    def create_developer_role(cls, name):
        dev_role = CfTemplate.template.add_resource(Role(
            "%sRole" % CfTemplate.resource_title(name),
            AssumeRolePolicyDocument=Policy(
                Statement=[
                    Statement(Effect=Allow,
                              Principal=Principal("Service", ["ec2.amazonaws.com"]),
                              Action=[AssumeRole], )
                ]
            ),
            ManagedPolicyArns=[
                "arn:aws:iam::aws:policy/AWSElasticBeanstalkFullAccess",
                "arn:aws:iam::aws:policy/AmazonElasticMapReduceFullAccess",
                "arn:aws:iam::aws:policy/service-role/AWSDataPipelineRole"
            ]
        ))

        CfTemplate.template.add_output(
            Output(
                "%sOutput" % cls.resource_title(name),
                Description="RoleArn",
                Value=GetAtt(dev_role, "Arn")
            )
        )

        return dev_role

    @classmethod
    def create_developer_policy(cls, developer_role):
        developer_policy = CfTemplate.template.add_resource(PolicyType(
            "DeveloperPolicy",
            PolicyName="DeveloperPolicy",
            Roles=[Ref(developer_role)],
            PolicyDocument=Policy(
                Statement=[
                    Statement(
                        Effect=Deny,
                        Action=[
                            # VPCs
                            Action("ec2", "CreateVpc"),
                            Action("ec2", "ModifyVpcAttribute"),
                            Action("ec2", "DeleteVpc"),
                            # VPC endpoints
                            Action("ec2", "CreateVpcEndpoint"),
                            Action("ec2", "ModifyVpcEndpoint"),
                            Action("ec2", "DeleteVpcEndpoints"),
                            # VPC classic
                            Action("ec2", "AttachClassicLinkVpc"),
                            Action("ec2", "DetachClassicLinkVpc"),
                            Action("ec2", "EnableVpcClassicLink"),
                            Action("ec2", "DisableVpcClassicLink"),
                            Action("ec2", "EnableVpcClassicLinkDnsSupport"),
                            Action("ec2", "DisableVpcClassicLinkDnsSupport"),
                            # VPC peerings
                            Action("ec2", "CreateVpcPeeringConnection"),
                            Action("ec2", "ModifyVpcPeeringConnectionOptions"),
                            Action("ec2", "RejectVpcPeeringConnection"),
                            Action("ec2", "DeleteVpcPeeringConnection"),
                            # Subnets
                            Action("ec2", "CreateSubnet"),
                            Action("ec2", "DeleteSubnet"),
                            Action("ec2", "ModifySubnetAttribute"),
                            # Route Tables
                            Action("ec2", "AssociateRouteTable"),
                            Action("ec2", "ReplaceRouteTableAssociation"),
                            Action("ec2", "DisassociateRouteTable"),
                            # Routes
                            Action("ec2", "CreateRoute"),
                            Action("ec2", "ReplaceRoute"),
                            Action("ec2", "DeleteRoute"),
                            Action("ec2", "CreateRouteTable"),
                            Action("ec2", "DeleteRouteTable"),
                            # VPN routing
                            Action("ec2", "CreateVpnConnectionRoute"),
                            Action("ec2", "DeleteVpnConnectionRoute"),
                            Action("ec2", "EnableVgwRoutePropagation"),
                            Action("ec2", "DisableVgwRoutePropagation"),
                            # Customer Gateways
                            Action("ec2", "CreateCustomerGateway"),
                            Action("ec2", "DeleteCustomerGateway"),
                            # VPN Gateways
                            Action("ec2", "CreateVpnGateway"),
                            Action("ec2", "DeleteVpnGateway"),
                            Action("ec2", "CreateVpnConnection"),
                            Action("ec2", "DeleteVpnConnection"),
                            Action("ec2", "CreateVpnConnectionRoute"),
                            Action("ec2", "DeleteVpnConnectionRoute"),
                            Action("ec2", "AttachVpnGateway"),
                            Action("ec2", "DetachVpnGateway"),
                            # Network ACLs
                            Action("ec2", "CreateNetworkAcl"),
                            Action("ec2", "DeleteNetworkAcl"),
                            Action("ec2", "CreateNetworkAclEntry"),
                            Action("ec2", "ReplaceNetworkAclEntry"),
                            Action("ec2", "DeleteNetworkAclEntry"),
                            Action("ec2", "ReplaceNetworkAclAssociation"),
                            # Reserved, scheduled and dedicated instances
                            Action("ec2", "CancelReservedInstancesListing"),
                            Action("ec2", "ModifyReservedInstances"),
                            Action("ec2", "PurchaseReservedInstancesOffering"),
                            Action("ec2", "PurchaseHostReservation"),
                            Action("ec2", "PurchaseScheduledInstances"),
                        ],
                        Resource=["*"]
                    ),
                    Statement(
                        Effect=Deny,
                        Action=[
                            Action("*"),
                        ],
                        Resource=[
                            GetAtt(developer_role, "Arn"),
                            "arn:aws:cloudformation:::stack/BaseInfrastructure*",
                        ],
                    ),
                    Statement(
                        Effect=Allow,
                        Action=[
                            Action("support", "*"),
                            Action("ec2", "*"),
                            Action("ecs", "*"),
                            Action("ecr", "*"),
                            Action("lambda", "*"),
                            Action("elasticbeanstalk", "*"),
                            Action("s3", "*"),
                            Action("glacier", "*"),
                            Action("rds", "*"),
                            Action("dynamodb", "*"),
                            Action("elasticache", "*"),
                            Action("redshift", "*"),
                            Action("route53", "*"),
                            Action("cloudwatch", "*"),
                            Action("events", "*"),
                            Action("logs", "*"),
                            Action("cloudformation", "*"),
                            Action("elasticmapreduce", "*"),
                            Action("datapipeline", "*"),
                            Action("es", "*"),
                            Action("kinesis", "*"),
                            Action("kinesisanalytics", "*"),
                            Action("firehose", "*"),
                            Action("sns", "*"),
                            Action("sqs", "*"),
                            Action("apigateway", "*"),
                            Action("autoscaling", "*"),
                            Action("application-autoscaling", "*")
                        ],
                        Resource=["*"],
                    )
                ],
            )
        ))

admin = Security.create_admin_role("administrator")
dev = Security.create_developer_role("developer")
Security.create_developer_policy(dev)

t = CfTemplate.template.to_json(indent=None)
client = connect('us-east-1')
validate_template(client, t)


__________

You have to create common python file which create CF

common.py

from StringIO import StringIO
from base64 import b64encode
from datetime import datetime
from gzip import GzipFile
from pprint import pprint
from shutil import copyfileobj
from time import time, mktime
from uuid import uuid4

import botocore
from boto3 import Session
from troposphere.ec2 import SecurityGroupRule


def connect(region):
    session = Session(region_name=region)
    connectors = {
        'cloudformation': session.client('cloudformation'),
        'iam': session.client('iam'),
        'ec2': session.client('ec2'),
        's3': session.client('s3')
    }
    return connectors


def validate_template(aws, template):
    response = aws['cloudformation'].validate_template(
        TemplateBody=template
    )
    pprint(response)


def push_cloudformation(aws, template, stack_name, parameters_list=list()):
    def create_stack_change(aws, template, stack_name, parameters_list):
        response = aws['cloudformation'].create_change_set(
            StackName=stack_name,
            TemplateBody=template,
            ChangeSetName='changeset%s' % (datetime.fromtimestamp(time()).strftime('%Y%m%d%H%M%S')),
            Description=str(uuid4()),
            Capabilities=[
                'CAPABILITY_IAM',
            ],
            Parameters=parameters_list
        )
        pprint(response)

    def create_stack(aws, template, stack_name, parameters_list):
        response = aws['cloudformation'].create_stack(
            StackName=stack_name,
            TemplateBody=template,
            Capabilities=[
                'CAPABILITY_IAM',
            ],
            OnFailure='DO_NOTHING',
            Parameters=parameters_list
        )
        pprint(response)

    def lookup_stack(aws, stackname, ):
        try:
            response = aws['cloudformation'].describe_stacks(
                StackName=stackname,
            )
            for stack in response.get('Stacks'):
                if "PROGRESS" in stack.get('StackStatus'):
                    raise SystemExit("Stack update in progress")
                elif "FAILED" in stack.get('StackStatus'):
                    raise SystemExit("Stack in failed status")
                else:
                    return ("stack_ready")

        except botocore.exceptions.ClientError as e:
            if e.response['Error']['Code'] == 'ValidationError':
                return ("non_existing_stack")

    status = lookup_stack(aws, stack_name)
    if status is "non_existing_stack":
        print("Creating new stack...")
        create_stack(aws, template, stack_name, parameters_list)
    elif status is "stack_ready":
        print("Creating change set...")
        create_stack_change(aws, template, stack_name, parameters_list)


def generate_sg_rules(protocol, low_port, high_port, acl_ips):
    acl = []
    for ip in acl_ips:
        acl.append(SecurityGroupRule(
            IpProtocol=protocol,
            FromPort=low_port,
            ToPort=high_port,
            CidrIp=ip,
        ))
    return acl


def get_data_from_stack(region, stack_name, key):
    client = connect(region)
    response = client['cloudformation'].describe_stacks(
        StackName=stack_name,
    )
    for i in response.get('Stacks'):
        for j in i.get('Outputs'):
            if j.get('OutputKey') == key:
                return j.get('OutputValue')


def prepare_ansible_playbook(file_path):
    magicdate = datetime(2016, 1, 1, 2, 15)
    gz = StringIO()
    with open(file_path, 'rb') as f_in, GzipFile(fileobj=gz, mode="wb", mtime=mktime(magicdate.timetuple())) as f_out:
        copyfileobj(f_in, f_out)

    gz.seek(0)
    gz_b64 = b64encode(gz.read())



    return gz_b64

No comments:

Post a Comment