diff --git a/README.md b/README.md index 2e4a4757..66d34ea7 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ As of version 1.6.0, AFT collects anonymous operational metrics to help AWS impr | [ct\_home\_region](#input\_ct\_home\_region) | The region from which this module will be executed. This MUST be the same region as Control Tower is deployed. | `string` | n/a | yes | | [ct\_management\_account\_id](#input\_ct\_management\_account\_id) | Control Tower Management Account Id | `string` | n/a | yes | | [github\_enterprise\_url](#input\_github\_enterprise\_url) | GitHub enterprise URL, if GitHub Enterprise is being used | `string` | `"null"` | no | +| [gitlab\_selfmanaged\_url](#input\_gitlab\_selfmanaged\_url) | GitLab SelfManaged URL, if GitLab SelfManaged is being used | `string` | `"null"` | no | | [global\_codebuild\_timeout](#input\_global\_codebuild\_timeout) | Codebuild build timeout | `number` | `60` | no | | [global\_customizations\_repo\_branch](#input\_global\_customizations\_repo\_branch) | Branch to source global customizations repo from | `string` | `"main"` | no | | [global\_customizations\_repo\_name](#input\_global\_customizations\_repo\_name) | Repository name for the global customization files. For non-CodeCommit repos, name should be in the format of Org/Repo | `string` | `"aft-global-customizations"` | no | @@ -142,7 +143,7 @@ As of version 1.6.0, AFT collects anonymous operational metrics to help AWS impr | [terraform\_token](#input\_terraform\_token) | Terraform token for Cloud or Enterprise | `string` | `"null"` | no | | [terraform\_version](#input\_terraform\_version) | Terraform version being used for AFT | `string` | `"1.6.0"` | no | | [tf\_backend\_secondary\_region](#input\_tf\_backend\_secondary\_region) | AFT creates a backend for state tracking for its own state as well as OSS cases. The backend's primary region is the same as the AFT region, but this defines the secondary region to replicate to. | `string` | `""` | no | -| [vcs\_provider](#input\_vcs\_provider) | Customer VCS Provider - valid inputs are codecommit, bitbucket, github, or githubenterprise | `string` | `"codecommit"` | no | +| [vcs\_provider](#input\_vcs\_provider) | Customer VCS Provider - valid inputs are codecommit, bitbucket, github, githubenterprise, gitlab, or gitLab self-managed | `string` | `"codecommit"` | no | ## Outputs @@ -169,6 +170,7 @@ As of version 1.6.0, AFT collects anonymous operational metrics to help AWS impr | [ct\_home\_region](#output\_ct\_home\_region) | n/a | | [ct\_management\_account\_id](#output\_ct\_management\_account\_id) | n/a | | [github\_enterprise\_url](#output\_github\_enterprise\_url) | n/a | +| [gitlab\_selfmanaged\_url](#output\_gitlab\_selfmanaged\_url) | n/a | | [global\_customizations\_repo\_branch](#output\_global\_customizations\_repo\_branch) | n/a | | [global\_customizations\_repo\_name](#output\_global\_customizations\_repo\_name) | n/a | | [log\_archive\_account\_id](#output\_log\_archive\_account\_id) | n/a | diff --git a/VERSION b/VERSION index b50dd27d..61ce01b3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.13.1 +1.13.2 diff --git a/examples/gitlab+tf_oss/main.tf b/examples/gitlab+tf_oss/main.tf new file mode 100644 index 00000000..eb0e4aea --- /dev/null +++ b/examples/gitlab+tf_oss/main.tf @@ -0,0 +1,19 @@ +# Copyright Amazon.com, Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +module "aft" { + source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" + # Required Vars + ct_management_account_id = "111122223333" + log_archive_account_id = "444455556666" + audit_account_id = "123456789012" + aft_management_account_id = "777788889999" + ct_home_region = "us-east-1" + tf_backend_secondary_region = "us-west-2" + # VCS Vars + vcs_provider = "gitlab" + account_request_repo_name = "ExampleProject/example-repo-1" + global_customizations_repo_name = "ExampleProject/example-repo-2" + account_customizations_repo_name = "ExampleProject/example-repo-3" + account_provisioning_customizations_repo_name = "ExampleProject/example-repo-4" +} diff --git a/examples/gitlabselfmanaged+tf_cloud/main.tf b/examples/gitlabselfmanaged+tf_cloud/main.tf new file mode 100644 index 00000000..a70861aa --- /dev/null +++ b/examples/gitlabselfmanaged+tf_cloud/main.tf @@ -0,0 +1,24 @@ +# Copyright Amazon.com, Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +module "aft" { + source = "github.com/aws-ia/terraform-aws-control_tower_account_factory" + # Required Vars + ct_management_account_id = "111122223333" + log_archive_account_id = "444455556666" + audit_account_id = "123456789012" + aft_management_account_id = "777788889999" + ct_home_region = "us-east-1" + tf_backend_secondary_region = "us-west-2" + # VCS Vars + vcs_provider = "gitlabselfmanaged" + gitlab_selfmanaged_url = "https://gitlab.example.com" + account_request_repo_name = "ExampleProject/example-repo-1" + global_customizations_repo_name = "ExampleProject/example-repo-2" + account_customizations_repo_name = "ExampleProject/example-repo-3" + account_provisioning_customizations_repo_name = "ExampleProject/example-repo-4" + # TF Vars + terraform_distribution = "tfc" + terraform_token = "EXAMPLE-uoc1c1qsw7poexampleewjeno1pte3rw" + terraform_org_name = "ExampleOrg" +} diff --git a/main.tf b/main.tf index 0d2d7a67..33c89ebe 100644 --- a/main.tf +++ b/main.tf @@ -92,6 +92,7 @@ module "aft_code_repositories" { account_customizations_repo_name = var.account_customizations_repo_name global_customizations_repo_name = var.global_customizations_repo_name github_enterprise_url = var.github_enterprise_url + gitlab_selfmanaged_url = var.gitlab_selfmanaged_url vcs_provider = var.vcs_provider terraform_distribution = var.terraform_distribution account_provisioning_customizations_repo_name = var.account_provisioning_customizations_repo_name @@ -269,5 +270,6 @@ module "aft_ssm_parameters" { account_provisioning_customizations_repo_branch = var.account_provisioning_customizations_repo_branch maximum_concurrent_customizations = var.maximum_concurrent_customizations github_enterprise_url = var.github_enterprise_url + gitlab_selfmanaged_url = var.gitlab_selfmanaged_url aft_metrics_reporting = var.aft_metrics_reporting } diff --git a/modules/aft-code-repositories/codepipeline.tf b/modules/aft-code-repositories/codepipeline.tf index 3638bdb0..538b9728 100644 --- a/modules/aft-code-repositories/codepipeline.tf +++ b/modules/aft-code-repositories/codepipeline.tf @@ -140,7 +140,7 @@ resource "aws_codepipeline" "codestar_account_request" { output_artifacts = ["account-request"] configuration = { - ConnectionArn = lookup({ github = local.connection_arn.github, bitbucket = local.connection_arn.bitbucket, githubenterprise = local.connection_arn.githubenterprise }, var.vcs_provider) + ConnectionArn = lookup({ github = local.connection_arn.github, bitbucket = local.connection_arn.bitbucket, githubenterprise = local.connection_arn.githubenterprise, gitlab = local.connection_arn.gitlab, gitlabselfmanaged = local.connection_arn.gitlabselfmanaged }, var.vcs_provider) FullRepositoryId = var.account_request_repo_name BranchName = var.account_request_repo_branch DetectChanges = true @@ -271,7 +271,7 @@ resource "aws_codepipeline" "codestar_account_provisioning_customizations" { output_artifacts = ["account-provisioning-customizations"] configuration = { - ConnectionArn = lookup({ github = local.connection_arn.github, bitbucket = local.connection_arn.bitbucket, githubenterprise = local.connection_arn.githubenterprise }, var.vcs_provider) + ConnectionArn = lookup({ github = local.connection_arn.github, bitbucket = local.connection_arn.bitbucket, githubenterprise = local.connection_arn.githubenterprise, gitlab = local.connection_arn.gitlab, gitlabselfmanaged = local.connection_arn.gitlabselfmanaged }, var.vcs_provider) FullRepositoryId = var.account_provisioning_customizations_repo_name BranchName = var.account_provisioning_customizations_repo_branch DetectChanges = true diff --git a/modules/aft-code-repositories/codestar.tf b/modules/aft-code-repositories/codestar.tf index 5f461c40..683e1eb0 100644 --- a/modules/aft-code-repositories/codestar.tf +++ b/modules/aft-code-repositories/codestar.tf @@ -34,3 +34,31 @@ resource "aws_codestarconnections_host" "githubenterprise" { } } } + +resource "aws_codestarconnections_connection" "gitlab" { + count = local.vcs.is_gitlab ? 1 : 0 + name = "ct-aft-gitlab-connection" + provider_type = "GitLab" +} + +resource "aws_codestarconnections_connection" "gitlabselfmanaged" { + count = local.vcs.is_gitlab_selfmanaged ? 1 : 0 + name = "ct-aft-gitlab-selfmanaged-connection" + host_arn = aws_codestarconnections_host.gitlabselfmanaged[0].arn +} + +resource "aws_codestarconnections_host" "gitlabselfmanaged" { + count = local.vcs.is_gitlab_selfmanaged ? 1 : 0 + name = "gitlab-selfmanaged-host" + provider_endpoint = var.gitlab_selfmanaged_url + provider_type = "GitLabSelfManaged" + + dynamic "vpc_configuration" { + for_each = var.aft_enable_vpc ? [1] : [] + content { + security_group_ids = var.security_group_ids + subnet_ids = var.subnet_ids + vpc_id = var.vpc_id + } + } +} diff --git a/modules/aft-code-repositories/locals.tf b/modules/aft-code-repositories/locals.tf index 58954802..72d5f60a 100644 --- a/modules/aft-code-repositories/locals.tf +++ b/modules/aft-code-repositories/locals.tf @@ -3,15 +3,19 @@ # locals { vcs = { - is_codecommit = lower(var.vcs_provider) == "codecommit" ? true : false - is_bitbucket = lower(var.vcs_provider) == "bitbucket" ? true : false - is_github = lower(var.vcs_provider) == "github" ? true : false - is_github_enterprise = lower(var.vcs_provider) == "githubenterprise" ? true : false + is_codecommit = lower(var.vcs_provider) == "codecommit" ? true : false + is_bitbucket = lower(var.vcs_provider) == "bitbucket" ? true : false + is_github = lower(var.vcs_provider) == "github" ? true : false + is_github_enterprise = lower(var.vcs_provider) == "githubenterprise" ? true : false + is_gitlab = lower(var.vcs_provider) == "gitlab" ? true : false + is_gitlab_selfmanaged = lower(var.vcs_provider) == "gitlabselfmanaged" ? true : false } connection_arn = { - bitbucket = lower(var.vcs_provider) == "bitbucket" ? aws_codestarconnections_connection.bitbucket[0].arn : "" - github = lower(var.vcs_provider) == "github" ? aws_codestarconnections_connection.github[0].arn : "" - githubenterprise = lower(var.vcs_provider) == "githubenterprise" ? aws_codestarconnections_connection.githubenterprise[0].arn : "" - codecommit = "null" + bitbucket = lower(var.vcs_provider) == "bitbucket" ? aws_codestarconnections_connection.bitbucket[0].arn : "" + github = lower(var.vcs_provider) == "github" ? aws_codestarconnections_connection.github[0].arn : "" + githubenterprise = lower(var.vcs_provider) == "githubenterprise" ? aws_codestarconnections_connection.githubenterprise[0].arn : "" + gitlab = lower(var.vcs_provider) == "gitlab" ? aws_codestarconnections_connection.gitlab[0].arn : "" + gitlabselfmanaged = lower(var.vcs_provider) == "gitlabselfmanaged" ? aws_codestarconnections_connection.gitlabselfmanaged[0].arn : "" + codecommit = "null" } } diff --git a/modules/aft-code-repositories/variables.tf b/modules/aft-code-repositories/variables.tf index 1d662ab1..9bd1ff64 100644 --- a/modules/aft-code-repositories/variables.tf +++ b/modules/aft-code-repositories/variables.tf @@ -30,6 +30,10 @@ variable "github_enterprise_url" { type = string } +variable "gitlab_selfmanaged_url" { + type = string +} + variable "account_request_table_name" { type = string } diff --git a/modules/aft-ssm-parameters/ssm.tf b/modules/aft-ssm-parameters/ssm.tf index b8a987ad..149a26d2 100644 --- a/modules/aft-ssm-parameters/ssm.tf +++ b/modules/aft-ssm-parameters/ssm.tf @@ -346,6 +346,12 @@ resource "aws_ssm_parameter" "github_enterprise_url" { value = var.github_enterprise_url } +resource "aws_ssm_parameter" "gitlab_selfmanaged_url" { + name = "/aft/config/vcs/gitlab-selfmanaged-url" + type = "String" + value = var.gitlab_selfmanaged_url +} + resource "aws_ssm_parameter" "aft_logging_bucket_arn" { name = "/aft/account/log-archive/log_bucket_arn" type = "String" diff --git a/modules/aft-ssm-parameters/variables.tf b/modules/aft-ssm-parameters/variables.tf index 234ac2f8..fe4bb0cd 100644 --- a/modules/aft-ssm-parameters/variables.tf +++ b/modules/aft-ssm-parameters/variables.tf @@ -229,7 +229,9 @@ variable "codestar_connection_arn" { variable "github_enterprise_url" { type = string } - +variable "gitlab_selfmanaged_url" { + type = string +} variable "aft_logging_bucket_arn" { type = string } diff --git a/outputs.tf b/outputs.tf index ec836646..1084f54f 100644 --- a/outputs.tf +++ b/outputs.tf @@ -69,6 +69,10 @@ output "github_enterprise_url" { value = var.github_enterprise_url } +output "gitlab_selfmanaged_url" { + value = var.gitlab_selfmanaged_url +} + output "account_request_repo_name" { value = var.account_request_repo_name } diff --git a/sources/aft-lambda-layer/aft_common/feature_options.py b/sources/aft-lambda-layer/aft_common/feature_options.py index 432a6eac..c3e9ab23 100644 --- a/sources/aft-lambda-layer/aft_common/feature_options.py +++ b/sources/aft-lambda-layer/aft_common/feature_options.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # import logging -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple import aft_common.aft_utils as utils from boto3.session import Session @@ -36,7 +36,8 @@ def get_aws_regions(client: EC2Client) -> List[str]: def get_default_vpc(client: EC2Client) -> Optional[str]: logger.info("Getting default VPC") try: - response = client.describe_vpcs( + describe_vpcs = client.get_paginator("describe_vpcs") + for page in describe_vpcs.paginate( Filters=[ { "Name": "isDefault", @@ -45,11 +46,11 @@ def get_default_vpc(client: EC2Client) -> Optional[str]: ], }, ] - ) - for v in response["Vpcs"]: - vpc_id: str = v["VpcId"] - logger.info(vpc_id) - return vpc_id + ): + for v in page["Vpcs"]: + vpc_id: str = v["VpcId"] + logger.info(vpc_id) + return vpc_id return None except ClientError as e: region = client.meta.region_name @@ -280,3 +281,19 @@ def get_log_bucket_arns(session: Session) -> List[str]: ) logger.info(str(bucket_arns)) return bucket_arns + + +def get_target_account_and_customization_id_from_event( + event: Dict[str, Any] +) -> Tuple[str, str]: + request_id = event["customization_request_id"] + target_account_id = event.get("account_info", {}).get("account", {}).get("id", "") + if not target_account_id or not is_valid_account_id(target_account_id): + raise ValueError( + f"Event does not contain a valid target account ID: {target_account_id}" + ) + return request_id, target_account_id + + +def is_valid_account_id(account_id: str) -> bool: + return account_id.isdigit() and len(account_id) == 12 diff --git a/sources/aft-lambda-layer/pyproject.toml b/sources/aft-lambda-layer/pyproject.toml index 414292d3..a70da7cb 100644 --- a/sources/aft-lambda-layer/pyproject.toml +++ b/sources/aft-lambda-layer/pyproject.toml @@ -3,7 +3,7 @@ [build-system] requires = [ - "setuptools", + "setuptools >= 70.0.0", "wheel", ] @@ -28,6 +28,7 @@ dependencies = [ "botocore == 1.31.17", "requests == 2.32.2", "jsonschema == 4.3.2", + "urllib3 >= 1.26.19" ] @@ -36,11 +37,11 @@ dev = [ "pytest == 7.1.2", "pytest-cov == 4.1.0", "pytest-subtests == 0.8.0", - "black == 22.6.0", + "black == 24.3.0", "isort == 5.10.1", "pre-commit == 2.19.0", "mypy == 0.961", - "boto3-stubs[support, stepfunctions, ec2, organizations, servicecatalog, sqs, lambda, sns, sts, cloudtrail, ssm, iam, dynamodb] == 1.27.1", + "boto3-stubs[support, stepfunctions, ec2, organizations, servicecatalog, sqs, lambda, sns, sts, cloudtrail, ssm, iam, dynamodb, inspector2] == 1.27.1", "aws_lambda_powertools == 1.25.9", "types-requests == 2.27.5", ] diff --git a/sources/scripts/workspace_manager.py b/sources/scripts/workspace_manager.py index 9dc997e3..fe74b0be 100755 --- a/sources/scripts/workspace_manager.py +++ b/sources/scripts/workspace_manager.py @@ -46,7 +46,8 @@ def setup_workspace( def stage_run(workspace_id, assume_role_arn, role_session_name, api_token): cv_id, upload_url = terraform.create_configuration_version(workspace_id, api_token) print("Successfully created a new configuration version: {}".format(cv_id)) - data = open(LOCAL_CONFIGURATION_PATH, "rb") + with open(LOCAL_CONFIGURATION_PATH, "rb") as file: + data = file.read() terraform.upload_configuration_content(data, upload_url) print( "Successfully uploaded configuration content to upload URL: {}".format( diff --git a/src/aft_lambda/aft_feature_options/aft_enroll_support.py b/src/aft_lambda/aft_feature_options/aft_enroll_support.py index b3e72b34..a7e44e55 100644 --- a/src/aft_lambda/aft_feature_options/aft_enroll_support.py +++ b/src/aft_lambda/aft_feature_options/aft_enroll_support.py @@ -6,7 +6,7 @@ import aft_common.ssm from aft_common import constants as utils -from aft_common import notifications +from aft_common import feature_options, notifications from aft_common.account_provisioning_framework import ProvisionRoles from aft_common.auth import AuthClient from aft_common.logger import customization_request_logger @@ -23,6 +23,10 @@ def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None: request_id = event["customization_request_id"] target_account_id = event["account_info"]["account"]["id"] + request_id, target_account_id = ( + feature_options.get_target_account_and_customization_id_from_event(event=event) + ) + logger = customization_request_logger( aws_account_id=target_account_id, customization_request_id=request_id ) diff --git a/variables.tf b/variables.tf index eabf36d8..ba91a33e 100644 --- a/variables.tf +++ b/variables.tf @@ -102,7 +102,7 @@ variable "aft_backend_bucket_access_logs_object_expiration_days" { default = 365 validation { condition = var.aft_backend_bucket_access_logs_object_expiration_days > 0 - error_message = "aft_backend_bucket_access_logs_object_expiration_days must be an integer greater than 0." + error_message = "Aft_backend_bucket_access_logs_object_expiration_days must be an integer greater than 0." } } @@ -188,12 +188,12 @@ variable "aft_feature_delete_default_vpcs_enabled" { variable "vcs_provider" { - description = "Customer VCS Provider - valid inputs are codecommit, bitbucket, github, or githubenterprise" + description = "Customer VCS Provider - valid inputs are codecommit, bitbucket, github, githubenterprise, gitlab, or gitLab self-managed" type = string default = "codecommit" validation { - condition = contains(["codecommit", "bitbucket", "github", "githubenterprise"], var.vcs_provider) - error_message = "Valid values for var: vcs_provider are (codecommit, bitbucket, github, githubenterprise)." + condition = contains(["codecommit", "bitbucket", "github", "githubenterprise", "gitlab", "gitlabselfmanaged"], var.vcs_provider) + error_message = "Valid values for var: vcs_provider are (codecommit, bitbucket, github, githubenterprise, gitlab, gitlabselfmanaged)." } } @@ -202,7 +202,11 @@ variable "github_enterprise_url" { type = string default = "null" } - +variable "gitlab_selfmanaged_url" { + description = "GitLab SelfManaged URL, if GitLab SelfManaged is being used" + type = string + default = "null" +} variable "account_request_repo_name" { description = "Repository name for the account request files. For non-CodeCommit repos, name should be in the format of Org/Repo" type = string