CloudFormation helper scripts are very useful, aren't they? Just by defining a script in the template, you can install the software and start the service when creating EC2.
This time, I had the opportunity to install the Cloudwatch agent and create a configuration file using that helper script, so I'd like to write it so that I don't forget it.
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
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}
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.
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.
--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.
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
Finally, create an instance profile to pass the role you just created to EC2.
  CWAgentInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - !Ref CloudwatchRole
I've been addicted to creating CloudFormation, but it's easier to do it with CFn than to set it up with a wizard!
CloudFormation is full of things I still don't understand. It's deep.
(I would be grateful if you could tell me if there is another better way, I'm making a mistake here!)
--CloudFormation Helper Script Reference --AWS CloudFormation
--Manually create or edit CloudWatch agent configuration file --Amazon CloudWatch
--Install AWS CloudFormation helper scripts on Ubuntu or RHEL AMI
-First CloudWatch Agent introduction (SSM & CentOS) --Serverworks Engineer Blog
-(For beginners) Install CloudWatch Agent on EC2 and launch it on SSM | Developers.IO
-Try CloudWatch Agent-ngyuki's diary
Recommended Posts