Install CloudWatch Agent on Ubuntu using CloudFormation

On Ubuntu 20 LTS

Create CFn

Preparing helper scripts

Immediately install the cloudwatch agent using the helper script! Unfortunately, it is not installed on Ubuntu by default. So, first download the aws-cfn-bootstrap package. Add the following script to the helper script in UserDate that is executed when EC2 starts.

apt-get update
apt-get install -y python-setuptools
mkdir -p /opt/aws/bin
apt-get install -y wget
wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.tar.gz

▶ Helper Script Reference

Install CloudWatch Agent

Then install the cloudwatch agent. Add this to UserDate as well.

wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -O /tmp/amazon-cloudwatch-agent.deb
dpkg -i /tmp/amazon-cloudwatch-agent.deb
/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource EC2 --region ${AWS::Region} --configsets default
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource EC2 --region ${AWS::Region}

Creating amazon-cloudwatch-agent.json

Now that we have installed the helper script and cloudwatch agent, let’s create the configuration file amazon-cloudwatch-agent.json. For how to write the configuration file, I referred to Official Document.

Creating a helper script

Next is finally the helper script. You can bring metadata for the cfn-init helper script into EC2 by using the AWS :: CloudFormation :: Init type.

  • cfn-hup It is a daemon that detects changes in resource metadata and executes the operation specified by the user when the changes are made. This time, the stack update event is detected as a trigger, and when it is detected, the action in the configuration file under hooks.d is executed.

–hooks.conf configuration file A user action that the cfn-hup daemon calls on a regular basis is defined.

–cfn-hup.conf configuration file Contains the name of the stack targeted by the cfn-hup daemon and AWS credentials.

This time, when creating a new stack, go to default, and when updating the metadata by updating the stack, go to UpdateEnvironment.

  EC2:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          default:
            - "01_setupCfnHup"
            - "02_config_amazon-cloudwatch-agent"
            - "03_restart_amazon-cloudwatch-agent"
          UpdateEnvironment:
            - "02_config_amazon-cloudwatch-agent"
            - "03_restart_amazon-cloudwatch-agent"
 
01_setupCfnHup:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackName}
                region=${AWS::Region}
                interval=1
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.EC2.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2 --configsets UpdateEnvironment --region ${AWS::Region}
                runas=root
              mode: '000400'
              owner: root
              group: root
            /lib/systemd/system/cfn-hup.service:
              content: !Sub |
                [Unit]
                Description=cfn-hup daemon
                [Service]
                Type=simple
                ExecStart=/opt/aws/bin/cfn-hup
                Restart=always
                [Install]
                WantedBy=multi-user.target
          commands:
            01enable_cfn_hup:
              command: "systemctl enable cfn-hup.service"
            02start_cfn_hup:
              command: "systemctl start cfn-hup.service"
        02_config_amazon-cloudwatch-agent:
          files:
            /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:
              content: !Sub |
                {
                  //Here amazon-cloudwatch-agent.write json//
                }
              mode: '000644'
              owner: root
              group: root

        03_restart_amazon-cloudwatch-agent:
          commands:
            01_stop_service:
              command: "/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop"
            02_start_service:
              command: "/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json"
                

It’s a bit of a mess, but the “03_restart_amazon-cloudwatch-agent” command

/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json

Then, it seems that the agent is started by converting the json file to the toml format configuration file amazon-cloudwatch-agent.toml. Therefore, the file amazon-cloudwatch-agent.json does not exist in EC2 created with this CFn.

Create roles for CloudWatch Agent

You must have permission to access AWS resources. Let’s create an IAM role that gives EC2 the necessary privileges. I have created a role CloudwatchRole with the following policy attached.

–AWS Managed Policy – CloudWatchAgentServerPolicy – CloudWatchAgentAdminPolicy – AmazonSSMManagedInstanceCore –Custom policy –This time, I added a policy to allow access to S3 and logs. Good luck with the situation.

  CloudwatchRole:
    Type: AWS::IAM::Role
    Properties: 
      RoleName: "CloudwatchRole"
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: CloudwatchRolePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement: 
                  - Effect: Allow
                    Action:
                      //abridgement//
                    Resource: '*'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
        - arn:aws:iam::aws:policy/CloudWatchAgentAdminPolicy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

Creating an instance profile

Finally, create an instance profile to pass the role you just created to EC2.

  CWAgentInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - !Ref CloudwatchRole