{"id":4335,"date":"2021-04-18T21:44:19","date_gmt":"2021-04-18T19:44:19","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=4335"},"modified":"2021-06-27T17:40:46","modified_gmt":"2021-06-27T15:40:46","slug":"cloudformation-6","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2021\/04\/18\/cloudformation-6\/","title":{"rendered":"CloudFormation &#8211; 6 &#8211; DependsOn, Lambda"},"content":{"rendered":"<p><!--more--><\/p>\n<p><span style=\"color: #3366ff;\">DependsOn<\/span><\/p>\n<p>DependsOn is a way to say that some resource can&#8217;t be created before other resource is created.\u00a0 Consider a template::<\/p>\n<pre class=\"lang:default decode:true \">AWSTemplateFormatVersion: '2010-09-09'\r\nMappings:\r\n  AWSRegionArch2AMI:\r\n    us-east-1:\r\n      HVM64: ami-6869aa05\r\n    us-west-2:\r\n      HVM64: ami-7172b611\r\n    us-west-1:\r\n      HVM64: ami-31490d51\r\n    eu-west-1:\r\n      HVM64: ami-f9dd458a\r\n    eu-central-1:\r\n      HVM64: ami-ea26ce85\r\n    ap-northeast-1:\r\n      HVM64: ami-374db956\r\n    ap-northeast-2:\r\n      HVM64: ami-2b408b45\r\n    ap-southeast-1:\r\n      HVM64: ami-a59b49c6\r\n    ap-southeast-2:\r\n      HVM64: ami-dc361ebf\r\n    ap-south-1:\r\n      HVM64: ami-ffbdd790\r\n    us-east-2:\r\n      HVM64: ami-f6035893\r\n    sa-east-1:\r\n      HVM64: ami-6dd04501\r\n    cn-north-1:\r\n      HVM64: ami-8e6aa0e3\r\n\r\nResources:\r\n  Ec2Instance:\r\n    Type: AWS::EC2::Instance\r\n    Properties:\r\n      ImageId: !FindInMap [AWSRegionArch2AMI, !Ref 'AWS::Region', HVM64]\r\n    DependsOn: MyDB\r\n\r\n  MyDB:\r\n    Type: AWS::RDS::DBInstance\r\n    Properties:\r\n      AllocatedStorage: '5'\r\n      DBInstanceClass: db.t2.micro\r\n      Engine: MySQL\r\n      EngineVersion: \"5.7.22\"\r\n      MasterUsername: MyName\r\n      MasterUserPassword: MyPassword\r\n    # Readme - note: Added a DeletionPolicy (not shown in the video)\r\n    # This ensures that the RDS DBInstance does not create a snapshot when it's deleted\r\n    # Otherwise, you would be billed for the snapshot :)\r\n    DeletionPolicy: Delete\r\n<\/pre>\n<p>EC2 instance will not be created until MyDB resource is successfully created.<\/p>\n<p>Let&#8217;s run the template:<\/p>\n<p><code>CloudFormation -&gt; Stacks -&gt; Create stack<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4342 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation24.jpg\" alt=\"\" width=\"710\" height=\"482\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation24.jpg 710w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation24-300x204.jpg 300w\" sizes=\"(max-width: 710px) 100vw, 710px\" \/><\/p>\n<p><code>Next -&gt; Next -&gt; Create Stack<\/code><\/p>\n<p>As we see the MyDB is beeing created as a first resource in the stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4344 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation25.jpg\" alt=\"\" width=\"846\" height=\"466\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation25.jpg 846w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation25-300x165.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation25-768x423.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Deploying Lambda Functions<\/span><\/p>\n<p>First way of defining a lambda function in CloudFormation is Inline.<\/p>\n<p>Consider such a template:<\/p>\n<pre class=\"lang:default decode:true \">Resources: \r\n  LambdaExecutionRole:\r\n    Type: AWS::IAM::Role\r\n    Properties:\r\n      AssumeRolePolicyDocument:\r\n        Version: '2012-10-17'\r\n        Statement:\r\n        - Effect: Allow\r\n          Principal:\r\n            Service:\r\n            - lambda.amazonaws.com\r\n          Action:\r\n          - sts:AssumeRole\r\n      Path: \"\/\"\r\n      Policies:\r\n      - PolicyName: root\r\n        PolicyDocument:\r\n          Version: '2012-10-17'\r\n          Statement:\r\n          - Effect: Allow\r\n            Action:\r\n            - \"s3:*\"\r\n            Resource: \"*\"\r\n          - Effect: Allow\r\n            Action:\r\n            - \"logs:CreateLogGroup\"\r\n            - \"logs:CreateLogStream\"\r\n            - \"logs:PutLogEvents\"\r\n            Resource: \"*\"\r\n\r\n  ListBucketsS3Lambda: \r\n    Type: \"AWS::Lambda::Function\"\r\n    Properties: \r\n      Handler: \"index.handler\"\r\n      Role: \r\n        Fn::GetAtt: \r\n          - \"LambdaExecutionRole\"\r\n          - \"Arn\"\r\n      Runtime: \"python3.7\"\r\n      Code: \r\n        ZipFile: |\r\n          import boto3\r\n\r\n          # Create an S3 client\r\n          s3 = boto3.client('s3')\r\n\r\n          def handler(event, context):\r\n            # Call S3 to list current buckets\r\n            response = s3.list_buckets()\r\n\r\n            # Get a list of all bucket names from the response\r\n            buckets = [bucket['Name'] for bucket in response['Buckets']]\r\n\r\n            # Print out the bucket list\r\n            print(\"Bucket List: %s\" % buckets)\r\n\r\n            return buckets<\/pre>\n<p>Let&#8217;s run the template:<\/p>\n<p><code>CloudFormation -&gt; Stacks -&gt; Create stack<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4347 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation26.jpg\" alt=\"\" width=\"715\" height=\"488\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation26.jpg 715w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation26-300x205.jpg 300w\" sizes=\"(max-width: 715px) 100vw, 715px\" \/><\/p>\n<p><code>Next -&gt; Next -&gt; Create stack -&gt; Requires capabilities : [CAPABILITY_IAM] -&gt;\u00a0 Review LambdaInline<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4348 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation27.jpg\" alt=\"\" width=\"693\" height=\"336\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation27.jpg 693w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation27-300x145.jpg 300w\" sizes=\"(max-width: 693px) 100vw, 693px\" \/><\/p>\n<p><code>Create stack -&gt;<\/code><\/p>\n<p>As we see the lambda function has been created:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4351 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation28.jpg\" alt=\"\" width=\"927\" height=\"734\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation28.jpg 927w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation28-300x238.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation28-768x608.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>Let&#8217;s test the lambda function:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4352 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation30.jpg\" alt=\"\" width=\"805\" height=\"797\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation30.jpg 805w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation30-300x297.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation30-150x150.jpg 150w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation30-768x760.jpg 768w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation30-100x100.jpg 100w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>Lambda function simply list all s3 buckets which we have in AWS account.<\/p>\n<p>A second way of defining a lambda function in CloudFormation is to use a zip from S3.<\/p>\n<p>First we need to zip an <code>index.py<\/code> file:<\/p>\n<pre class=\"lang:default decode:true\">import boto3\r\n\r\n# Create an S3 client\r\ns3 = boto3.client('s3')\r\n\r\ndef handler(event, context):\r\n    # Call S3 to list current buckets\r\n    response = s3.list_buckets()\r\n\r\n    # Get a list of all bucket names from the response\r\n    buckets = [bucket['Name'] for bucket in response['Buckets']]\r\n\r\n    # Print out the bucket list\r\n    print(\"Bucket List: %s\" % buckets)\r\n\r\n    return buckets<\/pre>\n<p>And then upload the zip file to the S3 service.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4357\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation31.jpg\" alt=\"\" width=\"811\" height=\"679\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation31.jpg 811w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation31-300x251.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation31-768x643.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Upload-&gt;<\/code><\/p>\n<p>In CloudFormation template file we need to reference to the uploaded zip file:<\/p>\n<pre class=\"lang:default decode:true\">Parameters:\r\n  S3BucketParam:\r\n    Type: String\r\n  S3KeyParam:\r\n    Type: String\r\n\r\nResources: \r\n  LambdaExecutionRole:\r\n    Type: AWS::IAM::Role\r\n    Properties:\r\n      AssumeRolePolicyDocument:\r\n        Version: '2012-10-17'\r\n        Statement:\r\n        - Effect: Allow\r\n          Principal:\r\n            Service:\r\n            - lambda.amazonaws.com\r\n          Action:\r\n          - sts:AssumeRole\r\n      Path: \"\/\"\r\n      Policies:\r\n      - PolicyName: root\r\n        PolicyDocument:\r\n          Version: '2012-10-17'\r\n          Statement:\r\n          - Effect: Allow\r\n            Action:\r\n            - \"s3:*\"\r\n            Resource: \"*\"\r\n          - Effect: Allow\r\n            Action:\r\n            - \"logs:CreateLogGroup\"\r\n            - \"logs:CreateLogStream\"\r\n            - \"logs:PutLogEvents\"\r\n            Resource: \"*\"\r\n\r\n  ListBucketsS3Lambda: \r\n    Type: \"AWS::Lambda::Function\"\r\n    Properties: \r\n      Handler: \"index.handler\"\r\n      Role: \r\n        Fn::GetAtt: \r\n          - \"LambdaExecutionRole\"\r\n          - \"Arn\"\r\n      Runtime: \"python3.7\"\r\n      Code: \r\n        S3Bucket: !Ref S3BucketParam\r\n        S3Key: !Ref S3KeyParam\r\n<\/pre>\n<p>Now let&#8217;s create a stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4360 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation32.jpg\" alt=\"\" width=\"775\" height=\"741\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation32.jpg 775w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation32-300x287.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation32-768x734.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-4363 size-full\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation33-1.jpg\" alt=\"\" width=\"771\" height=\"582\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation33-1.jpg 771w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation33-1-300x226.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation33-1-768x580.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt; Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4364\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation34.jpg\" alt=\"\" width=\"753\" height=\"265\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation34.jpg 753w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation34-300x106.jpg 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p><code>Create stack -&gt;<\/code><\/p>\n<p>A new lambda function has been created:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4366 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation35.jpg\" alt=\"\" width=\"806\" height=\"748\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation35.jpg 806w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation35-300x278.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation35-768x713.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>The code of new function is the same as old code because CloudFormation retrieved zip file and unziped it.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4367 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36.jpg\" alt=\"\" width=\"789\" height=\"400\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36.jpg 789w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36-300x152.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36-768x389.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>What if we want to upload a new version of lambda-function.zip. We can change <code> S3Bucket: !Ref S3BucketParam<\/code> or <code>S3Key: !Ref S3KeyParam<\/code> but better way is to use a template with versioning:<\/p>\n<pre class=\"lang:default decode:true \">Parameters:\r\n  S3BucketParam:\r\n    Type: String\r\n  S3KeyParam:\r\n    Type: String\r\n  S3ObjectVersionParam:\r\n    Type: String\r\n\r\nResources: \r\n  LambdaExecutionRole:\r\n    Type: AWS::IAM::Role\r\n    Properties:\r\n      AssumeRolePolicyDocument:\r\n        Version: '2012-10-17'\r\n        Statement:\r\n        - Effect: Allow\r\n          Principal:\r\n            Service:\r\n            - lambda.amazonaws.com\r\n          Action:\r\n          - sts:AssumeRole\r\n      Path: \"\/\"\r\n      Policies:\r\n      - PolicyName: root\r\n        PolicyDocument:\r\n          Version: '2012-10-17'\r\n          Statement:\r\n          - Effect: Allow\r\n            Action:\r\n            - \"s3:*\"\r\n            Resource: \"*\"\r\n          - Effect: Allow\r\n            Action:\r\n            - \"logs:CreateLogGroup\"\r\n            - \"logs:CreateLogStream\"\r\n            - \"logs:PutLogEvents\"\r\n            Resource: \"*\"\r\n\r\n  ListBucketsS3Lambda: \r\n    Type: \"AWS::Lambda::Function\"\r\n    Properties: \r\n      Handler: \"index.handler\"\r\n      Role: \r\n        Fn::GetAtt: \r\n          - \"LambdaExecutionRole\"\r\n          - \"Arn\"\r\n      Runtime: \"python3.7\"\r\n      Code: \r\n        S3Bucket: \r\n          Ref: S3BucketParam\r\n        S3Key: \r\n          Ref: S3KeyParam\r\n        S3ObjectVersion:\r\n          Ref: S3ObjectVersionParam<\/pre>\n<p>Let&#8217;s upload the same lambda-function.zip file to the S3 bucket and check the version-id:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4371 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36-2.jpg\" alt=\"\" width=\"771\" height=\"539\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36-2.jpg 771w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36-2-300x210.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation36-2-768x537.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>Now let&#8217;s update the stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4372 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation38.jpg\" alt=\"\" width=\"866\" height=\"675\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation38.jpg 866w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation38-300x234.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation38-768x599.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4373 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation39.jpg\" alt=\"\" width=\"704\" height=\"466\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation39.jpg 704w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation39-300x199.jpg 300w\" sizes=\"(max-width: 704px) 100vw, 704px\" \/><\/p>\n<p>Next -&gt;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-4375 size-full aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation41-1.jpg\" alt=\"\" width=\"763\" height=\"646\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation41-1.jpg 763w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation41-1-300x254.jpg 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p><code>Update stack -&gt;<\/code><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Custom Resources<\/span><\/p>\n<p>You can define a Custom Resource in CloudFormation to address any of these use cases:<\/p>\n<ul>\n<li>An AWS resource is yet not covered (new service for example)<\/li>\n<li>An On-Premise resource<\/li>\n<li>Emptying an S3 bucket before being deleted<\/li>\n<li>Fetch an AMI id<\/li>\n<li>Anything you want&#8230;!<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4381 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation42.jpg\" alt=\"\" width=\"519\" height=\"803\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation42.jpg 519w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation42-194x300.jpg 194w\" sizes=\"(max-width: 519px) 100vw, 519px\" \/><\/p>\n<p>CloudFormation Custom Resources (Lambda)<\/p>\n<ul>\n<li>The Lambda Function will get invoked only if there is a Create, Update or Delete event, not every time you run the template<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true \">{ \r\n  \"RequestType\" : \"Create\", \r\n  \"ResponseURL\" : \"http:\/\/pre\u2014signed\u2014S3\u2014url\u2014for\u2014response\", \r\n  \"StackId\" : \"arn:aws:cloudformation:us\u2014west-2:123456789012:stack\/stack\u2014name\/guid\", \r\n  \"RequestId\" : \"unique id for this create request\", \r\n  \"ResourceType\" : \"Custom::TestResource\", \r\n  \"LogicalResourceId\" : \"MyTestResource\", \r\n  \"ResourceProperties\" : { \r\n     \"Name\" : \"Value\", \r\n     \"List\" : [ \"1\", \"2\", \"3\" ]\r\n     } \r\n} \r\n<\/pre>\n<p>Consider a yaml tamplate, this is an inline method:<\/p>\n<pre class=\"lang:default decode:true \">Resources: \r\n  LambdaExecutionRole:\r\n    Type: AWS::IAM::Role\r\n    Properties:\r\n      AssumeRolePolicyDocument:\r\n        Version: '2012-10-17'\r\n        Statement:\r\n        - Effect: Allow\r\n          Principal:\r\n            Service:\r\n            - lambda.amazonaws.com\r\n          Action:\r\n          - sts:AssumeRole\r\n      Path: \"\/\"\r\n      Policies:\r\n      - PolicyName: root\r\n        PolicyDocument:\r\n          Version: '2012-10-17'\r\n          Statement:\r\n          - Effect: Allow\r\n            Action:\r\n            - \"s3:*\"\r\n            Resource: \"*\"\r\n          - Effect: Allow\r\n            Action:\r\n            - \"logs:CreateLogGroup\"\r\n            - \"logs:CreateLogStream\"\r\n            - \"logs:PutLogEvents\"\r\n            Resource: \"*\"\r\n\r\n  EmptyS3BucketLambda: \r\n    Type: \"AWS::Lambda::Function\"\r\n    Properties: \r\n      Handler: \"index.handler\"\r\n      Role: \r\n        Fn::GetAtt: \r\n          - \"LambdaExecutionRole\"\r\n          - \"Arn\"\r\n      Runtime: \"python3.7\"\r\n      # we give the function a large timeout \r\n      # so we can wait for the bucket to be empty\r\n      Timeout: 600\r\n      Code: \r\n        ZipFile: |\r\n          #!\/usr\/bin\/env python\r\n          # -*- coding: utf-8 -*-\r\n          import json\r\n          import boto3\r\n          from botocore.vendored import requests\r\n\r\n          def handler(event, context):\r\n              try:\r\n                  bucket = event['ResourceProperties']['BucketName']\r\n\r\n                  if event['RequestType'] == 'Delete':\r\n                      s3 = boto3.resource('s3')\r\n                      bucket = s3.Bucket(bucket)\r\n                      for obj in bucket.objects.filter():\r\n                          s3.Object(bucket.name, obj.key).delete()\r\n\r\n                  sendResponseCfn(event, context, \"SUCCESS\")\r\n              except Exception as e:\r\n                  print(e)\r\n                  sendResponseCfn(event, context, \"FAILED\")\r\n\r\n\r\n          def sendResponseCfn(event, context, responseStatus):\r\n              response_body = {'Status': responseStatus,\r\n                              'Reason': 'Log stream name: ' + context.log_stream_name,\r\n                              'PhysicalResourceId': context.log_stream_name,\r\n                              'StackId': event['StackId'],\r\n                              'RequestId': event['RequestId'],\r\n                              'LogicalResourceId': event['LogicalResourceId'],\r\n                              'Data': json.loads(\"{}\")}\r\n\r\n              requests.put(event['ResponseURL'], data=json.dumps(response_body))\r\n\r\nOutputs:\r\n  StackSSHSecurityGroup:\r\n    Description: The ARN of the Lambda function that empties an S3 bucket\r\n    Value: !GetAtt EmptyS3BucketLambda.Arn\r\n    Export:\r\n      Name: EmptyS3BucketLambda\r\n<\/pre>\n<p>Let&#8217;s create a stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4386 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation46.jpg\" alt=\"\" width=\"661\" height=\"715\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation46.jpg 661w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation46-277x300.jpg 277w\" sizes=\"(max-width: 661px) 100vw, 661px\" \/><\/p>\n<p><code>Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4387 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation47.jpg\" alt=\"\" width=\"675\" height=\"481\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation47.jpg 675w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation47-300x214.jpg 300w\" sizes=\"(max-width: 675px) 100vw, 675px\" \/><\/p>\n<p><code>Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4388 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation48.jpg\" alt=\"\" width=\"658\" height=\"287\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation48.jpg 658w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation48-300x131.jpg 300w\" sizes=\"(max-width: 658px) 100vw, 658px\" \/><\/p>\n<p><code>Create stack -&gt;<\/code><\/p>\n<p>Labda funcion has been created:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4389 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation49.jpg\" alt=\"\" width=\"833\" height=\"599\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation49.jpg 833w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation49-300x216.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation49-768x552.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>Consider a yaml template with custom resource:<\/p>\n<pre class=\"lang:default decode:true \">---\r\nAWSTemplateFormatVersion: '2010-09-09'\r\n\r\nResources:\r\n  myBucketResource:\r\n    Type: AWS::S3::Bucket\r\n\r\n  LambdaUsedToCleanUp:\r\n    Type: Custom::cleanupbucket\r\n    Properties:\r\n      ServiceToken: !ImportValue EmptyS3BucketLambda\r\n      BucketName: !Ref myBucketResource<\/pre>\n<p>Now let&#8217;s create a stack with custom recource:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4390 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation50.jpg\" alt=\"\" width=\"659\" height=\"715\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation50.jpg 659w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation50-277x300.jpg 277w\" sizes=\"(max-width: 659px) 100vw, 659px\" \/><\/p>\n<p>Next -&gt;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4391 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation51.jpg\" alt=\"\" width=\"677\" height=\"486\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation51.jpg 677w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation51-300x215.jpg 300w\" sizes=\"(max-width: 677px) 100vw, 677px\" \/><\/p>\n<p><code>Next -&gt; Create stack<\/code><\/p>\n<p>After th new stack has been created we can go to the rosurces and see that S3 bucket has been created also:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4393 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation53.jpg\" alt=\"\" width=\"746\" height=\"557\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation53.jpg 746w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation53-300x224.jpg 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p>We can\u00a0 go to this 33 bucket and upload a random file here:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4394 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation54.jpg\" alt=\"\" width=\"755\" height=\"676\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation54.jpg 755w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation54-300x269.jpg 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p><code>Upload -&gt;<\/code><\/p>\n<p>Now let&#8217;s delete the stack.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4397 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation55-1.jpg\" alt=\"\" width=\"784\" height=\"498\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation55-1.jpg 784w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation55-1-300x191.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation55-1-768x488.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>When the stack has been deleted the lambda funcion cleaned up the S3 Bucket.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4398 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation56.jpg\" alt=\"\" width=\"674\" height=\"242\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation56.jpg 674w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation56-300x108.jpg 300w\" sizes=\"(max-width: 674px) 100vw, 674px\" \/><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[89],"tags":[],"_links":{"self":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4335"}],"collection":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/comments?post=4335"}],"version-history":[{"count":30,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4335\/revisions"}],"predecessor-version":[{"id":4399,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4335\/revisions\/4399"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=4335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=4335"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=4335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}