Search code examples
yamlaws-cloudformationaws-cloudformation-custom-resource

How to update a file application.properties in EC2 instance using CloudFormation script?


I have created AMI to launch an instance using a cloud formation Template. I have created a centos instance and configured my application on it and created AMI from that instance.

Now I want to create a CF template that will create a bucket and update the file (application.properties) with created bucket info while launching the instance.

How can I achieve this?

My Sample CF Template, it's not updating my application.properties

AWSTemplateFormatVersion: '2010-09-09'
Description: AWS CloudFormation Template with EC2InstanceWithSecurityGroup
Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t2.medium
    AllowedValues:
      - t2.medium
      - t2.large
    ConstraintDescription: must be a valid EC2 instance type.
  RemoteAccessLocation:
    Description: The IP address range that can be used to access to the EC2 instances
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 0.0.0.0/0
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Metadata: 
      AWS::CloudFormation::Init:
        config:
          files: 
            /usr/local//application.properties:
              content:
                Fn::Join:
                  - 'SSSSSSSSSSSSSSSSSSSSSSSSSSSS'
                  - - 'YYYYYYYYYYYYYYYYYYYYYYYYYYYY'
    Properties:
      InstanceType: !Ref 'InstanceType'
      SecurityGroups:
        - !Ref 'InstanceSecurityGroup'
      KeyName: !Ref 'KeyName'
      ImageId: ami-xxxxxxxxxxxxxxx 
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable SSH (22), HTTP (8080)
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Ref 'RemoteAccessLocation'
        - CidrIp: 0.0.0.0/0
          FromPort: '8080'
          IpProtocol: tcp
          ToPort: '8080'
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    DeletionPolicy: Retain
    Properties:
      BucketName: sample-bucket
      AccessControl: Private 
Outputs:
  InstanceId:
    Description: InstanceId of the newly created EC2 instance
    Value: !Ref 'EC2Instance'
  AZ:
    Description: Availability Zone of the newly created EC2 instance
    Value: !GetAtt 'EC2Instance.AvailabilityZone'
  PublicDNS:
    Description: Public DNSName of the newly created EC2 instance
    Value: !GetAtt 'EC2Instance.PublicDnsName'
  PublicIP:
    Description: Public IP address of the newly created EC2 instance
    Value: !GetAtt 'EC2Instance.PublicIp'

Any inputs here really appreciated.


Solution

  • AWS::CloudFormation::Init does not execute on it own. You have to explicitly call it using cfn-init in your UserData.

    Thus, if you have to to create UserData, its probably easier to create your file directly in it. For example:

    AWSTemplateFormatVersion: '2010-09-09'
    Description: AWS CloudFormation Template with EC2InstanceWithSecurityGroup
    Parameters:
      KeyName:
        Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
        Type: AWS::EC2::KeyPair::KeyName
        ConstraintDescription: must be the name of an existing EC2 KeyPair.
      InstanceType:
        Description: EC2 instance type
        Type: String
        Default: t2.medium
        AllowedValues:
          - t2.medium
          - t2.large
        ConstraintDescription: must be a valid EC2 instance type.
      RemoteAccessLocation:
        Description: The IP address range that can be used to access to the EC2 instances
        Type: String
        MinLength: '9'
        MaxLength: '18'
        Default: 0.0.0.0/0
        AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
        ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
    Resources:
      EC2Instance:
        Type: AWS::EC2::Instance
        Properties:
          InstanceType: !Ref 'InstanceType'
          SecurityGroups:
            - !Ref 'InstanceSecurityGroup'
          KeyName: !Ref 'KeyName'
          ImageId: ami-xxxxxxxxxxxxxxx
          UserData:
            Fn::Base64: !Sub |
                  #!/bin/bash -ex
    
                  cat >/usr/local//application.properties <<EOL
                  BucketName: ${S3Bucket}
                  BucketArn: ${S3Bucket.Arn}
                  # some other info from S3Bucket 
                  EOL
    
      InstanceSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          GroupDescription: Enable SSH (22), HTTP (8080)
          SecurityGroupIngress:
            - IpProtocol: tcp
              FromPort: '22'
              ToPort: '22'
              CidrIp: !Ref 'RemoteAccessLocation'
            - CidrIp: 0.0.0.0/0
              FromPort: '8080'
              IpProtocol: tcp
              ToPort: '8080'
      S3Bucket:
        Type: 'AWS::S3::Bucket'
        DeletionPolicy: Retain
        Properties:
          BucketName: sample-bucket
          AccessControl: Private 
    Outputs:
      InstanceId:
        Description: InstanceId of the newly created EC2 instance
        Value: !Ref 'EC2Instance'
      AZ:
        Description: Availability Zone of the newly created EC2 instance
        Value: !GetAtt 'EC2Instance.AvailabilityZone'
      PublicDNS:
        Description: Public DNSName of the newly created EC2 instance
        Value: !GetAtt 'EC2Instance.PublicDnsName'
      PublicIP:
        Description: Public IP address of the newly created EC2 instance
        Value: !GetAtt 'EC2Instance.PublicIp'