I'm trying to set environment variables to my EC2 instance(s), using NodeJS SDK. I was unable to find an answer in other threads, after trying alternate approaches they suggested.
My goal is to bind USER_ID
and SERVER_ID
to the new EC2 instance for a configuration script I would like to run.
TL;DR
- I tried various approaches, including Parameter Store, and was unable to set environment variables
- implementation details provided below
- I've only tried with
UserData
with NodeJS SDK and the CLI, both resulted in the same outcome - Open to alternate approaches to bind variables to a new instance including SSH
- seeking suggestions or the identification of issues that may help
Approach 1
I tried to set variables with UserData
, but this failed. There was no indication user data was acted upon in the cloud init logs (/var/log/cloud-init-output.log). I was not able to find any output or reason why this failed.
function userDataScript(userId, serverId) {
return `
#!/usr/bin/bash
echo 'export USER_ID=${userId}'
echo 'export SERVER_ID=${serverId}'
`;
}
// Launch EC2 instance
exports.launchEC2 = async function(server) {
try {
// Set the name of the key pair
const keyName = server.id;
// Create a new key pair in EC2
const keyPair = await EC2.createKeyPair({KeyName: keyName}).promise();
server.key = keyPair.KeyMaterial;
const instanceParams = {
ImageId: imageAMI,
InstanceType: 't2.micro',
MinCount: 1,
MaxCount: 1,
KeyName: keyName,
SecurityGroupIds: [SEC_GROUP],
UserData: Buffer.from(userDataScript(server.userId, server.id)).toString('base64')
};
const data = await EC2.runInstances(instanceParams).promise();
const instanceId = data.Instances[0].InstanceId;
server.instanceId = instanceId;
await server.save();
console.log(`Launched instance ${instanceId}`);
} catch (err) {
console.log(`Error launching instance: ${err.message}`);
}
}
Approach 2 I tried the above as a file, same results. The environment variables did not exist or were not set in my instance.
Approach 3 Based on an earlier suggestion from SO, I tried using Parameter Store. My VM is pre-configured with AWS-CLI and I can run the commands successfully in the terminal of the VM, however, I was unsuccessful to get this to run with UserData.
async function createParameterStoreKeys(userId, serverId) {
const params = {
Name: `/paramstore/${serverId}/environment-variables`,
Type: 'String',
Value: JSON.stringify({
USER_ID: userId,
SERVER_ID: serverId
})
};
await SSM.putParameter(params).promise();
console.log(`Created parameter store key for ${serverId}`);
}
async function userDataScript(identifier) {
return `
export USER_ID=$(aws --region=us-east-2 ssm get-parameter --name /paramstore/${identifier}/environment-variables --query 'Parameter.Value' --output text | jq -r '.USER_ID')
export SERVER_ID=$(aws --region=us-east-2 ssm get-parameter --name /paramstore/${identifier}/environment-variables --query 'Parameter.Value' --output text | jq -r '.SERVER_ID')
`;
}
// Launch EC2 instance
exports.launchEC2 = async function(server) {
try {
await createParameterStoreKeys(server.userId, server.id);
// Set the name of the key pair
const keyName = server.id;
// Create a new key pair in EC2
const keyPair = await EC2.createKeyPair({KeyName: keyName}).promise();
server.key = keyPair.KeyMaterial;
const instanceParams = {
ImageId: imageAMI,
InstanceType: 't2.micro',
MinCount: 1,
MaxCount: 1,
KeyName: keyName,
SecurityGroupIds: [SEC_GROUP],
UserData: Buffer.from(await userDataScript(server.id)).toString('base64')
};
const data = await EC2.runInstances(instanceParams).promise();
const instanceId = data.Instances[0].InstanceId;
server.instanceId = instanceId;
await server.save();
console.log(`Launched instance ${instanceId}`);
} catch (err) {
console.log(`Error launching instance: ${err.message}`);
}
}
On SSH in to the EC2 instance, echo $USER_ID
returned empty.
Approach 4
I tried to explicitly get cloud init to work, since I think the script is not being run at all. This was a change to userDataScript
to use #cloud-config
as per examples in the documentation.
async function userDataScript(userId, serverId) {
return `#cloud-config
write_files:
- content: |
export USER_ID=${userId}
export SERVER_ID=${serverId}
path: /etc/profile.d/env.sh
owner: ubuntu:ubuntu
permissions: '0755'
runcmd:
- source /etc/profile.d/env.sh
`
}
exports.launchEC2 = async function(server) {
try {
// Set the name of the key pair
const keyName = server.id;
const instanceParams = {
ImageId: imageAMI,
InstanceType: 't2.micro',
MinCount: 1,
MaxCount: 1,
KeyName: keyName,
SecurityGroupIds: [SEC_GROUP],
UserData: Buffer.from(await userDataScript(server.userId, server.id)).toString('base64')
};
const data = await EC2.runInstances(instanceParams).promise();
const instanceId = data.Instances[0].InstanceId;
server.instanceId = instanceId;
await server.save();
console.log(`Launched instance ${instanceId}`);
} catch (err) {
console.log(`Error launching instance: ${err.message}`);
}
}
As before, this launches, yet, when after I SSH and echo $USER_ID
the response remains empty.
Edit (more details):
Additional user scripts were attempted under approaches 1 & 2, as below, and all failed regardless of the destination to profile, environment, and .bashrc:
function userDataScript(userId, serverId) {
return `
#!/usr/bin/bash
echo "export USER_ID=${userId}" >> ~/.bashrc # also failed for /etc/environment and /etc/profile
echo "export SERVER_ID=${serverId}" >> ~/.bashrc
source ~/.bashrc # with and without this line
`;
}
function userDataScript(userId, serverId) {
return `
#!/usr/bin/bash
echo "export USER_ID=${userId}" | sudo tee -a /etc/profile # also tried for /etc/environment
echo "export SERVER_ID=${serverId}" | sudo tee -a /etc/profile
source /etc/environment # with and without this line
`;
}
The following AWS-CLI commands also failed (no value on echo $USER_ID
after SSHing in to instance after its creation), which is the same behavior as all of the above, indicating that either the script is not called or that its changes are not persisted:
aws ec2 run-instances --image-id $AMI --count 1 --instance-type t2.micro --key-name $KP --security-group-ids $SEC_GRP --user-data '#!/bin/bash
echo export USER_ID=user123 >> /etc/profile
echo export SERVER_ID=server123 >> /etc/profile
'
aws ec2 run-instances --image-id $AMI --count 1 --instance-type t2.micro --key-name $KP --security-group-ids $SEC_GRP --user-data '#!/bin/bash
echo "export USER_ID=user123" | sudo tee -a /etc/profile
echo "export SERVER_ID=serv123" | sudo tee -a /etc/profile
'