Search code examples
pythonamazon-web-servicesbashamazon-ec2aws-cdk

Can't execute commands on EC2 with AWS CDK


My current objective is to utilize CDK to create an EC2 instance for automation. Thus far, I have been successful in creating an EC2 instance with CDK, complete with VPC, security group, and key.

However, after deploying the instance via CDK, I have encountered issues attempting to execute commands on the instance, as they fail to execute properly. To elaborate, the commands in question do not execute on the EC2 instance as intended.

For reference, I attached the code below :

from aws_cdk import (
    Stack,
    aws_ec2 as ec2,
    CfnOutput,
    CfnTag,
    aws_iam as iam,
    aws_route53 as route53,
    aws_route53_targets as targets
)
from constructs import Construct

 
class CdkTrsting1Stack(Stack):
 
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
 
        # VPC
        vpc = ec2.Vpc.from_vpc_attributes(
            self, "VPC",
            vpc_id="vpc-",
            availability_zones=['us-east-1a'],
            public_subnet_ids=['subnet-'] # Specify the actual subnet IDs here
        )
 
        # Security Group with inbound rule from everywhere
        security_group = ec2.SecurityGroup(
            self, "SecurityGroup",
            vpc=vpc,
            allow_all_outbound=True,
            security_group_name="AllowAllInbound",
        )
       
        security_group.add_ingress_rule(
            ec2.Peer.any_ipv4(),
            ec2.Port.tcp(22),  
        )
        security_group.add_ingress_rule(
            ec2.Peer.any_ipv4(),
            ec2.Port.tcp(80),  
        )
        security_group.add_ingress_rule(
            ec2.Peer.any_ipv4(),
            ec2.Port.tcp(443)  # SSH port
        )
 
        # IAM Role
        role = iam.Role(
            self, "EC2FullAccessRole",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
        )
 
        # Attach policy with full EC2 access to the role
        policy = iam.PolicyStatement(
            actions=["ec2:*"],
            resources=["*"]
        )
        role.add_to_policy(policy)
       
       
        # Create EC2 instance
        instance = ec2.Instance(
            self,
            "MyInstance",
            instance_type=ec2.InstanceType("t2.micro"),
            machine_image=ec2.MachineImage.generic_linux({
                "us-east-1": "ami-07d9b9ddc6cd8dd30"
            }),
            vpc=vpc,
            security_group=security_group,
            associate_public_ip_address=True,
            key_name="<key>",
            role=role,
            user_data=ec2.UserData.custom(
                "sudo mkdir Trial101"
            )
        )
       
        user_data = ec2.UserData.for_linux()
        user_data.add_commands(
            "#!/bin/bash",
            "sudo apt update && sudo apt upgrade -y",  
            "sudo apt install git",  
            "git clone  <git_url>",
            "cd <folder_path>",
            "sh startup_run.sh"
        )
 
     
       
        publicIp = instance.instance_public_ip;
 
        # Output Instance ID
        CfnOutput(self, "InstanceId", value=instance.instance_id)
        CfnOutput(self, "InstanceIp", value=instance.instance_public_dns_name)
user_data = ec2.UserData.for_linux()
user_data.add_commands(
            "#!/bin/bash",
            "sudo apt update && sudo apt upgrade -y",  # Update all packages
            "sudo apt install git",  # Install Git
            "git clone <git_link>",
            "cd /<folder_path>",
            "sh startup_run.sh"
)

#Attach user data to instance instance.user_data.add_commands(str(user_data))


Solution

  • Explanation

    You don't say if you get any errors to the log, but adding the extra shebang is unnecessary.

    for_linux already automatically uses #!/bin/bash as the shebang. See CDK sources. Shebang can also be customized if needed (see Documentation), but that's not really needed here either.

    That being said, the shebang is not necessarily the biggest problem. Running as sudo on the other hand might be.

    From Documentation

    Scripts entered as user data are run as the root user, so do not use the sudo command in the script.

    So you need to remove the sudo instructions as unnecessary too.

    Third problem is running apt install without automatic accept with -y.

    Solution

    This should work:

    user_data = ec2.UserData.for_linux()
    user_data.add_commands(
                "apt update && apt upgrade -y",  # Update all packages
                "apt -y install git",  # Install Git
                "git clone <git_link>",
                "cd /<folder_path>",
                "sh startup_run.sh"
    )