From ec7e57d974161c9a09277b574e4a9f67181c73ed Mon Sep 17 00:00:00 2001 From: Michele Cereda Date: Sat, 8 Jun 2024 01:29:02 +0200 Subject: [PATCH] feat(pulumi/examples): add generic aws ec2 instance --- examples/pulumi/aws/ec2 instance/.env.fish | 2 + examples/pulumi/aws/ec2 instance/.gitignore | 3 + .../pulumi/aws/ec2 instance/Pulumi.any.yaml | 8 ++ examples/pulumi/aws/ec2 instance/Pulumi.yaml | 9 ++ examples/pulumi/aws/ec2 instance/index.ts | 122 ++++++++++++++++++ examples/pulumi/aws/ec2 instance/package.json | 13 ++ .../pulumi/aws/ec2 instance/tsconfig.json | 18 +++ knowledge base/cloud computing/aws/README.md | 2 + knowledge base/cloud computing/aws/ssm.md | 26 +++- snippets/aws.fish | 23 ++++ snippets/systemd.sh | 5 + 11 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 examples/pulumi/aws/ec2 instance/.env.fish create mode 100644 examples/pulumi/aws/ec2 instance/.gitignore create mode 100644 examples/pulumi/aws/ec2 instance/Pulumi.any.yaml create mode 100644 examples/pulumi/aws/ec2 instance/Pulumi.yaml create mode 100644 examples/pulumi/aws/ec2 instance/index.ts create mode 100644 examples/pulumi/aws/ec2 instance/package.json create mode 100644 examples/pulumi/aws/ec2 instance/tsconfig.json diff --git a/examples/pulumi/aws/ec2 instance/.env.fish b/examples/pulumi/aws/ec2 instance/.env.fish new file mode 100644 index 0000000..20e80dc --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/.env.fish @@ -0,0 +1,2 @@ +set -x PULUMI_BACKEND_URL 'file://.' +set -x PULUMI_CONFIG_PASSPHRASE 'test123' diff --git a/examples/pulumi/aws/ec2 instance/.gitignore b/examples/pulumi/aws/ec2 instance/.gitignore new file mode 100644 index 0000000..37a8612 --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/node_modules/ +/package-lock.json diff --git a/examples/pulumi/aws/ec2 instance/Pulumi.any.yaml b/examples/pulumi/aws/ec2 instance/Pulumi.any.yaml new file mode 100644 index 0000000..9419678 --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/Pulumi.any.yaml @@ -0,0 +1,8 @@ +encryptionsalt: v1:55yDA5Kuyzs=:v1:+kFXkziA9Bd7nNZQ:OSBtNRAVCGBXwzTtHOGA5Ti9Dz+FTQ== +config: + aws:region: eu-west-1 + aws:defaultTags: + tags: + ManagedByPulumi: true + Owner: "somebody@example.com" + PulumiProject: ec2-instance diff --git a/examples/pulumi/aws/ec2 instance/Pulumi.yaml b/examples/pulumi/aws/ec2 instance/Pulumi.yaml new file mode 100644 index 0000000..89f46da --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/Pulumi.yaml @@ -0,0 +1,9 @@ +name: ec2-instance +runtime: nodejs +description: AWS EC2 instance example +config: + pulumi:tags: + value: + pulumi:template: aws-typescript +backend: + url: file://. diff --git a/examples/pulumi/aws/ec2 instance/index.ts b/examples/pulumi/aws/ec2 instance/index.ts new file mode 100644 index 0000000..d159765 --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/index.ts @@ -0,0 +1,122 @@ +import * as aws from "@pulumi/aws"; +import * as cloudinit from "@pulumi/cloudinit"; +import * as yaml from "yaml"; + +const ami = aws.ec2.getAmiOutput({ + owners: [ "amazon", ], + nameRegex: "^al2023-ami-2023.*", + filters: [ + { + name: "architecture", + values: [ "arm64" ], + }, + { + name: "state", + values: [ "available" ], + }, + ], + mostRecent: true, +}); +const keyPair = aws.ec2.getKeyPairOutput({ keyName: "somebody-ec2Instances" }); +const subnet = aws.ec2.getSubnetOutput({ + filters: [{ + name: "tag:Name", + values: [ "Private C" ], + }], +}); + +const securityGroup = new aws.ec2.SecurityGroup( + "ec2-instance-example", + { + name: "Ec2InstanceExample", + description: "Regulate communications to and from the EC2 Instance", + tags: { + Name: "EC2 Instance Example", + }, + }, +); +const role = new aws.iam.Role( + "ec2-instance-example", + { + name: "Ec2InstanceExample", + assumeRolePolicy: JSON.stringify({ + Version: "2012-10-17", + Statement: [{ + Effect: "Allow", + Action: "sts:AssumeRole", + Principal: { + Service: "ec2.amazonaws.com", + }, + }], + }), + managedPolicyArns: [ "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" ], + }, +); +const instanceProfile = new aws.iam.InstanceProfile( + "ec2-instance-example", + { + name: "Ec2InstanceExample", + role: role.name, + }, +); +const userData = new cloudinit.Config( + "ec2-instance-example", + { + gzip: true, + base64Encode: true, + parts: [ + { + contentType: "text/cloud-config", + content: yaml.stringify({ + package_upgrade: false, + packages: [ "amazon-ssm-agent" ], + runcmd: [ + "systemctl daemon-reload", + "systemctl enable --now 'amazon-ssm-agent.service'", + ] + }), + filename: "cloud-config.managed-by.ssm.yml", + }, + { + contentType: "text/cloud-config", + content: yaml.stringify({ + package_upgrade: false, + packages: [ "python" ], + }), + filename: "cloud-config.managed-by.ansible.yml", + mergeType: "dict(recurse_array,no_replace)+list(append)", + }, + ], + }, +); +new aws.ec2.Instance( + "ec2-instance-example", + { + ami: ami.apply(ami => ami.id), + iamInstanceProfile: instanceProfile.name, + instanceType: "t4g.small", + keyName: keyPair.apply(keyPair => keyPair.keyName!), + rootBlockDevice: { + volumeType: "gp3", + volumeSize: 10, + tags: { + Description: "Instance root disk", + Name: "EC2 Instance Example", + }, + }, + subnetId: subnet.apply(subnet => subnet.id), + tags: { + Name: "EC2 Instance Example", + ManagedBySsm: "true", + ManagedByAnsible: "true", + }, + userData: userData.rendered, + vpcSecurityGroupIds: [ securityGroup.id ], + }, + { + ignoreChanges: [ + // avoid being replaced just because a new version of the base image came out + "ami", + ], + } +); diff --git a/examples/pulumi/aws/ec2 instance/package.json b/examples/pulumi/aws/ec2 instance/package.json new file mode 100644 index 0000000..85eff6e --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/package.json @@ -0,0 +1,13 @@ +{ + "name": "ec2-instance", + "main": "index.ts", + "devDependencies": { + "@types/node": "^18", + "typescript": "^5.0.0" + }, + "dependencies": { + "@pulumi/aws": "^6.0.0", + "@pulumi/cloudinit": "^1.0.0", + "yaml": "^2.0.0" + } +} \ No newline at end of file diff --git a/examples/pulumi/aws/ec2 instance/tsconfig.json b/examples/pulumi/aws/ec2 instance/tsconfig.json new file mode 100644 index 0000000..f960d51 --- /dev/null +++ b/examples/pulumi/aws/ec2 instance/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2020", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/knowledge base/cloud computing/aws/README.md b/knowledge base/cloud computing/aws/README.md index 54ad9ea..189425e 100644 --- a/knowledge base/cloud computing/aws/README.md +++ b/knowledge base/cloud computing/aws/README.md @@ -267,6 +267,7 @@ Examples: - [Automating DNS-challenge based LetsEncrypt certificates with AWS Route 53] - [Working with DB instance read replicas] - AWS' [CLI] +- [Configuring EC2 Disk alert using Amazon CloudWatch] ### Sources @@ -347,6 +348,7 @@ Examples: [automating dns-challenge based letsencrypt certificates with aws route 53]: https://johnrix.medium.com/automating-dns-challenge-based-letsencrypt-certificates-with-aws-route-53-8ba799dd207b [aws config tutorial by stephane maarek]: https://www.youtube.com/watch?v=qHdFoYSrUvk [aws icons]: https://aws-icons.com/ +[configuring ec2 disk alert using amazon cloudwatch]: https://medium.com/@chandinims001/configuring-ec2-disk-alert-using-amazon-cloudwatch-793807e40d72 [date & time policy conditions at aws - 1-minute iam lesson]: https://www.youtube.com/watch?v=4wpKP1HLEXg [introduction to aws iam assumerole]: https://aws.plainenglish.io/introduction-to-aws-iam-assumerole-fbef3ce8e90b [not authorized to perform: sts:assumerole]: https://repost.aws/questions/QUOY5XngCtRyOX4Desaygz8Q/not-authorized-to-perform-sts-assumerole diff --git a/knowledge base/cloud computing/aws/ssm.md b/knowledge base/cloud computing/aws/ssm.md index b672597..c81cbb2 100644 --- a/knowledge base/cloud computing/aws/ssm.md +++ b/knowledge base/cloud computing/aws/ssm.md @@ -29,22 +29,39 @@ aws ssm start-session \ aws ssm send-command \ --instance-ids 'i-0123456789abcdef0' \ --document-name 'AWS-RunShellScript' \ - --parameters commands="echo 'hallo!'" + --parameters "commands="echo 'hallo'" + +# Wait for commands execution. +aws ssm wait command-executed --instance-id 'i-0123456789abcdef0' --command-id 'abcdef01-2345-abcd-6789-abcdef012345' + +# Get commands results. +aws ssm get-command-invocation --instance-id 'i-0123456789abcdef0' --command-id 'abcdef01-2345-abcd-6789-abcdef012345' +aws ssm get-command-invocation \ + --instance-id 'i-0123456789abcdef0' --command-id 'abcdef01-2345-abcd-6789-abcdef012345' \ + --query '{"status": Status, "rc": ResponseCode, "stdout": StandardOutputContent, "stderr": StandardErrorContent}' ```
Real world use cases +Also check out the [snippets]. + ```sh # Connect to instances if they are available. instance_id='i-08fc83ad07487d72f' \ -&& eval $(aws ssm get-connection-status --target "$instance_id" --query "Status=='connected'" --output text) \ +&& eval $(aws ssm get-connection-status --target "$instance_id" --query "Status=='connected'" --output 'text') \ && aws ssm start-session --target "$instance_id" \ || (echo "instance ${instance_id} not available" >&2 && false) -aws ssm send-command --instance-ids "i-08fc83ad07487d72f" \ - --document-name "AWS-RunShellScript" --parameters commands="echo 'hallo!'" +# Run commands and get their output. +instance_id='i-0915612f182914822' \ +&& command_id=$(aws ssm send-command --instance-ids "$instance_id" \ + --document-name 'AWS-RunShellScript' --parameters 'commands="echo hallo"' \ + --query 'Command.CommandId' --output 'text') \ +&& aws ssm wait command-executed --command-id "$command_id" --instance-id "$instance_id" \ +&& aws ssm get-command-invocation --command-id "$command_id" --instance-id "$instance_id" \ + --query '{"status": Status, "rc": ResponseCode, "stdout": StandardOutputContent, "stderr": StandardErrorContent}' ```
@@ -267,6 +284,7 @@ $ sudo ssm-cli get-diagnostics --output 'table' [amazon web services]: README.md [cli]: cli.md [ec2]: ec2.md +[snippets]: ../../../snippets/aws.fish [aws_ssm connection plugin notes]: https://docs.ansible.com/ansible/latest/collections/community/aws/aws_ssm_connection.html#notes diff --git a/snippets/aws.fish b/snippets/aws.fish index 0fe5724..2f3bbab 100644 --- a/snippets/aws.fish +++ b/snippets/aws.fish @@ -73,3 +73,26 @@ aws ec2 describe-images --output 'yaml' \ aws iam list-instance-profiles | grep -i 'ssm' sudo ssm-cli get-diagnostics --output 'table' + +# Check instances are available +aws ssm get-connection-status --target "i-0915612ff82914822" --query "Status=='connected'" --output 'text' + +# Connect to instances if they are available +instance_id='i-08fc83ad07487d72f' \ +&& eval $(aws ssm get-connection-status --target "$instance_id" --query "Status=='connected'" --output 'text') \ +&& aws ssm start-session --target "$instance_id" \ +|| (echo "instance ${instance_id} not available" >&2 && false) + +# Send commands +aws ssm send-command --instance-ids 'i-08fc83ad07487d72f' --document-name 'AWS-RunShellScript' --parameters "commands='echo hallo'" +aws ssm wait command-executed --command-id 'e5f7ca0e-4d74-4316-84be-9ccaf3ae1f70' --instance-id 'i-08fc83ad07487d72f' +aws ssm get-command-invocation --command-id 'e5f7ca0e-4d74-4316-84be-9ccaf3ae1f70' --instance-id 'i-08fc83ad07487d72f' + +# Run commands and get their output. +set instance_id 'i-0915612f182914822' \ +&& set command_id (aws ssm send-command --instance-ids "$instance_id" \ + --document-name 'AWS-RunShellScript' --parameters 'commands="echo hallo"' \ + --query 'Command.CommandId' --output 'text') \ +&& aws ssm wait command-executed --command-id "$command_id" --instance-id "$instance_id" \ +&& aws ssm get-command-invocation --command-id "$command_id" --instance-id "$instance_id" \ + --query '{"status": Status, "rc": ResponseCode, "stdout": StandardOutputContent, "stderr": StandardErrorContent}' diff --git a/snippets/systemd.sh b/snippets/systemd.sh index 2201557..29f3683 100644 --- a/snippets/systemd.sh +++ b/snippets/systemd.sh @@ -2,3 +2,8 @@ sudo systemctl enable --now 'gitlab-runner' sudo journalctl -xefu 'gitlab-runner' + +sudo hostnamectl +sudo hostnamectl status --static + +sudo hostnamectl set-hostname --pretty 'prometheus'