EC2 for JavaScript (v3)를 사용한 Amazon SDK 예제 - AWS SDK 코드 예제

AWS Doc SDK ExamplesWord AWS SDK 리포지토리에는 더 많은 GitHub 예제가 있습니다.

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

EC2 for JavaScript (v3)를 사용한 Amazon SDK 예제

다음 코드 예제에서는 Amazon EC2와 함께 AWS SDK for JavaScript (v3)를 사용하여 작업을 수행하고 일반적인 시나리오를 구현하는 방법을 보여줍니다.

기본 사항은 서비스 내에서 필수 작업을 수행하는 방법을 보여주는 코드 예제입니다.

작업은 대규모 프로그램에서 발췌한 코드이며 컨텍스트에 맞춰 실행해야 합니다. 작업은 개별 서비스 함수를 직접적으로 호출하는 방법을 보여주며 관련 시나리오의 컨텍스트에 맞는 작업을 볼 수 있습니다.

시나리오는 동일한 서비스 내에서 또는 다른 AWS 서비스와 결합된 상태에서 여러 함수를 호출하여 특정 태스크를 수행하는 방법을 보여주는 코드 예제입니다.

각 예제에는 컨텍스트에서 코드를 설정하고 실행하는 방법에 대한 지침을 찾을 수 있는 전체 소스 코드에 대한 링크가 포함되어 있습니다.

시작

다음 코드 예제에서는 Amazon EC2 사용을 시작하는 방법을 보여줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DescribeSecurityGroupsCommand, EC2Client } from "@aws-sdk/client-ec2"; // Call DescribeSecurityGroups and display the result. export const main = async () => { const client = new EC2Client(); try { const { SecurityGroups } = await client.send( new DescribeSecurityGroupsCommand({}), ); const securityGroupList = SecurityGroups.slice(0, 9) .map((sg) => ` • ${sg.GroupId}: ${sg.GroupName}`) .join("\n"); console.log( "Hello, Amazon EC2! Let's list up to 10 of your security groups:", ); console.log(securityGroupList); } catch (err) { console.error(err); } }; // Call function if run directly. import { fileURLToPath } from "node:url"; if (process.argv[1] === fileURLToPath(import.meta.url)) { main(); }

기본 사항

다음 코드 예시는 다음과 같은 작업을 수행하는 방법을 보여줍니다.

  • 키 페어 및 보안 그룹을 생성합니다.

  • Amazon Machine Image(AMI)와 호환되는 인스턴스 유형을 선택한 다음 인스턴스를 생성합니다.

  • 인스턴스를 중지한 후 다시 시작합니다.

  • 인스턴스와 탄력적 IP 주소 연결.

  • SSH를 사용하여 인스턴스에 연결한 다음 리소스를 정리합니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

이 파일에는 EC2에 사용되는 일반적인 작업 목록이 포함되어 있습니다. 단계는 대화형 예제 실행을 간소화하는 시나리오 프레임워크로 구성됩니다. 전체 컨텍스트는 GitHub 리포지토리를 참조하세요.

import { tmpdir } from "node:os"; import { writeFile, mkdtemp, rm } from "node:fs/promises"; import { join } from "node:path"; import { get } from "node:http"; import { AllocateAddressCommand, AssociateAddressCommand, AuthorizeSecurityGroupIngressCommand, CreateKeyPairCommand, CreateSecurityGroupCommand, DeleteKeyPairCommand, DeleteSecurityGroupCommand, DisassociateAddressCommand, paginateDescribeImages, paginateDescribeInstances, paginateDescribeInstanceTypes, ReleaseAddressCommand, RunInstancesCommand, StartInstancesCommand, StopInstancesCommand, TerminateInstancesCommand, waitUntilInstanceStatusOk, waitUntilInstanceStopped, waitUntilInstanceTerminated, } from "@aws-sdk/client-ec2"; import { ScenarioAction, ScenarioInput, ScenarioOutput, } from "@aws-doc-sdk-examples/lib/scenario/index.js"; import { paginateGetParametersByPath, SSMClient } from "@aws-sdk/client-ssm"; /** * @typedef {{ * ec2Client: import('@aws-sdk/client-ec2').EC2Client, * errors: Error[], * keyPairId?: string, * tmpDirectory?: string, * securityGroupId?: string, * ipAddress?: string, * images?: import('@aws-sdk/client-ec2').Image[], * image?: import('@aws-sdk/client-ec2').Image, * instanceTypes?: import('@aws-sdk/client-ec2').InstanceTypeInfo[], * instanceId?: string, * instanceIpAddress?: string, * allocationId?: string, * allocatedIpAddress?: string, * associationId?: string, * }} State */ /** * A skip function provided to the `skipWhen` of a Step when you want * to ignore that step if any errors have occurred. * @param {State} state */ const skipWhenErrors = (state) => state.errors.length > 0; const MAX_WAITER_TIME_IN_SECONDS = 60 * 8; export const confirm = new ScenarioInput("confirmContinue", "Continue?", { type: "confirm", skipWhen: skipWhenErrors, }); export const exitOnNoConfirm = new ScenarioAction( "exitOnConfirmContinueFalse", (/** @type { { earlyExit: boolean } & Record<string, any>} */ state) => { if (!state[confirm.name]) { state.earlyExit = true; } }, { skipWhen: skipWhenErrors, }, ); export const greeting = new ScenarioOutput( "greeting", ` Welcome to the Amazon EC2 basic usage scenario. Before you launch an instances, you'll need to provide a few things: - A key pair - This is for SSH access to your EC2 instance. You only need to provide the name. - A security group - This is used for configuring access to your instance. Again, only the name is needed. - An IP address - Your public IP address will be fetched. - An Amazon Machine Image (AMI) - A compatible instance type`, { header: true, preformatted: true, skipWhen: skipWhenErrors }, ); export const provideKeyPairName = new ScenarioInput( "keyPairName", "Provide a name for a new key pair.", { type: "input", default: "ec2-example-key-pair", skipWhen: skipWhenErrors }, ); export const createKeyPair = new ScenarioAction( "createKeyPair", async (/** @type {State} */ state) => { try { // Create a key pair in Amazon EC2. const { KeyMaterial, KeyPairId } = await state.ec2Client.send( // A unique name for the key pair. Up to 255 ASCII characters. new CreateKeyPairCommand({ KeyName: state[provideKeyPairName.name] }), ); state.keyPairId = KeyPairId; // Save the private key in a temporary location. state.tmpDirectory = await mkdtemp(join(tmpdir(), "ec2-scenario-tmp")); await writeFile( `${state.tmpDirectory}/${state[provideKeyPairName.name]}.pem`, KeyMaterial, { mode: 0o400, }, ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidKeyPair.Duplicate" ) { caught.message = `${caught.message}. Try another key name.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logKeyPair = new ScenarioOutput( "logKeyPair", (/** @type {State} */ state) => `Created the key pair ${state[provideKeyPairName.name]}.`, { skipWhen: skipWhenErrors }, ); export const confirmDeleteKeyPair = new ScenarioInput( "confirmDeleteKeyPair", "Do you want to delete the key pair?", { type: "confirm", // Don't do anything when a key pair was never created. skipWhen: (/** @type {State} */ state) => !state.keyPairId, }, ); export const maybeDeleteKeyPair = new ScenarioAction( "deleteKeyPair", async (/** @type {State} */ state) => { try { // Delete a key pair by name from EC2 await state.ec2Client.send( new DeleteKeyPairCommand({ KeyName: state[provideKeyPairName.name] }), ); } catch (caught) { if ( caught instanceof Error && // Occurs when a required parameter (e.g. KeyName) is undefined. caught.name === "MissingParameter" ) { caught.message = `${caught.message}. Did you provide the required value?`; } state.errors.push(caught); } }, { // Don't do anything when there's no key pair to delete or the user chooses // to keep it. skipWhen: (/** @type {State} */ state) => !state.keyPairId || !state[confirmDeleteKeyPair.name], }, ); export const provideSecurityGroupName = new ScenarioInput( "securityGroupName", "Provide a name for a new security group.", { type: "input", default: "ec2-scenario-sg", skipWhen: skipWhenErrors }, ); export const createSecurityGroup = new ScenarioAction( "createSecurityGroup", async (/** @type {State} */ state) => { try { // Create a new security group that will be used to configure ingress/egress for // an EC2 instance. const { GroupId } = await state.ec2Client.send( new CreateSecurityGroupCommand({ GroupName: state[provideSecurityGroupName.name], Description: "A security group for the Amazon EC2 example.", }), ); state.securityGroupId = GroupId; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidGroup.Duplicate") { caught.message = `${caught.message}. Please provide a different name for your security group.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logSecurityGroup = new ScenarioOutput( "logSecurityGroup", (/** @type {State} */ state) => `Created the security group ${state.securityGroupId}.`, { skipWhen: skipWhenErrors }, ); export const confirmDeleteSecurityGroup = new ScenarioInput( "confirmDeleteSecurityGroup", "Do you want to delete the security group?", { type: "confirm", // Don't do anything when a security group was never created. skipWhen: (/** @type {State} */ state) => !state.securityGroupId, }, ); export const maybeDeleteSecurityGroup = new ScenarioAction( "deleteSecurityGroup", async (/** @type {State} */ state) => { try { // Delete the security group if the 'skipWhen' condition below is not met. await state.ec2Client.send( new DeleteSecurityGroupCommand({ GroupId: state.securityGroupId, }), ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidGroupId.Malformed" ) { caught.message = `${caught.message}. Please provide a valid GroupId.`; } state.errors.push(caught); } }, { // Don't do anything when there's no security group to delete // or the user chooses to keep it. skipWhen: (/** @type {State} */ state) => !state.securityGroupId || !state[confirmDeleteSecurityGroup.name], }, ); export const authorizeSecurityGroupIngress = new ScenarioAction( "authorizeSecurity", async (/** @type {State} */ state) => { try { // Get the public IP address of the machine running this example. const ipAddress = await new Promise((res, rej) => { get("http://checkip.amazonaws.com", (response) => { let data = ""; response.on("data", (chunk) => { data += chunk; }); response.on("end", () => res(data.trim())); }).on("error", (err) => { rej(err); }); }); state.ipAddress = ipAddress; // Allow ingress from the IP address above to the security group. // This will allow you to SSH into the EC2 instance. const command = new AuthorizeSecurityGroupIngressCommand({ GroupId: state.securityGroupId, IpPermissions: [ { IpProtocol: "tcp", FromPort: 22, ToPort: 22, IpRanges: [{ CidrIp: `${ipAddress}/32` }], }, ], }); await state.ec2Client.send(command); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidGroupId.Malformed" ) { caught.message = `${caught.message}. Please provide a valid GroupId.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logSecurityGroupIngress = new ScenarioOutput( "logSecurityGroupIngress", (/** @type {State} */ state) => `Allowed SSH access from your public IP: ${state.ipAddress}.`, { skipWhen: skipWhenErrors }, ); export const getImages = new ScenarioAction( "images", async (/** @type {State} */ state) => { const AMIs = []; // Some AWS services publish information about common artifacts as AWS Systems Manager (SSM) // public parameters. For example, the Amazon Elastic Compute Cloud (Amazon EC2) // service publishes information about Amazon Machine Images (AMIs) as public parameters. // Create the paginator for getting images. Actions that return multiple pages of // results have paginators to simplify those calls. const getParametersByPathPaginator = paginateGetParametersByPath( { // Not storing this client in state since it's only used once. client: new SSMClient({}), }, { // The path to the public list of the latest amazon-linux instances. Path: "/aws/service/ami-amazon-linux-latest", }, ); try { for await (const page of getParametersByPathPaginator) { for (const param of page.Parameters) { // Filter by Amazon Linux 2 if (param.Name.includes("amzn2")) { AMIs.push(param.Value); } } } } catch (caught) { if (caught instanceof Error && caught.name === "InvalidFilterValue") { caught.message = `${caught.message} Please provide a valid filter value for paginateGetParametersByPath.`; } state.errors.push(caught); return; } const imageDetails = []; const describeImagesPaginator = paginateDescribeImages( { client: state.ec2Client }, // The images found from the call to SSM. { ImageIds: AMIs }, ); try { // Get more details for the images found above. for await (const page of describeImagesPaginator) { imageDetails.push(...(page.Images || [])); } // Store the image details for later use. state.images = imageDetails; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidAMIID.NotFound") { caught.message = `${caught.message}. Please provide a valid image id.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const provideImage = new ScenarioInput( "image", "Select one of the following images.", { type: "select", choices: (/** @type { State } */ state) => state.images.map((image) => ({ name: `${image.Description}`, value: image, })), default: (/** @type { State } */ state) => state.images[0], skipWhen: skipWhenErrors, }, ); export const getCompatibleInstanceTypes = new ScenarioAction( "getCompatibleInstanceTypes", async (/** @type {State} */ state) => { // Get more details about instance types that match the architecture of // the provided image. const paginator = paginateDescribeInstanceTypes( { client: state.ec2Client, pageSize: 25 }, { Filters: [ { Name: "processor-info.supported-architecture", // The value selected from provideImage() Values: [state.image.Architecture], }, // Filter for smaller, less expensive, types. { Name: "instance-type", Values: ["*.micro", "*.small"] }, ], }, ); const instanceTypes = []; try { for await (const page of paginator) { if (page.InstanceTypes.length) { instanceTypes.push(...(page.InstanceTypes || [])); } } if (!instanceTypes.length) { state.errors.push( "No instance types matched the instance type filters.", ); } } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { caught.message = `${caught.message}. Please check the provided values and try again.`; } state.errors.push(caught); } state.instanceTypes = instanceTypes; }, { skipWhen: skipWhenErrors }, ); export const provideInstanceType = new ScenarioInput( "instanceType", "Select an instance type.", { choices: (/** @type {State} */ state) => state.instanceTypes.map((instanceType) => ({ name: `${instanceType.InstanceType} - Memory:${instanceType.MemoryInfo.SizeInMiB}`, value: instanceType.InstanceType, })), type: "select", default: (/** @type {State} */ state) => state.instanceTypes[0].InstanceType, skipWhen: skipWhenErrors, }, ); export const runInstance = new ScenarioAction( "runInstance", async (/** @type { State } */ state) => { const { Instances } = await state.ec2Client.send( new RunInstancesCommand({ KeyName: state[provideKeyPairName.name], SecurityGroupIds: [state.securityGroupId], ImageId: state.image.ImageId, InstanceType: state[provideInstanceType.name], // Availability Zones have capacity limitations that may impact your ability to launch instances. // The `RunInstances` operation will only succeed if it can allocate at least the `MinCount` of instances. // However, EC2 will attempt to launch up to the `MaxCount` of instances, even if the full request cannot be satisfied. // If you need a specific number of instances, use `MinCount` and `MaxCount` set to the same value. // If you want to launch up to a certain number of instances, use `MaxCount` and let EC2 provision as many as possible. // If you require a minimum number of instances, but do not want to exceed a maximum, use both `MinCount` and `MaxCount`. MinCount: 1, MaxCount: 1, }), ); state.instanceId = Instances[0].InstanceId; try { // Poll `DescribeInstanceStatus` until status is "ok". await waitUntilInstanceStatusOk( { client: state.ec2Client, maxWaitTime: MAX_WAITER_TIME_IN_SECONDS, }, { InstanceIds: [Instances[0].InstanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logRunInstance = new ScenarioOutput( "logRunInstance", "The next step is to run your EC2 instance for the first time. This can take a few minutes.", { header: true, skipWhen: skipWhenErrors }, ); export const describeInstance = new ScenarioAction( "describeInstance", async (/** @type { State } */ state) => { /** @type { import("@aws-sdk/client-ec2").Instance[] } */ const instances = []; try { const paginator = paginateDescribeInstances( { client: state.ec2Client, }, { // Only get our created instance. InstanceIds: [state.instanceId], }, ); for await (const page of paginator) { for (const reservation of page.Reservations) { instances.push(...reservation.Instances); } } if (instances.length !== 1) { throw new Error(`Instance ${state.instanceId} not found.`); } // The only info we need is the IP address for SSH purposes. state.instanceIpAddress = instances[0].PublicIpAddress; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { caught.message = `${caught.message}. Please check provided values and try again.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logSSHConnectionInfo = new ScenarioOutput( "logSSHConnectionInfo", (/** @type { State } */ state) => `You can now SSH into your instance using the following command: ssh -i ${state.tmpDirectory}/${state[provideKeyPairName.name]}.pem ec2-user@${state.instanceIpAddress}`, { preformatted: true, skipWhen: skipWhenErrors }, ); export const logStopInstance = new ScenarioOutput( "logStopInstance", "Stopping your EC2 instance.", { skipWhen: skipWhenErrors }, ); export const stopInstance = new ScenarioAction( "stopInstance", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new StopInstancesCommand({ InstanceIds: [state.instanceId], }), ); await waitUntilInstanceStopped( { client: state.ec2Client, maxWaitTime: MAX_WAITER_TIME_IN_SECONDS, }, { InstanceIds: [state.instanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, // Don't try to stop an instance that doesn't exist. { skipWhen: (/** @type { State } */ state) => !state.instanceId }, ); export const logIpAddressBehavior = new ScenarioOutput( "logIpAddressBehavior", [ "When you run an instance, by default it's assigned an IP address.", "That IP address is not static. It will change every time the instance is restarted.", "The next step is to stop and restart your instance to demonstrate this behavior.", ].join(" "), { header: true, skipWhen: skipWhenErrors }, ); export const logStartInstance = new ScenarioOutput( "logStartInstance", (/** @type { State } */ state) => `Starting instance ${state.instanceId}`, { skipWhen: skipWhenErrors }, ); export const startInstance = new ScenarioAction( "startInstance", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new StartInstancesCommand({ InstanceIds: [state.instanceId], }), ); await waitUntilInstanceStatusOk( { client: state.ec2Client, maxWaitTime: MAX_WAITER_TIME_IN_SECONDS, }, { InstanceIds: [state.instanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logIpAllocation = new ScenarioOutput( "logIpAllocation", [ "It is possible to have a static IP address.", "To demonstrate this, an IP will be allocated and associated to your EC2 instance.", ].join(" "), { header: true, skipWhen: skipWhenErrors }, ); export const allocateIp = new ScenarioAction( "allocateIp", async (/** @type { State } */ state) => { try { // An Elastic IP address is allocated to your AWS account, and is yours until you release it. const { AllocationId, PublicIp } = await state.ec2Client.send( new AllocateAddressCommand({}), ); state.allocationId = AllocationId; state.allocatedIpAddress = PublicIp; } catch (caught) { if (caught instanceof Error && caught.name === "MissingParameter") { caught.message = `${caught.message}. Did you provide these values?`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const associateIp = new ScenarioAction( "associateIp", async (/** @type { State } */ state) => { try { // Associate an allocated IP address to an EC2 instance. An IP address can be allocated // with the AllocateAddress action. const { AssociationId } = await state.ec2Client.send( new AssociateAddressCommand({ AllocationId: state.allocationId, InstanceId: state.instanceId, }), ); state.associationId = AssociationId; // Update the IP address that is being tracked to match // the one just associated. state.instanceIpAddress = state.allocatedIpAddress; } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { caught.message = `${caught.message}. Did you provide the ID of a valid Elastic IP address AllocationId?`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logStaticIpProof = new ScenarioOutput( "logStaticIpProof", "The IP address should remain the same even after stopping and starting the instance.", { header: true, skipWhen: skipWhenErrors }, ); export const logCleanUp = new ScenarioOutput( "logCleanUp", "That's it! You can choose to clean up the resources now, or clean them up on your own later.", { header: true, skipWhen: skipWhenErrors }, ); export const confirmDisassociateAddress = new ScenarioInput( "confirmDisassociateAddress", "Do you want to disassociate and release the static IP address created earlier?", { type: "confirm", skipWhen: (/** @type { State } */ state) => !state.associationId, }, ); export const maybeDisassociateAddress = new ScenarioAction( "maybeDisassociateAddress", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new DisassociateAddressCommand({ AssociationId: state.associationId, }), ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAssociationID.NotFound" ) { caught.message = `${caught.message}. Please provide a valid association ID.`; } state.errors.push(caught); } }, { skipWhen: (/** @type { State } */ state) => !state[confirmDisassociateAddress.name] || !state.associationId, }, ); export const maybeReleaseAddress = new ScenarioAction( "maybeReleaseAddress", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new ReleaseAddressCommand({ AllocationId: state.allocationId, }), ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { caught.message = `${caught.message}. Please provide a valid AllocationID.`; } state.errors.push(caught); } }, { skipWhen: (/** @type { State } */ state) => !state[confirmDisassociateAddress.name] || !state.allocationId, }, ); export const confirmTerminateInstance = new ScenarioInput( "confirmTerminateInstance", "Do you want to terminate the instance?", // Don't do anything when an instance was never run. { skipWhen: (/** @type { State } */ state) => !state.instanceId, type: "confirm", }, ); export const maybeTerminateInstance = new ScenarioAction( "terminateInstance", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new TerminateInstancesCommand({ InstanceIds: [state.instanceId], }), ); await waitUntilInstanceTerminated( { client: state.ec2Client }, { InstanceIds: [state.instanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, { // Don't do anything when there's no instance to terminate or the // use chooses not to terminate. skipWhen: (/** @type { State } */ state) => !state.instanceId || !state[confirmTerminateInstance.name], }, ); export const deleteTemporaryDirectory = new ScenarioAction( "deleteTemporaryDirectory", async (/** @type { State } */ state) => { try { await rm(state.tmpDirectory, { recursive: true }); } catch (caught) { state.errors.push(caught); } }, ); export const logErrors = new ScenarioOutput( "logErrors", (/** @type {State}*/ state) => { const errorList = state.errors .map((err) => ` - ${err.name}: ${err.message}`) .join("\n"); return `Scenario errors found:\n${errorList}`; }, { preformatted: true, header: true, // Don't log errors when there aren't any! skipWhen: (/** @type {State} */ state) => state.errors.length === 0, }, );

작업

다음 코드 예시에서는 AllocateAddress을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { AllocateAddressCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Allocates an Elastic IP address to your AWS account. */ export const main = async () => { const client = new EC2Client({}); const command = new AllocateAddressCommand({}); try { const { AllocationId, PublicIp } = await client.send(command); console.log("A new IP address has been allocated to your account:"); console.log(`ID: ${AllocationId} Public IP: ${PublicIp}`); console.log( "You can view your IP addresses in the AWS Management Console for Amazon EC2. Look under Network & Security > Elastic IPs", ); } catch (caught) { if (caught instanceof Error && caught.name === "MissingParameter") { console.warn(`${caught.message}. Did you provide these values?`); } else { throw caught; } } }; import { fileURLToPath } from "node:url"; // Call function if run directly. if (process.argv[1] === fileURLToPath(import.meta.url)) { main(); }
  • API 세부 정보는 AllocateAddress AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 AssociateAddress을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { AssociateAddressCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Associates an Elastic IP address, or carrier IP address (for instances that are in subnets in Wavelength Zones) * with an instance or a network interface. * @param {{ instanceId: string, allocationId: string }} options */ export const main = async ({ instanceId, allocationId }) => { const client = new EC2Client({}); const command = new AssociateAddressCommand({ // You need to allocate an Elastic IP address before associating it with an instance. // You can do that with the AllocateAddressCommand. AllocationId: allocationId, // You need to create an EC2 instance before an IP address can be associated with it. // You can do that with the RunInstancesCommand. InstanceId: instanceId, }); try { const { AssociationId } = await client.send(command); console.log( `Address with allocation ID ${allocationId} is now associated with instance ${instanceId}.`, `The association ID is ${AssociationId}.`, ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { console.warn( `${caught.message}. Did you provide the ID of a valid Elastic IP address AllocationId?`, ); } else { throw caught; } } };
  • API 세부 정보는 AssociateAddress AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 AuthorizeSecurityGroupIngress을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { AuthorizeSecurityGroupIngressCommand, EC2Client, } from "@aws-sdk/client-ec2"; /** * Adds the specified inbound (ingress) rules to a security group. * @param {{ groupId: string, ipAddress: string }} options */ export const main = async ({ groupId, ipAddress }) => { const client = new EC2Client({}); const command = new AuthorizeSecurityGroupIngressCommand({ // Use a group ID from the AWS console or // the DescribeSecurityGroupsCommand. GroupId: groupId, IpPermissions: [ { IpProtocol: "tcp", FromPort: 22, ToPort: 22, // The IP address to authorize. // For more information on this notation, see // https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation IpRanges: [{ CidrIp: `${ipAddress}/32` }], }, ], }); try { const { SecurityGroupRules } = await client.send(command); console.log(JSON.stringify(SecurityGroupRules, null, 2)); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidGroupId.Malformed") { console.warn(`${caught.message}. Please provide a valid GroupId.`); } else { throw caught; } } };

다음 코드 예시에서는 CreateKeyPair을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { CreateKeyPairCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Creates an ED25519 or 2048-bit RSA key pair with the specified name and in the specified PEM or PPK format. * Amazon EC2 stores the public key and displays the private key for you to save to a file. * @param {{ keyName: string }} options */ export const main = async ({ keyName }) => { const client = new EC2Client({}); const command = new CreateKeyPairCommand({ KeyName: keyName, }); try { const { KeyMaterial, KeyName } = await client.send(command); console.log(KeyName); console.log(KeyMaterial); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidKeyPair.Duplicate") { console.warn(`${caught.message}. Try another key name.`); } else { throw caught; } } };
  • API 세부 정보는 CreateKeyPair AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 CreateLaunchTemplate을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

const ssmClient = new SSMClient({}); const { Parameter } = await ssmClient.send( new GetParameterCommand({ Name: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", }), ); const ec2Client = new EC2Client({}); await ec2Client.send( new CreateLaunchTemplateCommand({ LaunchTemplateName: NAMES.launchTemplateName, LaunchTemplateData: { InstanceType: "t3.micro", ImageId: Parameter.Value, IamInstanceProfile: { Name: NAMES.instanceProfileName }, UserData: readFileSync( join(RESOURCES_PATH, "server_startup_script.sh"), ).toString("base64"), KeyName: NAMES.keyPairName, }, }),
  • API 세부 정보는 CreateLaunchTemplate AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 CreateSecurityGroup을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { CreateSecurityGroupCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Creates a security group. * @param {{ groupName: string, description: string }} options */ export const main = async ({ groupName, description }) => { const client = new EC2Client({}); const command = new CreateSecurityGroupCommand({ // Up to 255 characters in length. Cannot start with sg-. GroupName: groupName, // Up to 255 characters in length. Description: description, }); try { const { GroupId } = await client.send(command); console.log(GroupId); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { console.warn(`${caught.message}.`); } else { throw caught; } } };
  • API 세부 정보는 CreateSecurityGroup AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DeleteKeyPair을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DeleteKeyPairCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Deletes the specified key pair, by removing the public key from Amazon EC2. * @param {{ keyName: string }} options */ export const main = async ({ keyName }) => { const client = new EC2Client({}); const command = new DeleteKeyPairCommand({ KeyName: keyName, }); try { await client.send(command); console.log("Successfully deleted key pair."); } catch (caught) { if (caught instanceof Error && caught.name === "MissingParameter") { console.warn(`${caught.message}. Did you provide the required value?`); } else { throw caught; } } };
  • API 세부 정보는 DeleteKeyPair AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DeleteLaunchTemplate을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

await client.send( new DeleteLaunchTemplateCommand({ LaunchTemplateName: NAMES.launchTemplateName, }), );
  • API 세부 정보는 DeleteLaunchTemplate AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DeleteSecurityGroup을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DeleteSecurityGroupCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Deletes a security group. * @param {{ groupId: string }} options */ export const main = async ({ groupId }) => { const client = new EC2Client({}); const command = new DeleteSecurityGroupCommand({ GroupId: groupId, }); try { await client.send(command); console.log("Security group deleted successfully."); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidGroupId.Malformed") { console.warn(`${caught.message}. Please provide a valid GroupId.`); } else { throw caught; } } };
  • API 세부 정보는 DeleteSecurityGroup AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeAddresses을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DescribeAddressesCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Describes the specified Elastic IP addresses or all of your Elastic IP addresses. * @param {{ allocationId: string }} options */ export const main = async ({ allocationId }) => { const client = new EC2Client({}); const command = new DescribeAddressesCommand({ // You can omit this property to show all addresses. AllocationIds: [allocationId], }); try { const { Addresses } = await client.send(command); const addressList = Addresses.map((address) => ` • ${address.PublicIp}`); console.log("Elastic IP addresses:"); console.log(addressList.join("\n")); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { console.warn(`${caught.message}. Please provide a valid AllocationId.`); } else { throw caught; } } };
  • API 세부 정보는 DescribeAddresses AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeIamInstanceProfileAssociations을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

const ec2Client = new EC2Client({}); const { IamInstanceProfileAssociations } = await ec2Client.send( new DescribeIamInstanceProfileAssociationsCommand({ Filters: [ { Name: "instance-id", Values: [state.targetInstance.InstanceId] }, ], }), );

다음 코드 예시에서는 DescribeImages을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, paginateDescribeImages } from "@aws-sdk/client-ec2"; /** * Describes the specified images (AMIs, AKIs, and ARIs) available to you or all of the images available to you. * @param {{ architecture: string, pageSize: number }} options */ export const main = async ({ architecture, pageSize }) => { pageSize = Number.parseInt(pageSize); const client = new EC2Client({}); // The paginate function is a wrapper around the base command. const paginator = paginateDescribeImages( // Without limiting the page size, this call can take a long time. pageSize is just sugar for // the MaxResults property in the base command. { client, pageSize }, { // There are almost 70,000 images available. Be specific with your filtering // to increase efficiency. // See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ec2/interfaces/describeimagescommandinput.html#filters Filters: [{ Name: "architecture", Values: [architecture] }], }, ); /** * @type {import('@aws-sdk/client-ec2').Image[]} */ const images = []; let recordsScanned = 0; try { for await (const page of paginator) { recordsScanned += pageSize; if (page.Images.length) { images.push(...page.Images); break; } console.log( `No matching image found yet. Searched ${recordsScanned} records.`, ); } if (images.length) { console.log( `Found ${images.length} images:\n\n${images.map((image) => image.Name).join("\n")}\n`, ); } else { console.log( `No matching images found. Searched ${recordsScanned} records.\n`, ); } return images; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { console.warn(`${caught.message}`); return []; } throw caught; } };
  • API 세부 정보는 DescribeImages AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeInstanceTypes을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, paginateDescribeInstanceTypes } from "@aws-sdk/client-ec2"; /** * Describes the specified instance types. By default, all instance types for the * current Region are described. Alternatively, you can filter the results. * @param {{ pageSize: string, supportedArch: string[], freeTier: boolean }} options */ export const main = async ({ pageSize, supportedArch, freeTier }) => { pageSize = Number.parseInt(pageSize); const client = new EC2Client({}); // The paginate function is a wrapper around the underlying command. const paginator = paginateDescribeInstanceTypes( // Without limiting the page size, this call can take a long time. pageSize is just sugar for // the MaxResults property in the underlying command. { client, pageSize }, { Filters: [ { Name: "processor-info.supported-architecture", Values: supportedArch, }, { Name: "free-tier-eligible", Values: [freeTier ? "true" : "false"] }, ], }, ); try { /** * @type {import('@aws-sdk/client-ec2').InstanceTypeInfo[]} */ const instanceTypes = []; for await (const page of paginator) { if (page.InstanceTypes.length) { instanceTypes.push(...page.InstanceTypes); // When we have at least 1 result, we can stop. if (instanceTypes.length >= 1) { break; } } } console.log( `Memory size in MiB for matching instance types:\n\n${instanceTypes.map((it) => `${it.InstanceType}: ${it.MemoryInfo.SizeInMiB} MiB`).join("\n")}`, ); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { console.warn(`${caught.message}`); return []; } throw caught; } };

다음 코드 예시에서는 DescribeInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, paginateDescribeInstances } from "@aws-sdk/client-ec2"; /** * List all of your EC2 instances running with the provided architecture that * were launched in the past month. * @param {{ pageSize: string, architectures: string[] }} options */ export const main = async ({ pageSize, architectures }) => { pageSize = Number.parseInt(pageSize); const client = new EC2Client({}); const d = new Date(); const year = d.getFullYear(); const month = `0${d.getMonth() + 1}`.slice(-2); const launchTimePattern = `${year}-${month}-*`; const paginator = paginateDescribeInstances( { client, pageSize, }, { Filters: [ { Name: "architecture", Values: architectures }, { Name: "instance-state-name", Values: ["running"] }, { Name: "launch-time", Values: [launchTimePattern], }, ], }, ); try { /** * @type {import('@aws-sdk/client-ec2').Instance[]} */ const instanceList = []; for await (const page of paginator) { const { Reservations } = page; for (const reservation of Reservations) { instanceList.push(...reservation.Instances); } } console.log( `Running instances launched this month:\n\n${instanceList.map((instance) => instance.InstanceId).join("\n")}`, ); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { console.warn(`${caught.message}.`); } else { throw caught; } } };
  • API 세부 정보는 DescribeInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeKeyPairs을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DescribeKeyPairsCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * List all key pairs in the current AWS account. * @param {{ dryRun: boolean }} */ export const main = async ({ dryRun }) => { const client = new EC2Client({}); const command = new DescribeKeyPairsCommand({ DryRun: dryRun }); try { const { KeyPairs } = await client.send(command); const keyPairList = KeyPairs.map( (kp) => ` • ${kp.KeyPairId}: ${kp.KeyName}`, ).join("\n"); console.log("The following key pairs were found in your account:"); console.log(keyPairList); } catch (caught) { if (caught instanceof Error && caught.name === "DryRunOperation") { console.log(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 DescribeKeyPairs AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeRegions을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DescribeRegionsCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * List all available AWS regions. * @param {{ regionNames: string[], includeOptInRegions: boolean }} options */ export const main = async ({ regionNames, includeOptInRegions }) => { const client = new EC2Client({}); const command = new DescribeRegionsCommand({ // By default this command will not show regions that require you to opt-in. // When AllRegions is true, even the regions that require opt-in will be returned. AllRegions: includeOptInRegions, // You can omit the Filters property if you want to get all regions. Filters: regionNames?.length ? [ { Name: "region-name", // You can specify multiple values for a filter. // You can also use '*' as a wildcard. This will return all // of the regions that start with `us-east-`. Values: regionNames, }, ] : undefined, }); try { const { Regions } = await client.send(command); const regionsList = Regions.map((reg) => ` • ${reg.RegionName}`); console.log("Found regions:"); console.log(regionsList.join("\n")); } catch (caught) { if (caught instanceof Error && caught.name === "DryRunOperation") { console.log(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 DescribeRegions AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeSecurityGroups을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DescribeSecurityGroupsCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Describes the specified security groups or all of your security groups. * @param {{ groupIds: string[] }} options */ export const main = async ({ groupIds = [] }) => { const client = new EC2Client({}); const command = new DescribeSecurityGroupsCommand({ GroupIds: groupIds, }); try { const { SecurityGroups } = await client.send(command); const sgList = SecurityGroups.map( (sg) => `• ${sg.GroupName} (${sg.GroupId}): ${sg.Description}`, ).join("\n"); if (sgList.length) { console.log(`Security groups:\n${sgList}`); } else { console.log("No security groups found."); } } catch (caught) { if (caught instanceof Error && caught.name === "InvalidGroupId.Malformed") { console.warn(`${caught.message}. Please provide a valid GroupId.`); } else if ( caught instanceof Error && caught.name === "InvalidGroup.NotFound" ) { console.warn(caught.message); } else { throw caught; } } };

다음 코드 예시에서는 DescribeSubnets을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

const client = new EC2Client({}); const { Subnets } = await client.send( new DescribeSubnetsCommand({ Filters: [ { Name: "vpc-id", Values: [state.defaultVpc] }, { Name: "availability-zone", Values: state.availabilityZoneNames }, { Name: "default-for-az", Values: ["true"] }, ], }), );
  • API 세부 정보는 DescribeSubnets AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DescribeVpcs을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

const client = new EC2Client({}); const { Vpcs } = await client.send( new DescribeVpcsCommand({ Filters: [{ Name: "is-default", Values: ["true"] }], }), );
  • API 세부 정보는 DescribeVpcs AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 DisassociateAddress을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { DisassociateAddressCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Disassociate an Elastic IP address from an instance. * @param {{ associationId: string }} options */ export const main = async ({ associationId }) => { const client = new EC2Client({}); const command = new DisassociateAddressCommand({ // You can also use PublicIp, but that is for EC2 classic which is being retired. AssociationId: associationId, }); try { await client.send(command); console.log("Successfully disassociated address"); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAssociationID.NotFound" ) { console.warn(`${caught.message}.`); } else { throw caught; } } };
  • API 세부 정보는 DisassociateAddress AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 MonitorInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, MonitorInstancesCommand } from "@aws-sdk/client-ec2"; /** * Turn on detailed monitoring for the selected instance. * By default, metrics are sent to Amazon CloudWatch every 5 minutes. * For a cost you can enable detailed monitoring which sends metrics every minute. * @param {{ instanceIds: string[] }} options */ export const main = async ({ instanceIds }) => { const client = new EC2Client({}); const command = new MonitorInstancesCommand({ InstanceIds: instanceIds, }); try { const { InstanceMonitorings } = await client.send(command); const instancesBeingMonitored = InstanceMonitorings.map( (im) => ` • Detailed monitoring state for ${im.InstanceId} is ${im.Monitoring.State}.`, ); console.log("Monitoring status:"); console.log(instancesBeingMonitored.join("\n")); } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { console.warn(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 MonitorInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 RebootInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, RebootInstancesCommand } from "@aws-sdk/client-ec2"; /** * Requests a reboot of the specified instances. This operation is asynchronous; * it only queues a request to reboot the specified instances. * @param {{ instanceIds: string[] }} options */ export const main = async ({ instanceIds }) => { const client = new EC2Client({}); const command = new RebootInstancesCommand({ InstanceIds: instanceIds, }); try { await client.send(command); console.log("Instance rebooted successfully."); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidInstanceID.NotFound" ) { console.warn( `${caught.message}. Please provide the InstanceId of a valid instance to reboot.`, ); } else { throw caught; } } };
  • API 세부 정보는 RebootInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 ReleaseAddress을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { ReleaseAddressCommand, EC2Client } from "@aws-sdk/client-ec2"; /** * Release an Elastic IP address. * @param {{ allocationId: string }} options */ export const main = async ({ allocationId }) => { const client = new EC2Client({}); const command = new ReleaseAddressCommand({ // You can also use PublicIp, but that is for EC2 classic which is being retired. AllocationId: allocationId, }); try { await client.send(command); console.log("Successfully released address."); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { console.warn(`${caught.message}. Please provide a valid AllocationID.`); } else { throw caught; } } };
  • API 세부 정보는 ReleaseAddress AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 ReplaceIamInstanceProfileAssociation을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

await retry({ intervalInMs: 1000, maxRetries: 30 }, () => ec2Client.send( new ReplaceIamInstanceProfileAssociationCommand({ AssociationId: state.instanceProfileAssociationId, IamInstanceProfile: { Name: NAMES.ssmOnlyInstanceProfileName }, }), ), );

다음 코드 예시에서는 RunInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, RunInstancesCommand } from "@aws-sdk/client-ec2"; /** * Create new EC2 instances. * @param {{ * keyName: string, * securityGroupIds: string[], * imageId: string, * instanceType: import('@aws-sdk/client-ec2')._InstanceType, * minCount?: number, * maxCount?: number }} options */ export const main = async ({ keyName, securityGroupIds, imageId, instanceType, minCount = "1", maxCount = "1", }) => { const client = new EC2Client({}); minCount = Number.parseInt(minCount); maxCount = Number.parseInt(maxCount); const command = new RunInstancesCommand({ // Your key pair name. KeyName: keyName, // Your security group. SecurityGroupIds: securityGroupIds, // An Amazon Machine Image (AMI). There are multiple ways to search for AMIs. For more information, see: // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html ImageId: imageId, // An instance type describing the resources provided to your instance. There are multiple // ways to search for instance types. For more information see: // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-discovery.html InstanceType: instanceType, // Availability Zones have capacity limitations that may impact your ability to launch instances. // The `RunInstances` operation will only succeed if it can allocate at least the `MinCount` of instances. // However, EC2 will attempt to launch up to the `MaxCount` of instances, even if the full request cannot be satisfied. // If you need a specific number of instances, use `MinCount` and `MaxCount` set to the same value. // If you want to launch up to a certain number of instances, use `MaxCount` and let EC2 provision as many as possible. // If you require a minimum number of instances, but do not want to exceed a maximum, use both `MinCount` and `MaxCount`. MinCount: minCount, MaxCount: maxCount, }); try { const { Instances } = await client.send(command); const instanceList = Instances.map( (instance) => `• ${instance.InstanceId}`, ).join("\n"); console.log(`Launched instances:\n${instanceList}`); } catch (caught) { if (caught instanceof Error && caught.name === "ResourceCountExceeded") { console.warn(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 RunInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 StartInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, StartInstancesCommand } from "@aws-sdk/client-ec2"; import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; /** * Starts an Amazon EBS-backed instance that you've previously stopped. * @param {{ instanceIds }} options */ export const main = async ({ instanceIds }) => { const client = new EC2Client({}); const command = new StartInstancesCommand({ InstanceIds: instanceIds, }); try { const { StartingInstances } = await client.send(command); const instanceIdList = StartingInstances.map( (instance) => ` • ${instance.InstanceId}`, ); console.log("Starting instances:"); console.log(instanceIdList.join("\n")); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidInstanceID.NotFound" ) { console.warn(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 StartInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 StopInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, StopInstancesCommand } from "@aws-sdk/client-ec2"; import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; /** * Stop one or more EC2 instances. * @param {{ instanceIds: string[] }} options */ export const main = async ({ instanceIds }) => { const client = new EC2Client({}); const command = new StopInstancesCommand({ InstanceIds: instanceIds, }); try { const { StoppingInstances } = await client.send(command); const instanceIdList = StoppingInstances.map( (instance) => ` • ${instance.InstanceId}`, ); console.log("Stopping instances:"); console.log(instanceIdList.join("\n")); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidInstanceID.NotFound" ) { console.warn(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 StopInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 TerminateInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, TerminateInstancesCommand } from "@aws-sdk/client-ec2"; import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; /** * Terminate one or more EC2 instances. * @param {{ instanceIds: string[] }} options */ export const main = async ({ instanceIds }) => { const client = new EC2Client({}); const command = new TerminateInstancesCommand({ InstanceIds: instanceIds, }); try { const { TerminatingInstances } = await client.send(command); const instanceList = TerminatingInstances.map( (instance) => ` • ${instance.InstanceId}`, ); console.log("Terminating instances:"); console.log(instanceList.join("\n")); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidInstanceID.NotFound" ) { console.warn(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 TerminateInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

다음 코드 예시에서는 UnmonitorInstances을 사용하는 방법을 보여 줍니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

import { EC2Client, UnmonitorInstancesCommand } from "@aws-sdk/client-ec2"; import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; /** * Turn off detailed monitoring for the selected instance. * @param {{ instanceIds: string[] }} options */ export const main = async ({ instanceIds }) => { const client = new EC2Client({}); const command = new UnmonitorInstancesCommand({ InstanceIds: instanceIds, }); try { const { InstanceMonitorings } = await client.send(command); const instanceMonitoringsList = InstanceMonitorings.map( (im) => ` • Detailed monitoring state for ${im.InstanceId} is ${im.Monitoring.State}.`, ); console.log("Monitoring status:"); console.log(instanceMonitoringsList.join("\n")); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidInstanceID.NotFound" ) { console.warn(`${caught.message}`); } else { throw caught; } } };
  • API 세부 정보는 UnmonitorInstances AWS SDK for JavaScript 참조의 API를 참조하세요.

시나리오

다음 코드 예제에서는 책, 영화, 노래 추천을 반환하는 로드 밸런싱 웹 서비스를 만드는 방법을 보여줍니다. 이 예제에서는 서비스가 장애에 대응하는 방법과 장애 발생 시 복원력을 높이기 위해 서비스를 재구성하는 방법을 보여줍니다.

  • Amazon EC2 Auto Scaling 그룹을 사용하여 시작 템플릿을 기반으로 Amazon Elastic Compute Cloud(Amazon EC2) 인스턴스를 생성하고 인스턴스 수를 지정된 범위로 유지합니다.

  • Elastic Load Balancing을 사용하여 HTTP 요청을 처리하고 배포합니다.

  • Auto Scaling 그룹의 인스턴스 상태를 모니터링하고 요청을 정상 인스턴스로만 전달합니다.

  • 각 EC2 인스턴스에서 Python 웹 서버를 실행하여 HTTP 요청을 처리합니다. 웹 서버는 추천 및 상태 확인으로 응답합니다.

  • Amazon DynamoDB 테이블을 사용하여 추천 서비스를 시뮬레이션합니다.

  • AWS Systems Manager 파라미터를 업데이트하여 요청 및 상태 확인에 대한 웹 서버 응답을 제어합니다.

SDK for JavaScript (v3)
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

명령 프롬프트에서 대화형 시나리오를 실행합니다.

#!/usr/bin/env node // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Scenario, parseScenarioArgs, } from "@aws-doc-sdk-examples/lib/scenario/index.js"; /** * The workflow steps are split into three stages: * - deploy * - demo * - destroy * * Each of these stages has a corresponding file prefixed with steps-*. */ import { deploySteps } from "./steps-deploy.js"; import { demoSteps } from "./steps-demo.js"; import { destroySteps } from "./steps-destroy.js"; /** * The context is passed to every scenario. Scenario steps * will modify the context. */ const context = {}; /** * Three Scenarios are created for the workflow. A Scenario is an orchestration class * that simplifies running a series of steps. */ export const scenarios = { // Deploys all resources necessary for the workflow. deploy: new Scenario("Resilient Workflow - Deploy", deploySteps, context), // Demonstrates how a fragile web service can be made more resilient. demo: new Scenario("Resilient Workflow - Demo", demoSteps, context), // Destroys the resources created for the workflow. destroy: new Scenario("Resilient Workflow - Destroy", destroySteps, context), }; // Call function if run directly import { fileURLToPath } from "node:url"; if (process.argv[1] === fileURLToPath(import.meta.url)) { parseScenarioArgs(scenarios, { name: "Resilient Workflow", synopsis: "node index.js --scenario <deploy | demo | destroy> [-h|--help] [-y|--yes] [-v|--verbose]", description: "Deploy and interact with scalable EC2 instances.", }); }

모든 리소스를 배포하기 위한 단계를 생성합니다.

import { join } from "node:path"; import { readFileSync, writeFileSync } from "node:fs"; import axios from "axios"; import { BatchWriteItemCommand, CreateTableCommand, DynamoDBClient, waitUntilTableExists, } from "@aws-sdk/client-dynamodb"; import { EC2Client, CreateKeyPairCommand, CreateLaunchTemplateCommand, DescribeAvailabilityZonesCommand, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, AuthorizeSecurityGroupIngressCommand, } from "@aws-sdk/client-ec2"; import { IAMClient, CreatePolicyCommand, CreateRoleCommand, CreateInstanceProfileCommand, AddRoleToInstanceProfileCommand, AttachRolePolicyCommand, waitUntilInstanceProfileExists, } from "@aws-sdk/client-iam"; import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm"; import { CreateAutoScalingGroupCommand, AutoScalingClient, AttachLoadBalancerTargetGroupsCommand, } from "@aws-sdk/client-auto-scaling"; import { CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, ElasticLoadBalancingV2Client, waitUntilLoadBalancerAvailable, } from "@aws-sdk/client-elastic-load-balancing-v2"; import { ScenarioOutput, ScenarioInput, ScenarioAction, } from "@aws-doc-sdk-examples/lib/scenario/index.js"; import { saveState } from "@aws-doc-sdk-examples/lib/scenario/steps-common.js"; import { retry } from "@aws-doc-sdk-examples/lib/utils/util-timers.js"; import { MESSAGES, NAMES, RESOURCES_PATH, ROOT } from "./constants.js"; import { initParamsSteps } from "./steps-reset-params.js"; /** * @type {import('@aws-doc-sdk-examples/lib/scenario.js').Step[]} */ export const deploySteps = [ new ScenarioOutput("introduction", MESSAGES.introduction, { header: true }), new ScenarioInput("confirmDeployment", MESSAGES.confirmDeployment, { type: "confirm", }), new ScenarioAction( "handleConfirmDeployment", (c) => c.confirmDeployment === false && process.exit(), ), new ScenarioOutput( "creatingTable", MESSAGES.creatingTable.replace("${TABLE_NAME}", NAMES.tableName), ), new ScenarioAction("createTable", async () => { const client = new DynamoDBClient({}); await client.send( new CreateTableCommand({ TableName: NAMES.tableName, ProvisionedThroughput: { ReadCapacityUnits: 5, WriteCapacityUnits: 5, }, AttributeDefinitions: [ { AttributeName: "MediaType", AttributeType: "S", }, { AttributeName: "ItemId", AttributeType: "N", }, ], KeySchema: [ { AttributeName: "MediaType", KeyType: "HASH", }, { AttributeName: "ItemId", KeyType: "RANGE", }, ], }), ); await waitUntilTableExists({ client }, { TableName: NAMES.tableName }); }), new ScenarioOutput( "createdTable", MESSAGES.createdTable.replace("${TABLE_NAME}", NAMES.tableName), ), new ScenarioOutput( "populatingTable", MESSAGES.populatingTable.replace("${TABLE_NAME}", NAMES.tableName), ), new ScenarioAction("populateTable", () => { const client = new DynamoDBClient({}); /** * @type {{ default: import("@aws-sdk/client-dynamodb").PutRequest['Item'][] }} */ const recommendations = JSON.parse( readFileSync(join(RESOURCES_PATH, "recommendations.json")), ); return client.send( new BatchWriteItemCommand({ RequestItems: { [NAMES.tableName]: recommendations.map((item) => ({ PutRequest: { Item: item }, })), }, }), ); }), new ScenarioOutput( "populatedTable", MESSAGES.populatedTable.replace("${TABLE_NAME}", NAMES.tableName), ), new ScenarioOutput( "creatingKeyPair", MESSAGES.creatingKeyPair.replace("${KEY_PAIR_NAME}", NAMES.keyPairName), ), new ScenarioAction("createKeyPair", async () => { const client = new EC2Client({}); const { KeyMaterial } = await client.send( new CreateKeyPairCommand({ KeyName: NAMES.keyPairName, }), ); writeFileSync(`${NAMES.keyPairName}.pem`, KeyMaterial, { mode: 0o600 }); }), new ScenarioOutput( "createdKeyPair", MESSAGES.createdKeyPair.replace("${KEY_PAIR_NAME}", NAMES.keyPairName), ), new ScenarioOutput( "creatingInstancePolicy", MESSAGES.creatingInstancePolicy.replace( "${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName, ), ), new ScenarioAction("createInstancePolicy", async (state) => { const client = new IAMClient({}); const { Policy: { Arn }, } = await client.send( new CreatePolicyCommand({ PolicyName: NAMES.instancePolicyName, PolicyDocument: readFileSync( join(RESOURCES_PATH, "instance_policy.json"), ), }), ); state.instancePolicyArn = Arn; }), new ScenarioOutput("createdInstancePolicy", (state) => MESSAGES.createdInstancePolicy .replace("${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName) .replace("${INSTANCE_POLICY_ARN}", state.instancePolicyArn), ), new ScenarioOutput( "creatingInstanceRole", MESSAGES.creatingInstanceRole.replace( "${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName, ), ), new ScenarioAction("createInstanceRole", () => { const client = new IAMClient({}); return client.send( new CreateRoleCommand({ RoleName: NAMES.instanceRoleName, AssumeRolePolicyDocument: readFileSync( join(ROOT, "assume-role-policy.json"), ), }), ); }), new ScenarioOutput( "createdInstanceRole", MESSAGES.createdInstanceRole.replace( "${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName, ), ), new ScenarioOutput( "attachingPolicyToRole", MESSAGES.attachingPolicyToRole .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName) .replace("${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName), ), new ScenarioAction("attachPolicyToRole", async (state) => { const client = new IAMClient({}); await client.send( new AttachRolePolicyCommand({ RoleName: NAMES.instanceRoleName, PolicyArn: state.instancePolicyArn, }), ); }), new ScenarioOutput( "attachedPolicyToRole", MESSAGES.attachedPolicyToRole .replace("${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName), ), new ScenarioOutput( "creatingInstanceProfile", MESSAGES.creatingInstanceProfile.replace( "${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName, ), ), new ScenarioAction("createInstanceProfile", async (state) => { const client = new IAMClient({}); const { InstanceProfile: { Arn }, } = await client.send( new CreateInstanceProfileCommand({ InstanceProfileName: NAMES.instanceProfileName, }), ); state.instanceProfileArn = Arn; await waitUntilInstanceProfileExists( { client }, { InstanceProfileName: NAMES.instanceProfileName }, ); }), new ScenarioOutput("createdInstanceProfile", (state) => MESSAGES.createdInstanceProfile .replace("${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName) .replace("${INSTANCE_PROFILE_ARN}", state.instanceProfileArn), ), new ScenarioOutput( "addingRoleToInstanceProfile", MESSAGES.addingRoleToInstanceProfile .replace("${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName), ), new ScenarioAction("addRoleToInstanceProfile", () => { const client = new IAMClient({}); return client.send( new AddRoleToInstanceProfileCommand({ RoleName: NAMES.instanceRoleName, InstanceProfileName: NAMES.instanceProfileName, }), ); }), new ScenarioOutput( "addedRoleToInstanceProfile", MESSAGES.addedRoleToInstanceProfile .replace("${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName), ), ...initParamsSteps, new ScenarioOutput("creatingLaunchTemplate", MESSAGES.creatingLaunchTemplate), new ScenarioAction("createLaunchTemplate", async () => { const ssmClient = new SSMClient({}); const { Parameter } = await ssmClient.send( new GetParameterCommand({ Name: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", }), ); const ec2Client = new EC2Client({}); await ec2Client.send( new CreateLaunchTemplateCommand({ LaunchTemplateName: NAMES.launchTemplateName, LaunchTemplateData: { InstanceType: "t3.micro", ImageId: Parameter.Value, IamInstanceProfile: { Name: NAMES.instanceProfileName }, UserData: readFileSync( join(RESOURCES_PATH, "server_startup_script.sh"), ).toString("base64"), KeyName: NAMES.keyPairName, }, }), ); }), new ScenarioOutput( "createdLaunchTemplate", MESSAGES.createdLaunchTemplate.replace( "${LAUNCH_TEMPLATE_NAME}", NAMES.launchTemplateName, ), ), new ScenarioOutput( "creatingAutoScalingGroup", MESSAGES.creatingAutoScalingGroup.replace( "${AUTO_SCALING_GROUP_NAME}", NAMES.autoScalingGroupName, ), ), new ScenarioAction("createAutoScalingGroup", async (state) => { const ec2Client = new EC2Client({}); const { AvailabilityZones } = await ec2Client.send( new DescribeAvailabilityZonesCommand({}), ); state.availabilityZoneNames = AvailabilityZones.map((az) => az.ZoneName); const autoScalingClient = new AutoScalingClient({}); await retry({ intervalInMs: 1000, maxRetries: 30 }, () => autoScalingClient.send( new CreateAutoScalingGroupCommand({ AvailabilityZones: state.availabilityZoneNames, AutoScalingGroupName: NAMES.autoScalingGroupName, LaunchTemplate: { LaunchTemplateName: NAMES.launchTemplateName, Version: "$Default", }, MinSize: 3, MaxSize: 3, }), ), ); }), new ScenarioOutput( "createdAutoScalingGroup", /** * @param {{ availabilityZoneNames: string[] }} state */ (state) => MESSAGES.createdAutoScalingGroup .replace("${AUTO_SCALING_GROUP_NAME}", NAMES.autoScalingGroupName) .replace( "${AVAILABILITY_ZONE_NAMES}", state.availabilityZoneNames.join(", "), ), ), new ScenarioInput("confirmContinue", MESSAGES.confirmContinue, { type: "confirm", }), new ScenarioOutput("loadBalancer", MESSAGES.loadBalancer), new ScenarioOutput("gettingVpc", MESSAGES.gettingVpc), new ScenarioAction("getVpc", async (state) => { const client = new EC2Client({}); const { Vpcs } = await client.send( new DescribeVpcsCommand({ Filters: [{ Name: "is-default", Values: ["true"] }], }), ); state.defaultVpc = Vpcs[0].VpcId; }), new ScenarioOutput("gotVpc", (state) => MESSAGES.gotVpc.replace("${VPC_ID}", state.defaultVpc), ), new ScenarioOutput("gettingSubnets", MESSAGES.gettingSubnets), new ScenarioAction("getSubnets", async (state) => { const client = new EC2Client({}); const { Subnets } = await client.send( new DescribeSubnetsCommand({ Filters: [ { Name: "vpc-id", Values: [state.defaultVpc] }, { Name: "availability-zone", Values: state.availabilityZoneNames }, { Name: "default-for-az", Values: ["true"] }, ], }), ); state.subnets = Subnets.map((subnet) => subnet.SubnetId); }), new ScenarioOutput( "gotSubnets", /** * @param {{ subnets: string[] }} state */ (state) => MESSAGES.gotSubnets.replace("${SUBNETS}", state.subnets.join(", ")), ), new ScenarioOutput( "creatingLoadBalancerTargetGroup", MESSAGES.creatingLoadBalancerTargetGroup.replace( "${TARGET_GROUP_NAME}", NAMES.loadBalancerTargetGroupName, ), ), new ScenarioAction("createLoadBalancerTargetGroup", async (state) => { const client = new ElasticLoadBalancingV2Client({}); const { TargetGroups } = await client.send( new CreateTargetGroupCommand({ Name: NAMES.loadBalancerTargetGroupName, Protocol: "HTTP", Port: 80, HealthCheckPath: "/healthcheck", HealthCheckIntervalSeconds: 10, HealthCheckTimeoutSeconds: 5, HealthyThresholdCount: 2, UnhealthyThresholdCount: 2, VpcId: state.defaultVpc, }), ); const targetGroup = TargetGroups[0]; state.targetGroupArn = targetGroup.TargetGroupArn; state.targetGroupProtocol = targetGroup.Protocol; state.targetGroupPort = targetGroup.Port; }), new ScenarioOutput( "createdLoadBalancerTargetGroup", MESSAGES.createdLoadBalancerTargetGroup.replace( "${TARGET_GROUP_NAME}", NAMES.loadBalancerTargetGroupName, ), ), new ScenarioOutput( "creatingLoadBalancer", MESSAGES.creatingLoadBalancer.replace("${LB_NAME}", NAMES.loadBalancerName), ), new ScenarioAction("createLoadBalancer", async (state) => { const client = new ElasticLoadBalancingV2Client({}); const { LoadBalancers } = await client.send( new CreateLoadBalancerCommand({ Name: NAMES.loadBalancerName, Subnets: state.subnets, }), ); state.loadBalancerDns = LoadBalancers[0].DNSName; state.loadBalancerArn = LoadBalancers[0].LoadBalancerArn; await waitUntilLoadBalancerAvailable( { client }, { Names: [NAMES.loadBalancerName] }, ); }), new ScenarioOutput("createdLoadBalancer", (state) => MESSAGES.createdLoadBalancer .replace("${LB_NAME}", NAMES.loadBalancerName) .replace("${DNS_NAME}", state.loadBalancerDns), ), new ScenarioOutput( "creatingListener", MESSAGES.creatingLoadBalancerListener .replace("${LB_NAME}", NAMES.loadBalancerName) .replace("${TARGET_GROUP_NAME}", NAMES.loadBalancerTargetGroupName), ), new ScenarioAction("createListener", async (state) => { const client = new ElasticLoadBalancingV2Client({}); const { Listeners } = await client.send( new CreateListenerCommand({ LoadBalancerArn: state.loadBalancerArn, Protocol: state.targetGroupProtocol, Port: state.targetGroupPort, DefaultActions: [ { Type: "forward", TargetGroupArn: state.targetGroupArn }, ], }), ); const listener = Listeners[0]; state.loadBalancerListenerArn = listener.ListenerArn; }), new ScenarioOutput("createdListener", (state) => MESSAGES.createdLoadBalancerListener.replace( "${LB_LISTENER_ARN}", state.loadBalancerListenerArn, ), ), new ScenarioOutput( "attachingLoadBalancerTargetGroup", MESSAGES.attachingLoadBalancerTargetGroup .replace("${TARGET_GROUP_NAME}", NAMES.loadBalancerTargetGroupName) .replace("${AUTO_SCALING_GROUP_NAME}", NAMES.autoScalingGroupName), ), new ScenarioAction("attachLoadBalancerTargetGroup", async (state) => { const client = new AutoScalingClient({}); await client.send( new AttachLoadBalancerTargetGroupsCommand({ AutoScalingGroupName: NAMES.autoScalingGroupName, TargetGroupARNs: [state.targetGroupArn], }), ); }), new ScenarioOutput( "attachedLoadBalancerTargetGroup", MESSAGES.attachedLoadBalancerTargetGroup, ), new ScenarioOutput("verifyingInboundPort", MESSAGES.verifyingInboundPort), new ScenarioAction( "verifyInboundPort", /** * * @param {{ defaultSecurityGroup: import('@aws-sdk/client-ec2').SecurityGroup}} state */ async (state) => { const client = new EC2Client({}); const { SecurityGroups } = await client.send( new DescribeSecurityGroupsCommand({ Filters: [{ Name: "group-name", Values: ["default"] }], }), ); if (!SecurityGroups) { state.verifyInboundPortError = new Error(MESSAGES.noSecurityGroups); } state.defaultSecurityGroup = SecurityGroups[0]; /** * @type {string} */ const ipResponse = (await axios.get("http://checkip.amazonaws.com")).data; state.myIp = ipResponse.trim(); const myIpRules = state.defaultSecurityGroup.IpPermissions.filter( ({ IpRanges }) => IpRanges.some( ({ CidrIp }) => CidrIp.startsWith(state.myIp) || CidrIp === "0.0.0.0/0", ), ) .filter(({ IpProtocol }) => IpProtocol === "tcp") .filter(({ FromPort }) => FromPort === 80); state.myIpRules = myIpRules; }, ), new ScenarioOutput( "verifiedInboundPort", /** * @param {{ myIpRules: any[] }} state */ (state) => { if (state.myIpRules.length > 0) { return MESSAGES.foundIpRules.replace( "${IP_RULES}", JSON.stringify(state.myIpRules, null, 2), ); } return MESSAGES.noIpRules; }, ), new ScenarioInput( "shouldAddInboundRule", /** * @param {{ myIpRules: any[] }} state */ (state) => { if (state.myIpRules.length > 0) { return false; } return MESSAGES.noIpRules; }, { type: "confirm" }, ), new ScenarioAction( "addInboundRule", /** * @param {{ defaultSecurityGroup: import('@aws-sdk/client-ec2').SecurityGroup }} state */ async (state) => { if (!state.shouldAddInboundRule) { return; } const client = new EC2Client({}); await client.send( new AuthorizeSecurityGroupIngressCommand({ GroupId: state.defaultSecurityGroup.GroupId, CidrIp: `${state.myIp}/32`, FromPort: 80, ToPort: 80, IpProtocol: "tcp", }), ); }, ), new ScenarioOutput("addedInboundRule", (state) => { if (state.shouldAddInboundRule) { return MESSAGES.addedInboundRule.replace("${IP_ADDRESS}", state.myIp); } return false; }), new ScenarioOutput("verifyingEndpoint", (state) => MESSAGES.verifyingEndpoint.replace("${DNS_NAME}", state.loadBalancerDns), ), new ScenarioAction("verifyEndpoint", async (state) => { try { const response = await retry({ intervalInMs: 2000, maxRetries: 30 }, () => axios.get(`http://${state.loadBalancerDns}`), ); state.endpointResponse = JSON.stringify(response.data, null, 2); } catch (e) { state.verifyEndpointError = e; } }), new ScenarioOutput("verifiedEndpoint", (state) => { if (state.verifyEndpointError) { console.error(state.verifyEndpointError); } else { return MESSAGES.verifiedEndpoint.replace( "${ENDPOINT_RESPONSE}", state.endpointResponse, ); } }), saveState, ];

데모를 실행하기 위한 단계를 생성합니다.

import { readFileSync } from "node:fs"; import { join } from "node:path"; import axios from "axios"; import { DescribeTargetGroupsCommand, DescribeTargetHealthCommand, ElasticLoadBalancingV2Client, } from "@aws-sdk/client-elastic-load-balancing-v2"; import { DescribeInstanceInformationCommand, PutParameterCommand, SSMClient, SendCommandCommand, } from "@aws-sdk/client-ssm"; import { IAMClient, CreatePolicyCommand, CreateRoleCommand, AttachRolePolicyCommand, CreateInstanceProfileCommand, AddRoleToInstanceProfileCommand, waitUntilInstanceProfileExists, } from "@aws-sdk/client-iam"; import { AutoScalingClient, DescribeAutoScalingGroupsCommand, TerminateInstanceInAutoScalingGroupCommand, } from "@aws-sdk/client-auto-scaling"; import { DescribeIamInstanceProfileAssociationsCommand, EC2Client, RebootInstancesCommand, ReplaceIamInstanceProfileAssociationCommand, } from "@aws-sdk/client-ec2"; import { ScenarioAction, ScenarioInput, ScenarioOutput, } from "@aws-doc-sdk-examples/lib/scenario/scenario.js"; import { retry } from "@aws-doc-sdk-examples/lib/utils/util-timers.js"; import { MESSAGES, NAMES, RESOURCES_PATH } from "./constants.js"; import { findLoadBalancer } from "./shared.js"; const getRecommendation = new ScenarioAction( "getRecommendation", async (state) => { const loadBalancer = await findLoadBalancer(NAMES.loadBalancerName); if (loadBalancer) { state.loadBalancerDnsName = loadBalancer.DNSName; try { state.recommendation = ( await axios.get(`http://${state.loadBalancerDnsName}`) ).data; } catch (e) { state.recommendation = e instanceof Error ? e.message : e; } } else { throw new Error(MESSAGES.demoFindLoadBalancerError); } }, ); const getRecommendationResult = new ScenarioOutput( "getRecommendationResult", (state) => `Recommendation:\n${JSON.stringify(state.recommendation, null, 2)}`, { preformatted: true }, ); const getHealthCheck = new ScenarioAction("getHealthCheck", async (state) => { const client = new ElasticLoadBalancingV2Client({}); const { TargetGroups } = await client.send( new DescribeTargetGroupsCommand({ Names: [NAMES.loadBalancerTargetGroupName], }), ); const { TargetHealthDescriptions } = await client.send( new DescribeTargetHealthCommand({ TargetGroupArn: TargetGroups[0].TargetGroupArn, }), ); state.targetHealthDescriptions = TargetHealthDescriptions; }); const getHealthCheckResult = new ScenarioOutput( "getHealthCheckResult", /** * @param {{ targetHealthDescriptions: import('@aws-sdk/client-elastic-load-balancing-v2').TargetHealthDescription[]}} state */ (state) => { const status = state.targetHealthDescriptions .map((th) => `${th.Target.Id}: ${th.TargetHealth.State}`) .join("\n"); return `Health check:\n${status}`; }, { preformatted: true }, ); const loadBalancerLoop = new ScenarioAction( "loadBalancerLoop", getRecommendation.action, { whileConfig: { whileFn: ({ loadBalancerCheck }) => loadBalancerCheck, input: new ScenarioInput( "loadBalancerCheck", MESSAGES.demoLoadBalancerCheck, { type: "confirm", }, ), output: getRecommendationResult, }, }, ); const healthCheckLoop = new ScenarioAction( "healthCheckLoop", getHealthCheck.action, { whileConfig: { whileFn: ({ healthCheck }) => healthCheck, input: new ScenarioInput("healthCheck", MESSAGES.demoHealthCheck, { type: "confirm", }), output: getHealthCheckResult, }, }, ); const statusSteps = [ getRecommendation, getRecommendationResult, getHealthCheck, getHealthCheckResult, ]; /** * @type {import('@aws-doc-sdk-examples/lib/scenario.js').Step[]} */ export const demoSteps = [ new ScenarioOutput("header", MESSAGES.demoHeader, { header: true }), new ScenarioOutput("sanityCheck", MESSAGES.demoSanityCheck), ...statusSteps, new ScenarioInput( "brokenDependencyConfirmation", MESSAGES.demoBrokenDependencyConfirmation, { type: "confirm" }, ), new ScenarioAction("brokenDependency", async (state) => { if (!state.brokenDependencyConfirmation) { process.exit(); } else { const client = new SSMClient({}); state.badTableName = `fake-table-${Date.now()}`; await client.send( new PutParameterCommand({ Name: NAMES.ssmTableNameKey, Value: state.badTableName, Overwrite: true, Type: "String", }), ); } }), new ScenarioOutput("testBrokenDependency", (state) => MESSAGES.demoTestBrokenDependency.replace( "${TABLE_NAME}", state.badTableName, ), ), ...statusSteps, new ScenarioInput( "staticResponseConfirmation", MESSAGES.demoStaticResponseConfirmation, { type: "confirm" }, ), new ScenarioAction("staticResponse", async (state) => { if (!state.staticResponseConfirmation) { process.exit(); } else { const client = new SSMClient({}); await client.send( new PutParameterCommand({ Name: NAMES.ssmFailureResponseKey, Value: "static", Overwrite: true, Type: "String", }), ); } }), new ScenarioOutput("testStaticResponse", MESSAGES.demoTestStaticResponse), ...statusSteps, new ScenarioInput( "badCredentialsConfirmation", MESSAGES.demoBadCredentialsConfirmation, { type: "confirm" }, ), new ScenarioAction("badCredentialsExit", (state) => { if (!state.badCredentialsConfirmation) { process.exit(); } }), new ScenarioAction("fixDynamoDBName", async () => { const client = new SSMClient({}); await client.send( new PutParameterCommand({ Name: NAMES.ssmTableNameKey, Value: NAMES.tableName, Overwrite: true, Type: "String", }), ); }), new ScenarioAction( "badCredentials", /** * @param {{ targetInstance: import('@aws-sdk/client-auto-scaling').Instance }} state */ async (state) => { await createSsmOnlyInstanceProfile(); const autoScalingClient = new AutoScalingClient({}); const { AutoScalingGroups } = await autoScalingClient.send( new DescribeAutoScalingGroupsCommand({ AutoScalingGroupNames: [NAMES.autoScalingGroupName], }), ); state.targetInstance = AutoScalingGroups[0].Instances[0]; const ec2Client = new EC2Client({}); const { IamInstanceProfileAssociations } = await ec2Client.send( new DescribeIamInstanceProfileAssociationsCommand({ Filters: [ { Name: "instance-id", Values: [state.targetInstance.InstanceId] }, ], }), ); state.instanceProfileAssociationId = IamInstanceProfileAssociations[0].AssociationId; await retry({ intervalInMs: 1000, maxRetries: 30 }, () => ec2Client.send( new ReplaceIamInstanceProfileAssociationCommand({ AssociationId: state.instanceProfileAssociationId, IamInstanceProfile: { Name: NAMES.ssmOnlyInstanceProfileName }, }), ), ); await ec2Client.send( new RebootInstancesCommand({ InstanceIds: [state.targetInstance.InstanceId], }), ); const ssmClient = new SSMClient({}); await retry({ intervalInMs: 20000, maxRetries: 15 }, async () => { const { InstanceInformationList } = await ssmClient.send( new DescribeInstanceInformationCommand({}), ); const instance = InstanceInformationList.find( (info) => info.InstanceId === state.targetInstance.InstanceId, ); if (!instance) { throw new Error("Instance not found."); } }); await ssmClient.send( new SendCommandCommand({ InstanceIds: [state.targetInstance.InstanceId], DocumentName: "AWS-RunShellScript", Parameters: { commands: ["cd / && sudo python3 server.py 80"] }, }), ); }, ), new ScenarioOutput( "testBadCredentials", /** * @param {{ targetInstance: import('@aws-sdk/client-ssm').InstanceInformation}} state */ (state) => MESSAGES.demoTestBadCredentials.replace( "${INSTANCE_ID}", state.targetInstance.InstanceId, ), ), loadBalancerLoop, new ScenarioInput( "deepHealthCheckConfirmation", MESSAGES.demoDeepHealthCheckConfirmation, { type: "confirm" }, ), new ScenarioAction("deepHealthCheckExit", (state) => { if (!state.deepHealthCheckConfirmation) { process.exit(); } }), new ScenarioAction("deepHealthCheck", async () => { const client = new SSMClient({}); await client.send( new PutParameterCommand({ Name: NAMES.ssmHealthCheckKey, Value: "deep", Overwrite: true, Type: "String", }), ); }), new ScenarioOutput("testDeepHealthCheck", MESSAGES.demoTestDeepHealthCheck), healthCheckLoop, loadBalancerLoop, new ScenarioInput( "killInstanceConfirmation", /** * @param {{ targetInstance: import('@aws-sdk/client-ssm').InstanceInformation }} state */ (state) => MESSAGES.demoKillInstanceConfirmation.replace( "${INSTANCE_ID}", state.targetInstance.InstanceId, ), { type: "confirm" }, ), new ScenarioAction("killInstanceExit", (state) => { if (!state.killInstanceConfirmation) { process.exit(); } }), new ScenarioAction( "killInstance", /** * @param {{ targetInstance: import('@aws-sdk/client-ssm').InstanceInformation }} state */ async (state) => { const client = new AutoScalingClient({}); await client.send( new TerminateInstanceInAutoScalingGroupCommand({ InstanceId: state.targetInstance.InstanceId, ShouldDecrementDesiredCapacity: false, }), ); }, ), new ScenarioOutput("testKillInstance", MESSAGES.demoTestKillInstance), healthCheckLoop, loadBalancerLoop, new ScenarioInput("failOpenConfirmation", MESSAGES.demoFailOpenConfirmation, { type: "confirm", }), new ScenarioAction("failOpenExit", (state) => { if (!state.failOpenConfirmation) { process.exit(); } }), new ScenarioAction("failOpen", () => { const client = new SSMClient({}); return client.send( new PutParameterCommand({ Name: NAMES.ssmTableNameKey, Value: `fake-table-${Date.now()}`, Overwrite: true, Type: "String", }), ); }), new ScenarioOutput("testFailOpen", MESSAGES.demoFailOpenTest), healthCheckLoop, loadBalancerLoop, new ScenarioInput( "resetTableConfirmation", MESSAGES.demoResetTableConfirmation, { type: "confirm" }, ), new ScenarioAction("resetTableExit", (state) => { if (!state.resetTableConfirmation) { process.exit(); } }), new ScenarioAction("resetTable", async () => { const client = new SSMClient({}); await client.send( new PutParameterCommand({ Name: NAMES.ssmTableNameKey, Value: NAMES.tableName, Overwrite: true, Type: "String", }), ); }), new ScenarioOutput("testResetTable", MESSAGES.demoTestResetTable), healthCheckLoop, loadBalancerLoop, ]; async function createSsmOnlyInstanceProfile() { const iamClient = new IAMClient({}); const { Policy } = await iamClient.send( new CreatePolicyCommand({ PolicyName: NAMES.ssmOnlyPolicyName, PolicyDocument: readFileSync( join(RESOURCES_PATH, "ssm_only_policy.json"), ), }), ); await iamClient.send( new CreateRoleCommand({ RoleName: NAMES.ssmOnlyRoleName, AssumeRolePolicyDocument: JSON.stringify({ Version: "2012-10-17", Statement: [ { Effect: "Allow", Principal: { Service: "ec2.amazonaws.com" }, Action: "sts:AssumeRole", }, ], }), }), ); await iamClient.send( new AttachRolePolicyCommand({ RoleName: NAMES.ssmOnlyRoleName, PolicyArn: Policy.Arn, }), ); await iamClient.send( new AttachRolePolicyCommand({ RoleName: NAMES.ssmOnlyRoleName, PolicyArn: "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", }), ); const { InstanceProfile } = await iamClient.send( new CreateInstanceProfileCommand({ InstanceProfileName: NAMES.ssmOnlyInstanceProfileName, }), ); await waitUntilInstanceProfileExists( { client: iamClient }, { InstanceProfileName: NAMES.ssmOnlyInstanceProfileName }, ); await iamClient.send( new AddRoleToInstanceProfileCommand({ InstanceProfileName: NAMES.ssmOnlyInstanceProfileName, RoleName: NAMES.ssmOnlyRoleName, }), ); return InstanceProfile; }

모든 리소스를 폐기하는 단계를 생성합니다.

import { unlinkSync } from "node:fs"; import { DynamoDBClient, DeleteTableCommand } from "@aws-sdk/client-dynamodb"; import { EC2Client, DeleteKeyPairCommand, DeleteLaunchTemplateCommand, RevokeSecurityGroupIngressCommand, } from "@aws-sdk/client-ec2"; import { IAMClient, DeleteInstanceProfileCommand, RemoveRoleFromInstanceProfileCommand, DeletePolicyCommand, DeleteRoleCommand, DetachRolePolicyCommand, paginateListPolicies, } from "@aws-sdk/client-iam"; import { AutoScalingClient, DeleteAutoScalingGroupCommand, TerminateInstanceInAutoScalingGroupCommand, UpdateAutoScalingGroupCommand, paginateDescribeAutoScalingGroups, } from "@aws-sdk/client-auto-scaling"; import { DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DescribeTargetGroupsCommand, ElasticLoadBalancingV2Client, } from "@aws-sdk/client-elastic-load-balancing-v2"; import { ScenarioOutput, ScenarioInput, ScenarioAction, } from "@aws-doc-sdk-examples/lib/scenario/index.js"; import { loadState } from "@aws-doc-sdk-examples/lib/scenario/steps-common.js"; import { retry } from "@aws-doc-sdk-examples/lib/utils/util-timers.js"; import { MESSAGES, NAMES } from "./constants.js"; import { findLoadBalancer } from "./shared.js"; /** * @type {import('@aws-doc-sdk-examples/lib/scenario.js').Step[]} */ export const destroySteps = [ loadState, new ScenarioInput("destroy", MESSAGES.destroy, { type: "confirm" }), new ScenarioAction( "abort", (state) => state.destroy === false && process.exit(), ), new ScenarioAction("deleteTable", async (c) => { try { const client = new DynamoDBClient({}); await client.send(new DeleteTableCommand({ TableName: NAMES.tableName })); } catch (e) { c.deleteTableError = e; } }), new ScenarioOutput("deleteTableResult", (state) => { if (state.deleteTableError) { console.error(state.deleteTableError); return MESSAGES.deleteTableError.replace( "${TABLE_NAME}", NAMES.tableName, ); } return MESSAGES.deletedTable.replace("${TABLE_NAME}", NAMES.tableName); }), new ScenarioAction("deleteKeyPair", async (state) => { try { const client = new EC2Client({}); await client.send( new DeleteKeyPairCommand({ KeyName: NAMES.keyPairName }), ); unlinkSync(`${NAMES.keyPairName}.pem`); } catch (e) { state.deleteKeyPairError = e; } }), new ScenarioOutput("deleteKeyPairResult", (state) => { if (state.deleteKeyPairError) { console.error(state.deleteKeyPairError); return MESSAGES.deleteKeyPairError.replace( "${KEY_PAIR_NAME}", NAMES.keyPairName, ); } return MESSAGES.deletedKeyPair.replace( "${KEY_PAIR_NAME}", NAMES.keyPairName, ); }), new ScenarioAction("detachPolicyFromRole", async (state) => { try { const client = new IAMClient({}); const policy = await findPolicy(NAMES.instancePolicyName); if (!policy) { state.detachPolicyFromRoleError = new Error( `Policy ${NAMES.instancePolicyName} not found.`, ); } else { await client.send( new DetachRolePolicyCommand({ RoleName: NAMES.instanceRoleName, PolicyArn: policy.Arn, }), ); } } catch (e) { state.detachPolicyFromRoleError = e; } }), new ScenarioOutput("detachedPolicyFromRole", (state) => { if (state.detachPolicyFromRoleError) { console.error(state.detachPolicyFromRoleError); return MESSAGES.detachPolicyFromRoleError .replace("${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName); } return MESSAGES.detachedPolicyFromRole .replace("${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName); }), new ScenarioAction("deleteInstancePolicy", async (state) => { const client = new IAMClient({}); const policy = await findPolicy(NAMES.instancePolicyName); if (!policy) { state.deletePolicyError = new Error( `Policy ${NAMES.instancePolicyName} not found.`, ); } else { return client.send( new DeletePolicyCommand({ PolicyArn: policy.Arn, }), ); } }), new ScenarioOutput("deletePolicyResult", (state) => { if (state.deletePolicyError) { console.error(state.deletePolicyError); return MESSAGES.deletePolicyError.replace( "${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName, ); } return MESSAGES.deletedPolicy.replace( "${INSTANCE_POLICY_NAME}", NAMES.instancePolicyName, ); }), new ScenarioAction("removeRoleFromInstanceProfile", async (state) => { try { const client = new IAMClient({}); await client.send( new RemoveRoleFromInstanceProfileCommand({ RoleName: NAMES.instanceRoleName, InstanceProfileName: NAMES.instanceProfileName, }), ); } catch (e) { state.removeRoleFromInstanceProfileError = e; } }), new ScenarioOutput("removeRoleFromInstanceProfileResult", (state) => { if (state.removeRoleFromInstanceProfile) { console.error(state.removeRoleFromInstanceProfileError); return MESSAGES.removeRoleFromInstanceProfileError .replace("${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName); } return MESSAGES.removedRoleFromInstanceProfile .replace("${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName) .replace("${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName); }), new ScenarioAction("deleteInstanceRole", async (state) => { try { const client = new IAMClient({}); await client.send( new DeleteRoleCommand({ RoleName: NAMES.instanceRoleName, }), ); } catch (e) { state.deleteInstanceRoleError = e; } }), new ScenarioOutput("deleteInstanceRoleResult", (state) => { if (state.deleteInstanceRoleError) { console.error(state.deleteInstanceRoleError); return MESSAGES.deleteInstanceRoleError.replace( "${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName, ); } return MESSAGES.deletedInstanceRole.replace( "${INSTANCE_ROLE_NAME}", NAMES.instanceRoleName, ); }), new ScenarioAction("deleteInstanceProfile", async (state) => { try { const client = new IAMClient({}); await client.send( new DeleteInstanceProfileCommand({ InstanceProfileName: NAMES.instanceProfileName, }), ); } catch (e) { state.deleteInstanceProfileError = e; } }), new ScenarioOutput("deleteInstanceProfileResult", (state) => { if (state.deleteInstanceProfileError) { console.error(state.deleteInstanceProfileError); return MESSAGES.deleteInstanceProfileError.replace( "${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName, ); } return MESSAGES.deletedInstanceProfile.replace( "${INSTANCE_PROFILE_NAME}", NAMES.instanceProfileName, ); }), new ScenarioAction("deleteAutoScalingGroup", async (state) => { try { await terminateGroupInstances(NAMES.autoScalingGroupName); await retry({ intervalInMs: 60000, maxRetries: 60 }, async () => { await deleteAutoScalingGroup(NAMES.autoScalingGroupName); }); } catch (e) { state.deleteAutoScalingGroupError = e; } }), new ScenarioOutput("deleteAutoScalingGroupResult", (state) => { if (state.deleteAutoScalingGroupError) { console.error(state.deleteAutoScalingGroupError); return MESSAGES.deleteAutoScalingGroupError.replace( "${AUTO_SCALING_GROUP_NAME}", NAMES.autoScalingGroupName, ); } return MESSAGES.deletedAutoScalingGroup.replace( "${AUTO_SCALING_GROUP_NAME}", NAMES.autoScalingGroupName, ); }), new ScenarioAction("deleteLaunchTemplate", async (state) => { const client = new EC2Client({}); try { await client.send( new DeleteLaunchTemplateCommand({ LaunchTemplateName: NAMES.launchTemplateName, }), ); } catch (e) { state.deleteLaunchTemplateError = e; } }), new ScenarioOutput("deleteLaunchTemplateResult", (state) => { if (state.deleteLaunchTemplateError) { console.error(state.deleteLaunchTemplateError); return MESSAGES.deleteLaunchTemplateError.replace( "${LAUNCH_TEMPLATE_NAME}", NAMES.launchTemplateName, ); } return MESSAGES.deletedLaunchTemplate.replace( "${LAUNCH_TEMPLATE_NAME}", NAMES.launchTemplateName, ); }), new ScenarioAction("deleteLoadBalancer", async (state) => { try { const client = new ElasticLoadBalancingV2Client({}); const loadBalancer = await findLoadBalancer(NAMES.loadBalancerName); await client.send( new DeleteLoadBalancerCommand({ LoadBalancerArn: loadBalancer.LoadBalancerArn, }), ); await retry({ intervalInMs: 1000, maxRetries: 60 }, async () => { const lb = await findLoadBalancer(NAMES.loadBalancerName); if (lb) { throw new Error("Load balancer still exists."); } }); } catch (e) { state.deleteLoadBalancerError = e; } }), new ScenarioOutput("deleteLoadBalancerResult", (state) => { if (state.deleteLoadBalancerError) { console.error(state.deleteLoadBalancerError); return MESSAGES.deleteLoadBalancerError.replace( "${LB_NAME}", NAMES.loadBalancerName, ); } return MESSAGES.deletedLoadBalancer.replace( "${LB_NAME}", NAMES.loadBalancerName, ); }), new ScenarioAction("deleteLoadBalancerTargetGroup", async (state) => { const client = new ElasticLoadBalancingV2Client({}); try { const { TargetGroups } = await client.send( new DescribeTargetGroupsCommand({ Names: [NAMES.loadBalancerTargetGroupName], }), ); await retry({ intervalInMs: 1000, maxRetries: 30 }, () => client.send( new DeleteTargetGroupCommand({ TargetGroupArn: TargetGroups[0].TargetGroupArn, }), ), ); } catch (e) { state.deleteLoadBalancerTargetGroupError = e; } }), new ScenarioOutput("deleteLoadBalancerTargetGroupResult", (state) => { if (state.deleteLoadBalancerTargetGroupError) { console.error(state.deleteLoadBalancerTargetGroupError); return MESSAGES.deleteLoadBalancerTargetGroupError.replace( "${TARGET_GROUP_NAME}", NAMES.loadBalancerTargetGroupName, ); } return MESSAGES.deletedLoadBalancerTargetGroup.replace( "${TARGET_GROUP_NAME}", NAMES.loadBalancerTargetGroupName, ); }), new ScenarioAction("detachSsmOnlyRoleFromProfile", async (state) => { try { const client = new IAMClient({}); await client.send( new RemoveRoleFromInstanceProfileCommand({ InstanceProfileName: NAMES.ssmOnlyInstanceProfileName, RoleName: NAMES.ssmOnlyRoleName, }), ); } catch (e) { state.detachSsmOnlyRoleFromProfileError = e; } }), new ScenarioOutput("detachSsmOnlyRoleFromProfileResult", (state) => { if (state.detachSsmOnlyRoleFromProfileError) { console.error(state.detachSsmOnlyRoleFromProfileError); return MESSAGES.detachSsmOnlyRoleFromProfileError .replace("${ROLE_NAME}", NAMES.ssmOnlyRoleName) .replace("${PROFILE_NAME}", NAMES.ssmOnlyInstanceProfileName); } return MESSAGES.detachedSsmOnlyRoleFromProfile .replace("${ROLE_NAME}", NAMES.ssmOnlyRoleName) .replace("${PROFILE_NAME}", NAMES.ssmOnlyInstanceProfileName); }), new ScenarioAction("detachSsmOnlyCustomRolePolicy", async (state) => { try { const iamClient = new IAMClient({}); const ssmOnlyPolicy = await findPolicy(NAMES.ssmOnlyPolicyName); await iamClient.send( new DetachRolePolicyCommand({ RoleName: NAMES.ssmOnlyRoleName, PolicyArn: ssmOnlyPolicy.Arn, }), ); } catch (e) { state.detachSsmOnlyCustomRolePolicyError = e; } }), new ScenarioOutput("detachSsmOnlyCustomRolePolicyResult", (state) => { if (state.detachSsmOnlyCustomRolePolicyError) { console.error(state.detachSsmOnlyCustomRolePolicyError); return MESSAGES.detachSsmOnlyCustomRolePolicyError .replace("${ROLE_NAME}", NAMES.ssmOnlyRoleName) .replace("${POLICY_NAME}", NAMES.ssmOnlyPolicyName); } return MESSAGES.detachedSsmOnlyCustomRolePolicy .replace("${ROLE_NAME}", NAMES.ssmOnlyRoleName) .replace("${POLICY_NAME}", NAMES.ssmOnlyPolicyName); }), new ScenarioAction("detachSsmOnlyAWSRolePolicy", async (state) => { try { const iamClient = new IAMClient({}); await iamClient.send( new DetachRolePolicyCommand({ RoleName: NAMES.ssmOnlyRoleName, PolicyArn: "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", }), ); } catch (e) { state.detachSsmOnlyAWSRolePolicyError = e; } }), new ScenarioOutput("detachSsmOnlyAWSRolePolicyResult", (state) => { if (state.detachSsmOnlyAWSRolePolicyError) { console.error(state.detachSsmOnlyAWSRolePolicyError); return MESSAGES.detachSsmOnlyAWSRolePolicyError .replace("${ROLE_NAME}", NAMES.ssmOnlyRoleName) .replace("${POLICY_NAME}", "AmazonSSMManagedInstanceCore"); } return MESSAGES.detachedSsmOnlyAWSRolePolicy .replace("${ROLE_NAME}", NAMES.ssmOnlyRoleName) .replace("${POLICY_NAME}", "AmazonSSMManagedInstanceCore"); }), new ScenarioAction("deleteSsmOnlyInstanceProfile", async (state) => { try { const iamClient = new IAMClient({}); await iamClient.send( new DeleteInstanceProfileCommand({ InstanceProfileName: NAMES.ssmOnlyInstanceProfileName, }), ); } catch (e) { state.deleteSsmOnlyInstanceProfileError = e; } }), new ScenarioOutput("deleteSsmOnlyInstanceProfileResult", (state) => { if (state.deleteSsmOnlyInstanceProfileError) { console.error(state.deleteSsmOnlyInstanceProfileError); return MESSAGES.deleteSsmOnlyInstanceProfileError.replace( "${INSTANCE_PROFILE_NAME}", NAMES.ssmOnlyInstanceProfileName, ); } return MESSAGES.deletedSsmOnlyInstanceProfile.replace( "${INSTANCE_PROFILE_NAME}", NAMES.ssmOnlyInstanceProfileName, ); }), new ScenarioAction("deleteSsmOnlyPolicy", async (state) => { try { const iamClient = new IAMClient({}); const ssmOnlyPolicy = await findPolicy(NAMES.ssmOnlyPolicyName); await iamClient.send( new DeletePolicyCommand({ PolicyArn: ssmOnlyPolicy.Arn, }), ); } catch (e) { state.deleteSsmOnlyPolicyError = e; } }), new ScenarioOutput("deleteSsmOnlyPolicyResult", (state) => { if (state.deleteSsmOnlyPolicyError) { console.error(state.deleteSsmOnlyPolicyError); return MESSAGES.deleteSsmOnlyPolicyError.replace( "${POLICY_NAME}", NAMES.ssmOnlyPolicyName, ); } return MESSAGES.deletedSsmOnlyPolicy.replace( "${POLICY_NAME}", NAMES.ssmOnlyPolicyName, ); }), new ScenarioAction("deleteSsmOnlyRole", async (state) => { try { const iamClient = new IAMClient({}); await iamClient.send( new DeleteRoleCommand({ RoleName: NAMES.ssmOnlyRoleName, }), ); } catch (e) { state.deleteSsmOnlyRoleError = e; } }), new ScenarioOutput("deleteSsmOnlyRoleResult", (state) => { if (state.deleteSsmOnlyRoleError) { console.error(state.deleteSsmOnlyRoleError); return MESSAGES.deleteSsmOnlyRoleError.replace( "${ROLE_NAME}", NAMES.ssmOnlyRoleName, ); } return MESSAGES.deletedSsmOnlyRole.replace( "${ROLE_NAME}", NAMES.ssmOnlyRoleName, ); }), new ScenarioAction( "revokeSecurityGroupIngress", async ( /** @type {{ myIp: string, defaultSecurityGroup: { GroupId: string } }} */ state, ) => { const ec2Client = new EC2Client({}); try { await ec2Client.send( new RevokeSecurityGroupIngressCommand({ GroupId: state.defaultSecurityGroup.GroupId, CidrIp: `${state.myIp}/32`, FromPort: 80, ToPort: 80, IpProtocol: "tcp", }), ); } catch (e) { state.revokeSecurityGroupIngressError = e; } }, ), new ScenarioOutput("revokeSecurityGroupIngressResult", (state) => { if (state.revokeSecurityGroupIngressError) { console.error(state.revokeSecurityGroupIngressError); return MESSAGES.revokeSecurityGroupIngressError.replace( "${IP}", state.myIp, ); } return MESSAGES.revokedSecurityGroupIngress.replace("${IP}", state.myIp); }), ]; /** * @param {string} policyName */ async function findPolicy(policyName) { const client = new IAMClient({}); const paginatedPolicies = paginateListPolicies({ client }, {}); for await (const page of paginatedPolicies) { const policy = page.Policies.find((p) => p.PolicyName === policyName); if (policy) { return policy; } } } /** * @param {string} groupName */ async function deleteAutoScalingGroup(groupName) { const client = new AutoScalingClient({}); try { await client.send( new DeleteAutoScalingGroupCommand({ AutoScalingGroupName: groupName, }), ); } catch (err) { if (!(err instanceof Error)) { throw err; } console.log(err.name); throw err; } } /** * @param {string} groupName */ async function terminateGroupInstances(groupName) { const autoScalingClient = new AutoScalingClient({}); const group = await findAutoScalingGroup(groupName); await autoScalingClient.send( new UpdateAutoScalingGroupCommand({ AutoScalingGroupName: group.AutoScalingGroupName, MinSize: 0, }), ); for (const i of group.Instances) { await retry({ intervalInMs: 1000, maxRetries: 30 }, () => autoScalingClient.send( new TerminateInstanceInAutoScalingGroupCommand({ InstanceId: i.InstanceId, ShouldDecrementDesiredCapacity: true, }), ), ); } } async function findAutoScalingGroup(groupName) { const client = new AutoScalingClient({}); const paginatedGroups = paginateDescribeAutoScalingGroups({ client }, {}); for await (const page of paginatedGroups) { const group = page.AutoScalingGroups.find( (g) => g.AutoScalingGroupName === groupName, ); if (group) { return group; } } throw new Error(`Auto scaling group ${groupName} not found.`); }