mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-11 22:49:54 +00:00
Teleport ECS Terraform module (#68481)
Adds a Terraform module that deploys a Teleport instance to AWS ECS.
This commit is contained in:
@@ -61,6 +61,7 @@ release: build
|
||||
|
||||
PLUGIN_TYPE := terraform-module
|
||||
# This list controls which modules are actually published.
|
||||
# TODO(gavin): publish teleport/container-service/aws after updating the promote-terraform cmd in teleport.e to handle hyphens in module names
|
||||
PUBLISHED_TF_MODULES := \
|
||||
teleport/discovery/aws \
|
||||
teleport/discovery/azure \
|
||||
@@ -99,6 +100,13 @@ tfclean:
|
||||
-or -name 'terraform.tfstate*' \
|
||||
\) -exec rm -rf {} \+
|
||||
|
||||
#
|
||||
# update-version updates Terraform source code that depends on the current release version.
|
||||
#
|
||||
.PHONY: update-version
|
||||
update-version:
|
||||
./gen/update-version.sh $(VERSION)
|
||||
|
||||
.PHONY: create-module
|
||||
create-module:
|
||||
ifndef MODULE
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<EOF >&2
|
||||
Usage: update-version.sh <version>
|
||||
|
||||
Updates source to match the current version.
|
||||
|
||||
Examples:
|
||||
version.sh 19.0.0
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
VERSION="${1}"
|
||||
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
usage
|
||||
echo "error: <version> must be non-empty" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cat > "${script_dir}"/../teleport/container-service/aws/teleport_version_variable.tf <<EOF
|
||||
variable "teleport_version" {
|
||||
default = "${VERSION}"
|
||||
description = <<EOD
|
||||
The version of Teleport to deploy.
|
||||
Generally, the version of Teleport should be controlled by using the appropriate version of this module.
|
||||
This variable is intended for development usage.
|
||||
EOD
|
||||
type = string
|
||||
}
|
||||
EOF
|
||||
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,103 @@
|
||||
## Teleport ECS Deployment
|
||||
|
||||
This module deploys a Teleport service to AWS ECS.
|
||||
|
||||
## Prerequisites
|
||||
<!-- lint ignore absolute-docs-links -->
|
||||
- [Configure Teleport Terraform Provider](https://goteleport.com/docs/configuration/terraform-provider/)
|
||||
- [Configure AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)
|
||||
|
||||
## Examples
|
||||
|
||||
Refer to the [examples](./examples) for example usage of this module.
|
||||
|
||||
## How to get help
|
||||
|
||||
If you're having trouble, check out our [GitHub Discussions](https://github.com/gravitational/teleport/discussions).
|
||||
|
||||
For bugs related to this code, please [open an issue](https://github.com/gravitational/teleport/issues/new/choose).
|
||||
|
||||
<!-- BEGIN_TF_DOCS -->
|
||||
## Requirements
|
||||
|
||||
| Name | Version |
|
||||
| ---- | ------- |
|
||||
| terraform | >= 1.5.7 |
|
||||
| aws | >= 6.0 |
|
||||
| http | >= 3.0 |
|
||||
|
||||
## Providers
|
||||
|
||||
| Name | Version |
|
||||
| ---- | ------- |
|
||||
| aws | >= 6.0 |
|
||||
| http | >= 3.0 |
|
||||
|
||||
## Modules
|
||||
|
||||
No modules.
|
||||
|
||||
## Resources
|
||||
|
||||
| Name | Type |
|
||||
| ---- | ---- |
|
||||
| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
|
||||
| [aws_ecs_cluster.teleport_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |
|
||||
| [aws_ecs_service.teleport_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource |
|
||||
| [aws_ecs_task_definition.teleport_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |
|
||||
| [aws_iam_role.ecs_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
|
||||
| [aws_iam_role.ecs_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
|
||||
| [aws_iam_role_policy.ecs_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
|
||||
| [aws_iam_role_policy.ecs_task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
|
||||
| [aws_security_group.teleport_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
|
||||
| [aws_vpc_security_group_egress_rule.allow_all_outbound_from_teleport_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
|
||||
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
|
||||
| [aws_iam_policy_document.ecs_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
|
||||
| [aws_iam_policy_document.ecs_execution_trust](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
|
||||
| [aws_iam_policy_document.ecs_task_trust](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
|
||||
| [aws_partition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
|
||||
| [aws_region.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
|
||||
| [aws_subnet.teleport_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
|
||||
| [http_http.managed_updates](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source |
|
||||
|
||||
## Inputs
|
||||
|
||||
| Name | Description | Type | Default | Required |
|
||||
| ---- | ----------- | ---- | ------- | :------: |
|
||||
| apply\_aws\_tags | Additional AWS tags to apply to all created AWS resources. | `map(string)` | `{}` | no |
|
||||
| assign\_public\_ip | Whether to assign public IP addresses to Teleport agent ECS tasks. If this is set to true, then var.ecs\_service\_subnets must be public subnets (route to an internet gateway). Otherwise, var.ecs\_service\_subnets must be private subnets (route to a NAT gateway). | `bool` | `false` | no |
|
||||
| create | Toggle creation of all resources. | `bool` | `true` | no |
|
||||
| create\_security\_group | Whether to create a security group for the Teleport agent ECS tasks. | `bool` | `true` | no |
|
||||
| ecs\_cluster\_name | Name of the ECS cluster. | `string` | `"teleport"` | no |
|
||||
| ecs\_service\_name | Name of the ECS service. | `string` | `"teleport-service"` | no |
|
||||
| ecs\_service\_subnets | Subnet IDs where the Teleport agent will be deployed. If var.assign\_public\_ip is true, then all of these subnets must be public subnets (route to an internet gateway). If var.assign\_public\_ip is false, then all of these subnets must be private subnets (route to a NAT gateway). | `list(string)` | n/a | yes |
|
||||
| ecs\_task\_cloudwatch\_log\_group\_name | Name for the ECS task CloudWatch log group. | `string` | `"ecs-teleport"` | no |
|
||||
| ecs\_task\_cloudwatch\_log\_group\_region | AWS region for the ECS task CloudWatch log group. Defaults to the AWS provider region. | `string` | `null` | no |
|
||||
| ecs\_task\_cloudwatch\_log\_group\_retention\_days | Number of days to retain logs in the ECS task CloudWatch log group. | `number` | `30` | no |
|
||||
| ecs\_task\_cloudwatch\_log\_group\_skip\_destroy | Whether to preserve the ECS task CloudWatch log group when destroying module resources. Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state. | `bool` | `false` | no |
|
||||
| ecs\_task\_cpu | Number of cpu units used by the ECS task. | `string` | `"2048"` | no |
|
||||
| ecs\_task\_desired\_count | Desired number of Teleport ECS tasks to run. | `number` | `2` | no |
|
||||
| ecs\_task\_force\_new\_deployment | Set to true to force the ECS service to redeploy tasks without configuration changes. | `bool` | `false` | no |
|
||||
| ecs\_task\_memory | Amount (in MiB) of memory used by the ECS task. | `string` | `"4096"` | no |
|
||||
| ecs\_task\_name | Name of the ECS task. | `string` | `"teleport-agent"` | no |
|
||||
| ecs\_task\_role\_inline\_policy | Optional JSON policy document to attach inline to the ECS task IAM role. | `string` | `null` | no |
|
||||
| environment\_vars | Environment variables to set on the Teleport ECS container. | `map(string)` | `{}` | no |
|
||||
| managed\_updates\_enabled | Whether to resolve the Teleport container version from the configured Managed Updates endpoint when applying this module. | `bool` | `false` | no |
|
||||
| managed\_updates\_group | Update group to query through the v2 Managed Updates endpoint. | `string` | `"default"` | no |
|
||||
| security\_group\_ids | Additional security group IDs to attach to the Teleport agent ECS tasks. | `list(string)` | `[]` | no |
|
||||
| teleport\_config | Teleport configuration. Write the configuration using native Terraform syntax. Warning: sensitive data, such as static join tokens, is visible to anyone who can read the task definition. | `any` | n/a | yes |
|
||||
| teleport\_container\_image | Container image used for Teleport ECS tasks. | `string` | `"public.ecr.aws/gravitational/teleport-ent-distroless"` | no |
|
||||
| teleport\_version | The version of Teleport to deploy. Generally, the version of Teleport should be controlled by using the appropriate version of this module. This variable is intended for development usage. | `string` | `"19.0.0-prealpha.2"` | no |
|
||||
| vpc\_id | VPC ID where the Teleport agent will be deployed. | `string` | n/a | yes |
|
||||
|
||||
## Outputs
|
||||
|
||||
| Name | Description |
|
||||
| ---- | ----------- |
|
||||
| ecs\_execution\_role\_arn | The ARN of the execution IAM role for the Teleport ECS task. |
|
||||
| ecs\_execution\_role\_name | The name of the execution IAM role for the Teleport ECS task. |
|
||||
| ecs\_task\_role\_arn | The ARN of the task IAM role for the Teleport agent ECS task. |
|
||||
| ecs\_task\_role\_name | The name of the task IAM role for the Teleport agent ECS task. |
|
||||
| security\_group\_id | Security group ID created for the Teleport agent ECS service. |
|
||||
| teleport\_provision\_token\_allow\_aws\_arn | A value that can be used with a Teleport IAM join token to allow the ECS cluster to join the Teleport cluster using its IAM credentials. |
|
||||
<!-- END_TF_DOCS -->
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
resource "aws_cloudwatch_log_group" "this" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
name = var.ecs_task_cloudwatch_log_group_name
|
||||
region = coalesce(
|
||||
var.ecs_task_cloudwatch_log_group_region,
|
||||
one(data.aws_region.this[*].name),
|
||||
)
|
||||
retention_in_days = var.ecs_task_cloudwatch_log_group_retention_days
|
||||
skip_destroy = var.ecs_task_cloudwatch_log_group_skip_destroy
|
||||
tags = var.apply_aws_tags
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
################################################################################
|
||||
# ECS Cluster
|
||||
################################################################################
|
||||
|
||||
resource "aws_ecs_cluster" "teleport_agent" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
name = var.ecs_cluster_name
|
||||
tags = var.apply_aws_tags
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
locals {
|
||||
create_security_group = var.create && var.create_security_group
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# ECS Service
|
||||
################################################################################
|
||||
|
||||
resource "aws_ecs_service" "teleport_agent" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
# Roles are usable by ECS only once their inline policies are
|
||||
# attached; the role-ARN reference alone does not order this.
|
||||
depends_on = [
|
||||
aws_iam_role_policy.ecs_execution,
|
||||
aws_iam_role_policy.ecs_task,
|
||||
]
|
||||
|
||||
cluster = one(aws_ecs_cluster.teleport_agent[*].id)
|
||||
desired_count = var.ecs_task_desired_count
|
||||
force_new_deployment = var.ecs_task_force_new_deployment
|
||||
launch_type = "FARGATE"
|
||||
name = var.ecs_service_name
|
||||
tags = var.apply_aws_tags
|
||||
task_definition = one(aws_ecs_task_definition.teleport_agent[*].arn)
|
||||
|
||||
network_configuration {
|
||||
# Public subnets: tasks need a public IP for IGW egress.
|
||||
# Private subnets: tasks egress via the NAT route instead.
|
||||
assign_public_ip = var.assign_public_ip
|
||||
security_groups = concat(aws_security_group.teleport_agent[*].id, var.security_group_ids)
|
||||
subnets = var.ecs_service_subnets
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = var.create_security_group || length(var.security_group_ids) > 0
|
||||
error_message = "At least one security group is required. Set var.create_security_group to true or provide security group IDs via var.security_group_ids."
|
||||
}
|
||||
|
||||
precondition {
|
||||
condition = length(var.ecs_service_subnets) > 0
|
||||
error_message = "At least one subnet must be provided for the Teleport agent ECS deployment."
|
||||
}
|
||||
|
||||
precondition {
|
||||
condition = alltrue([
|
||||
for subnet in data.aws_subnet.teleport_agent :
|
||||
subnet.vpc_id == var.vpc_id
|
||||
])
|
||||
error_message = "Each Teleport agent subnet must belong to the configured vpc_id."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "teleport_agent" {
|
||||
count = local.create_security_group ? 1 : 0
|
||||
|
||||
description = "Teleport agent security group for ${var.vpc_id}."
|
||||
name_prefix = var.ecs_service_name
|
||||
tags = var.apply_aws_tags
|
||||
vpc_id = var.vpc_id
|
||||
}
|
||||
|
||||
resource "aws_vpc_security_group_egress_rule" "allow_all_outbound_from_teleport_agent" {
|
||||
count = local.create_security_group ? 1 : 0
|
||||
|
||||
cidr_ipv4 = "0.0.0.0/0"
|
||||
ip_protocol = "-1"
|
||||
security_group_id = one(aws_security_group.teleport_agent[*].id)
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
################################################################################
|
||||
# Task definition
|
||||
################################################################################
|
||||
|
||||
locals {
|
||||
managed_updates_proxy_addr = try(var.teleport_config.teleport.proxy_server, "")
|
||||
managed_updates_version = (
|
||||
length(data.http.managed_updates) == 1
|
||||
? jsondecode(data.http.managed_updates[0].response_body).auto_update.agent_version
|
||||
: null
|
||||
)
|
||||
teleport_version = trimprefix(
|
||||
trimspace(
|
||||
coalesce(
|
||||
local.managed_updates_version,
|
||||
var.teleport_version
|
||||
),
|
||||
),
|
||||
"v"
|
||||
)
|
||||
}
|
||||
|
||||
resource "aws_ecs_task_definition" "teleport_agent" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
container_definitions = jsonencode([
|
||||
{
|
||||
command = [
|
||||
# rewrite SIGTERM (15) to SIGQUIT (3) so ECS stop signal triggers graceful Teleport shutdown
|
||||
"--rewrite",
|
||||
"15:3",
|
||||
"--",
|
||||
"teleport",
|
||||
"start",
|
||||
"--config-string",
|
||||
base64encode(yamlencode(var.teleport_config)),
|
||||
]
|
||||
entryPoint = ["/usr/bin/dumb-init"]
|
||||
environment = [
|
||||
for name in sort(keys(var.environment_vars)) : {
|
||||
name = name
|
||||
value = var.environment_vars[name]
|
||||
}
|
||||
]
|
||||
image = "${var.teleport_container_image}:${local.teleport_version}"
|
||||
logConfiguration = {
|
||||
logDriver = "awslogs"
|
||||
options = {
|
||||
"awslogs-group" = one(aws_cloudwatch_log_group.this[*].name)
|
||||
"awslogs-region" = one(aws_cloudwatch_log_group.this[*].region)
|
||||
"awslogs-stream-prefix" = "${var.ecs_cluster_name}-${var.ecs_service_name}"
|
||||
}
|
||||
}
|
||||
name = "teleport"
|
||||
}
|
||||
])
|
||||
cpu = var.ecs_task_cpu
|
||||
execution_role_arn = one(aws_iam_role.ecs_execution[*].arn)
|
||||
family = var.ecs_task_name
|
||||
memory = var.ecs_task_memory
|
||||
network_mode = "awsvpc"
|
||||
requires_compatibilities = ["FARGATE"]
|
||||
tags = var.apply_aws_tags
|
||||
task_role_arn = one(aws_iam_role.ecs_task[*].arn)
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = !var.managed_updates_enabled || local.managed_updates_proxy_addr != ""
|
||||
error_message = "Managed Updates require teleport.proxy_server in teleport_config."
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
################################################################################
|
||||
# Execution role
|
||||
################################################################################
|
||||
|
||||
resource "aws_iam_role" "ecs_execution" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
assume_role_policy = one(data.aws_iam_policy_document.ecs_execution_trust[*].json)
|
||||
description = "Execution role used by the Teleport ECS agent task."
|
||||
name_prefix = "${var.ecs_cluster_name}-exec"
|
||||
tags = var.apply_aws_tags
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "ecs_execution_trust" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
statement {
|
||||
actions = ["sts:AssumeRole"]
|
||||
|
||||
effect = "Allow"
|
||||
|
||||
condition {
|
||||
test = "StringEquals"
|
||||
values = [one(data.aws_caller_identity.this[*].account_id)]
|
||||
variable = "aws:SourceAccount"
|
||||
}
|
||||
|
||||
condition {
|
||||
test = "ArnLike"
|
||||
values = [
|
||||
format(
|
||||
"arn:%s:ecs:%s:%s:*",
|
||||
one(data.aws_partition.this[*].partition),
|
||||
one(data.aws_region.this[*].name),
|
||||
one(data.aws_caller_identity.this[*].account_id),
|
||||
),
|
||||
]
|
||||
variable = "aws:SourceArn"
|
||||
}
|
||||
|
||||
principals {
|
||||
identifiers = ["ecs-tasks.amazonaws.com"]
|
||||
type = "Service"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "ecs_execution" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
name = "ecs-execution"
|
||||
policy = one(data.aws_iam_policy_document.ecs_execution[*].json)
|
||||
role = one(aws_iam_role.ecs_execution[*].id)
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "ecs_execution" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
statement {
|
||||
actions = [
|
||||
"logs:CreateLogStream",
|
||||
"logs:PutLogEvents",
|
||||
]
|
||||
effect = "Allow"
|
||||
resources = [
|
||||
one(aws_cloudwatch_log_group.this[*].arn),
|
||||
"${one(aws_cloudwatch_log_group.this[*].arn)}:*",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
################################################################################
|
||||
# Task role
|
||||
################################################################################
|
||||
|
||||
resource "aws_iam_role" "ecs_task" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
assume_role_policy = one(data.aws_iam_policy_document.ecs_task_trust[*].json)
|
||||
description = "Task role used by the Teleport ECS agent task."
|
||||
name_prefix = "${var.ecs_cluster_name}-task"
|
||||
tags = var.apply_aws_tags
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "ecs_task" {
|
||||
count = var.create && var.ecs_task_role_inline_policy != null ? 1 : 0
|
||||
|
||||
name = "ecs-task"
|
||||
policy = var.ecs_task_role_inline_policy
|
||||
role = one(aws_iam_role.ecs_task[*].id)
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "ecs_task_trust" {
|
||||
count = var.create ? 1 : 0
|
||||
|
||||
statement {
|
||||
actions = ["sts:AssumeRole"]
|
||||
|
||||
effect = "Allow"
|
||||
|
||||
condition {
|
||||
test = "StringEquals"
|
||||
values = [one(data.aws_caller_identity.this[*].account_id)]
|
||||
variable = "aws:SourceAccount"
|
||||
}
|
||||
|
||||
condition {
|
||||
test = "ArnLike"
|
||||
values = [
|
||||
format(
|
||||
"arn:%s:ecs:%s:%s:*",
|
||||
one(data.aws_partition.this[*].partition),
|
||||
one(data.aws_region.this[*].name),
|
||||
one(data.aws_caller_identity.this[*].account_id),
|
||||
),
|
||||
]
|
||||
variable = "aws:SourceArn"
|
||||
}
|
||||
|
||||
principals {
|
||||
identifiers = ["ecs-tasks.amazonaws.com"]
|
||||
type = "Service"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
data "aws_caller_identity" "this" {
|
||||
count = var.create ? 1 : 0
|
||||
}
|
||||
|
||||
data "aws_region" "this" {
|
||||
count = var.create ? 1 : 0
|
||||
}
|
||||
|
||||
data "aws_partition" "this" {
|
||||
count = var.create ? 1 : 0
|
||||
}
|
||||
|
||||
data "aws_subnet" "teleport_agent" {
|
||||
count = var.create ? length(var.ecs_service_subnets) : 0
|
||||
|
||||
id = var.ecs_service_subnets[count.index]
|
||||
}
|
||||
|
||||
data "http" "managed_updates" {
|
||||
count = var.create && var.managed_updates_enabled ? 1 : 0
|
||||
|
||||
url = format(
|
||||
"https://%s/webapi/find?group=%s",
|
||||
local.managed_updates_proxy_addr,
|
||||
urlencode(coalesce(var.managed_updates_group, "default")),
|
||||
)
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = !var.managed_updates_enabled || local.managed_updates_proxy_addr != ""
|
||||
error_message = "Managed Updates require teleport.proxy_server in teleport_config."
|
||||
}
|
||||
|
||||
postcondition {
|
||||
condition = self.status_code == 200 && can(
|
||||
regex(
|
||||
"^v?[0-9]+\\.[0-9]+\\.[0-9]+.*",
|
||||
trimspace(jsondecode(self.response_body).auto_update.agent_version),
|
||||
)
|
||||
)
|
||||
error_message = <<EOF
|
||||
Managed Updates endpoint must return HTTP 200 and a valid Teleport version.
|
||||
Ensure the cluster supports Managed Updates and the configured update group exists.
|
||||
EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
output "security_group_id" {
|
||||
description = "Security group ID created for the Teleport agent ECS service."
|
||||
value = one(aws_security_group.teleport_agent[*].id)
|
||||
}
|
||||
|
||||
output "ecs_execution_role_arn" {
|
||||
description = "The ARN of the execution IAM role for the Teleport ECS task."
|
||||
value = one(aws_iam_role.ecs_execution[*].arn)
|
||||
}
|
||||
|
||||
output "ecs_execution_role_name" {
|
||||
description = "The name of the execution IAM role for the Teleport ECS task."
|
||||
value = one(aws_iam_role.ecs_execution[*].name)
|
||||
}
|
||||
|
||||
output "ecs_task_role_arn" {
|
||||
description = "The ARN of the task IAM role for the Teleport agent ECS task."
|
||||
value = one(aws_iam_role.ecs_task[*].arn)
|
||||
}
|
||||
|
||||
output "ecs_task_role_name" {
|
||||
description = "The name of the task IAM role for the Teleport agent ECS task."
|
||||
value = one(aws_iam_role.ecs_task[*].name)
|
||||
}
|
||||
|
||||
output "teleport_provision_token_allow_aws_arn" {
|
||||
description = <<EOF
|
||||
A value that can be used with a Teleport IAM join token to allow the ECS cluster to join the Teleport cluster using its IAM credentials.
|
||||
EOF
|
||||
value = (
|
||||
var.create
|
||||
? format(
|
||||
"arn:%s:sts::%s:assumed-role/%s/*",
|
||||
one(data.aws_partition.this[*].partition),
|
||||
one(data.aws_caller_identity.this[*].account_id),
|
||||
one(aws_iam_role.ecs_task[*].name),
|
||||
)
|
||||
: null
|
||||
)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
variable "teleport_version" {
|
||||
default = "19.0.0-prealpha.2"
|
||||
description = <<EOD
|
||||
The version of Teleport to deploy.
|
||||
Generally, the version of Teleport should be controlled by using the appropriate version of this module.
|
||||
This variable is intended for development usage.
|
||||
EOD
|
||||
type = string
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
################################################################################
|
||||
# Required variables
|
||||
################################################################################
|
||||
|
||||
variable "ecs_service_subnets" {
|
||||
description = <<EOF
|
||||
Subnet IDs where the Teleport agent will be deployed.
|
||||
If var.assign_public_ip is true, then all of these subnets must be public subnets (route to an internet gateway).
|
||||
If var.assign_public_ip is false, then all of these subnets must be private subnets (route to a NAT gateway).
|
||||
EOF
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
variable "vpc_id" {
|
||||
description = "VPC ID where the Teleport agent will be deployed."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "teleport_config" {
|
||||
description = "Teleport configuration. Write the configuration using native Terraform syntax. Warning: sensitive data, such as static join tokens, is visible to anyone who can read the task definition."
|
||||
type = any
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Optional variables
|
||||
################################################################################
|
||||
|
||||
variable "apply_aws_tags" {
|
||||
default = {}
|
||||
description = "Additional AWS tags to apply to all created AWS resources."
|
||||
type = map(string)
|
||||
}
|
||||
|
||||
variable "managed_updates_enabled" {
|
||||
default = false
|
||||
description = "Whether to resolve the Teleport container version from the configured Managed Updates endpoint when applying this module."
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "managed_updates_group" {
|
||||
default = "default"
|
||||
description = "Update group to query through the v2 Managed Updates endpoint."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "assign_public_ip" {
|
||||
default = false
|
||||
description = <<EOF
|
||||
Whether to assign public IP addresses to Teleport agent ECS tasks.
|
||||
If this is set to true, then var.ecs_service_subnets must be public subnets (route to an internet gateway).
|
||||
Otherwise, var.ecs_service_subnets must be private subnets (route to a NAT gateway).
|
||||
EOF
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "create" {
|
||||
default = true
|
||||
description = "Toggle creation of all resources."
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "create_security_group" {
|
||||
default = true
|
||||
description = "Whether to create a security group for the Teleport agent ECS tasks."
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "ecs_cluster_name" {
|
||||
default = "teleport"
|
||||
description = "Name of the ECS cluster."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_service_name" {
|
||||
default = "teleport-service"
|
||||
description = "Name of the ECS service."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_task_cloudwatch_log_group_name" {
|
||||
default = "ecs-teleport"
|
||||
description = "Name for the ECS task CloudWatch log group."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_task_cloudwatch_log_group_region" {
|
||||
default = null
|
||||
description = "AWS region for the ECS task CloudWatch log group. Defaults to the AWS provider region."
|
||||
nullable = true
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_task_cloudwatch_log_group_retention_days" {
|
||||
default = 30
|
||||
description = "Number of days to retain logs in the ECS task CloudWatch log group."
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "ecs_task_cloudwatch_log_group_skip_destroy" {
|
||||
default = false
|
||||
description = <<EOF
|
||||
Whether to preserve the ECS task CloudWatch log group when destroying module resources.
|
||||
Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state.
|
||||
EOF
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "ecs_task_cpu" {
|
||||
default = "2048"
|
||||
description = "Number of cpu units used by the ECS task."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_task_desired_count" {
|
||||
default = 2
|
||||
description = "Desired number of Teleport ECS tasks to run."
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "ecs_task_force_new_deployment" {
|
||||
default = false
|
||||
description = "Set to true to force the ECS service to redeploy tasks without configuration changes."
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "ecs_task_memory" {
|
||||
default = "4096"
|
||||
description = "Amount (in MiB) of memory used by the ECS task."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_task_name" {
|
||||
default = "teleport-agent"
|
||||
description = "Name of the ECS task."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ecs_task_role_inline_policy" {
|
||||
default = null
|
||||
description = "Optional JSON policy document to attach inline to the ECS task IAM role."
|
||||
nullable = true
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "environment_vars" {
|
||||
default = {}
|
||||
description = "Environment variables to set on the Teleport ECS container."
|
||||
type = map(string)
|
||||
}
|
||||
|
||||
variable "security_group_ids" {
|
||||
default = []
|
||||
description = "Additional security group IDs to attach to the Teleport agent ECS tasks."
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
variable "teleport_container_image" {
|
||||
default = "public.ecr.aws/gravitational/teleport-ent-distroless"
|
||||
description = "Container image used for Teleport ECS tasks."
|
||||
type = string
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
terraform {
|
||||
required_version = ">= 1.5.7"
|
||||
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = ">= 6.0"
|
||||
}
|
||||
http = {
|
||||
source = "hashicorp/http"
|
||||
version = ">= 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ func init() { Gitref = \"$(GITREF)\" }\n"
|
||||
setver: validate-semver helm-version tsh-version
|
||||
GOWORK=off CGO_ENABLED=0 go -C build.assets/tooling run ./cmd/apiversion "$(VERSION)" > api/version.go
|
||||
@printf $(GITREF_GO) | gofmt > gitref.go
|
||||
$(MAKE) -C integrations/terraform-modules update-version VERSION=$(VERSION)
|
||||
|
||||
# helm-version automatically updates the versions of Helm charts to match the version set in the Makefile,
|
||||
# so that chart versions are also kept in sync when the Teleport version is updated for a release.
|
||||
|
||||
Reference in New Issue
Block a user