UNPKG

@pulumi/awsx

Version:

[![Actions Status](https://github.com/pulumi/pulumi-awsx/workflows/master/badge.svg)](https://github.com/pulumi/pulumi-awsx/actions) [![Slack](http://www.pulumi.com/images/docs/badges/slack.svg)](https://slack.pulumi.com) [![NPM version](https://badge.fur

251 lines (249 loc) • 11.2 kB
"use strict"; // Copyright 2016-2018, Pulumi Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.AutoScalingLaunchConfiguration = void 0; const aws = __importStar(require("@pulumi/aws")); const pulumi = __importStar(require("@pulumi/pulumi")); const ec2 = __importStar(require("../ec2")); const roleUtils = __importStar(require("../role")); const utils = __importStar(require("../utils")); class AutoScalingLaunchConfiguration extends pulumi.ComponentResource { launchConfiguration; id; securityGroups; instanceProfile; /** * Name to give the auto-scaling-group's cloudformation stack name. */ stackName; constructor(name, vpc, args = {}, opts = {}) { super("awsx:x:autoscaling:AutoScalingLaunchConfiguration", name, {}, opts); // Create the full name of our CloudFormation stack here explicitly. Since the CFN stack // references the launch configuration and vice-versa, we use this to break the cycle. // TODO[pulumi/pulumi#381]: Creating an S3 bucket is an inelegant way to get a durable, // unique name. this.stackName = pulumi.output(args.stackName).apply(sn => sn ? pulumi.output(sn) : new aws.s3.Bucket(name, {}, { parent: this }).id); // Use the instance provided, or create a new one. this.instanceProfile = args.instanceProfile || AutoScalingLaunchConfiguration.createInstanceProfile(name, /*assumeRolePolicy:*/ undefined, /*policyArns:*/ undefined, { parent: this }); this.securityGroups = ec2.getSecurityGroups(vpc, name, args.securityGroups, { parent: this }) || []; this.launchConfiguration = new aws.ec2.LaunchConfiguration(name, { ...args, securityGroups: this.securityGroups.map(g => g.id), imageId: utils.ifUndefined(args.imageId, getEcsAmiId(args.ecsOptimizedAMIName, { parent: this })), instanceType: utils.ifUndefined(args.instanceType, "t2.micro"), iamInstanceProfile: this.instanceProfile.id, enableMonitoring: utils.ifUndefined(args.enableMonitoring, true), placementTenancy: utils.ifUndefined(args.placementTenancy, "default"), rootBlockDevice: utils.ifUndefined(args.rootBlockDevice, defaultRootBlockDevice), ebsBlockDevices: utils.ifUndefined(args.ebsBlockDevices, defaultEbsBlockDevices), userData: getInstanceUserData(args, this.stackName), }, { parent: this }); this.id = this.launchConfiguration.id; this.registerOutputs(); } static defaultInstanceProfilePolicyDocument() { return { Version: "2012-10-17", Statement: [{ Action: [ "sts:AssumeRole", ], Effect: "Allow", Principal: { Service: ["ec2.amazonaws.com"], }, }], }; } static defaultInstanceProfilePolicyARNs() { return [aws.iam.ManagedPolicy.AmazonEC2ContainerServiceforEC2Role, aws.iam.ManagedPolicy.AmazonEC2ReadOnlyAccess]; } /** * Creates the [instanceProfile] for a [ClusterAutoScalingLaunchConfiguration] if not provided * explicitly. If [assumeRolePolicy] is provided it will be used when creating the task, * otherwise [defaultInstanceProfilePolicyDocument] will be used. If [policyArns] are provided, * they will be used to create [RolePolicyAttachment]s for the Role. Otherwise, * [defaultInstanceProfilePolicyARNs] will be used. */ static createInstanceProfile(name, assumeRolePolicy, policyArns, opts) { const { role, policies } = roleUtils.createRoleAndPolicies(name, assumeRolePolicy || AutoScalingLaunchConfiguration.defaultInstanceProfilePolicyDocument(), policyArns || AutoScalingLaunchConfiguration.defaultInstanceProfilePolicyARNs(), opts); return new aws.iam.InstanceProfile(name, { role }, { ...opts, dependsOn: policies }); } } exports.AutoScalingLaunchConfiguration = AutoScalingLaunchConfiguration; // http://docs.aws.amazon.com/AmazonECS/latest/developerguide/container_agent_versions.html async function getEcsAmiId(name, opts) { // If a name was not provided, use the latest recommended version. if (!name) { // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/retrieve-ecs-optimized_AMI.html const ecsRecommendedAMI = await aws.ssm.getParameter({ name: "/aws/service/ecs/optimized-ami/amazon-linux/recommended", }, { ...opts, async: true }); return JSON.parse(ecsRecommendedAMI.value).image_id; } // Else, if a name was provided, look it up and use that imageId. const result = await aws.ec2.getAmi({ owners: [ "591542846629", // Amazon ], filters: [ { name: "name", values: [name], }, ], mostRecent: true, }, { ...opts, async: true }); return result.imageId; } const defaultRootBlockDevice = { volumeSize: 32, // GiB volumeType: "gp2", // default is "standard" deleteOnTermination: true, }; const defaultEbsBlockDevices = [{ // Swap volume deviceName: "/dev/xvdb", volumeSize: 5, // GiB volumeType: "gp2", // default is "standard" deleteOnTermination: true, }, { // Docker image and metadata volume deviceName: "/dev/xvdcz", volumeSize: 50, // GiB volumeType: "gp2", deleteOnTermination: true, }]; // http://cloudinit.readthedocs.io/en/latest/topics/format.html#cloud-config-data // ours seems inspired by: // https://github.com/convox/rack/blob/023831d8/provider/aws/dist/rack.json#L1669 // https://github.com/awslabs/amazon-ecs-amazon-efs/blob/d92791f3/amazon-efs-ecs.json#L655 function getInstanceUserData(args, cloudFormationStackName) { const autoScalingUserData = args.userData; if (args.userData !== undefined && !isAutoScalingUserData(args.userData)) { return args.userData; } const additionalBootcmdLines = getAdditionalBootcmdLines(autoScalingUserData); const additionalRuncmdLines = getAdditionalRuncmdLines(autoScalingUserData); return pulumi.all([additionalBootcmdLines, additionalRuncmdLines, cloudFormationStackName]) .apply(([additionalBootcmdLines, additionalRuncmdLines, cloudFormationStackName]) => { let userData = `#cloud-config repo_upgrade_exclude: - kernel* packages: - aws-cfn-bootstrap - aws-cli - nfs-utils mounts: - ['/dev/xvdb', 'none', 'swap', 'sw', '0', '0'] bootcmd: - mkswap /dev/xvdb - swapon /dev/xvdb - echo ECS_ENGINE_AUTH_TYPE=docker >> /etc/ecs/ecs.config `; userData += collapseLines(additionalBootcmdLines); userData += ` runcmd: # Set and use variables in the same command, since it's not obvious if # different commands will run in different shells. - | # Knock one letter off of availability zone to get region. AWS_AVAILABILITY_ZONE=$(curl -s 169.254.169.254/2016-09-02/meta-data/placement/availability-zone) AWS_REGION=$(echo $AWS_AVAILABILITY_ZONE | sed 's/.$//') `; userData += collapseLines(additionalRuncmdLines); userData += ` # Disable container access to EC2 metadata instance # See http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP service iptables save /opt/aws/bin/cfn-signal \ --region "\${AWS_REGION}" \ --stack "${cloudFormationStackName}" \ --resource Instances `; return userData; }); } function collapseLines(additionalBootcmdLines) { let result = ""; for (const line of additionalBootcmdLines) { let contents = line.contents; // By default, automatically indent. Do not indent only in the case where the client // explicitly passes 'false'. if (line.automaticallyIndent !== false) { contents = " " + contents; if (contents[contents.length - 1] !== "\n") { contents += "\n"; } } result += contents; } return result; } function getAdditionalBootcmdLines(args) { if (!args || !args.extraBootcmdLines) { return pulumi.output([]); } return pulumi.output(args.extraBootcmdLines()); } function getAdditionalRuncmdLines(args) { if (!args || !args.extraRuncmdLines) { return pulumi.output([]); } return pulumi.output(args.extraRuncmdLines()); } function isAutoScalingUserData(obj) { return obj !== undefined && (obj.extraBootcmdLines instanceof Function || obj.extraRuncmdLines instanceof Function); } // Make sure our exported args shape is compatible with the overwrite shape we're trying to provide. utils.checkCompat(); //# sourceMappingURL=launchConfiguration.js.map