Create Amazon Linux 2 EC2 ARM and x86 based instances with CDK and Java

Hamza Sabljakovic
4 min readDec 10, 2022
Output from running cdk deploy

For this post, we will assume you have the AWS CDK installed on your machine and some experience with the CDK. If you are looking for a more in depth tutorial check out my other post where I get into more details on how to run Ubuntu on EC2 ARM processors and CDK basics.

The first step is to generate a new infrastructure project.

mkdir aws-linux-arm-and-x86 && cd aws-linux-arm-and-x86  && cdk init app --language java

The above command results in the following files and directories:

.
├── README.md
├── cdk.json
├── pom.xml
├── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── myorg
│ │ ├── AwsLinuxArmAndX86App.java
│ │ └── AwsLinuxArmAndX86Stack.java
│ └── test
│ └── java
│ └── com
│ └── myorg
│ └── AwsLinuxArmAndX86Test.java
└── target
├── aws-linux-arm-and-x86-0.1.jar
├── classes
│ └── com
│ └── myorg
│ ├── AwsLinuxArmAndX86App.class
│ └── AwsLinuxArmAndX86Stack.class
├── generated-sources
│ └── annotations
├── generated-test-sources
│ └── test-annotations
├── maven-archiver
│ └── pom.properties
├── maven-status
│ └── maven-compiler-plugin
│ ├── compile
│ │ └── default-compile
│ │ ├── createdFiles.lst
│ │ └── inputFiles.lst
│ └── testCompile
│ └── default-testCompile
│ ├── createdFiles.lst
│ └── inputFiles.lst
└── test-classes

25 directories, 14 files

By default, the project name and classes are based on the directory where the ckd init command is run.

The stack class consists of 4 resources, two ec2 machines, one VPC, and a security group shared by the two instances.

package com.myorg;

import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.ec2.*;
import software.constructs.Construct;

public class AwsLinuxArmAndX86Stack extends Stack {
public AwsLinuxArmAndX86Stack(final Construct scope, final String id) {
this(scope, id, null);
}

public AwsLinuxArmAndX86Stack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);

Vpc vpc = Vpc.Builder.create(this, id + "-vpc")
.vpcName(id + "-vpc")
.build();

final ISecurityGroup securityGroup = SecurityGroup.Builder.create(this, id + "-sg")
.securityGroupName(id)
.vpc(vpc)
.build();

securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(22));

final Instance armEC2Instance = Instance.Builder.create(this, id + "-arm-ec2")
.instanceName(id + "-arm-ec2")
.machineImage(MachineImage.latestAmazonLinux(
AmazonLinuxImageProps.builder()
.cpuType(AmazonLinuxCpuType.ARM_64)
.generation(AmazonLinuxGeneration.AMAZON_LINUX_2)
.build()
))
.securityGroup(securityGroup)
.instanceType(InstanceType.of(
InstanceClass.BURSTABLE4_GRAVITON,
InstanceSize.SMALL
))
.vpcSubnets(
SubnetSelection.builder()
.subnetType(SubnetType.PUBLIC)
.build()
)
.vpc(vpc)
.build();

final Instance x86EC2Instance = Instance.Builder.create(this, id + "-x86-ec2")
.instanceName(id + "-x86-ec2")
.machineImage(MachineImage.latestAmazonLinux(
AmazonLinuxImageProps.builder()
.cpuType(AmazonLinuxCpuType.X86_64)
.generation(AmazonLinuxGeneration.AMAZON_LINUX_2)
.build()
))
.securityGroup(securityGroup)
.instanceType(InstanceType.of(
InstanceClass.BURSTABLE3,
InstanceSize.MICRO
))
.vpcSubnets(
SubnetSelection.builder()
.subnetType(SubnetType.PUBLIC)
.build()
)
.vpc(vpc)
.build();

}
}

If machine Amazon Linux Image props are not set, the CDK will default to X86 architecture and the AMI generation 1 (amzn-ami-hvm-2018.03.0.20221018.0-x86_64-gp2).

final Instance ec2InstanceWithDefaultSettings = Instance.Builder.create(this, id + "-default-settings")
.instanceName(id + "-default-settings")
.machineImage(MachineImage.latestAmazonLinux())
.securityGroup(securityGroup)
.instanceType(InstanceType.of(
InstanceClass.BURSTABLE3,
InstanceSize.MICRO
))
.vpcSubnets(
SubnetSelection.builder()
.subnetType(SubnetType.PUBLIC)
.build()
)
.vpc(vpc)
.build();

Once you are done with the changes to the stack class (AwsLinuxArmAndX86Stack), make sure to configure the region and the account numbers. One way to do so is by defining it in the app class (AwsLinuxArmAndX86App.java).

package com.myorg;

import software.amazon.awscdk.App;
import software.amazon.awscdk.Environment;
import software.amazon.awscdk.StackProps;

public class AwsLinuxArmAndX86App {
public static void main(final String[] args) {
App app = new App();

new AwsLinuxArmAndX86Stack(app, "AwsLinuxArmAndX86Stack", StackProps.builder()
.env(Environment.builder()
.account("94843111111") // Replace with your account number
.region("eu-west-1") // Replace with your region
.build())
.build());

app.synth();
}
}

Once you are happy with the changes, run the cdk deploy command to start the deployment process.

Also, if you are just trying out or learning about CDK, remember to run cdk destroy after you are done. The default VPC setup we used above will create two or three managed NAT gateways which can get expensive if left running.

NAT gateways created by using the CDK default VPC builder

If you don’t want NAT gateways at all, do the following change to the VPC configuration.

        Vpc vpc = Vpc.Builder.create(this, id + "-vpc")
.vpcName(id + "-vpc")
.natGateways(0) // To not create NATs at all
.build();

The full source code can be found on github

--

--