How to do it...

  1. Create a new CloudFormation template. Add the following Parameters to it:
      AmiId: 
Type: AWS::EC2::Image::Id
Description: AMI ID to launch instances from
Default: ami-0b33d91d
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where load balancer and instance will launch
SubnetIDs:
Type: List<AWS::EC2::Subnet::Id>
Description: Public subnet where the instance will launch
(pick at least 1)
KeyPair:
Type: AWS::EC2::KeyPair::KeyName
Description: Key to launch EC2 instance with
AlertEmail:
Type: String
Description: Email Address which alert emails will be sent to
  1. Now for the Resources, we need to define a Role and InstanceProfile for our EC2 instance. This will give our server the appropriate permissions to send logs to CloudWatch.
      ExampleRole: 
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
-
PolicyName: WriteToCloudWatchLogs
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogStreams
Resource: "*"
ExampleInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref ExampleRole
Path: /
  1. Our instance will need to live in a security group which allows SSH access, so let's add that now:
      ExampleEC2InstanceSecurityGroup: 
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security Group for example Instance
SecurityGroupIngress:
- IpProtocol: tcp
CidrIp: "0.0.0.0/0"
FromPort: 22
ToPort: 22
VpcId: !Ref VpcId
  1. Next we can define our instance. We make sure to use the profile and security group we just created and we also add a small amount of user-data which does the following:
    1. Install the awslogs package.
    2. Writes a configuration file which will ship /var/log/secure to CloudWatch logs.
    3. Starts the awslogs service.
    4. Make the awslogs service start on boot (in case of reboot).
              ExampleEC2Instance: 
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref ExampleInstanceProfile
InstanceType: t2.nano
KeyName: !Ref KeyPair
UserData:
Fn::Base64:
Fn::Sub: |
#!/bin/bash -ex
yum update -y
yum install -y awslogs
cat << EOF >
/etc/awslogs/config/var-log-secure.conf
[/var/log/secure]
datetime_format = %b %d %H:%M:%S
file = /var/log/secure
buffer_duration = 5000
log_stream_name = {instance_id}
initial_position = start_of_file
log_group_name = /var/log/secure
EOF
service awslogs start
chkconfig awslogs on
ImageId: !Ref AmiId
SecurityGroupIds:
- Fn::GetAtt: ExampleEC2InstanceSecurityGroup.GroupId
SubnetId: !Select [ 0, Ref: SubnetIDs ]
  1. We're now going to add an SNS topic. This topic will receive alerts and forward them to the e-mail address we're using for alerts:
      ExampleSNSTopic:  
Type: AWS::SNS::Topic
Properties:
Subscription:
-
Endpoint: !Ref AlertEmail
Protocol: email
  1. Next, we need to filter our /var/log/secure logs for logins. A MetricFilter resource allows us to do this. CloudFormation will throw an error if we refer to a log group which doesn't yet exist, so we add that here too (with a DependsOn reference):
      ExampleLogGroup: 
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /var/log/secure
RetentionInDays: 7
ExampleLogsMetricFilter:
Type: AWS::Logs::MetricFilter
Properties:
FilterPattern: '"Accepted publickey for ec2-user from"'
LogGroupName: /var/log/secure
MetricTransformations:
-
MetricValue: "1"
MetricNamespace: SSH/Logins
MetricName: LoginCount
DependsOn: ExampleLogGroup
  1. The last Resource we need is the actual Alarm. Add it like so:
      ExampleLoginAlarm: 
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: SSH Login Alarm
AlarmActions:
- Ref: ExampleSNSTopic
MetricName: LoginCount
Namespace: SSH/Logins
Statistic: Sum
Period: 60
EvaluationPeriods: 1
Threshold: 0
ComparisonOperator: GreaterThanThreshold
  1. Lastly, we'll add the public IP address of our instance to the Outputs so we don't need to go to the EC2 web console to look it up:
      Outputs: 
ExampleEC2InstancePublicIp:
Value: !GetAtt [ ExampleEC2Instance, PublicIp ]
  1. Go ahead and launch this CloudFormation stack. You can do it from the AWS CLI like this:
      aws cloudformation create-stack \ 
--template-body \
file://05-feed-log-files-in-to-cloudwatch-logs.yaml \

--stack-name example-cloudwatchlogs \
--capabilities CAPABILITY_IAM \
--parameters \
ParameterKey=VpcId,ParameterValue=<your-vpc-id> \
ParameterKey=SubnetIDs,ParameterValue='<your-subnet-id>' \
ParameterKey=KeyPair,ParameterValue=<your-ssh-key-name> \
ParameterKey=AlertEmail,ParameterValue=<your-email-address>
  1. Before proceeding you'll need to check your e-mail and confirm your subscription to the SNS topic. If you don't do this you won't receive any alerts from CloudWatch:
In the following screenshot, an example of confirmed subscription is illustrated:
  1. Go ahead and SSH to your instance. If your login is successful, you'll see your alarm triggered in the CloudWatch web console:

An e-mail will land in your inbox as shown in the following screenshot: