Refine the EC2 deployment
In the previous article, we performed a basic set pf an EC2 virtual machine, using AWS CDK. Our goals were these:
- An EC2 instance
- The instance should run Linux, we will pick Amazon Linux for simplicity
- The instance should not be reachable from the internet
- We should be able to login and access the machine from a command-line prompt in the AWS Console.
- We should use an existing VPC and its subnets.
- We do not care about which availability zone the machine ends up in.
If you have not read the previous article, I recommend you to start with that article first. When you are done there, jump back in here!
The goals in bold were the ones we managed to do in the previous article, so now we have one items on the list left to do - be able to login to the machine via the AWS Console, and still keep the machine private and not reachable from the internet.
Update:
This article series uses Typescript as an example language. However, there are repositories with example code for multiple languages.
- https://github.com/cloudgnosis/iac-ninja-aws-cdk-ts
- https://github.com/cloudgnosis/iac-ninja-aws-cdk-python
- https://github.com/cloudgnosis/iac-ninja-aws-cdk-go
The repositories will contain all the code examples from the articles series, implemented in different languages with the AWS CDK. You can view the code from specific parts and stages in the series, by checking out the code tagged with a specific part and step in that part, see the README file in each repository for more details.
The road to login - a history lesson
In the old days of EC2, you would generally always create or re-use a key pair in order to login to an EC2 virtual machine. The key data associated with that key pair was something you downloaded, and sometimes may have shared with other colleagues that also needed access to the same machine.
This was a troublesome and cumbersome approach for many reasons, but for a long time pretty much the only approach available. Other public cloud vendors provided better solutions, and in the end so did also AWS.
The approach we are going to use with our virtual machine is to take advantage of a feature in the AWS Systems Manager service, with the name Session Manager. This service allows us to tunnel traffic to an EC2 instance without opening up the security groups for any traffic.
Instead, AWS System Manager has an agent that connect to the AWS Systems Manager service from the EC2 virtual machine. The user who wants to login communicates with the AWS System Manager service, which then can forward traffic via the connection that the Systems Manager agent established to Systems Manager itself.
Luckily for us, we are using Amazon Linux latest version, which already has the System Manager agent installed on the EC2 virtual machine. What we will need to do is to add Identity & Access Management (IAM) permissions to the EC2 virtual machine, so that it (actually the Systems Manger agent) can call Systems Manager and perform the required steps.
EC2 instance profiles
AWS has something that is called instance profiles. It is essentially a configuration that associates a set of IAM permissions in the form of an IAM Role to an EC2 instance. This allows any software that is running on that EC2 instance to use any services that the IAM Role has permissions for.. No need for any access keys or secret information to be stored on the instance itself.
So by using this feature, we can allow the Systems Manager agent on our virtual to perform the necessary operations to allow us to login to the machine, without any port openings or secret keys exposed. We just need the necessary permissions to allow the connection via the AWS Console. If you have full administrative permissions, then you have those permissions.
More detailed permissions to allow such access via AWS Console is out of the scope of this article, that is for a more in-depth article on this particular feature.
The anatomy of the IAM Role
There are two things we need to include in the IAM Role:
- Who can assume the role
- What permissions are needed to perform the tasks for using Systems Manager Session Manager
Since we are using an EC2 instance to communicate with Systems Manager, it is the EC2 service itself that must be able to assume the role. There are a number of permissions needed for Session Manager to work, but luckily we can simply use a predefined policy that AWS provides for this. This policy has the name AmazonSSMManagedInstanceCore. With this information, we can create the required IAM Role in our AWS CDK code:
const role = new Role(stack, 'ec2-role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore') ],
});
We then need to attach this role to the EC2 instance we create.
const instance = new Instance(stack, 'my-ec2', {
instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.MICRO),
machineImage: MachineImage.latestAmazonLinux(),
role,
vpc,
});
This means that our complete code now looks like this:
import {App, Stack} from 'aws-cdk-lib';
import {Instance, InstanceClass, InstanceSize, InstanceType, MachineImage, Vpc} from 'aws-cdk-lib/aws-ec2';
import {ManagedPolicy, Role, ServicePrincipal} from 'aws-cdk-lib/aws-iam';
const app = new App();
const stack = new Stack(app, 'my-stack', {
env: {
account: process.env.CDK\_DEFAULT\_ACCOUNT,
region: process.env.CDK\_DEFAULT\_REGION,
},
});
const role = new Role(stack, 'ec2-role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore') ],
});
const vpc = Vpc.fromLookup(stack, 'my-vpc', {
isDefault: true,
});
const instance = new Instance(stack, 'my-ec2', {
instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.MICRO),
machineImage: MachineImage.latestAmazonLinux(),
role,
vpc,
});
Now with this refinement of our code we can run cdk deploy to deploy our EC2 instance again. After a few minutes, the EC2 instance has been deployed and is running. Now, if you login to the AWS Console and go to the EC2 dashboard with the running instance, you will see a view similar to this below. Select the instance in the checkbox on the left side and click on the Connect button.
You will get a different page with a few options to connect to the virtual machine instance. In this case, you should select the Session Manager tab. If the setup worked as expected, the Connect button at the bottom right should have clear colours and not be greyed out.
Click on this Connect button and a new page/tab should open up, and you should get a command line prompt that you can use. You have now connected to the virtual machine instance!
Feel free to play around and look around on the machine itself, then just terminate the session, run exit command door anything else to end the command-line session.
Final remarks
The last addition we did here allowed us to complete all the goals we had for this initial infrastructure-as-code task using the AWS CDK. Well done!
- An EC2 instance
- The instance should run Linux, we will pick Amazon Linux for simplicity
- The instance should not be reachable from the internet
- We should be able to login and access the machine from a command-line prompt in the AWS Console.
- We should use an existing VPC and its subnets.
- We do not care about which availability zone the machine ends up in.
Feel free to use cdk destroy command to remove the virtual machine instance. There are a few valuable lessons that we have covered so far in this series:
- A few key commands with the cdk command-line tool, including init, bootstrap, synth, deploy, destroy.
- How to import existing VPC information.
- How to create a new EC2 virtual machine
- How to create and attach IAM Role with permissions so that software on the machine can use AWS services
- How to set AWS account and region for a stack.
It is quite a few things to start with, and you should be proud of yourself to have done all of this!
In the next article, we are going to jump into the world of containers.
Top comments (0)