The aim of these labs is to provide some hands-on to have the fundamentals of Cloud Operations in AWS. This is a fundamentals level, but an advanced level might be provided in the future, if required.
NOTE: The source code can be found in my Git repository https://github.com/ronaldtf/aws-cloudops-workshop
First of all, we need to set up our environment to later be able to deploy our resources. To do so, we must follow the AWS documentation. We describe below the procedure anyway.
In the IAM Section in the AWS Console, we have to go to users and select the user we want to use for the deployment. We select Security credentials
and Create access key
(if not previously created). Copy the user access key
and secret access key
provided.
If you do not find the file
~/.aws/credentials
in the cases below, type:
$ aws configure
and set the default region (e.g. eu-west-1).
If you are not using MFA and simply use an account for users and services (which is not recommended), just set the values copied above in an ~/.aws/credentials
file:
[default]
aws_access_key_id = YOURACCESSKEYID
aws_secret_access_key = YOURSECRETACCESSKEY
If you use MFA and a user account for the IAM users and a service account for the services (and where you want to deploy your services) as shown in the image below…
… you must follow this configuration:
$ pip install aws-mfa
[profile default]
...
[profile myprofile-long-term]
region = eu-west-1
output = json
role_arn = arn:aws:iam::123456789012:role/role_to_be_assumed
source_profile = default
Note that the
source_profile
is the profile that gives us permissions to do an assume role. Therefore, we need to define those credentials for it in the~/.aws/config
file.
[myprofile-long-term]
aws_access_key_id = YOURACCESSKEYID
aws_secret_access_key = YOURSECRETACCESSKEY
aws_mfa_device = arn:aws:iam::123456789012:mfa/iam_user
aws-mfa --profile myprofile
This will place the right credentials in the ~/.aws/credentials
file. However, note that this credentials will expire and we will need to execute the command below again once they are expired.
Task: Set up your own environment.
An EC2 instance is a virtual machine in AWS. In order to define an EC2 instance we need, at least:
NOTE: An EC2 instance can have multiple network interfaces from different subnets. However, all the network interfaces from the different subnets must belong to the same Availability Zone
Instance storage is not supported by all the instance types
NOTE: HDD disks cannot be used as root volumes
Task: Go to the AWS Console and launch an instance in a default VPC (public subnet) and by assigning permissions to connect via SSH. Try to connect to the EC2 instance by specifying your private key.
In this section we are going to create a lambda function with some basic settings.
Task: Go to the AWS Console and try to create an AWS Lambda. You’ll find more details below in this section
These are the steps when creating an AWS Lambda:
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Purpose
Shows how to implement an AWS Lambda function that handles input from direct
invocation.
"""
import logging
import os
logger = logging.getLogger()
# Define a list of Python lambda functions that are called by this AWS Lambda function.
ACTIONS = {
'plus': lambda x, y: x + y,
'minus': lambda x, y: x - y,
'times': lambda x, y: x * y,
'divided-by': lambda x, y: x / y}
def lambda_handler(event, context):
"""
Accepts an action and two numbers, performs the specified action on the numbers,
and returns the result.
:param event: The event dict that contains the parameters sent when the function
is invoked.
:param context: The context in which the function is called.
:return: The result of the specified action.
"""
# Set the log level based on a variable configured in the Lambda environment.
logger.setLevel(os.environ.get('LOG_LEVEL', logging.INFO))
logger.debug('Event: %s', event)
action = event.get('action')
func = ACTIONS.get(action)
x = int(event.get('x'))
y = int(event.get('y'))
result = None
try:
if func is not None and x is not None and y is not None:
result = func(x, y)
logger.info("%s %s %s is %s", x, action, y, result)
else:
logger.error("I can't calculate %s %s %s.", x, action, y)
except ZeroDivisionError:
logger.warning("I can't divide %s by 0!", x)
response = {'result': result}
return response
Task: Test the lambda function and the ‘divided-by’ action
{ "action": "divided-by", "x": "27", "y": "4" }
Task: Test the lambda function by changing the action and/or the values. What would happen if you divide by 0?
Task: Would you know how to debug the lambda? Ask the instructor
NOTE: Lambdas can be implemented within VPCs, we can set additional configuration settings such as parameters, etc. Understanding these settings goes out of the scope of this workshop.
In this lab you have to create a role, which you will later assume in the console.
Task: try to create a role by yourself and assume it on your own. If you do not know how to do it or you need some help, see the steps below.
In order to create a role we need to define 2 components:
We are going to assume that someone from the same account is going to assume this role. If we opt for the simplest way to do it, we can just indicate this when creating the role. Otherwise, we can set this later by specifying the following (replace AWS_ACCOUNT_ID below, if needed):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "<AWS_ACCOUNT_ID>"
},
"Condition": {}
}
]
}
For this part, we have 2 options:
Task: For this lab, create a new policy. Use the AWS Policy Generator to create a policy that grants S3 permissions for all the resources and all the Get, List and Describe operations.
Task: Do you need a permissions boundary?
In order to assume a role, if we opt to do it in the AWS Console, we need to the right side of the console and click on “Switch Role”. To switch a role we need to specify:
NOTE: Be aware that the role name is case sensitive!
In this lab you just need to do the same as before but, instead of using a trust relationship for the account, you need to specify the EC2 service.
Task: Create a role as before but specify the EC2 services in the trust relationship
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
}
}
]
}
Now, you need to launch an EC2 instance.
Task: Go to “Actions”=>“Security”=>“Modify IAM role”. Specify the role you just created.
Task: Enter in the EC2 instance and verify that you can list the services (in the example below we assume you’re using the Ireland region)
$> aws s3 ls --region eu-west-1
Task: Why do not you need to specify
aws configure
in this case?
In this lab, you have to:
Task: Can you read the object again?
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": [
"arn:aws:s3:::<BUCKET_NAME>",
"arn:aws:s3:::<BUCKET_NAME>/*"
]
}
]
}
Task: Replace the policy above with the following one and test it again
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:Get*"
],
"Resource": [
"arn:aws:s3:::<BUCKET_NAME>",
"arn:aws:s3:::<BUCKET_NAME>/*"
]
}
]
}
For this lab, you can use this CloudFormation template file from my Github account
Task: Use the CloudFormation designer to validate the template. Stop a while to see the interaction of the components, understand the CloudFormation template, etc.
Task: Try to modify the template and make it inconsistent. What happens with the validation?
For this lab, you can use this CloudFormation template file from my Github account
Create a CloudFormation stack with the template provided. In the output you should see an URL to access to a web page.
Task: Do you have access to it? Are you able to find the problem?
Now that you have found the problem, you have to update the stack with the modified template.
Task: Update the stack and access to the URL. You should be able to access it.
In order to create the same stacks in multiple accounts/regions, you can use AWS CloudFormation Stack Set.
In this lab we are going to deploy a CloudFormation Stack Set in a single account, but we could do exactly the same thing by specifying other multiple accounts.
These are the steps to follow:
NOTE: When you create a Stack Set, a CloudFormation stack is created in every region and account you specify.
The templates above are placed in this repository: https://github.com/ronaldtf/aws-cloudops-workshop/tree/master/cf-stackset
Task: Create a Stack Set that deploys the Stack Set in 2 regions in the same account.
Task: Try to create the Stack Set created before. What is the problem? How do you fix it?
In this lab we are going to use a metric that monitors the CPU utilization of an EC2 instance. We will use that metric and a threshold to trigger an alarm.
First of all, remember the states of an ALARM are:
In case you do not have any instance available for this step, follow the steps in a previous lab to launch an EC2 instance.
Go to CloudWatch Alarms and create an alarm with the following data:
By setting this action we have created an alarm which is triggered when the CPUUtilization for the selected metric is higher than 80% during 5 minutes.
NOTE: for this example, we are using default metrics with standard resolution (datapoints collected every minute)
You should see that the current state of the alarm is INSUFFICIENT_DATA but, after a while, it will change to OK.
In order to make the alarm be triggered, we need to increase the CPU utilization of the EC2 instance. To do so, we need to login to the console and run the following actions:
$> sudo yum update -y
$> sudo amazon-linux-extras install epel -y
$> sudo yum install stress -y
$> stress -c 100 -t 300
Task: Check again the alarm status. Has it reached the ALARM state?
The purpose here is to demonstrate how can we create a custom metric filter from CloudWatch logs and how can we use these as normal metrics.
NOTE: Because of the lack of log data, the instructor will demonstrate how this can be configured in a real scenario
In order to perform the actions below you must have an EC2 instance. Create one if you do not have any by following one of the labs in a previous section
NOTE: Steps indicated above has not been details as they had been part of previous labs
$> sudo yum install amazon-cloudwatch-agent
$> sudo amazon-linux-extras install collectd -y
$> sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
$> sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json
Create ana alarm based on one of the OS metrics
In this lab we will create an EventBridge rule that:
Task: Create the EventBridge rule with the specifications above. All the dependent services should have already been in place from previous labs.
Task: what is the difference between S3 events and CloudTrail events?
Task: what is the difference between CloudWatch logs and CloudTrail events?
In this lab we are going to trigger an event every time we instert an object (with a PUT action) in a specific bucket.
Task: Have you received an email once you have put an object in the bucket?
When creating a CloudTrail trail, we need to specify:
Task: create a trail with logs in an existing bucket in S3, without encryption
For the trail created before, go to the selected bucket and open one of the files. If nothing has been created yet, do not worry! CloudTrail trails can need up to 15 minutes to send the audit information to S3 and, optionally, CloudWatch.
In order to avoid you keep waiting, you can have a look at the following extracts:
{"Records": [{
"eventVersion": "1.0",
"userIdentity": {
"type": "IAMUser",
"principalId": "EX_PRINCIPAL_ID",
"arn": "arn:aws:iam::123456789012:user/Alice",
"accessKeyId": "EXAMPLE_KEY_ID",
"accountId": "123456789012",
"userName": "Alice"
},
"eventTime": "2014-03-06T21:22:54Z",
"eventSource": "ec2.amazonaws.com",
"eventName": "StartInstances",
"awsRegion": "us-east-2",
"sourceIPAddress": "205.251.233.176",
"userAgent": "ec2-api-tools 1.6.12.2",
"requestParameters": {"instancesSet": {"items": [{"instanceId": "i-ebeaf9e2"}]}},
"responseElements": {"instancesSet": {"items": [{
"instanceId": "i-ebeaf9e2",
"currentState": {
"code": 0,
"name": "pending"
},
"previousState": {
"code": 80,
"name": "stopped"
}
}]}}
}]}
Task: Could you identify what this audit log corresponds to?
{"Records": [{
"eventVersion": "1.0",
"userIdentity": {
"type": "IAMUser",
"principalId": "EX_PRINCIPAL_ID",
"arn": "arn:aws:iam::123456789012:user/Alice",
"accountId": "123456789012",
"accessKeyId": "EXAMPLE_KEY_ID",
"userName": "Alice"
},
"eventTime": "2014-03-06T21:01:59Z",
"eventSource": "ec2.amazonaws.com",
"eventName": "StopInstances",
"awsRegion": "us-east-2",
"sourceIPAddress": "205.251.233.176",
"userAgent": "ec2-api-tools 1.6.12.2",
"requestParameters": {
"instancesSet": {"items": [{"instanceId": "i-ebeaf9e2"}]},
"force": false
},
"responseElements": {"instancesSet": {"items": [{
"instanceId": "i-ebeaf9e2",
"currentState": {
"code": 64,
"name": "stopping"
},
"previousState": {
"code": 16,
"name": "running"
}
}]}}
}]}
Task: Could you identify what this audit log corresponds to?
{"Records": [{
"eventVersion": "1.0",
"userIdentity": {
"type": "IAMUser",
"principalId": "EX_PRINCIPAL_ID",
"arn": "arn:aws:iam::123456789012:user/Alice",
"accountId": "123456789012",
"accessKeyId": "EXAMPLE_KEY_ID",
"userName": "Alice"
},
"eventTime": "2014-03-24T21:11:59Z",
"eventSource": "iam.amazonaws.com",
"eventName": "CreateUser",
"awsRegion": "us-east-2",
"sourceIPAddress": "127.0.0.1",
"userAgent": "aws-cli/1.3.2 Python/2.7.5 Windows/7",
"requestParameters": {"userName": "Bob"},
"responseElements": {"user": {
"createDate": "Mar 24, 2014 9:11:59 PM",
"userName": "Bob",
"arn": "arn:aws:iam::123456789012:user/Bob",
"path": "/",
"userId": "EXAMPLEUSERID"
}}
}]}
Task: Could you identify what this audit log corresponds to?
{"Records": [{
"eventVersion": "1.04",
"userIdentity": {
"type": "IAMUser",
"principalId": "EX_PRINCIPAL_ID",
"arn": "arn:aws:iam::123456789012:user/Alice",
"accountId": "123456789012",
"accessKeyId": "EXAMPLE_KEY_ID",
"userName": "Alice"
},
"eventTime": "2016-07-14T19:15:45Z",
"eventSource": "cloudtrail.amazonaws.com",
"eventName": "UpdateTrail",
"awsRegion": "us-east-2",
"sourceIPAddress": "205.251.233.182",
"userAgent": "aws-cli/1.10.32 Python/2.7.9 Windows/7 botocore/1.4.22",
"errorCode": "TrailNotFoundException",
"errorMessage": "Unknown trail: myTrail2 for the user: 123456789012",
"requestParameters": {"name": "myTrail2"},
"responseElements": null,
"requestID": "5d40662a-49f7-11e6-97e4-d9cb6ff7d6a3",
"eventID": "b7d4398e-b2f0-4faa-9c76-e2d316a8d67f",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012"
}]}
Task: Could you identify what this audit log corresponds to?
In this lab we are going to demonstrate how to perform the private access to an S3 bucket privately (without internet access).
This is not actually a lab but a demo the instructor will provide in the live workshop.
In a previous lab you were able to create a S3 bucket policy that granted/restricted access to a given account/role.
Task: would you be able to make a bucket publicly readable?
If we want to make a bucket publicly readable, we need to modify it’s bucket policy to grant its access. One way is by specifying the following bucket policy (remember to replace the BUCKET_NAME with the right name):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject*",
"s3:GetBucket*"
],
"Resource": [
"arn:aws:s3:::<BUCKET_NAME>",
"arn:aws:s3:::<BUCKET_NAME>/*"
]
}
]
}
Task: Does it work?
For this lab you will need to:
The important part of the lifecycle is what action to perform
Task: Is there any action you do not understand? Ask the instructor
Even if this is an advanced topic, we will show it how to do it as a demo.
Note: The instructor will demonstrate how to perform this during the workshop
The goal of this lab is to allow to access a S3 bucket from another account:
Task: You should be able to do this lab with the concepts you have so far. However, you would need to have multiple accounts.
NOTE: The instructor will perform a demo for this.
Now that you know…
… it’s time to put your hands on new projects and start new challenges!