diff --git a/.doc_gen/metadata/ec2_metadata.yaml b/.doc_gen/metadata/ec2_metadata.yaml
index 73ed60a9d66..97b32d325d6 100644
--- a/.doc_gen/metadata/ec2_metadata.yaml
+++ b/.doc_gen/metadata/ec2_metadata.yaml
@@ -76,6 +76,17 @@ ec2_Hello:
excerpts:
- snippet_tags:
- ec2.rust.ec2-helloworld
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description: The Package.swift file.
+ snippet_tags:
+ - swift.ec2.hello.package
+ - description: The entry.swift file.
+ snippet_tags:
+ - swift.ec2.hello
services:
ec2: {DescribeSecurityGroups}
ec2_GetPasswordData:
@@ -188,6 +199,15 @@ ec2_CreateKeyPair:
- description: A function that calls the create_key impl and securely saves the PEM private key.
snippet_tags:
- ec2.rust.create_key.wrapper
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.CreateKeyPair
services:
ec2: {CreateKeyPair}
ec2_DescribeKeyPairs:
@@ -274,6 +294,15 @@ ec2_DescribeKeyPairs:
excerpts:
- snippet_tags:
- ec2.rust.list_keys.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DescribeKeyPairs
services:
ec2: {DescribeKeyPairs}
@@ -370,6 +399,15 @@ ec2_CreateSecurityGroup:
excerpts:
- snippet_tags:
- ec2.rust.create_security_group.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.CreateSecurityGroup
services:
ec2: {CreateSecurityGroup}
ec2_RunInstances:
@@ -457,6 +495,15 @@ ec2_RunInstances:
excerpts:
- snippet_tags:
- ec2.rust.create_instance.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.RunInstances
services:
ec2: {RunInstances}
ec2_StartInstances:
@@ -557,6 +604,15 @@ ec2_StartInstances:
snippet_tags:
- aws-cli.bash-linux.ec2.errecho
- aws-cli.bash-linux.ec2.aws_cli_error_log
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.StartInstances
services:
ec2: {StartInstances}
ec2_StopInstances:
@@ -656,6 +712,15 @@ ec2_StopInstances:
snippet_tags:
- aws-cli.bash-linux.ec2.errecho
- aws-cli.bash-linux.ec2.aws_cli_error_log
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.StopInstances
services:
ec2: {StopInstances}
ec2_AllocateAddress:
@@ -752,6 +817,15 @@ ec2_AllocateAddress:
excerpts:
- snippet_tags:
- ec2.rust.allocate_address.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.AllocateAddress
services:
ec2: {AllocateAddress}
ec2_AssociateAddress:
@@ -848,6 +922,15 @@ ec2_AssociateAddress:
excerpts:
- snippet_tags:
- ec2.rust.associate_address.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.AssociateAddress
services:
ec2: {AssociateAddress}
ec2_DisassociateAddress:
@@ -918,6 +1001,15 @@ ec2_DisassociateAddress:
excerpts:
- snippet_tags:
- ec2.rust.disassociate_address.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DisassociateAddress
services:
ec2: {DisassociateAddress}
ec2_ReleaseAddress:
@@ -1013,6 +1105,15 @@ ec2_ReleaseAddress:
excerpts:
- snippet_tags:
- ec2.rust.deallocate_address.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.ReleaseAddress
services:
ec2: {ReleaseAddress}
ec2_AuthorizeSecurityGroupIngress:
@@ -1094,6 +1195,15 @@ ec2_AuthorizeSecurityGroupIngress:
excerpts:
- snippet_tags:
- ec2.rust.authorize_security_group_ssh_ingress.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.AuthorizeSecurityGroupIngress
services:
ec2: {AuthorizeSecurityGroupIngress}
ec2_DeleteKeyPair:
@@ -1183,6 +1293,15 @@ ec2_DeleteKeyPair:
- ec2.rust.delete_key.wrapper
- snippet_tags:
- ec2.rust.delete_key.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DeleteKeyPair
services:
ec2: {DeleteKeyPair}
ec2_DescribeSecurityGroups:
@@ -1269,6 +1388,19 @@ ec2_DescribeSecurityGroups:
excerpts:
- snippet_tags:
- ec2.rust.ec2-helloworld
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description: Using pagination with describeSecurityGroupsPaginated()
.
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DescribeSecurityGroupsPaginated
+ - description: Without pagination.
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DescribeSecurityGroups
services:
ec2: {DescribeSecurityGroups}
ec2_DeleteSecurityGroup:
@@ -1355,6 +1487,15 @@ ec2_DeleteSecurityGroup:
excerpts:
- snippet_tags:
- ec2.rust.delete_security_group.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DeleteSecurityGroup
services:
ec2: {DeleteSecurityGroup}
ec2_DeleteSnapshot:
@@ -1473,6 +1614,15 @@ ec2_TerminateInstances:
- description: Wait for an instance to be in the terminted state, using the Waiters API. Using the Waiters API requires `use aws_sdk_ec2::client::Waiters` in the rust file.
snippet_tags:
- ec2.rust.wait_for_instance_terminated.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.TerminateInstances
services:
ec2: {TerminateInstances}
ec2_DescribeInstances:
@@ -1807,6 +1957,15 @@ ec2_DescribeImages:
snippet_tags:
- aws-cli.bash-linux.ec2.errecho
- aws-cli.bash-linux.ec2.aws_cli_error_log
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DescribeImages
services:
ec2: {DescribeImages}
ec2_DescribeInstanceTypes:
@@ -1877,6 +2036,15 @@ ec2_DescribeInstanceTypes:
excerpts:
- snippet_tags:
- ec2.rust.list_instance_types.impl
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description:
+ snippet_tags:
+ - swift.ec2.import
+ - swift.ec2.DescribeInstanceTypes
services:
ec2: {DescribeInstanceTypes}
ec2_DescribeAddresses:
@@ -2419,6 +2587,17 @@ ec2_Scenario_GetStartedInstances:
- description: The main entry point for the scenario.
snippet_files:
- rustv1/examples/ec2/src/bin/getting-started.rs
+ Swift:
+ versions:
+ - sdk_version: 1
+ github: swift/example_code/ec2
+ excerpts:
+ - description: The Package.swift file.
+ snippet_tags:
+ - swift.ec2.scenario.package
+ - description: The entry.swift file.
+ snippet_tags:
+ - swift.ec2.scenario
services:
ec2:
{
diff --git a/swift/example_code/ec2/README.md b/swift/example_code/ec2/README.md
new file mode 100644
index 00000000000..41fc4793ddd
--- /dev/null
+++ b/swift/example_code/ec2/README.md
@@ -0,0 +1,138 @@
+# Amazon EC2 code examples for the SDK for Swift
+
+## Overview
+
+Shows how to use the AWS SDK for Swift to work with Amazon Elastic Compute Cloud (Amazon EC2).
+
+
+
+
+_Amazon EC2 is a web service that provides resizable computing capacity—literally, servers in Amazon's data centers—that you use to build and host your software systems._
+
+## ⚠ Important
+
+* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/).
+* Running the tests might result in charges to your AWS account.
+* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
+* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
+
+
+
+
+## Code examples
+
+### Prerequisites
+
+For prerequisites, see the [README](../../README.md#Prerequisites) in the `swift` folder.
+
+
+
+
+
+### Get started
+
+- [Hello Amazon EC2](hello/Package.swift#L8) (`DescribeSecurityGroups`)
+
+
+### Basics
+
+Code examples that show you how to perform the essential operations within a service.
+
+- [Learn the basics](scenario/Package.swift)
+
+
+### Single actions
+
+Code excerpts that show you how to call individual service functions.
+
+- [AllocateAddress](scenario/Sources/entry.swift#L1019)
+- [AssociateAddress](scenario/Sources/entry.swift#L1043)
+- [AuthorizeSecurityGroupIngress](scenario/Sources/entry.swift#L932)
+- [CreateKeyPair](scenario/Sources/entry.swift#L410)
+- [CreateSecurityGroup](scenario/Sources/entry.swift#L907)
+- [DeleteKeyPair](scenario/Sources/entry.swift#L473)
+- [DeleteSecurityGroup](scenario/Sources/entry.swift#L997)
+- [DescribeImages](scenario/Sources/entry.swift#L813)
+- [DescribeInstanceTypes](scenario/Sources/entry.swift#L541)
+- [DescribeKeyPairs](scenario/Sources/entry.swift#L450)
+- [DescribeSecurityGroups](hello/Sources/entry.swift#L44)
+- [DisassociateAddress](scenario/Sources/entry.swift#L1069)
+- [ReleaseAddress](scenario/Sources/entry.swift#L1086)
+- [RunInstances](scenario/Sources/entry.swift#L849)
+- [StartInstances](scenario/Sources/entry.swift#L715)
+- [StopInstances](scenario/Sources/entry.swift#L665)
+- [TerminateInstances](scenario/Sources/entry.swift#L764)
+
+
+
+
+
+## Run the examples
+
+### Instructions
+
+To build any of these examples from a terminal window, navigate into its
+directory, then use the following command:
+
+```
+$ swift build
+```
+
+To build one of these examples in Xcode, navigate to the example's directory
+(such as the `ListUsers` directory, to build that example). Then type `xed.`
+to open the example directory in Xcode. You can then use standard Xcode build
+and run commands.
+
+
+
+
+#### Hello Amazon EC2
+
+This example shows you how to get started using Amazon EC2.
+
+
+#### Learn the basics
+
+This example shows you how to do the following:
+
+- Create a key pair and security group.
+- Select an Amazon Machine Image (AMI) and compatible instance type, then create an instance.
+- Stop and restart the instance.
+- Associate an Elastic IP address with your instance.
+- Connect to your instance with SSH, then clean up resources.
+
+
+
+
+
+
+
+
+
+### Tests
+
+⚠ Running tests might result in charges to your AWS account.
+
+
+To find instructions for running these tests, see the [README](../../README.md#Tests)
+in the `swift` folder.
+
+
+
+
+
+
+## Additional resources
+
+- [Amazon EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html)
+- [Amazon EC2 API Reference](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Welcome.html)
+- [SDK for Swift Amazon EC2 reference](https://sdk.amazonaws.com/swift/api/awsec2/latest/documentation/awsec2)
+
+
+
+
+---
+
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
diff --git a/swift/example_code/ec2/hello/Package.swift b/swift/example_code/ec2/hello/Package.swift
new file mode 100644
index 00000000000..98ab159210b
--- /dev/null
+++ b/swift/example_code/ec2/hello/Package.swift
@@ -0,0 +1,47 @@
+// swift-tools-version: 5.9
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+// (swift-tools-version has two lines here because it needs to be the first
+// line in the file, but it should also appear in the snippet below)
+//
+// snippet-start:[swift.ec2.hello.package]
+// swift-tools-version: 5.9
+//
+// The swift-tools-version declares the minimum version of Swift required to
+// build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "hello-ec2",
+ // Let Xcode know the minimum Apple platforms supported.
+ platforms: [
+ .macOS(.v13),
+ .iOS(.v15)
+ ],
+ dependencies: [
+ // Dependencies declare other packages that this package depends on.
+ .package(
+ url: "https://github.com/awslabs/aws-sdk-swift",
+ from: "1.0.0"),
+ .package(
+ url: "https://github.com/apple/swift-argument-parser.git",
+ branch: "main"
+ )
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package, defining a module or a test suite.
+ // Targets can depend on other targets in this package and products
+ // from dependencies.
+ .executableTarget(
+ name: "hello-ec2",
+ dependencies: [
+ .product(name: "AWSEC2", package: "aws-sdk-swift"),
+ .product(name: "ArgumentParser", package: "swift-argument-parser")
+ ],
+ path: "Sources")
+
+ ]
+)
+// snippet-end:[swift.ec2.hello.package]
diff --git a/swift/example_code/ec2/hello/Sources/entry.swift b/swift/example_code/ec2/hello/Sources/entry.swift
new file mode 100644
index 00000000000..7fda09cfe6b
--- /dev/null
+++ b/swift/example_code/ec2/hello/Sources/entry.swift
@@ -0,0 +1,108 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+// snippet-start:[swift.ec2.hello]
+// An example that shows how to use the AWS SDK for Swift to perform a simple
+// operation using Amazon Elastic Compute Cloud (EC2).
+//
+
+import ArgumentParser
+import Foundation
+
+// snippet-start:[swift.ec2.import]
+import AWSEC2
+// snippet-end:[swift.ec2.import]
+
+struct ExampleCommand: ParsableCommand {
+ @Option(help: "The AWS Region to run AWS API calls in.")
+ var awsRegion = "us-east-1"
+
+ @Option(
+ help: ArgumentHelp("The level of logging for the Swift SDK to perform."),
+ completion: .list([
+ "critical",
+ "debug",
+ "error",
+ "info",
+ "notice",
+ "trace",
+ "warning"
+ ])
+ )
+ var logLevel: String = "error"
+
+ static var configuration = CommandConfiguration(
+ commandName: "hello-ec2",
+ abstract: """
+ Demonstrates a simple operation using Amazon EC2.
+ """,
+ discussion: """
+ An example showing how to make a call to Amazon EC2 using the AWS SDK for Swift.
+ """
+ )
+
+ // snippet-start:[swift.ec2.DescribeSecurityGroupsPaginated]
+ /// Return an array of strings giving the names of every security group
+ /// the user is a member of.
+ ///
+ /// - Parameter ec2Client: The `EC2Client` to use when calling
+ /// `describeSecurityGroupsPaginated()`.
+ ///
+ /// - Returns: An array of strings giving the names of every security
+ /// group the user is a member of.
+ func getSecurityGroupNames(ec2Client: EC2Client) async -> [String] {
+ let pages = ec2Client.describeSecurityGroupsPaginated(
+ input: DescribeSecurityGroupsInput()
+ )
+
+ var groupNames: [String] = []
+
+ do {
+ for try await page in pages {
+ guard let groups = page.securityGroups else {
+ print("*** Error: No groups returned.")
+ continue
+ }
+
+ for group in groups {
+ groupNames.append(group.groupName ?? "")
+ }
+ }
+ } catch {
+ print("*** Error: \(error.localizedDescription)")
+ }
+
+ return groupNames
+ }
+ // snippet-end:[swift.ec2.DescribeSecurityGroupsPaginated]
+
+ /// Called by ``main()`` to run the bulk of the example.
+ func runAsync() async throws {
+ let ec2Config = try await EC2Client.EC2ClientConfiguration(region: awsRegion)
+ let ec2Client = EC2Client(config: ec2Config)
+
+ let groupNames = await getSecurityGroupNames(ec2Client: ec2Client)
+
+ print("Found \(groupNames.count) security group(s):")
+
+ for group in groupNames {
+ print(" \(group)")
+ }
+ }
+}
+
+/// The program's asynchronous entry point.
+@main
+struct Main {
+ static func main() async {
+ let args = Array(CommandLine.arguments.dropFirst())
+
+ do {
+ let command = try ExampleCommand.parse(args)
+ try await command.runAsync()
+ } catch {
+ ExampleCommand.exit(withError: error)
+ }
+ }
+}
+// snippet-end:[swift.ec2.hello]
diff --git a/swift/example_code/ec2/scenario/Package.swift b/swift/example_code/ec2/scenario/Package.swift
new file mode 100644
index 00000000000..777f85e941f
--- /dev/null
+++ b/swift/example_code/ec2/scenario/Package.swift
@@ -0,0 +1,48 @@
+// swift-tools-version: 5.9
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+// (swift-tools-version has two lines here because it needs to be the first
+// line in the file, but it should also appear in the snippet below)
+//
+// snippet-start:[swift.ec2.scenario.package]
+// swift-tools-version: 5.9
+//
+// The swift-tools-version declares the minimum version of Swift required to
+// build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "ec2-scenario",
+ // Let Xcode know the minimum Apple platforms supported.
+ platforms: [
+ .macOS(.v13),
+ .iOS(.v15)
+ ],
+ dependencies: [
+ // Dependencies declare other packages that this package depends on.
+ .package(
+ url: "https://github.com/awslabs/aws-sdk-swift",
+ from: "1.4.0"),
+ .package(
+ url: "https://github.com/apple/swift-argument-parser.git",
+ branch: "main"
+ )
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package, defining a module or a test suite.
+ // Targets can depend on other targets in this package and products
+ // from dependencies.
+ .executableTarget(
+ name: "ec2-scenario",
+ dependencies: [
+ .product(name: "AWSEC2", package: "aws-sdk-swift"),
+ .product(name: "AWSSSM", package: "aws-sdk-swift"),
+ .product(name: "ArgumentParser", package: "swift-argument-parser")
+ ],
+ path: "Sources")
+
+ ]
+)
+// snippet-end:[swift.ec2.scenario.package]
diff --git a/swift/example_code/ec2/scenario/Sources/entry.swift b/swift/example_code/ec2/scenario/Sources/entry.swift
new file mode 100644
index 00000000000..243cacc3d84
--- /dev/null
+++ b/swift/example_code/ec2/scenario/Sources/entry.swift
@@ -0,0 +1,1135 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+// snippet-start:[swift.ec2.scenario]
+// An example that shows how to use the AWS SDK for Swift to perform a variety
+// of operations using Amazon Elastic Compute Cloud (EC2).
+//
+
+import ArgumentParser
+import Foundation
+import AWSEC2
+
+// Allow waiters to be used.
+
+import class SmithyWaitersAPI.Waiter
+import struct SmithyWaitersAPI.WaiterOptions
+
+import AWSSSM
+
+struct ExampleCommand: ParsableCommand {
+ @Option(help: "The AWS Region to run AWS API calls in.")
+ var awsRegion = "us-east-1"
+
+ @Option(
+ help: ArgumentHelp("The level of logging for the Swift SDK to perform."),
+ completion: .list([
+ "critical",
+ "debug",
+ "error",
+ "info",
+ "notice",
+ "trace",
+ "warning"
+ ])
+ )
+ var logLevel: String = "error"
+
+ static var configuration = CommandConfiguration(
+ commandName: "ec2-scenario",
+ abstract: """
+ Performs various operations to demonstrate the use of Amazon EC2 using the
+ AWS SDK for Swift.
+ """,
+ discussion: """
+ """
+ )
+
+ /// Called by ``main()`` to run the bulk of the example.
+ func runAsync() async throws {
+ let ssmConfig = try await SSMClient.SSMClientConfiguration(region: awsRegion)
+ let ssmClient = SSMClient(config: ssmConfig)
+
+ let ec2Config = try await EC2Client.EC2ClientConfiguration(region: awsRegion)
+ let ec2Client = EC2Client(config: ec2Config)
+
+ let example = Example(ec2Client: ec2Client, ssmClient: ssmClient)
+
+ await example.run()
+ }
+}
+
+class Example {
+ let ec2Client: EC2Client
+ let ssmClient: SSMClient
+
+ // Storage for AWS EC2 properties.
+
+ var keyName: String? = nil
+ var securityGroupId: String? = nil
+ var instanceId: String? = nil
+ var allocationId: String? = nil
+ var associationId: String? = nil
+
+ init(ec2Client: EC2Client, ssmClient: SSMClient) {
+ self.ec2Client = ec2Client
+ self.ssmClient = ssmClient
+ }
+
+ /// The example's main body.
+ func run() async {
+ //=====================================================================
+ // 1. Create an RSA key pair, saving the private key as a `.pem` file.
+ // Create a `defer` block that will delete the private key when the
+ // program exits.
+ //=====================================================================
+
+ print("Creating an RSA key pair...")
+
+ keyName = self.tempName(prefix: "ExampleKeyName")
+ let keyUrl = await self.createKeyPair(name: keyName!)
+
+ guard let keyUrl else {
+ print("*** Failed to create the key pair!")
+ return
+ }
+
+ print("Created the private key at: \(keyUrl.absoluteString)")
+
+ // Schedule deleting the private key file to occur automatically when
+ // the program exits, no matter how it exits.
+
+ defer {
+ do {
+ try FileManager.default.removeItem(at: keyUrl)
+ } catch {
+ print("*** Failed to delete the private key at \(keyUrl.absoluteString)")
+ }
+ }
+
+ //=====================================================================
+ // 2. List the key pairs by calling `DescribeKeyPairs`.
+ //=====================================================================
+
+ print("Describing available key pairs...")
+ await self.describeKeyPairs()
+
+ //=====================================================================
+ // 3. Create a security group for the default VPC, and add an inbound
+ // rule to allow SSH from the current computer's public IPv4
+ // address.
+ //=====================================================================
+
+ print("Creating the security group...")
+
+ let secGroupName = self.tempName(prefix: "ExampleSecurityGroup")
+ let ipAddress = self.getMyIPAddress()
+
+ guard let ipAddress else {
+ print("*** Unable to get the device's IP address.")
+ return
+ }
+
+ print("IP address is: \(ipAddress)")
+
+ securityGroupId = await self.createSecurityGroup(
+ name: secGroupName,
+ description: "An example security group created using the AWS SDK for Swift"
+ )
+
+ if securityGroupId == nil {
+ await cleanUp()
+ return
+ }
+
+ print("Created security group: \(securityGroupId ?? "")")
+
+ if !(await self.authorizeSecurityGroupIngress(groupId: securityGroupId!, ipAddress: ipAddress)) {
+ await cleanUp()
+ return
+ }
+
+ //=====================================================================
+ // 4. Display security group information for the new security group
+ // using DescribeSecurityGroups.
+ //=====================================================================
+
+ if !(await self.describeSecurityGroups(groupId: securityGroupId!)) {
+ await cleanUp()
+ return
+ }
+
+ //=====================================================================
+ // 5. Get a list of Amazon Linux 2023 AMIs and pick one (SSM is the
+ // best practice), using path and then filter the list after the
+ // fact to include "al2023" in the Name field
+ // (ssm.GetParametersByPath). Paginate to get all images.
+ //=====================================================================
+
+ print("Searching available images for Amazon Linux 2023 images...")
+
+ let options = await self.findAMIsMatchingFilter("al2023")
+
+ //=====================================================================
+ // 6. The information in the AMI options isn't great, so make a list
+ // of the image IDs (the "Value" field in the AMI options) and get
+ // more information about them from EC2. Display the Description
+ // field and select one of them (DescribeImages with ImageIds
+ // filter).
+ //=====================================================================
+
+ print("Images matching Amazon Linux 2023:")
+
+ var imageIds: [String] = []
+ for option in options {
+ guard let id = option.value else {
+ continue
+ }
+ imageIds.append(id)
+ }
+
+ let images = await self.describeImages(imageIds)
+
+ // This is where you would normally let the user choose which AMI to
+ // use. However, for this example, we're just going to use the first
+ // one, whatever it is.
+
+ let chosenImage = images[0]
+
+ //=====================================================================
+ // 7. Get a list of instance types that are compatible with the
+ // selected AMI's architecture (such as "x86_64") and are either
+ // small or micro. Select one (DescribeInstanceTypes).
+ //=====================================================================
+
+ print("Getting the instance types compatible with the selected image...")
+
+ guard let arch = chosenImage.architecture else {
+ print("*** The selected image doesn't have a valid architecture.")
+ await cleanUp()
+ return
+ }
+
+ let imageTypes = await self.getMatchingInstanceTypes(architecture: arch)
+
+ for type in imageTypes {
+ guard let instanceType = type.instanceType else {
+ continue
+ }
+ print(" \(instanceType.rawValue)")
+ }
+
+ // This example selects the first returned instance type. A real-world
+ // application would probably ask the user to select one here.
+
+ let chosenInstanceType = imageTypes[0]
+
+ //=====================================================================
+ // 8. Create an instance with the key pair, security group, AMI, and
+ // instance type (RunInstances).
+ //=====================================================================
+
+ print("Creating an instance...")
+
+ guard let imageId = chosenImage.imageId else {
+ print("*** Cannot start image without a valid image ID.")
+ await cleanUp()
+ return
+ }
+ guard let instanceType = chosenInstanceType.instanceType else {
+ print("*** Unable to start image without a valid image type.")
+ await cleanUp()
+ return
+ }
+
+ let instance = await self.runInstance(
+ imageId: imageId,
+ instanceType: instanceType,
+ keyPairName: keyName!,
+ securityGroups: [securityGroupId!]
+ )
+
+ guard let instance else {
+ await cleanUp()
+ return
+ }
+
+ instanceId = instance.instanceId
+ if instanceId == nil {
+ print("*** Instance is missing an ID. Canceling.")
+ await cleanUp()
+ return
+ }
+
+ //=====================================================================
+ // 9. Wait for the instance to be ready and then display its
+ // information (DescribeInstances).
+ //=====================================================================
+
+ print("Waiting a few seconds to let the instance come up...")
+
+ do {
+ try await Task.sleep(for: .seconds(20))
+ } catch {
+ print("*** Error pausing the task.")
+ }
+ print("Success! Your new instance is ready:")
+
+ //=====================================================================
+ // 10. Display SSH connection info for the instance.
+ //=====================================================================
+
+ var runningInstance = await self.describeInstance(instanceId: instanceId!)
+
+ if (runningInstance != nil) && (runningInstance!.publicIpAddress != nil) {
+ print("\nYou can SSH to this instance using the following command:")
+ print("ssh -i \(keyUrl.path) ec2-user@\(runningInstance!.publicIpAddress!)")
+ }
+
+ //=====================================================================
+ // 11. Stop the instance and wait for it to stop (StopInstances).
+ //=====================================================================
+
+ print("Stopping the instance...")
+
+ if !(await self.stopInstance(instanceId: instanceId!, waitUntilStopped: true)) {
+ await cleanUp()
+ return
+ }
+
+ //=====================================================================
+ // 12. Start the instance and wait for it to start (StartInstances).
+ //=====================================================================
+
+ print("Starting the instance again...")
+
+ if !(await self.startInstance(instanceId: instanceId!, waitUntilStarted: true)) {
+ await cleanUp()
+ return
+ }
+
+ //=====================================================================
+ // 13. Display SSH connection info for the instance. Note that it's
+ // changed.
+ //=====================================================================
+
+ runningInstance = await self.describeInstance(instanceId: instanceId!)
+ if (runningInstance != nil) && (runningInstance!.publicIpAddress != nil) {
+ print("\nYou can SSH to this instance using the following command.")
+ print("This is probably different from when the instance was running before.")
+ print("ssh -i \(keyUrl.path) ec2-user@\(runningInstance!.publicIpAddress!)")
+ }
+
+ //=====================================================================
+ // 14. Allocate an elastic IP and associate it with the instance
+ // (AllocateAddress and AssociateAddress).
+ //=====================================================================
+
+ allocationId = await self.allocateAddress()
+
+ if allocationId == nil {
+ await cleanUp()
+ return
+ }
+
+ associationId = await self.associateAddress(instanceId: instanceId!, allocationId: allocationId)
+
+ if associationId == nil {
+ await cleanUp()
+ return
+ }
+
+ //=====================================================================
+ // 15. Display SSH connection info for the connection. Note that the
+ // public IP is now the Elastic IP, which stays constant.
+ //=====================================================================
+
+ runningInstance = await self.describeInstance(instanceId: instanceId!)
+ if (runningInstance != nil) && (runningInstance!.publicIpAddress != nil) {
+ print("\nYou can SSH to this instance using the following command.")
+ print("This has changed again, and is now the Elastic IP.")
+ print("ssh -i \(keyUrl.path) ec2-user@\(runningInstance!.publicIpAddress!)")
+ }
+
+ //=====================================================================
+ // Handle all cleanup tasks
+ //=====================================================================
+
+ await cleanUp()
+ }
+
+ /// Clean up by discarding and closing down all allocated EC2 items:
+ ///
+ /// * Elastic IP allocation and association
+ /// * Terminate the instance
+ /// * Delete the security group
+ /// * Delete the key pair
+ func cleanUp() async {
+ //=====================================================================
+ // 16. Disassociate and delete the Elastic IP (DisassociateAddress and
+ // ReleaseAddress).
+ //=====================================================================
+
+ if associationId != nil {
+ await self.disassociateAddress(associationId: associationId!)
+ }
+
+ if allocationId != nil {
+ await self.releaseAddress(allocationId: allocationId!)
+ }
+
+ //=====================================================================
+ // 17. Terminate the instance and wait for it to terminate
+ // (TerminateInstances).
+ //=====================================================================
+
+ if instanceId != nil {
+ print("Terminating the instance...")
+ _ = await self.terminateInstance(instanceId: instanceId!, waitUntilTerminated: true)
+ }
+
+ //=====================================================================
+ // 18. Delete the security group (DeleteSecurityGroup).
+ //=====================================================================
+
+ if securityGroupId != nil {
+ print("Deleting the security group...")
+ _ = await self.deleteSecurityGroup(groupId: securityGroupId!)
+ }
+
+ //=====================================================================
+ // 19. Delete the key pair (DeleteKeyPair).
+ //=====================================================================
+
+ if keyName != nil {
+ print("Deleting the key pair...")
+ _ = await self.deleteKeyPair(keyPair: keyName!)
+ }
+ }
+
+ // snippet-start:[swift.ec2.CreateKeyPair]
+ /// Create a new RSA key pair and save the private key to a randomly-named
+ /// file in the temporary directory.
+ ///
+ /// - Parameter name: The name of the key pair to create.
+ ///
+ /// - Returns: The URL of the newly created `.pem` file or `nil` if unable
+ /// to create the key pair.
+ func createKeyPair(name: String) async -> URL? {
+ do {
+ let output = try await ec2Client.createKeyPair(
+ input: CreateKeyPairInput(
+ keyName: name
+ )
+ )
+
+ guard let keyMaterial = output.keyMaterial else {
+ return nil
+ }
+
+ // Build the URL of the temporary private key file.
+
+ let fileURL = URL.temporaryDirectory
+ .appendingPathComponent(name)
+ .appendingPathExtension("pem")
+
+ do {
+ try keyMaterial.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
+ return fileURL
+ } catch {
+ print("*** Failed to write the private key.")
+ return nil
+ }
+ } catch {
+ print("*** Unable to create the key pair.")
+ return nil
+ }
+ }
+ // snippet-end:[swift.ec2.CreateKeyPair]
+
+ // snippet-start:[swift.ec2.DescribeKeyPairs]
+ /// Describe the key pairs associated with the user by outputting each key
+ /// pair's name and fingerprint.
+ func describeKeyPairs() async {
+ do {
+ let output = try await ec2Client.describeKeyPairs(
+ input: DescribeKeyPairsInput()
+ )
+
+ guard let keyPairs = output.keyPairs else {
+ print("*** No key pairs list available.")
+ return
+ }
+
+ for keyPair in keyPairs {
+ print(keyPair.keyName ?? "", ":", keyPair.keyFingerprint ?? "")
+ }
+ } catch {
+ print("*** Error: Unable to obtain a key pair list.")
+ }
+ }
+ // snippet-end:[swift.ec2.DescribeKeyPairs]
+
+ // snippet-start:[swift.ec2.DeleteKeyPair]
+ /// Delete an EC2 key pair.
+ ///
+ /// - Parameter keyPair: The name of the key pair to delete.
+ ///
+ /// - Returns: `true` if the key pair is deleted successfully; otherwise
+ /// `false`.
+ func deleteKeyPair(keyPair: String) async -> Bool {
+ do {
+ _ = try await ec2Client.deleteKeyPair(
+ input: DeleteKeyPairInput(
+ keyName: keyPair
+ )
+ )
+
+ return true
+ } catch {
+ print("*** Error deleting the key pair: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.DeleteKeyPair]
+
+ /// Return a list of AMI names that contain the specified string.
+ ///
+ /// - Parameter filter: A string that must be contained in all returned
+ /// AMI names.
+ ///
+ /// - Returns: An array of the parameters matching the specified substring.
+ func findAMIsMatchingFilter(_ filter: String) async -> [SSMClientTypes.Parameter] {
+ var parameterList: [SSMClientTypes.Parameter] = []
+ var matchingAMIs: [SSMClientTypes.Parameter] = []
+
+ do {
+ let pages = ssmClient.getParametersByPathPaginated(
+ input: GetParametersByPathInput(
+ path: "/aws/service/ami-amazon-linux-latest"
+ )
+ )
+
+ for try await page in pages {
+ guard let parameters = page.parameters else {
+ return matchingAMIs
+ }
+
+ for parameter in parameters {
+ parameterList.append(parameter)
+ }
+ }
+
+ print("Found \(parameterList.count) images total:")
+ for parameter in parameterList {
+ guard let name = parameter.name else {
+ continue
+ }
+ print(" \(name)")
+
+ if name.contains(filter) {
+ matchingAMIs.append(parameter)
+ }
+ }
+ } catch {
+ return matchingAMIs
+ }
+
+ return matchingAMIs
+ }
+
+ // snippet-start:[swift.ec2.DescribeInstanceTypes]
+ /// Return a list of instance types matching the specified architecture
+ /// and instance sizes.
+ ///
+ /// - Parameters:
+ /// - architecture: The architecture of the instance types to return, as
+ /// a member of `EC2ClientTypes.ArchitectureValues`.
+ /// - sizes: An array of one or more strings identifying sizes of
+ /// instance type to accept.
+ ///
+ /// - Returns: An array of `EC2ClientTypes.InstanceTypeInfo` records
+ /// describing the instance types matching the given requirements.
+ func getMatchingInstanceTypes(architecture: EC2ClientTypes.ArchitectureValues = EC2ClientTypes.ArchitectureValues.x8664,
+ sizes: [String] = ["*.micro", "*.small"]) async
+ -> [EC2ClientTypes.InstanceTypeInfo] {
+ var instanceTypes: [EC2ClientTypes.InstanceTypeInfo] = []
+
+ let archFilter = EC2ClientTypes.Filter(
+ name: "processor-info.supported-architecture",
+ values: [architecture.rawValue]
+ )
+ let sizeFilter = EC2ClientTypes.Filter(
+ name: "instance-type",
+ values: sizes
+ )
+
+ do {
+ let pages = ec2Client.describeInstanceTypesPaginated(
+ input: DescribeInstanceTypesInput(
+ filters: [archFilter, sizeFilter]
+ )
+ )
+
+ for try await page in pages {
+ guard let types = page.instanceTypes else {
+ return []
+ }
+
+ instanceTypes += types
+ }
+ } catch {
+ print("*** Error getting image types: \(error.localizedDescription)")
+ return []
+ }
+
+ return instanceTypes
+ }
+ // snippet-end:[swift.ec2.DescribeInstanceTypes]
+
+ /// Get the latest information about the specified instance and output it
+ /// to the screen, returning the instance details to the caller.
+ ///
+ /// - Parameters:
+ /// - instanceId: The ID of the instance to provide details about.
+ /// - stateFilter: The state to require the instance to be in.
+ ///
+ /// - Returns: The instance's details as an `EC2ClientTypes.Instance` object.
+ func describeInstance(instanceId: String,
+ stateFilter: EC2ClientTypes.InstanceStateName? = EC2ClientTypes.InstanceStateName.running) async
+ -> EC2ClientTypes.Instance? {
+ do {
+ let pages = ec2Client.describeInstancesPaginated(
+ input: DescribeInstancesInput(
+ instanceIds: [instanceId]
+ )
+ )
+
+ for try await page in pages {
+ guard let reservations = page.reservations else {
+ continue
+ }
+
+ for reservation in reservations {
+ guard let instances = reservation.instances else {
+ continue
+ }
+
+ for instance in instances {
+ guard let state = instance.state else {
+ print("*** Instance is missing its state...")
+ continue
+ }
+ let instanceState = state.name
+
+ if stateFilter != nil && (instanceState != stateFilter) {
+ continue
+ }
+
+ let instanceTypeName: String
+ if instance.instanceType == nil {
+ instanceTypeName = ""
+ } else {
+ instanceTypeName = instance.instanceType?.rawValue ?? ""
+ }
+
+ let instanceStateName: String
+ if instanceState == nil {
+ instanceStateName = ""
+ } else {
+ instanceStateName = instanceState?.rawValue ?? ""
+ }
+
+ print("""
+ Instance: \(instance.instanceId ?? "")
+ • Image ID: \(instance.imageId ?? "")
+ • Instance type: \(instanceTypeName)
+ • Key name: \(instance.keyName ?? "")
+ • VPC ID: \(instance.vpcId ?? "")
+ • Public IP: \(instance.publicIpAddress ?? "N/A")
+ • State: \(instanceStateName)
+ """)
+
+ return instance
+ }
+ }
+ }
+ } catch {
+ print("*** Error retrieving instance information to display: \(error.localizedDescription)")
+ return nil
+ }
+
+ return nil
+ }
+
+ // snippet-start:[swift.ec2.StopInstances]
+ // snippet-start:[swift.ec2.WaitUntilInstanceStopped]
+ /// Stop the specified instance.
+ ///
+ /// - Parameters:
+ /// - instanceId: The ID of the instance to stop.
+ /// - waitUntilStopped: If `true`, execution waits until the instance
+ /// has stopped. Otherwise, execution continues and the instance stops
+ /// asynchronously.
+ ///
+ /// - Returns: `true` if the image is successfully stopped (or is left to
+ /// stop asynchronously). `false` if the instance doesn't stop.
+ func stopInstance(instanceId: String, waitUntilStopped: Bool = false) async -> Bool {
+ let instanceList = [instanceId]
+
+ do {
+ _ = try await ec2Client.stopInstances(
+ input: StopInstancesInput(
+ instanceIds: instanceList
+ )
+ )
+
+ if waitUntilStopped {
+ print("Waiting for the instance to stop. Please be patient!")
+
+ let waitOptions = WaiterOptions(maxWaitTime: 600)
+ let output = try await ec2Client.waitUntilInstanceStopped(
+ options: waitOptions,
+ input: DescribeInstancesInput(
+ instanceIds: instanceList
+ )
+ )
+
+ switch output.result {
+ case .success:
+ return true
+ case .failure:
+ return false
+ }
+ } else {
+ return true
+ }
+ } catch {
+ print("*** Unable to stop the instance: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.WaitUntilInstanceStopped]
+ // snippet-end:[swift.ec2.StopInstances]
+
+ // snippet-start:[swift.ec2.StartInstances]
+ // snippet-start:[swift.ec2.WaitUntilInstanceRunning]
+ /// Start the specified instance.
+ ///
+ /// - Parameters:
+ /// - instanceId: The ID of the instance to start.
+ /// - waitUntilStarted: If `true`, execution waits until the instance
+ /// has started. Otherwise, execution continues and the instance starts
+ /// asynchronously.
+ ///
+ /// - Returns: `true` if the image is successfully started (or is left to
+ /// start asynchronously). `false` if the instance doesn't start.
+ func startInstance(instanceId: String, waitUntilStarted: Bool = false) async -> Bool {
+ let instanceList = [instanceId]
+
+ do {
+ _ = try await ec2Client.startInstances(
+ input: StartInstancesInput(
+ instanceIds: instanceList
+ )
+ )
+
+ if waitUntilStarted {
+ print("Waiting for the instance to start...")
+
+ let waitOptions = WaiterOptions(maxWaitTime: 60.0)
+ let output = try await ec2Client.waitUntilInstanceRunning(
+ options: waitOptions,
+ input: DescribeInstancesInput(
+ instanceIds: instanceList
+ )
+ )
+ switch output.result {
+ case .success:
+ return true
+ case .failure:
+ return false
+ }
+ } else {
+ return true
+ }
+ } catch {
+ print("*** Unable to start the instance: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.WaitUntilInstanceRunning]
+ // snippet-end:[swift.ec2.StartInstances]
+
+ // snippet-start:[swift.ec2.TerminateInstances]
+ // snippet-start:[swift.ec2.WaitUntilInstanceTerminated]
+ /// Terminate the specified instance.
+ ///
+ /// - Parameters:
+ /// - instanceId: The instance to terminate.
+ /// - waitUntilTerminated: Whether or not to wait until the instance is
+ /// terminated before returning.
+ ///
+ /// - Returns: `true` if terminated successfully. `false` if not or if an
+ /// error occurs.
+ func terminateInstance(instanceId: String, waitUntilTerminated: Bool = false) async -> Bool {
+ let instanceList = [instanceId]
+
+ do {
+ _ = try await ec2Client.terminateInstances(
+ input: TerminateInstancesInput(
+ instanceIds: instanceList
+ )
+ )
+
+ if waitUntilTerminated {
+ print("Waiting for the instance to terminate...")
+
+ let waitOptions = WaiterOptions(maxWaitTime: 600.0)
+ let output = try await ec2Client.waitUntilInstanceTerminated(
+ options: waitOptions,
+ input: DescribeInstancesInput(
+ instanceIds: instanceList
+ )
+ )
+
+ switch output.result {
+ case .success:
+ return true
+ case .failure:
+ return false
+ }
+ } else {
+ return true
+ }
+ } catch {
+ print("*** Unable to terminate the instance: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.WaitUntilInstanceTerminated]
+ // snippet-end:[swift.ec2.TerminateInstances]
+
+ // snippet-start:[swift.ec2.DescribeImages]
+ /// Return an array of `EC2ClientTypes.Image` objects describing all of
+ /// the images in the specified array.
+ ///
+ /// - Parameter idList: A list of image ID strings indicating the images
+ /// to return details about.
+ ///
+ /// - Returns: An array of the images.
+ func describeImages(_ idList: [String]) async -> [EC2ClientTypes.Image] {
+ do {
+ let output = try await ec2Client.describeImages(
+ input: DescribeImagesInput(
+ imageIds: idList
+ )
+ )
+
+ guard let images = output.images else {
+ print("*** No images found.")
+ return []
+ }
+
+ for image in images {
+ guard let id = image.imageId else {
+ continue
+ }
+ print(" \(id): \(image.description ?? "")")
+ }
+
+ return images
+ } catch {
+ print("*** Error getting image descriptions: \(error.localizedDescription)")
+ return []
+ }
+ }
+ // snippet-end:[swift.ec2.DescribeImages]
+
+ // snippet-start:[swift.ec2.RunInstances]
+ /// Create and return a new EC2 instance.
+ ///
+ /// - Parameters:
+ /// - imageId: The image ID of the AMI to use when creating the instance.
+ /// - instanceType: The type of instance to create.
+ /// - keyPairName: The RSA key pair's name to use to secure the instance.
+ /// - securityGroups: The security group or groups to add the instance
+ /// to.
+ ///
+ /// - Returns: The EC2 instance as an `EC2ClientTypes.Instance` object.
+ func runInstance(imageId: String, instanceType: EC2ClientTypes.InstanceType,
+ keyPairName: String, securityGroups: [String]?) async -> EC2ClientTypes.Instance? {
+ do {
+ let output = try await ec2Client.runInstances(
+ input: RunInstancesInput(
+ imageId: imageId,
+ instanceType: instanceType,
+ keyName: keyPairName,
+ maxCount: 1,
+ minCount: 1,
+ securityGroupIds: securityGroups
+ )
+ )
+
+ guard let instances = output.instances else {
+ print("*** Unable to create the instance.")
+ return nil
+ }
+
+ return instances[0]
+ } catch {
+ print("*** Error creating the instance: \(error.localizedDescription)")
+ return nil
+ }
+ }
+ // snippet-end:[swift.ec2.RunInstances]
+
+ // snippet-start:[swift.ec2.getMyIPAddress]
+ /// Return the device's external IP address.
+ ///
+ /// - Returns: A string containing the device's IP address.
+ func getMyIPAddress() -> String? {
+ guard let url = URL(string: "http://checkip.amazonaws.com") else {
+ print("Couldn't create the URL")
+ return nil
+ }
+
+ do {
+ print("Getting the IP address...")
+ return try String(contentsOf: url, encoding: String.Encoding.utf8).trim()
+ } catch {
+ print("*** Unable to get your public IP address.")
+ return nil
+ }
+ }
+ // snippet-end:[swift.ec2.getMyIPAddress]
+
+ // snippet-start:[swift.ec2.CreateSecurityGroup]
+ /// Create a new security group.
+ ///
+ /// - Parameters:
+ /// - groupName: The name of the group to create.
+ /// - groupDescription: A description of the new security group.
+ ///
+ /// - Returns: The ID string of the new security group.
+ func createSecurityGroup(name groupName: String, description groupDescription: String) async -> String? {
+ do {
+ let output = try await ec2Client.createSecurityGroup(
+ input: CreateSecurityGroupInput(
+ description: groupDescription,
+ groupName: groupName
+ )
+ )
+
+ return output.groupId
+ } catch {
+ print("*** Error creating the security group: \(error.localizedDescription)")
+ return nil
+ }
+ }
+ // snippet-end:[swift.ec2.CreateSecurityGroup]
+
+ // snippet-start:[swift.ec2.AuthorizeSecurityGroupIngress]
+ /// Authorize ingress of connections for the security group.
+ ///
+ /// - Parameters:
+ /// - groupId: The group ID of the security group to authorize access for.
+ /// - ipAddress: The IP address of the device to grant access to.
+ ///
+ /// - Returns: `true` if access is successfully granted; otherwise `false`.
+ func authorizeSecurityGroupIngress(groupId: String, ipAddress: String) async -> Bool {
+ let ipRange = EC2ClientTypes.IpRange(cidrIp: "\(ipAddress)/0")
+ let httpPermission = EC2ClientTypes.IpPermission(
+ fromPort: 80,
+ ipProtocol: "tcp",
+ ipRanges: [ipRange],
+ toPort: 80
+ )
+
+ let sshPermission = EC2ClientTypes.IpPermission(
+ fromPort: 22,
+ ipProtocol: "tcp",
+ ipRanges: [ipRange],
+ toPort: 22
+ )
+
+ do {
+ _ = try await ec2Client.authorizeSecurityGroupIngress(
+ input: AuthorizeSecurityGroupIngressInput(
+ groupId: groupId,
+ ipPermissions: [httpPermission, sshPermission]
+ )
+ )
+
+ return true
+ } catch {
+ print("*** Error authorizing ingress for the security group: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.AuthorizeSecurityGroupIngress]
+
+ // snippet-start:[swift.ec2.DescribeSecurityGroups]
+ func describeSecurityGroups(groupId: String) async -> Bool {
+ do {
+ let output = try await ec2Client.describeSecurityGroups(
+ input: DescribeSecurityGroupsInput(
+ groupIds: [groupId]
+ )
+ )
+
+ guard let securityGroups = output.securityGroups else {
+ print("No security groups found.")
+ return true
+ }
+
+ for group in securityGroups {
+ print("Group \(group.groupId ?? "") found with VPC \(group.vpcId ?? "")")
+ }
+ return true
+ } catch {
+ print("*** Error getting security group details: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.DescribeSecurityGroups]
+
+ // snippet-start:[swift.ec2.DeleteSecurityGroup]
+ /// Delete a security group.
+ ///
+ /// - Parameter groupId: The ID of the security group to delete.
+ ///
+ /// - Returns: `true` on successful deletion; `false` on error.
+ func deleteSecurityGroup(groupId: String) async -> Bool {
+ do {
+ _ = try await ec2Client.deleteSecurityGroup(
+ input: DeleteSecurityGroupInput(
+ groupId: groupId
+ )
+ )
+
+ return true
+ } catch {
+ print("*** Error deleting the security group: \(error.localizedDescription)")
+ return false
+ }
+ }
+ // snippet-end:[swift.ec2.DeleteSecurityGroup]
+
+ // snippet-start:[swift.ec2.AllocateAddress]
+ /// Allocate an Elastic IP address.
+ ///
+ /// - Returns: A string containing the ID of the Elastic IP.
+ func allocateAddress() async -> String? {
+ do {
+ let output = try await ec2Client.allocateAddress(
+ input: AllocateAddressInput(
+ domain: EC2ClientTypes.DomainType.vpc
+ )
+ )
+
+ guard let allocationId = output.allocationId else {
+ return nil
+ }
+
+ return allocationId
+ } catch {
+ print("*** Unable to allocate the IP address: \(error.localizedDescription)")
+ return nil
+ }
+ }
+ // snippet-end:[swift.ec2.AllocateAddress]
+
+ // snippet-start:[swift.ec2.AssociateAddress]
+ /// Associate the specified allocated Elastic IP to a given instance.
+ ///
+ /// - Parameters:
+ /// - instanceId: The instance to associate the Elastic IP with.
+ /// - allocationId: The ID of the allocated Elastic IP to associate with
+ /// the instance.
+ ///
+ /// - Returns: The association ID of the association.
+ func associateAddress(instanceId: String?, allocationId: String?) async -> String? {
+ do {
+ let output = try await ec2Client.associateAddress(
+ input: AssociateAddressInput(
+ allocationId: allocationId,
+ instanceId: instanceId
+ )
+ )
+
+ return output.associationId
+ } catch {
+ print("*** Unable to associate the IP address: \(error.localizedDescription)")
+ return nil
+ }
+ }
+ // snippet-end:[swift.ec2.AssociateAddress]
+
+ // snippet-start:[swift.ec2.DisassociateAddress]
+ /// Disassociate an Elastic IP.
+ ///
+ /// - Parameter associationId: The ID of the association to end.
+ func disassociateAddress(associationId: String?) async {
+ do {
+ _ = try await ec2Client.disassociateAddress(
+ input: DisassociateAddressInput(
+ associationId: associationId
+ )
+ )
+ } catch {
+ print("*** Unable to disassociate the IP address: \(error.localizedDescription)")
+ }
+ }
+ // snippet-end:[swift.ec2.DisassociateAddress]
+
+ // snippet-start:[swift.ec2.ReleaseAddress]
+ /// Release an allocated Elastic IP.
+ ///
+ /// - Parameter allocationId: The allocation ID of the Elastic IP to
+ /// release.
+ func releaseAddress(allocationId: String?) async {
+ do {
+ _ = try await ec2Client.releaseAddress(
+ input: ReleaseAddressInput(
+ allocationId: allocationId
+ )
+ )
+ } catch {
+ print("*** Unable to release the IP address: \(error.localizedDescription)")
+ }
+ }
+ // snippet-end:[swift.ec2.ReleaseAddress]
+
+ /// Generate and return a unique file name that begins with the specified
+ /// string.
+ ///
+ /// - Parameters:
+ /// - prefix: Text to use at the beginning of the returned name.
+ ///
+ /// - Returns: A string containing a unique filename that begins with the
+ /// specified `prefix`.
+ ///
+ /// The returned name uses a random number between 1 million and 1 billion to
+ /// provide reasonable certainty of uniqueness for the purposes of this
+ /// example.
+ func tempName(prefix: String) -> String {
+ return "\(prefix)-\(Int.random(in: 1000000..<1000000000))"
+ }
+}
+
+/// The program's asynchronous entry point.
+@main
+struct Main {
+ static func main() async {
+ let args = Array(CommandLine.arguments.dropFirst())
+
+ do {
+ let command = try ExampleCommand.parse(args)
+ try await command.runAsync()
+ } catch {
+ ExampleCommand.exit(withError: error)
+ }
+ }
+}
+// snippet-end:[swift.ec2.scenario]