User data in AWS CDK EC2 provides a mechanism for passing startup scripts and configuration to EC2 instances during launch, enabling automated instance initialization and configuration.
The base class for user data implementations:
abstract class UserData {
static forLinux(options?: LinuxUserDataOptions): UserData;
static forWindows(): UserData;
static custom(content: string): UserData;
abstract addCommands(...commands: string[]): void;
abstract render(): string;
// Additional utility methods
addExecuteFileCommand(params: ExecuteFileOptions): void;
addS3DownloadCommand(params: S3DownloadOptions): void;
addSignalOnExitCommand(resource: Resource): void;
addOnExitCommands(...commands: string[]): void;
}Specialized class for Linux instances:
class LinuxUserData extends UserData {
constructor(options?: LinuxUserDataOptions);
addCommands(...commands: string[]): void;
render(): string;
// Linux-specific methods
addSignalOnExitCommand(resource: Resource): void;
addOnExitCommands(...commands: string[]): void;
}
interface LinuxUserDataOptions {
readonly shebang?: string;
}Specialized class for Windows instances:
class WindowsUserData extends UserData {
constructor();
addCommands(...commands: string[]): void;
render(): string;
// Windows-specific methods
addSignalOnExitCommand(resource: Resource): void;
addOnExitCommands(...commands: string[]): void;
}Supporting interfaces for advanced user data operations:
interface ExecuteFileOptions {
readonly filePath: string;
readonly arguments?: string;
}
interface S3DownloadOptions {
readonly bucket: s3.IBucket;
readonly bucketKey: string;
readonly localFile?: string;
readonly region?: string;
}CloudFormation Init provides more advanced instance initialization:
class CloudFormationInit {
static fromElements(...elements: InitElement[]): CloudFormationInit;
static fromConfig(config: InitConfig): CloudFormationInit;
static fromConfigSets(props: ConfigSetProps): CloudFormationInit;
addConfig(configName: string, config: InitConfig): void;
addConfigSet(configSetName: string, configNames: string[]): void;
attach(attachedResource: CfnResource, options: AttachInitOptions): void;
}
class InitConfig {
constructor(elements: InitElement[]);
add(...elements: InitElement[]): void;
}
interface ConfigSetProps {
readonly configSets: Record<string, string[]>;
readonly configs: Record<string, InitConfig>;
}
interface AttachInitOptions {
readonly platform?: InitPlatform;
readonly userData?: UserData;
readonly ignoreFailures?: boolean;
readonly includeUrl?: boolean;
readonly includeRole?: boolean;
readonly printLog?: boolean;
readonly configSets?: string[];
}
interface ApplyCloudFormationInitOptions {
readonly timeout?: Duration;
readonly includeUrl?: boolean;
readonly includeRole?: boolean;
readonly printLog?: boolean;
readonly ignoreFailures?: boolean;
readonly configSets?: string[];
}
enum InitPlatform {
LINUX = 'linux',
WINDOWS = 'windows'
}import * as ec2 from "@aws-cdk/aws-ec2";
const vpc = new ec2.Vpc(this, "MyVpc");
// Create basic Linux user data
const userData = ec2.UserData.forLinux();
userData.addCommands(
"yum update -y",
"yum install -y httpd",
"systemctl start httpd",
"systemctl enable httpd",
"echo '<h1>Hello from EC2!</h1>' > /var/www/html/index.html"
);
const instance = new ec2.Instance(this, "WebServer", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.MachineImage.latestAmazonLinux(),
userData
});const windowsUserData = ec2.UserData.forWindows();
windowsUserData.addCommands(
"powershell -Command Install-WindowsFeature -name Web-Server -IncludeManagementTools",
"powershell -Command New-Item -Path C:\\inetpub\\wwwroot\\index.html -ItemType File -Force",
"powershell -Command Set-Content -Path C:\\inetpub\\wwwroot\\index.html -Value '<h1>Hello from Windows Server!</h1>'"
);
const windowsInstance = new ec2.Instance(this, "WindowsWebServer", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE),
machineImage: ec2.MachineImage.latestWindows(
ec2.WindowsVersion.WINDOWS_SERVER_2022_ENGLISH_FULL_BASE
),
userData: windowsUserData,
keyName: "windows-keypair"
});const customUserData = ec2.UserData.forLinux({
shebang: "#!/bin/bash -xe"
});
customUserData.addCommands(
"exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1",
"yum update -y",
"yum install -y docker",
"systemctl start docker",
"systemctl enable docker",
"usermod -a -G docker ec2-user"
);import * as s3 from "@aws-cdk/aws-s3";
const configBucket = s3.Bucket.fromBucketName(this, "ConfigBucket", "my-config-bucket");
const userData = ec2.UserData.forLinux();
// Download configuration file from S3
userData.addS3DownloadCommand({
bucket: configBucket,
bucketKey: "configs/app-config.json",
localFile: "/opt/app/config.json"
});
// Download and execute script from S3
userData.addS3DownloadCommand({
bucket: configBucket,
bucketKey: "scripts/setup.sh",
localFile: "/tmp/setup.sh"
});
userData.addExecuteFileCommand({
filePath: "/tmp/setup.sh",
arguments: "--environment production"
});
userData.addCommands(
"chmod +x /tmp/setup.sh",
"/tmp/setup.sh --environment production"
);const dockerUserData = ec2.UserData.forLinux();
dockerUserData.addCommands(
// Update system
"yum update -y",
// Install Docker
"amazon-linux-extras install docker -y",
"systemctl start docker",
"systemctl enable docker",
"usermod -a -G docker ec2-user",
// Install Docker Compose
"curl -L \"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose",
"chmod +x /usr/local/bin/docker-compose",
// Pull and run container
"docker pull nginx:latest",
"docker run -d -p 80:80 --name webserver nginx:latest",
// Create custom index page
"docker exec webserver sh -c 'echo \"<h1>Hello from Docker on EC2!</h1>\" > /usr/share/nginx/html/index.html'"
);
const dockerInstance = new ec2.Instance(this, "DockerInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL),
machineImage: ec2.MachineImage.latestAmazonLinux(),
userData: dockerUserData
});const dbUserData = ec2.UserData.forLinux();
dbUserData.addCommands(
"yum update -y",
// Install MySQL
"yum install -y mysql-server",
"systemctl start mysqld",
"systemctl enable mysqld",
// Secure installation (basic setup)
"mysql -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY 'MySecurePassword123!';\"",
"mysql -e \"DELETE FROM mysql.user WHERE User='';\"",
"mysql -e \"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');\"",
"mysql -e \"DROP DATABASE IF EXISTS test;\"",
"mysql -e \"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';\"",
"mysql -e \"FLUSH PRIVILEGES;\"",
// Create application database
"mysql -u root -pMySecurePassword123! -e \"CREATE DATABASE app_db;\"",
"mysql -u root -pMySecurePassword123! -e \"CREATE USER 'app_user'@'%' IDENTIFIED BY 'AppPassword123!';\"",
"mysql -u root -pMySecurePassword123! -e \"GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'%';\"",
"mysql -u root -pMySecurePassword123! -e \"FLUSH PRIVILEGES;\""
);
const dbInstance = new ec2.Instance(this, "DatabaseInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL),
machineImage: ec2.MachineImage.latestAmazonLinux(),
userData: dbUserData,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT
}
});const monitoringUserData = ec2.UserData.forLinux();
monitoringUserData.addCommands(
"yum update -y",
// Install CloudWatch agent
"wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm",
"rpm -U ./amazon-cloudwatch-agent.rpm",
// Create CloudWatch agent configuration
"cat <<EOF > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json",
"{",
" \"agent\": {",
" \"metrics_collection_interval\": 60,",
" \"run_as_user\": \"cwagent\"",
" },",
" \"metrics\": {",
" \"namespace\": \"MyApp/EC2\",",
" \"metrics_collected\": {",
" \"cpu\": {",
" \"measurement\": [",
" \"cpu_usage_idle\",",
" \"cpu_usage_iowait\",",
" \"cpu_usage_user\",",
" \"cpu_usage_system\"",
" ],",
" \"metrics_collection_interval\": 60",
" },",
" \"disk\": {",
" \"measurement\": [",
" \"used_percent\"",
" ],",
" \"metrics_collection_interval\": 60,",
" \"resources\": [",
" \"*\"",
" ]",
" },",
" \"mem\": {",
" \"measurement\": [",
" \"mem_used_percent\"",
" ],",
" \"metrics_collection_interval\": 60",
" }",
" }",
" },",
" \"logs\": {",
" \"logs_collected\": {",
" \"files\": {",
" \"collect_list\": [",
" {",
" \"file_path\": \"/var/log/messages\",",
" \"log_group_name\": \"/aws/ec2/var/log/messages\",",
" \"log_stream_name\": \"{instance_id}\"",
" }",
" ]",
" }",
" }",
" }",
"}",
"EOF",
// Start CloudWatch agent
"/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s"
);import * as cdk from "@aws-cdk/core";
const init = ec2.CloudFormationInit.fromElements(
// Install packages
ec2.InitPackage.yum("httpd"),
ec2.InitPackage.yum("mysql"),
// Create files
ec2.InitFile.fromString(
"/var/www/html/index.html",
"<html><body><h1>Hello from CloudFormation Init!</h1></body></html>"
),
ec2.InitFile.fromAsset(
"/etc/httpd/conf.d/custom.conf",
"assets/httpd-custom.conf"
),
// Configure services
ec2.InitService.enable("httpd", {
enabled: true,
ensureRunning: true,
serviceRestartHandle: new ec2.InitServiceRestartHandle()
}),
// Run commands
ec2.InitCommand.shellCommand("echo 'Setup complete' > /tmp/setup.log")
);
const initInstance = new ec2.Instance(this, "InitInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.MachineImage.latestAmazonLinux(),
init,
initOptions: {
timeout: cdk.Duration.minutes(10),
includeUrl: true,
includeRole: true,
printLog: true
}
});const webConfig = new ec2.InitConfig([
ec2.InitPackage.yum("httpd"),
ec2.InitService.enable("httpd")
]);
const appConfig = new ec2.InitConfig([
ec2.InitPackage.yum("python3"),
ec2.InitFile.fromString("/opt/app/app.py", "print('Hello World')"),
ec2.InitCommand.shellCommand("python3 /opt/app/app.py")
]);
const dbConfig = new ec2.InitConfig([
ec2.InitPackage.yum("mysql-server"),
ec2.InitService.enable("mysqld")
]);
const multiStageInit = ec2.CloudFormationInit.fromConfigSets({
configSets: {
"setup": ["web", "app"],
"full": ["web", "app", "database"]
},
configs: {
"web": webConfig,
"app": appConfig,
"database": dbConfig
}
});
const multiStageInstance = new ec2.Instance(this, "MultiStageInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL),
machineImage: ec2.MachineImage.latestAmazonLinux(),
init: multiStageInit,
initOptions: {
configSets: ["full"],
timeout: cdk.Duration.minutes(15)
}
});const signalUserData = ec2.UserData.forLinux();
signalUserData.addCommands(
"yum update -y",
"yum install -y httpd",
"systemctl start httpd",
"systemctl enable httpd"
);
// Signal successful completion
const waitConditionHandle = new cdk.CfnWaitConditionHandle(this, "WaitHandle");
const waitCondition = new cdk.CfnWaitCondition(this, "WaitCondition", {
handle: waitConditionHandle.ref,
timeout: "300",
count: 1
});
signalUserData.addSignalOnExitCommand(waitCondition);
const signalInstance = new ec2.Instance(this, "SignalInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.MachineImage.latestAmazonLinux(),
userData: signalUserData
});// Raw user data content
const customContent = `#!/bin/bash
yum update -y
yum install -y git
# Clone repository
cd /opt
git clone https://github.com/myorg/myapp.git
# Setup application
cd myapp
./setup.sh
# Start services
systemctl start myapp
systemctl enable myapp
`;
const customUserData = ec2.UserData.custom(customContent);
const customInstance = new ec2.Instance(this, "CustomInstance", {
vpc,
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.MachineImage.latestAmazonLinux(),
userData: customUserData
});const environment = "production"; // Could be parameter or context value
const envUserData = ec2.UserData.forLinux();
// Base setup
envUserData.addCommands(
"yum update -y",
"yum install -y httpd"
);
// Environment-specific configuration
if (environment === "production") {
envUserData.addCommands(
"yum install -y aws-logs-agent",
"systemctl start awslogs",
"systemctl enable awslogs"
);
} else {
envUserData.addCommands(
"yum install -y htop",
"echo 'Development environment' > /var/www/html/env.html"
);
}
// Common final steps
envUserData.addCommands(
"systemctl start httpd",
"systemctl enable httpd"
);const robustUserData = ec2.UserData.forLinux({
shebang: "#!/bin/bash -xe"
});
robustUserData.addCommands(
// Enable error logging
"exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1",
// Function for error handling
"function error_exit {",
" echo \"$1\" 1>&2",
" exit 1",
"}",
// Safe package installation
"yum update -y || error_exit 'Failed to update packages'",
"yum install -y httpd || error_exit 'Failed to install httpd'",
// Service management with error checking
"systemctl start httpd || error_exit 'Failed to start httpd'",
"systemctl enable httpd || error_exit 'Failed to enable httpd'",
// Verify service is running
"systemctl is-active --quiet httpd || error_exit 'httpd is not running'",
"echo 'User data completed successfully' > /tmp/user-data-success"
);