EC2 Instance with GO CDK

Building an EC2 instance gives us a few challenges:

Building

  • referencing a VPC
  • a security group
  • an instance profile
  • modifying the EBS volume
  • The Instance itself
  • bootstrapping the instance

Testing

  • testing the generated CloudFormation
  • integration test/end to end

Connecting

  • Connect to the instance

Building

referencing a VPC

	vpc := ec2.Vpc_FromLookup(stack, aws.String("vpc"), &ec2.VpcLookupOptions{
		IsDefault: aws.Bool(true),
	})

You have to set the right account data, so that the CDK can search the environment. Therefore in main/main.go you see:

awscdk.StackProps{
    Env: util.Env(),
},

Which calls the Env function, which gets the account from an environment var:

 Account: aws.String(os.Getenv("CDK_DEFAULT_ACCOUNT")),

That means you have to export the account number before starting cdk deploy

a security group

monolithSG := ec2.NewSecurityGroup(stack, aws.String("monolithSG"),
		&ec2.SecurityGroupProps{
			Vpc:               vpc,
			AllowAllOutbound:  aws.Bool(true),
			Description:       aws.String("SG for monolithSG"),
			SecurityGroupName: aws.String("monolithSG"),
		})

	monolithSG.AddIngressRule(ec2.Peer_Ipv4(aws.String("0.0.0.0/0")),
		ec2.NewPort(&ec2.PortProps{
			Protocol:             ec2.Protocol_TCP,
			StringRepresentation: aws.String("Incoming web"),
			FromPort:             aws.Float64(80),
			ToPort:               aws.Float64(80),
		}),
		aws.String("Incoming http"),
		aws.Bool(false),
	)

You define a Security Group, then add some ingress rules. This group will then be referenced in the instance creation:

monolith := ec2.NewInstance(stack, aws.String("monolith"),
    &ec2.InstanceProps{
        ...
        SecurityGroup: monolithSG,
        ...
    })

an instance profile

ssmPolicy := iam.ManagedPolicy_FromAwsManagedPolicyName(aws.String("AmazonSSMManagedInstanceCore"))

instanceRole := iam.NewRole(stack, aws.String("webinstancerole"),
    &iam.RoleProps{
        AssumedBy:       iam.NewServicePrincipal(aws.String("ec2.amazonaws.com"), nil),
        Description:     aws.String("Instance Role"),
        ManagedPolicies: &[]iam.IManagedPolicy{ssmPolicy},
    },
)

To give the instances and all users on this instance, you create a instance role. This role will be assumed by “ec2.amazonaws.com”.

With the managed AWS IAM policy AmazonSSMManagedInstanceCore you may connect with the session manager to the instance.

modifying the EBS volume

With the standard instance CDK construct you get an EBS volume.

If you want to alter this, you have to create an additional volume. With the right device name this will be used as boot volume:

volume := ec2.BlockDeviceVolume_Ebs(aws.Float64(30), &ec2.EbsDeviceOptions{
    VolumeType:          ec2.EbsDeviceVolumeType_GP3,
})
rootVolume :=  &ec2.BlockDevice{
    DeviceName: aws.String("/dev/xvda"),
    Volume: volume,
    };

The Instance itself

As with all CDk constructs, the properties have their own type, ec2.InstanceProps. So the instance goes like:

	monolith := ec2.NewInstance(stack, aws.String("monolith"),
		&ec2.InstanceProps{
			InstanceType:  ec2.InstanceType_Of(ec2.InstanceClass_MEMORY5_AMD, ec2.InstanceSize_LARGE),
			MachineImage:  linuxImage,
			BlockDevices: &[]*ec2.BlockDevice{rootVolume},
			Vpc:           vpc,
			InstanceName:  aws.String("monolith"),
			Role:          instanceRole,
			SecurityGroup: monolithSG,
			UserData:      userdata,
			VpcSubnets: &ec2.SubnetSelection{
				SubnetType: ec2.SubnetType_PUBLIC,
			},
		})

And all prepared resources will be used in the properties.

bootstrapping the instance

Testing

testing the generated CloudFormation

integration test/end to end

Connecting

Because we have given the instance profile the Systems Manager rights, we can just connect with the Session Manager Plugin:

If the instance has the id “i-0f4cec49522ba3f15” you get an terminal session with:

aws ssm start-session --target i-0f4cec49522ba3f15

Without ssh key and it makes no difference whether the instance is in public or private subnet.

This looks like:

aws ssm start-session --target i-0f4cec49522ba3f15


Starting session with SessionId: gglawe-039a13542185319ac
sh-4.2$

See also

Source

See the full source on github:

Sources