From bb14c50cfa3ea0525612edcea8358c6842f5dd9e Mon Sep 17 00:00:00 2001 From: "debash.bora" Date: Mon, 13 Oct 2025 17:20:16 +0530 Subject: [PATCH 1/3] Updated module to support multiple source type including NO_SOURCE --- README.md | 16 +- data.tf | 2 + examples/api/README.md | 161 +++++++++++--- examples/api/main.tf | 2 +- .../multi-account-ui-deployment/README.md | 172 ++++++++++++--- examples/multi-account-ui-deployment/main.tf | 2 +- examples/simple-codebuild/README.md | 123 +++++++++++ .../buildspec/buildspec-api.yaml | 33 +++ examples/simple-codebuild/data.tf | 2 + examples/simple-codebuild/dev.tfvars | 4 + examples/simple-codebuild/locals.tf | 58 +++++ examples/simple-codebuild/main.tf | 42 ++++ examples/simple-codebuild/output.tf | 0 examples/simple-codebuild/variables.tf | 24 +++ examples/terraform/README.md | 202 +++++++++++++++--- examples/terraform/locals.tf | 6 +- examples/terraform/main.tf | 2 +- main.tf | 6 +- modules/chatbot/main.tf | 29 ++- modules/chatbot/variables.tf | 12 +- modules/chatbot/version.tf | 2 +- modules/codebuild/data.tf | 1 + modules/codebuild/locals.tf | 1 - modules/codebuild/main.tf | 9 +- modules/codebuild/variables.tf | 25 +++ modules/codebuild/version.tf | 2 +- modules/codepipeline/version.tf | 2 +- modules/iam-role/locals.tf | 3 +- modules/iam-role/main.tf | 58 ++--- modules/iam-role/variables.tf | 3 +- modules/iam-role/version.tf | 2 +- variables.tf | 16 +- version.tf | 2 +- 33 files changed, 872 insertions(+), 152 deletions(-) create mode 100644 examples/simple-codebuild/README.md create mode 100644 examples/simple-codebuild/buildspec/buildspec-api.yaml create mode 100644 examples/simple-codebuild/data.tf create mode 100644 examples/simple-codebuild/dev.tfvars create mode 100644 examples/simple-codebuild/locals.tf create mode 100644 examples/simple-codebuild/main.tf create mode 100644 examples/simple-codebuild/output.tf create mode 100644 examples/simple-codebuild/variables.tf diff --git a/README.md b/README.md index b218348..4385114 100644 --- a/README.md +++ b/README.md @@ -345,13 +345,13 @@ terraform destroy -var-file dev.tfvars | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.0 | -| [aws](#requirement\_aws) | ~> 5.0 | +| [aws](#requirement\_aws) | > 5.0, < 7.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.53.0 | +| [aws](#provider\_aws) | 6.16.0 | ## Modules @@ -372,12 +372,12 @@ terraform destroy -var-file dev.tfvars | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [artifacts\_bucket](#input\_artifacts\_bucket) | s3 bucket used for codepipeline artifacts | `string` | n/a | yes | -| [chatbot\_data](#input\_chatbot\_data) | (optional) Chatbot details to create integration |
object({
name = string
slack_channel_id = string
slack_workspace_id = string
guardrail_policies = optional(list(string), ["arn:aws:iam::aws:policy/AWSAccountManagementReadOnlyAccess"])
enable_slack_integration = bool
role_polices = optional(list(object({
policy_document = any
policy_name = string

})), [])
managed_policy_arns = optional(list(string), ["arn:aws:iam::aws:policy/AWSResourceExplorerReadOnlyAccess"])
})
| `null` | no | -| [codebuild\_projects](#input\_codebuild\_projects) | Values to create Codebuild project |
map(object({
description = optional(string, "")
build_timeout = optional(number, 15)
queued_timeout = optional(number, 15)
compute_type = optional(string, "BUILD_GENERAL1_SMALL")
compute_image = optional(string, "aws/codebuild/amazonlinux2-x86_64-standard:5.0")
compute_type_container = optional(string, "LINUX_CONTAINER")
image_pull_credentials_type = optional(string, "CODEBUILD")
privileged_mode = optional(bool, false)
build_type = string
buildspec_file_name = optional(string, null)
buildspec_file = optional(string, null)
terraform_version = optional(string, "terraform-1.5.0-1.x86_64")
create_role = optional(bool, false)
role_data = optional(object({
name = string
pipeline_service = optional(string, null)
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}), null)
}))
| `null` | no | -| [codepipelines](#input\_codepipelines) | Codepipeline data to create pipeline and stages |
map(object({
artifact_store_s3_kms_arn = string

source_repositories = list(object({
name = string
output_artifacts = optional(list(string), ["source_output"])
github_repository = string
github_branch = string
auto_trigger = optional(bool, true)
}))

pipeline_stages = list(object({
stage_name = string
name = string
category = optional(string, "Build")
provider = optional(string, "CodeBuild")
input_artifacts = optional(list(string), [])
output_artifacts = optional(list(string), [])
version = string
project_name = optional(string, null)
environment_variables = optional(list(object({
name = string
value = string
type = optional(string, "PLAINTEXT")
})),
[]
)
}))
create_role = optional(bool, false)
role_data = optional(object({
name = string
github_secret_arn = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}),
null)

trigger = optional(list(object({
source_action_name = string

push = list(object({
branches = object({
includes = list(string)
excludes = list(string)
})
file_paths = object({
includes = list(string)
excludes = list(string)
})
})
)

pull_request = list(object({
events = list(string)
filter = list(object({
branches = object({
includes = list(string)
excludes = list(string)
})
file_paths = object({
includes = list(string)
excludes = list(string)
})
})
) }))

})), [])

notification_data = optional(map(object({
detail_type = optional(string, "FULL")
event_type_ids = optional(list(string), [
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-canceled",
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-resumed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-superseded",
"codepipeline-pipeline-manual-approval-failed",
"codepipeline-pipeline-manual-approval-needed"
])
targets = list(object({
address = string // eg SNS arn
type = optional(string, "SNS") // Type can be "SNS" , AWSChatbotSlack etc
}))
})), null)

}))
| `{}` | no | -| [codestar\_connection](#input\_codestar\_connection) | codestar connection arn for github repository | `string` | n/a | yes | -| [role\_data](#input\_role\_data) | Roles to be created |
map(object({
pipeline_service = string
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}))
| `{}` | no | +| [artifacts\_bucket](#input\_artifacts\_bucket) | s3 bucket used for codepipeline artifacts. Optional - not required when using NO\_SOURCE builds. | `string` | `null` | no | +| [chatbot\_data](#input\_chatbot\_data) | (optional) Chatbot details to create integration. Set chatbot\_data to null to disable chatbot completely. |
object({
name = string
slack_channel_id = optional(string, null) # Required only when enable_slack_integration is true
slack_workspace_id = optional(string, null) # Required only when enable_slack_integration is true. Must contain only uppercase letters and numbers.
guardrail_policies = optional(list(string), ["arn:aws:iam::aws:policy/AWSAccountManagementReadOnlyAccess"])
enable_slack_integration = optional(bool, false)
role_polices = optional(list(object({
policy_document = any
policy_name = string

})), [])
managed_policy_arns = optional(list(string), ["arn:aws:iam::aws:policy/AWSResourceExplorerReadOnlyAccess"])
})
| `null` | no | +| [codebuild\_projects](#input\_codebuild\_projects) | Values to create Codebuild project |
map(object({
description = optional(string, "")
build_timeout = optional(number, 15)
queued_timeout = optional(number, 15)
compute_type = optional(string, "BUILD_GENERAL1_SMALL")
compute_image = optional(string, "aws/codebuild/amazonlinux2-x86_64-standard:5.0")
compute_type_container = optional(string, "LINUX_CONTAINER")
image_pull_credentials_type = optional(string, "CODEBUILD")
privileged_mode = optional(bool, false)
build_type = string
buildspec_file_name = optional(string, null)
buildspec_file = optional(string, null)
terraform_version = optional(string, "terraform-1.5.0-1.x86_64")
source_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, CODECOMMIT, GITHUB, GITHUB_ENTERPRISE, BITBUCKET, S3, NO_SOURCE
source_location = optional(string, null) # Required when source_type is not CODEPIPELINE or NO_SOURCE
artifacts_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, NO_ARTIFACTS, S3
artifacts_location = optional(string, null) # Required when artifacts_type is S3
create_role = optional(bool, false)
role_data = optional(object({
name = string
pipeline_service = optional(string, null)
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}), null)
}))
| `null` | no | +| [codepipelines](#input\_codepipelines) | Codepipeline data to create pipeline and stages |
map(object({
artifact_store_s3_kms_arn = string

source_repositories = list(object({
name = string
output_artifacts = optional(list(string), ["source_output"])
github_repository = string
github_branch = string
auto_trigger = optional(bool, true)
}))

pipeline_stages = list(object({
stage_name = string
name = string
category = optional(string, "Build")
provider = optional(string, "CodeBuild")
input_artifacts = optional(list(string), [])
output_artifacts = optional(list(string), [])
version = string
project_name = optional(string, null)
environment_variables = optional(list(object({
name = string
value = string
type = optional(string, "PLAINTEXT")
})),
[]
)
}))
create_role = optional(bool, false)
role_data = optional(object({
name = string
github_secret_arn = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}),
null)

trigger = optional(list(object({
source_action_name = string

push = list(object({
branches = object({
includes = list(string)
excludes = list(string)
})
file_paths = object({
includes = list(string)
excludes = list(string)
})
})
)

pull_request = list(object({
events = list(string)
filter = list(object({
branches = object({
includes = list(string)
excludes = list(string)
})
file_paths = object({
includes = list(string)
excludes = list(string)
})
})
) }))

})), [])

notification_data = optional(map(object({
detail_type = optional(string, "FULL")
event_type_ids = optional(list(string), [
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-canceled",
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-resumed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-superseded",
"codepipeline-pipeline-manual-approval-failed",
"codepipeline-pipeline-manual-approval-needed"
])
targets = list(object({
address = string // eg SNS arn
type = optional(string, "SNS") // Type can be "SNS" , AWSChatbotSlack etc
}))
})), null)

}))
| `{}` | no | +| [codestar\_connection](#input\_codestar\_connection) | codestar connection arn for github repository | `string` | `null` | no | +| [role\_data](#input\_role\_data) | Roles to be created |
map(object({
pipeline_service = string
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}))
| `{}` | no | | [tags](#input\_tags) | Tags for AWS resources | `map(string)` | n/a | yes | ## Outputs diff --git a/data.tf b/data.tf index 32f6c0d..8da0df9 100644 --- a/data.tf +++ b/data.tf @@ -1,3 +1,5 @@ +# Conditionally fetch S3 bucket data only if artifacts_bucket is provided data "aws_s3_bucket" "artifact" { + count = var.artifacts_bucket != null && var.artifacts_bucket != "" ? 1 : 0 bucket = var.artifacts_bucket } diff --git a/examples/api/README.md b/examples/api/README.md index 209df44..5e0b972 100644 --- a/examples/api/README.md +++ b/examples/api/README.md @@ -1,66 +1,173 @@ -# [terraform-aws-arc-security](https://github.com/sourcefuse/terraform-aws-arc-security) +# API CI/CD Pipeline ## Overview -AWS Security for the SourceFuse DevOps Reference Architecture Infrastructure. +This example demonstrates how to create a comprehensive CI/CD pipeline for building and deploying API applications using the `terraform-aws-arc-cicd` module. This pipeline combines application building with infrastructure deployment in a single automated workflow. -## First Time Usage -```shell -terraform init -backend-config=config.dev.hcl -``` +Key features: +- **Multi-repository support**: Pulls from both application and infrastructure repositories +- **API build and deploy**: CodeBuild project for building and pushing Docker images to ECR +- **Terraform integration**: Separate CodeBuild project for deploying infrastructure changes +- **Manual approval gate**: Review changes before deployment +- **Slack notifications**: AWS Chatbot integration for pipeline status updates +- **Cross-account deployment**: Support for assuming roles in different AWS accounts +- **ECS deployment**: Automated task updates and service deployments -Create a `dev` workspace -```shell -terraform workspace new dev -``` +This is useful for: +- Deploying containerized API applications to ECS +- Managing both application code and infrastructure in a unified pipeline +- Implementing multi-stage deployment workflows with approvals +- Coordinating deployments across multiple AWS accounts -Apply Terraform -```shell -terraform apply -``` +## Usage + +### Prerequisites -## Production Setup +Before using this example, ensure you have: +- An existing CodeStar connection to GitHub (configured in `locals.tf`) +- S3 bucket for pipeline artifacts +- IAM roles in target AWS accounts for cross-account deployment +- ECR repository for Docker images +- Slack workspace and channel IDs for notifications (if using Chatbot) + +### Initialize Terraform ```shell -terraform init -backend-config=config.prod.hcl +terraform init ``` -Create a `prod` workspace +### Apply with tfvars file ```shell -terraform workspace new prod +terraform apply -var-file=dev.tfvars ``` -Apply Terraform -```shell -terraform apply -var-file=prod.tfvars +## What Gets Created + +This example creates: +- **2 CodeBuild Projects**: + - API build project with Docker support and ECR push permissions + - Terraform apply project with state management +- **1 CodePipeline** with multiple stages: + - Source stage (pulls from API and Terraform repositories) + - Approval stage (manual review) + - API build stage (builds Docker image, pushes to ECR) + - Terraform deploy stage (applies infrastructure changes) +- **3 IAM Roles**: + - CodePipeline execution role + - CodeBuild role for API with ECR, Parameter Store, and Secrets Manager access + - CodeBuild role for Terraform with state bucket and cross-account permissions +- **1 AWS Chatbot** configuration for Slack notifications +- **Pipeline notifications** for all pipeline events + +## Pipeline Workflow + +1. Pipeline triggers from GitHub repository changes +2. Source code is retrieved from both API and infrastructure repositories +3. Manual approval stage allows review before deployment +4. API build stage: + - Builds Docker image from application code + - Pushes image to ECR + - Updates ECS task definition + - Deploys to ECS service +5. Terraform deploy stage: + - Applies infrastructure changes + - Updates supporting AWS resources + +## Customization + +### Buildspec Files + +The pipeline uses custom buildspec files located in the `buildspec/` directory: +- `buildspec-api.yaml`: Defines API build, test, and deployment steps +- Buildspec for Terraform is generated automatically by the module + +### Environment Variables + +Modify environment variables in `locals.tf` to customize: +- Target AWS accounts and roles +- Application names and namespaces +- ECS task and service names +- Terraform workspace and backend configuration +- Working directories for Terraform + +### Multiple Environments + +The `branch_map` in `locals.tf` allows mapping different Git branches to different environments: +```hcl +branch_map = { + dev = { + api = "dev" + terraform = "dev" + } + poc = { + api = "staging" + terraform = "stg" + } +} ``` +### Slack Notifications + +Configure Slack integration by updating the `chatbot_data` in `locals.tf` with your: +- Slack channel ID +- Slack workspace ID +- Desired notification event types + +## Key Features Demonstrated +- Complete CI/CD workflow for containerized applications +- Multi-repository pipeline configuration +- Cross-account IAM role assumption +- ECR integration for Docker images +- ECS deployment automation +- Terraform infrastructure management +- Manual approval gates +- AWS Chatbot integration for notifications +- Parameter Store and Secrets Manager integration +- S3-based artifact storage + + ## Requirements | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | ~> 1.3, < 2.0.0 | -| [aws](#requirement\_aws) | ~> 4.0 | +| [terraform](#requirement\_terraform) | ~> 1.5 | +| [aws](#requirement\_aws) | > 5.0, < 7.0 | ## Providers -No providers. +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 6.16.0 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [pipelines](#module\_pipelines) | ../../ | n/a | +| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.3 | ## Resources -No resources. +| Name | Type | +|------|------| +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.ecr_push](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.parameters](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.pipeline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.secret_read](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | +| [namespace](#input\_namespace) | Namespace for the resources. | `string` | n/a | yes | +| [project](#input\_project) | The project name | `string` | `""` | no | | [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [chatbot\_sns\_arns](#output\_chatbot\_sns\_arns) | SNS topics created by AWS Chatbot | diff --git a/examples/api/main.tf b/examples/api/main.tf index 3b8c3a3..c6371dd 100644 --- a/examples/api/main.tf +++ b/examples/api/main.tf @@ -7,7 +7,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.0" + version = "> 5.0, < 7.0" } } diff --git a/examples/multi-account-ui-deployment/README.md b/examples/multi-account-ui-deployment/README.md index 209df44..fba3a96 100644 --- a/examples/multi-account-ui-deployment/README.md +++ b/examples/multi-account-ui-deployment/README.md @@ -1,66 +1,184 @@ -# [terraform-aws-arc-security](https://github.com/sourcefuse/terraform-aws-arc-security) +# Multi-Account UI Deployment Pipeline ## Overview -AWS Security for the SourceFuse DevOps Reference Architecture Infrastructure. +This example demonstrates how to create a CI/CD pipeline for building and deploying UI applications across multiple AWS accounts using the `terraform-aws-arc-cicd` module. This pipeline is specifically designed for frontend applications that need to be deployed to different environments in separate AWS accounts. -## First Time Usage -```shell -terraform init -backend-config=config.dev.hcl -``` +Key features: +- **UI-focused pipeline**: Optimized for building and deploying frontend applications +- **Cross-account deployment**: Deploy to different AWS accounts based on environment +- **Manual approval gate**: Review changes before deployment to target accounts +- **Slack notifications**: AWS Chatbot integration for deployment status updates +- **GitHub integration**: CodeStar connection for source code management +- **Environment-based branching**: Map different Git branches to different environments -Create a `dev` workspace -```shell -terraform workspace new dev -``` +This is useful for: +- Deploying React, Angular, Vue, or other frontend applications +- Managing UI deployments across dev, staging, and production accounts +- Building static assets and deploying to S3 or CloudFront +- Implementing controlled release processes with approvals +- Centralizing UI deployment from a shared CI/CD account -Apply Terraform -```shell -terraform apply -``` +## Usage + +### Prerequisites + +Before using this example, ensure you have: +- An existing CodeStar connection to GitHub (configured in `locals.tf`) +- S3 bucket for pipeline artifacts +- IAM roles in target AWS accounts with appropriate permissions for UI deployment +- Slack workspace and channel IDs for notifications (if using Chatbot) +- Target infrastructure (S3 buckets, CloudFront distributions, etc.) for UI deployment -## Production Setup +### Initialize Terraform ```shell -terraform init -backend-config=config.prod.hcl +terraform init ``` -Create a `prod` workspace +### Apply with tfvars file ```shell -terraform workspace new prod +terraform apply -var-file=dev.tfvars ``` -Apply Terraform -```shell -terraform apply -var-file=prod.tfvars +## What Gets Created + +This example creates: +- **1 CodeBuild Project** for UI: + - Configured for building frontend applications + - Custom buildspec from `buildspec/buildspec-ui.yaml` + - Cross-account role assumption capability +- **1 CodePipeline** with stages: + - Source stage (pulls from GitHub repository) + - Approval stage (manual review) + - UI build stage (builds and deploys frontend application) +- **2 IAM Roles**: + - CodePipeline execution role + - CodeBuild role with cross-account deployment permissions +- **1 AWS Chatbot** configuration for Slack notifications +- **Pipeline notifications** for all pipeline events + +## Pipeline Workflow + +1. Pipeline triggers from GitHub repository changes +2. Source code is retrieved from the UI repository +3. Manual approval stage allows review before deployment +4. UI build stage: + - Installs dependencies (npm, yarn, etc.) + - Builds frontend application + - Assumes role in target AWS account + - Deploys built assets to target infrastructure + - Invalidates CloudFront cache (if applicable) + +## Customization + +### Buildspec File + +The pipeline uses a custom buildspec file at `buildspec/buildspec-ui.yaml`. Customize it to: +- Use different package managers (npm, yarn, pnpm) +- Configure build commands +- Set environment variables for the build +- Define deployment steps +- Add testing phases + +### Environment Variables + +Modify environment variables in `locals.tf` to customize: +- Target AWS accounts and roles to assume +- Application name and namespace +- Deployment destinations +- Build configuration + +### Multiple Environments + +The `branch_map` in `locals.tf` allows mapping different Git branches to different environments: +```hcl +branch_map = { + dev = { + ui = "dev" + } + poc = { + ui = "staging" + } +} ``` +### Slack Notifications + +Configure Slack integration by updating the `chatbot_data` in `locals.tf` with your: +- Slack channel ID +- Slack workspace ID +- Desired notification event types +## Deployment Strategies + +This example supports various UI deployment strategies: + +### S3 + CloudFront +- Build static assets +- Upload to S3 bucket +- Invalidate CloudFront distribution + +### Containerized UI +- Build Docker image with static assets +- Push to ECR +- Deploy to ECS or EKS + +### Multi-Region Deployment +- Assume roles in different regions +- Deploy to multiple S3 buckets +- Update multiple CloudFront distributions + +## Key Features Demonstrated + +- UI-specific CI/CD workflow +- Cross-account deployment pattern +- GitHub integration via CodeStar connections +- Manual approval gates +- AWS Chatbot integration for notifications +- Environment-based branch mapping +- Flexible buildspec configuration +- IAM role assumption across accounts + + ## Requirements | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | ~> 1.3, < 2.0.0 | -| [aws](#requirement\_aws) | ~> 4.0 | +| [terraform](#requirement\_terraform) | ~> 1.5 | +| [aws](#requirement\_aws) | > 5.0, < 7.0 | ## Providers -No providers. +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 6.16.0 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [pipelines](#module\_pipelines) | ../../ | n/a | +| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.3 | ## Resources -No resources. +| Name | Type | +|------|------| +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.pipeline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | +| [namespace](#input\_namespace) | Namespace for the resources. | `string` | n/a | yes | +| [project](#input\_project) | The project name | `string` | `""` | no | | [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [chatbot\_sns\_arns](#output\_chatbot\_sns\_arns) | SNS topics created by AWS Chatbot | diff --git a/examples/multi-account-ui-deployment/main.tf b/examples/multi-account-ui-deployment/main.tf index f95cd64..127969f 100644 --- a/examples/multi-account-ui-deployment/main.tf +++ b/examples/multi-account-ui-deployment/main.tf @@ -7,7 +7,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.0" + version = "> 5.0, < 7.0" } } } diff --git a/examples/simple-codebuild/README.md b/examples/simple-codebuild/README.md new file mode 100644 index 0000000..7937d58 --- /dev/null +++ b/examples/simple-codebuild/README.md @@ -0,0 +1,123 @@ +# Simple CodeBuild + +## Overview + +This example demonstrates how to create a simple, standalone AWS CodeBuild project using the `terraform-aws-arc-cicd` module with the following characteristics: + +- **NO_SOURCE**: The CodeBuild project doesn't require any source code repository +- **NO_ARTIFACTS**: No artifacts are stored after build completion +- **No CodePipeline**: Standalone CodeBuild project without pipeline integration +- **No Slack/Chatbot**: No notification integration +- **No CodeStar Connection**: No GitHub/Bitbucket integration needed + +This is useful for: +- Manual build triggers +- Running administrative tasks +- Testing build environments +- Custom automation scripts + +## Usage + +### Initialize Terraform +```shell +terraform init +``` + +### Apply with tfvars file +```shell +terraform apply -var-file=dev.tfvars +``` + +### Start a Build Manually + +After the CodeBuild project is created, you can trigger a build manually via: + +**AWS Console:** +1. Navigate to AWS CodeBuild +2. Select your project: `{namespace}-{environment}-simple-build` +3. Click "Start build" + +**AWS CLI:** +```bash +aws codebuild start-build --project-name --simple-build +``` + +## What Gets Created + +This example creates: +- **1 IAM Role** for CodeBuild with minimal permissions +- **1 CodeBuild Project** with: + - Source type: `NO_SOURCE` + - Artifacts type: `NO_ARTIFACTS` + - Custom inline buildspec + - Basic CloudWatch Logs integration + +## Customization + +### Custom Buildspec + +The buildspec is defined inline in `locals.tf`. You can modify it to: +- Run custom scripts +- Install packages +- Execute tests +- Perform deployments +- Run maintenance tasks + +Example: Add AWS CLI commands, install dependencies, or run Docker commands. + +### Environment Variables + +Add environment variables in the buildspec `env.variables` section or pass them when starting the build. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.5 | +| [aws](#requirement\_aws) | > 5.0, < 7.0 | + +## Key Features Demonstrated + +- Standalone CodeBuild without source repository +- No S3 bucket required for artifacts +- Optional chatbot/Slack integration (disabled) +- Optional CodeStar connection (not used) +- Minimal IAM permissions +- Custom inline buildspec + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.5 | +| [aws](#requirement\_aws) | > 5.0, < 7.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [simple\_codebuild](#module\_simple\_codebuild) | ../../ | n/a | +| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.3 | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | +| [namespace](#input\_namespace) | Namespace for the resources. | `string` | n/a | yes | +| [project](#input\_project) | The project name | `string` | `""` | no | +| [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | + +## Outputs + +No outputs. + diff --git a/examples/simple-codebuild/buildspec/buildspec-api.yaml b/examples/simple-codebuild/buildspec/buildspec-api.yaml new file mode 100644 index 0000000..1fdaff4 --- /dev/null +++ b/examples/simple-codebuild/buildspec/buildspec-api.yaml @@ -0,0 +1,33 @@ +--- +version: 0.2 + +# Simple buildspec for demonstration purposes +# This example shows a basic build without any source code (NO_SOURCE) + +env: + variables: + MESSAGE: "Hello from Simple CodeBuild Example" + +phases: + pre_build: + commands: + - echo "Pre-build phase started at $(date)" + - echo "$MESSAGE" + - echo "CodeBuild Build ID: $CODEBUILD_BUILD_ID" + + build: + commands: + - echo "Build phase started" + - echo "Running example build commands..." + - ls -la + - pwd + - echo "You can add your custom build logic here" + + post_build: + commands: + - echo "Post-build phase completed at $(date)" + - echo "Build finished successfully!" + +artifacts: + files: + - '**/*' diff --git a/examples/simple-codebuild/data.tf b/examples/simple-codebuild/data.tf new file mode 100644 index 0000000..346c8b3 --- /dev/null +++ b/examples/simple-codebuild/data.tf @@ -0,0 +1,2 @@ +# This simple example only needs basic AWS account info +# The CodeBuild project with NO_SOURCE doesn't require additional IAM policies diff --git a/examples/simple-codebuild/dev.tfvars b/examples/simple-codebuild/dev.tfvars new file mode 100644 index 0000000..aefa5f9 --- /dev/null +++ b/examples/simple-codebuild/dev.tfvars @@ -0,0 +1,4 @@ +region = "us-east-1" +environment = "dev" +namespace = "arc" +project = "aws-modules" diff --git a/examples/simple-codebuild/locals.tf b/examples/simple-codebuild/locals.tf new file mode 100644 index 0000000..75387aa --- /dev/null +++ b/examples/simple-codebuild/locals.tf @@ -0,0 +1,58 @@ +locals { + prefix = "${var.namespace}-${var.environment}" + + // Simple IAM role for standalone CodeBuild project + role_data = { + "${local.prefix}-simple-codebuild-role" = { + pipeline_service = "codebuild" + assume_role_arns = [] + github_secret_arn = null + terraform_state_s3_bucket = null + dynamodb_lock_table = null + additional_iam_policy_doc_json_list = [] + } + } + + // Simple standalone CodeBuild project with NO_SOURCE + codebuild_projects = { + "${local.prefix}-simple-build" = { + description = "Simple CodeBuild project with NO_SOURCE for manual builds" + build_type = "UI" + buildspec_file = <<-BUILDSPEC + version: 0.2 + + env: + variables: + MESSAGE: "Hello from CodeBuild" + + phases: + pre_build: + commands: + - echo "Pre-build phase started" + - echo "Environment: $MESSAGE" + build: + commands: + - echo "Build phase started" + - echo "Running custom build commands..." + - date + post_build: + commands: + - echo "Post-build phase completed" + - echo "Build finished successfully!" + + artifacts: + files: + - '**/*' + BUILDSPEC + role_data = { + name = "${local.prefix}-simple-codebuild-role" + } + source_type = "NO_SOURCE" # No source code required + source_location = null + artifacts_type = "NO_ARTIFACTS" # No artifacts output + artifacts_location = null + artifacts_bucket = null # Not needed for NO_SOURCE + privileged_mode = false + } + } +} diff --git a/examples/simple-codebuild/main.tf b/examples/simple-codebuild/main.tf new file mode 100644 index 0000000..c4f07b3 --- /dev/null +++ b/examples/simple-codebuild/main.tf @@ -0,0 +1,42 @@ +################################################################################ +## defaults +################################################################################ +terraform { + required_version = "~> 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "> 5.0, < 7.0" + } + } + + // backend "s3" {} +} + +provider "aws" { + region = var.region +} + +module "tags" { + source = "sourcefuse/arc-tags/aws" + version = "1.2.3" + + environment = var.environment + project = var.project +} + + +module "simple_codebuild" { + source = "../../" + + artifacts_bucket = null # Not required for NO_SOURCE builds + codestar_connection = null # Not using CodeStar connection + + role_data = local.role_data + codebuild_projects = local.codebuild_projects + codepipelines = {} # Not creating any pipelines + chatbot_data = null # Not using Slack integration + + tags = module.tags.tags +} diff --git a/examples/simple-codebuild/output.tf b/examples/simple-codebuild/output.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple-codebuild/variables.tf b/examples/simple-codebuild/variables.tf new file mode 100644 index 0000000..b37c50e --- /dev/null +++ b/examples/simple-codebuild/variables.tf @@ -0,0 +1,24 @@ +## shared +################################################################################ +variable "namespace" { + type = string + description = "Namespace for the resources." +} + +variable "environment" { + type = string + default = "dev" + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "region" { + type = string + description = "AWS region" + default = "us-east-1" +} + +variable "project" { + type = string + description = "The project name" + default = "" +} diff --git a/examples/terraform/README.md b/examples/terraform/README.md index 5a412a5..04f1c06 100644 --- a/examples/terraform/README.md +++ b/examples/terraform/README.md @@ -1,66 +1,218 @@ -# [terraform-aws-arc-cicd](https://github.com/sourcefuse/terraform-aws-arc-cicd) +# Terraform Infrastructure Pipeline ## Overview -AWS cicd for the SourceFuse DevOps Reference Architecture Infrastructure. - -## First Time Usage +This example demonstrates how to create a CI/CD pipeline specifically for Terraform infrastructure deployments using the `terraform-aws-arc-cicd` module. This pipeline implements a production-ready Terraform workflow with plan, approval, and apply stages. + +Key features: +- **Terraform-optimized pipeline**: Designed specifically for infrastructure as code +- **Plan and Apply workflow**: Separate CodeBuild projects for plan and apply +- **Manual approval gate**: Review Terraform plans before applying changes +- **State management**: Integration with S3 backend and DynamoDB locking +- **Cross-account deployment**: Deploy infrastructure across multiple AWS accounts +- **Slack notifications**: AWS Chatbot integration for deployment status updates +- **Workspace support**: Terraform workspace management per environment + +This is useful for: +- Managing infrastructure as code deployments +- Implementing GitOps workflows for Terraform +- Enforcing review and approval processes for infrastructure changes +- Deploying infrastructure across multiple AWS accounts +- Managing multiple environments with Terraform workspaces +- Centralizing infrastructure deployment from a shared CI/CD account + +## Usage + +### Prerequisites + +Before using this example, ensure you have: +- An existing CodeStar connection to GitHub (configured in `locals.tf`) +- S3 bucket for pipeline artifacts +- S3 bucket for Terraform state with versioning enabled +- DynamoDB table for Terraform state locking +- IAM roles in target AWS accounts with appropriate permissions +- Slack workspace and channel IDs for notifications (if using Chatbot) + +### Initialize Terraform ```shell -terraform init -backend-config=config.dev.hcl +terraform init ``` -Create a `dev` workspace +### Apply with tfvars file ```shell -terraform workspace new dev +terraform apply -var-file=dev.tfvars ``` -Apply Terraform -```shell -terraform apply +## What Gets Created + +This example creates: +- **2 CodeBuild Projects**: + - Terraform plan project (runs `terraform plan`) + - Terraform apply project (runs `terraform apply`) +- **1 CodePipeline** with stages: + - Source stage (pulls from Terraform repository) + - Terraform plan stage (generates and displays plan) + - Approval stage (manual review of plan) + - Terraform apply stage (applies approved changes) +- **2 IAM Roles**: + - CodePipeline execution role + - CodeBuild role with Terraform state access and cross-account permissions +- **1 AWS Chatbot** configuration for Slack notifications +- **Pipeline notifications** for all pipeline events + +## Pipeline Workflow + +1. Pipeline triggers from GitHub repository changes +2. Source code is retrieved from the Terraform repository +3. Terraform plan stage: + - Initializes Terraform with remote backend + - Selects or creates workspace + - Runs `terraform plan` + - Outputs plan for review +4. Manual approval stage allows team to review plan +5. Terraform apply stage: + - Re-initializes Terraform + - Runs `terraform apply` with approved plan + - Updates infrastructure + +## Customization + +### Terraform Configuration + +Modify the Terraform configuration in `locals.tf`: + +```hcl +environment_variables = [ + { + name = "TF_VAR_FILE", + value = "tfvars/${var.environment}.tfvars" + }, + { + name = "WORKING_DIR", + value = "terraform/example-module" + }, + { + name = "BACKEND_CONFIG_FILE", + value = "backend/config.shared-services.hcl" + }, + { + name = "WORKSPACE", + value = var.environment + } +] ``` -## Production Setup -```shell -terraform init -backend-config=config.prod.hcl +### Terraform Version + +Specify the Terraform version in `locals.tf`: +```hcl +terraform_version = "terraform-1.8.3-1.x86_64" ``` -Create a `prod` workspace -```shell -terraform workspace new prod +### State Management + +Configure S3 backend and DynamoDB locking in the IAM role configuration: +```hcl +terraform_state_s3_bucket = "example-shared-services-terraform-state" +dynamodb_lock_table = "example-shared-services-terraform-state-lock" ``` -Apply Terraform -```shell -terraform apply -var-file=prod.tfvars +### Cross-Account Deployment + +Add multiple role ARNs for cross-account deployments: +```hcl +assume_role_arns = [ + local.environment_role[var.environment], + "arn:aws:iam::account-id:role/management-role" +] ``` +### Multiple Environments + +The `branch_map` in `locals.tf` allows mapping different Git branches to different environments: +```hcl +branch_map = { + dev = { + terraform = "dev" + } + poc = { + terraform = "stg" + } +} +``` +### Buildspec Customization + +The module automatically generates buildspec files for Terraform, but you can customize: +- Pre-plan validation steps +- Post-apply verification +- Custom Terraform commands +- Additional security scanning + +## Best Practices Demonstrated + +This example implements several Terraform CI/CD best practices: + +1. **Separation of Plan and Apply**: Dedicated CodeBuild projects ensure clear separation +2. **State Locking**: DynamoDB table prevents concurrent modifications +3. **Remote State**: S3 backend ensures consistent state across team +4. **Workspace Management**: Environment isolation using Terraform workspaces +5. **Manual Approval**: Human review before infrastructure changes +6. **Audit Trail**: CloudWatch Logs and pipeline history for compliance +7. **Cross-Account Access**: Secure role assumption for multi-account deployments + +## Key Features Demonstrated + +- Complete Terraform CI/CD workflow +- Plan before apply pattern +- Manual approval gates for infrastructure changes +- S3 backend and DynamoDB state locking +- Terraform workspace management +- Cross-account infrastructure deployment +- AWS Chatbot integration for notifications +- Environment-based branch mapping +- Custom Terraform version support +- Backend configuration management + ## Requirements | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | ~> 1.3, < 2.0.0 | -| [aws](#requirement\_aws) | ~> 4.0 | +| [terraform](#requirement\_terraform) | ~> 1.5 | +| [aws](#requirement\_aws) | > 5.0, < 7.0 | ## Providers -No providers. +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 6.16.0 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [pipelines](#module\_pipelines) | ../../ | n/a | +| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.3 | ## Resources -No resources. +| Name | Type | +|------|------| +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.pipeline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | +| [namespace](#input\_namespace) | Namespace for the resources. | `string` | n/a | yes | +| [project](#input\_project) | The project name | `string` | `""` | no | | [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [chatbot\_sns\_arns](#output\_chatbot\_sns\_arns) | SNS topics created by AWS Chatbot | diff --git a/examples/terraform/locals.tf b/examples/terraform/locals.tf index 7225cac..dc15df1 100644 --- a/examples/terraform/locals.tf +++ b/examples/terraform/locals.tf @@ -1,7 +1,7 @@ locals { environment_role = { - dev = "arn:aws:iam::xxxx:role/example-dev-cicd-role" + dev = "arn:aws:iam::884360309640:role/debash-iam-role" } branch_map = { @@ -24,8 +24,8 @@ locals { chatbot_data = { name = "${var.namespace}-slack" - slack_channel_id = "C0xxxxxxx5" - slack_workspace_id = "T0xxxxxxRT" + slack_channel_id = "C09L9CJ4EAG" + slack_workspace_id = "T04C3L3M2" managed_policy_arns = ["arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess"] guardrail_policies = ["arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess"] role_polices = local.policies diff --git a/examples/terraform/main.tf b/examples/terraform/main.tf index 3b8c3a3..c6371dd 100644 --- a/examples/terraform/main.tf +++ b/examples/terraform/main.tf @@ -7,7 +7,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.0" + version = "> 5.0, < 7.0" } } diff --git a/main.tf b/main.tf index c1ed39b..8dcadff 100644 --- a/main.tf +++ b/main.tf @@ -5,7 +5,7 @@ module "role" { name = each.key pipeline_service = each.value.pipeline_service assume_role_arns = each.value.assume_role_arns - artifact_bucket_arn = data.aws_s3_bucket.artifact.arn + artifact_bucket_arn = length(data.aws_s3_bucket.artifact) > 0 ? data.aws_s3_bucket.artifact[0].arn : "" codestar_connection = var.codestar_connection github_secret_arn = each.value.github_secret_arn terraform_state_s3_bucket = each.value.terraform_state_s3_bucket @@ -36,6 +36,10 @@ module "codebuild" { buildspec_file_name = each.value.buildspec_file_name buildspec_file = each.value.buildspec_file terraform_version = each.value.terraform_version + source_type = each.value.source_type + source_location = each.value.source_location + artifacts_type = each.value.artifacts_type + artifacts_location = each.value.artifacts_location create_role = each.value.create_role role_data = merge( each.value.role_data, diff --git a/modules/chatbot/main.tf b/modules/chatbot/main.tf index 9bd1a76..818bcf1 100644 --- a/modules/chatbot/main.tf +++ b/modules/chatbot/main.tf @@ -7,7 +7,7 @@ resource "awscc_chatbot_slack_channel_configuration" "this" { slack_channel_id = var.slack_channel_id slack_workspace_id = var.slack_workspace_id guardrail_policies = var.guardrail_policies - sns_topic_arns = [module.sns_topic.sns_topic_arn] + sns_topic_arns = [module.sns_topic.topic_arn] } @@ -81,24 +81,21 @@ data "aws_iam_policy_document" "sns_kms_key_policy" { } module "kms" { - source = "cloudposse/kms-key/aws" - version = "0.12.2" - - name = "${local.prefix}-pipeline-sns" - description = "KMS key for SNS topic" - enable_key_rotation = true - alias = "alias/${local.prefix}/pipeline-sns" - policy = data.aws_iam_policy_document.sns_kms_key_policy.json - tags = var.tags + source = "sourcefuse/arc-kms/aws" + version = "1.0.11" + deletion_window_in_days = var.deletion_window_in_days + enable_key_rotation = true + alias = "alias/${local.prefix}/pipeline-sns" + tags = var.tags + policy = data.aws_iam_policy_document.sns_kms_key_policy.json } module "sns_topic" { - source = "cloudposse/sns-topic/aws" - version = "0.21.0" + source = "sourcefuse/arc-sns/aws" + version = "0.0.2" - attributes = ["${local.prefix}-aws-chatbot"] - kms_master_key_id = module.kms.alias_name - allowed_aws_services_for_sns_published = ["chatbot.amazonaws.com"] - tags = var.tags + name = "${local.prefix}-chatbot-topic" + kms_master_key_id = module.kms.alias_name + tags = var.tags } diff --git a/modules/chatbot/variables.tf b/modules/chatbot/variables.tf index 1064032..07b3b0b 100644 --- a/modules/chatbot/variables.tf +++ b/modules/chatbot/variables.tf @@ -5,12 +5,14 @@ variable "name" { variable "slack_channel_id" { type = string - description = "Slack Channel ID" + description = "Slack Channel ID. Required when enable_slack_integration is true." + default = null } variable "slack_workspace_id" { type = string - description = "Slack Workspace ID" + description = "Slack Workspace ID. Must contain only uppercase letters and numbers (e.g., T01234ABCDE). Required when enable_slack_integration is true." + default = null } @@ -59,3 +61,9 @@ variable "managed_policy_arns" { description = "(optional) A list of Amazon Resource Names (ARNs) of the IAM managed policies that you want to attach to the role." default = ["arn:aws:iam::aws:policy/AWSCodePipeline_ReadOnlyAccess", "arn:aws:iam::aws:policy/AWSCodePipelineApproverAccess"] } + +variable "deletion_window_in_days" { + type = number + default = 10 + description = "Duration in days after which the key is deleted after destruction of the resource" +} diff --git a/modules/chatbot/version.tf b/modules/chatbot/version.tf index 91e8bdb..4bf8a58 100644 --- a/modules/chatbot/version.tf +++ b/modules/chatbot/version.tf @@ -6,7 +6,7 @@ terraform { required_providers { aws = { - version = "~> 5.0" + version = "> 5.0, < 7.0" source = "hashicorp/aws" } awscc = { diff --git a/modules/codebuild/data.tf b/modules/codebuild/data.tf index 30fb50b..5e3dbfb 100644 --- a/modules/codebuild/data.tf +++ b/modules/codebuild/data.tf @@ -4,5 +4,6 @@ data "aws_iam_role" "this" { } data "aws_s3_bucket" "artifact" { + count = var.artifacts_bucket != null && var.artifacts_bucket != "" ? 1 : 0 bucket = var.artifacts_bucket } diff --git a/modules/codebuild/locals.tf b/modules/codebuild/locals.tf index 6eaf0d4..38d896f 100644 --- a/modules/codebuild/locals.tf +++ b/modules/codebuild/locals.tf @@ -1,4 +1,3 @@ locals { role_arn = var.create_role ? module.role[0].arn : data.aws_iam_role.this[0].arn - } diff --git a/modules/codebuild/main.tf b/modules/codebuild/main.tf index 59c803e..a3e5b60 100644 --- a/modules/codebuild/main.tf +++ b/modules/codebuild/main.tf @@ -5,7 +5,7 @@ module "role" { name = var.role_data.name pipeline_service = try(var.role_data.pipeline_service, []) assume_role_arns = var.role_data.assume_role_arns - artifact_bucket_arn = data.aws_s3_bucket.artifact.arn + artifact_bucket_arn = length(data.aws_s3_bucket.artifact) > 0 ? data.aws_s3_bucket.artifact[0].arn : "" codestar_connection = var.role_data.codestar_connection github_secret_arn = var.role_data.github_secret_arn terraform_state_s3_bucket = var.role_data.terraform_state_s3_bucket @@ -24,8 +24,10 @@ resource "aws_codebuild_project" "this" { service_role = local.role_arn artifacts { - type = "CODEPIPELINE" + type = var.artifacts_type + location = var.artifacts_type == "S3" ? var.artifacts_location : null } + environment { compute_type = var.compute_type # Below chnages is to fix : YAML_FILE_ERROR: Unknown runtime version named '12' of nodejs. This build image has the following versions: 18 @@ -37,7 +39,8 @@ resource "aws_codebuild_project" "this" { } source { - type = "CODEPIPELINE" + type = var.source_type + location = var.source_type != "CODEPIPELINE" && var.source_type != "NO_SOURCE" ? var.source_location : null buildspec = var.build_type == "Terraform" ? templatefile("${path.module}/buildspec/${var.buildspec_file_name}.yaml", { TERRAFORM_VERSION = var.terraform_version }) : var.buildspec_file diff --git a/modules/codebuild/variables.tf b/modules/codebuild/variables.tf index 9a802f8..b647389 100644 --- a/modules/codebuild/variables.tf +++ b/modules/codebuild/variables.tf @@ -96,6 +96,31 @@ variable "role_data" { variable "artifacts_bucket" { type = string description = "s3 bucket used for codepipeline artifacts" + default = null +} + +variable "source_type" { + type = string + description = "Type of repository that contains the source code to be built. Valid values: CODEPIPELINE, CODECOMMIT, GITHUB, GITHUB_ENTERPRISE, BITBUCKET, S3, NO_SOURCE" + default = "CODEPIPELINE" +} + +variable "source_location" { + type = string + description = "Location of the source code from git or S3. Required when source_type is not CODEPIPELINE or NO_SOURCE" + default = null +} + +variable "artifacts_type" { + type = string + description = "Build output artifact's type. Valid values: CODEPIPELINE, NO_ARTIFACTS, S3" + default = "CODEPIPELINE" +} + +variable "artifacts_location" { + type = string + description = "Location of artifacts. Required when artifacts_type is S3" + default = null } variable "tags" { diff --git a/modules/codebuild/version.tf b/modules/codebuild/version.tf index 9121a5f..7f99bce 100644 --- a/modules/codebuild/version.tf +++ b/modules/codebuild/version.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.0" + version = "> 5.0, < 7.0" } } } diff --git a/modules/codepipeline/version.tf b/modules/codepipeline/version.tf index 9121a5f..7f99bce 100644 --- a/modules/codepipeline/version.tf +++ b/modules/codepipeline/version.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.0" + version = "> 5.0, < 7.0" } } } diff --git a/modules/iam-role/locals.tf b/modules/iam-role/locals.tf index 300960f..71c62d7 100644 --- a/modules/iam-role/locals.tf +++ b/modules/iam-role/locals.tf @@ -1,5 +1,6 @@ data "aws_codestarconnections_connection" "this" { - name = var.codestar_connection + count = var.codestar_connection != null && var.codestar_connection != "" ? 1 : 0 + name = var.codestar_connection } diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index 6edd789..b81f1ee 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -1,22 +1,28 @@ data "aws_iam_policy_document" "codepipeline" { - statement { - effect = "Allow" + dynamic "statement" { + for_each = var.artifact_bucket_arn != null && var.artifact_bucket_arn != "" ? [1] : [] + content { + effect = "Allow" - actions = [ - "s3:GetObject", - "s3:GetObjectVersion", - "s3:GetBucketVersioning", - "s3:PutObjectAcl", - "s3:PutObject", - ] + actions = [ + "s3:GetObject", + "s3:GetObjectVersion", + "s3:GetBucketVersioning", + "s3:PutObjectAcl", + "s3:PutObject", + ] - resources = [var.artifact_bucket_arn, "${var.artifact_bucket_arn}/*"] + resources = [var.artifact_bucket_arn, "${var.artifact_bucket_arn}/*"] + } } - statement { - effect = "Allow" - actions = ["codestar-connections:UseConnection"] - resources = [data.aws_codestarconnections_connection.this.arn] + dynamic "statement" { + for_each = var.codestar_connection != null && var.codestar_connection != "" ? [1] : [] + content { + effect = "Allow" + actions = ["codestar-connections:UseConnection"] + resources = [data.aws_codestarconnections_connection.this[0].arn] + } } statement { @@ -44,16 +50,20 @@ data "aws_iam_policy_document" "codebuild" { resources = ["*"] } - statement { - effect = "Allow" - actions = [ - "s3:PutObject", - "s3:GetObject", - "s3:GetObjectVersion", - "s3:GetBucketAcl", - "s3:GetBucketLocation" - ] - resources = [var.artifact_bucket_arn, "${var.artifact_bucket_arn}/*"] + + dynamic "statement" { + for_each = var.artifact_bucket_arn != null && var.artifact_bucket_arn != "" ? [1] : [] + content { + effect = "Allow" + actions = [ + "s3:PutObject", + "s3:GetObject", + "s3:GetObjectVersion", + "s3:GetBucketAcl", + "s3:GetBucketLocation" + ] + resources = [var.artifact_bucket_arn, "${var.artifact_bucket_arn}/*"] + } } dynamic "statement" { diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index 2c99cc2..7f430da 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -16,7 +16,8 @@ variable "assume_role_arns" { variable "artifact_bucket_arn" { type = string - description = "s3 buckets access for code pipeline and codebuild" + description = "s3 buckets access for code pipeline and codebuild. Optional - not required for NO_SOURCE builds." + default = "" } variable "codestar_connection" { diff --git a/modules/iam-role/version.tf b/modules/iam-role/version.tf index 9121a5f..7f99bce 100644 --- a/modules/iam-role/version.tf +++ b/modules/iam-role/version.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.0" + version = "> 5.0, < 7.0" } } } diff --git a/variables.tf b/variables.tf index 6083846..9ef569b 100644 --- a/variables.tf +++ b/variables.tf @@ -5,12 +5,14 @@ variable "tags" { variable "artifacts_bucket" { type = string - description = "s3 bucket used for codepipeline artifacts" + description = "s3 bucket used for codepipeline artifacts. Optional - not required when using NO_SOURCE builds." + default = null } variable "codestar_connection" { type = string description = "codestar connection arn for github repository" + default = null } variable "codebuild_projects" { @@ -27,6 +29,10 @@ variable "codebuild_projects" { buildspec_file_name = optional(string, null) buildspec_file = optional(string, null) terraform_version = optional(string, "terraform-1.5.0-1.x86_64") + source_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, CODECOMMIT, GITHUB, GITHUB_ENTERPRISE, BITBUCKET, S3, NO_SOURCE + source_location = optional(string, null) # Required when source_type is not CODEPIPELINE or NO_SOURCE + artifacts_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, NO_ARTIFACTS, S3 + artifacts_location = optional(string, null) # Required when artifacts_type is S3 create_role = optional(bool, false) role_data = optional(object({ name = string @@ -151,10 +157,10 @@ variable "role_data" { variable "chatbot_data" { type = object({ name = string - slack_channel_id = string - slack_workspace_id = string + slack_channel_id = optional(string, null) # Required only when enable_slack_integration is true + slack_workspace_id = optional(string, null) # Required only when enable_slack_integration is true. Must contain only uppercase letters and numbers. guardrail_policies = optional(list(string), ["arn:aws:iam::aws:policy/AWSAccountManagementReadOnlyAccess"]) - enable_slack_integration = bool + enable_slack_integration = optional(bool, false) role_polices = optional(list(object({ policy_document = any policy_name = string @@ -162,6 +168,6 @@ variable "chatbot_data" { })), []) managed_policy_arns = optional(list(string), ["arn:aws:iam::aws:policy/AWSResourceExplorerReadOnlyAccess"]) }) - description = "(optional) Chatbot details to create integration" + description = "(optional) Chatbot details to create integration. Set chatbot_data to null to disable chatbot completely." default = null } diff --git a/version.tf b/version.tf index 9121a5f..7f99bce 100644 --- a/version.tf +++ b/version.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.0" + version = "> 5.0, < 7.0" } } } From 2bdf7eefa307614f15028ae0365fdbc2c68d5de8 Mon Sep 17 00:00:00 2001 From: "debash.bora" Date: Mon, 13 Oct 2025 17:30:07 +0530 Subject: [PATCH 2/3] Updated module to support multiple source type including NO_SOURCE --- .github/workflows/snyk.yaml | 35 ----------------------------------- examples/terraform/locals.tf | 6 +++--- 2 files changed, 3 insertions(+), 38 deletions(-) delete mode 100644 .github/workflows/snyk.yaml diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml deleted file mode 100644 index 7d57e95..0000000 --- a/.github/workflows/snyk.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: snyk - -on: # yamllint disable-line rule:truthy - push: - branches: - - '**' # matches every branch - - '!main' # excludes main - pull_request: - branches: - - main - -jobs: - security: - runs-on: - - ubuntu-latest - name: snyk - steps: - - name: checkout - uses: actions/checkout@v2 - - name: Vulnerability scan - uses: snyk/actions/iac@master - with: - command: monitor - args: --severity-threshold=low - - name: Set up Node 16 - uses: actions/setup-node@v3 - with: - node-version: 16 - - name: install Snyk CLI - run: npm install -g snyk - - name: snyk monitor - run: snyk iac test --report - env: - SNYK_TOKEN: ${{ secrets.ARC_SNYK_TOKEN }} diff --git a/examples/terraform/locals.tf b/examples/terraform/locals.tf index dc15df1..b54a438 100644 --- a/examples/terraform/locals.tf +++ b/examples/terraform/locals.tf @@ -1,7 +1,7 @@ locals { environment_role = { - dev = "arn:aws:iam::884360309640:role/debash-iam-role" + dev = "arn:aws:iam::xxxx:role/debash-iam-role" } branch_map = { @@ -24,8 +24,8 @@ locals { chatbot_data = { name = "${var.namespace}-slack" - slack_channel_id = "C09L9CJ4EAG" - slack_workspace_id = "T04C3L3M2" + slack_channel_id = "CxxxxxxAG" + slack_workspace_id = "T0xxxxx2" managed_policy_arns = ["arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess"] guardrail_policies = ["arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess"] role_polices = local.policies From f51c6bc35f1a5a9927bc974cde9cc8bed818ddb5 Mon Sep 17 00:00:00 2001 From: "debash.bora" Date: Tue, 14 Oct 2025 11:25:37 +0530 Subject: [PATCH 3/3] Updated codebuild to have env and vpc config --- README.md | 4 ++-- examples/simple-codebuild/README.md | 10 ++++++++-- examples/simple-codebuild/data.tf | 22 ++++++++++++++++++++++ examples/simple-codebuild/locals.tf | 23 +++++++++++++++++++++++ main.tf | 3 +++ modules/codebuild/main.tf | 19 +++++++++++++++++++ modules/codebuild/variables.tf | 20 ++++++++++++++++++++ modules/iam-role/main.tf | 18 ++++++++++++++++++ modules/iam-role/variables.tf | 6 ++++++ variables.tf | 11 +++++++++++ 10 files changed, 132 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4385114..87d245d 100644 --- a/README.md +++ b/README.md @@ -374,10 +374,10 @@ terraform destroy -var-file dev.tfvars |------|-------------|------|---------|:--------:| | [artifacts\_bucket](#input\_artifacts\_bucket) | s3 bucket used for codepipeline artifacts. Optional - not required when using NO\_SOURCE builds. | `string` | `null` | no | | [chatbot\_data](#input\_chatbot\_data) | (optional) Chatbot details to create integration. Set chatbot\_data to null to disable chatbot completely. |
object({
name = string
slack_channel_id = optional(string, null) # Required only when enable_slack_integration is true
slack_workspace_id = optional(string, null) # Required only when enable_slack_integration is true. Must contain only uppercase letters and numbers.
guardrail_policies = optional(list(string), ["arn:aws:iam::aws:policy/AWSAccountManagementReadOnlyAccess"])
enable_slack_integration = optional(bool, false)
role_polices = optional(list(object({
policy_document = any
policy_name = string

})), [])
managed_policy_arns = optional(list(string), ["arn:aws:iam::aws:policy/AWSResourceExplorerReadOnlyAccess"])
})
| `null` | no | -| [codebuild\_projects](#input\_codebuild\_projects) | Values to create Codebuild project |
map(object({
description = optional(string, "")
build_timeout = optional(number, 15)
queued_timeout = optional(number, 15)
compute_type = optional(string, "BUILD_GENERAL1_SMALL")
compute_image = optional(string, "aws/codebuild/amazonlinux2-x86_64-standard:5.0")
compute_type_container = optional(string, "LINUX_CONTAINER")
image_pull_credentials_type = optional(string, "CODEBUILD")
privileged_mode = optional(bool, false)
build_type = string
buildspec_file_name = optional(string, null)
buildspec_file = optional(string, null)
terraform_version = optional(string, "terraform-1.5.0-1.x86_64")
source_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, CODECOMMIT, GITHUB, GITHUB_ENTERPRISE, BITBUCKET, S3, NO_SOURCE
source_location = optional(string, null) # Required when source_type is not CODEPIPELINE or NO_SOURCE
artifacts_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, NO_ARTIFACTS, S3
artifacts_location = optional(string, null) # Required when artifacts_type is S3
create_role = optional(bool, false)
role_data = optional(object({
name = string
pipeline_service = optional(string, null)
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}), null)
}))
| `null` | no | +| [codebuild\_projects](#input\_codebuild\_projects) | Values to create Codebuild project |
map(object({
description = optional(string, "")
build_timeout = optional(number, 15)
queued_timeout = optional(number, 15)
compute_type = optional(string, "BUILD_GENERAL1_SMALL")
compute_image = optional(string, "aws/codebuild/amazonlinux2-x86_64-standard:5.0")
compute_type_container = optional(string, "LINUX_CONTAINER")
image_pull_credentials_type = optional(string, "CODEBUILD")
privileged_mode = optional(bool, false)
build_type = string
buildspec_file_name = optional(string, null)
buildspec_file = optional(string, null)
terraform_version = optional(string, "terraform-1.5.0-1.x86_64")
source_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, CODECOMMIT, GITHUB, GITHUB_ENTERPRISE, BITBUCKET, S3, NO_SOURCE
source_location = optional(string, null) # Required when source_type is not CODEPIPELINE or NO_SOURCE
artifacts_type = optional(string, "CODEPIPELINE") # Valid values: CODEPIPELINE, NO_ARTIFACTS, S3
artifacts_location = optional(string, null) # Required when artifacts_type is S3
create_role = optional(bool, false)
role_data = optional(object({
name = string
pipeline_service = optional(string, null)
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}), null)
vpc_config = optional(object({
vpc_id = string
subnets = list(string)
security_group_ids = list(string)
}), null)
environment_variables = optional(list(object({
name = string
value = string
type = optional(string, "PLAINTEXT")
})), [])
}))
| `null` | no | | [codepipelines](#input\_codepipelines) | Codepipeline data to create pipeline and stages |
map(object({
artifact_store_s3_kms_arn = string

source_repositories = list(object({
name = string
output_artifacts = optional(list(string), ["source_output"])
github_repository = string
github_branch = string
auto_trigger = optional(bool, true)
}))

pipeline_stages = list(object({
stage_name = string
name = string
category = optional(string, "Build")
provider = optional(string, "CodeBuild")
input_artifacts = optional(list(string), [])
output_artifacts = optional(list(string), [])
version = string
project_name = optional(string, null)
environment_variables = optional(list(object({
name = string
value = string
type = optional(string, "PLAINTEXT")
})),
[]
)
}))
create_role = optional(bool, false)
role_data = optional(object({
name = string
github_secret_arn = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}),
null)

trigger = optional(list(object({
source_action_name = string

push = list(object({
branches = object({
includes = list(string)
excludes = list(string)
})
file_paths = object({
includes = list(string)
excludes = list(string)
})
})
)

pull_request = list(object({
events = list(string)
filter = list(object({
branches = object({
includes = list(string)
excludes = list(string)
})
file_paths = object({
includes = list(string)
excludes = list(string)
})
})
) }))

})), [])

notification_data = optional(map(object({
detail_type = optional(string, "FULL")
event_type_ids = optional(list(string), [
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-canceled",
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-resumed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-superseded",
"codepipeline-pipeline-manual-approval-failed",
"codepipeline-pipeline-manual-approval-needed"
])
targets = list(object({
address = string // eg SNS arn
type = optional(string, "SNS") // Type can be "SNS" , AWSChatbotSlack etc
}))
})), null)

}))
| `{}` | no | | [codestar\_connection](#input\_codestar\_connection) | codestar connection arn for github repository | `string` | `null` | no | -| [role\_data](#input\_role\_data) | Roles to be created |
map(object({
pipeline_service = string
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
}))
| `{}` | no | +| [role\_data](#input\_role\_data) | Roles to be created |
map(object({
pipeline_service = string
assume_role_arns = optional(list(string), null)
github_secret_arn = optional(string, null)
terraform_state_s3_bucket = optional(string, null)
dynamodb_lock_table = optional(string, null)
additional_iam_policy_doc_json_list = optional(list(any), [])
enable_vpc = optional(bool, false)
}))
| `{}` | no | | [tags](#input\_tags) | Tags for AWS resources | `map(string)` | n/a | yes | ## Outputs diff --git a/examples/simple-codebuild/README.md b/examples/simple-codebuild/README.md index 7937d58..0ffcd6a 100644 --- a/examples/simple-codebuild/README.md +++ b/examples/simple-codebuild/README.md @@ -95,7 +95,9 @@ Add environment variables in the buildspec `env.variables` section or pass them ## Providers -No providers. +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 6.16.0 | ## Modules @@ -106,7 +108,11 @@ No providers. ## Resources -No resources. +| Name | Type | +|------|------| +| [aws_security_group.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/security_group) | data source | +| [aws_subnets.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | data source | +| [aws_vpc.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | ## Inputs diff --git a/examples/simple-codebuild/data.tf b/examples/simple-codebuild/data.tf index 346c8b3..6ac9b75 100644 --- a/examples/simple-codebuild/data.tf +++ b/examples/simple-codebuild/data.tf @@ -1,2 +1,24 @@ # This simple example only needs basic AWS account info # The CodeBuild project with NO_SOURCE doesn't require additional IAM policies + +# Fetch default VPC +data "aws_vpc" "default" { + filter { + name = "tag:Name" + values = ["arc-poc-vpc"] + } +} + +# Fetch subnets from default VPC +data "aws_subnets" "default" { + filter { + name = "vpc-id" + values = [data.aws_vpc.default.id] + } +} + +# Fetch default security group +data "aws_security_group" "default" { + vpc_id = data.aws_vpc.default.id + name = "default" +} diff --git a/examples/simple-codebuild/locals.tf b/examples/simple-codebuild/locals.tf index 75387aa..754c943 100644 --- a/examples/simple-codebuild/locals.tf +++ b/examples/simple-codebuild/locals.tf @@ -10,6 +10,7 @@ locals { terraform_state_s3_bucket = null dynamodb_lock_table = null additional_iam_policy_doc_json_list = [] + enable_vpc = true # Enable VPC permissions for CodeBuild } } @@ -30,6 +31,7 @@ locals { commands: - echo "Pre-build phase started" - echo "Environment: $MESSAGE" + - echo "Custom Variable: $CUSTOM_VAR" build: commands: - echo "Build phase started" @@ -53,6 +55,27 @@ locals { artifacts_location = null artifacts_bucket = null # Not needed for NO_SOURCE privileged_mode = false + + # VPC Configuration (optional - using default VPC) + vpc_config = { + vpc_id = data.aws_vpc.default.id + subnets = data.aws_subnets.default.ids + security_group_ids = [data.aws_security_group.default.id] + } + + # Environment Variables + environment_variables = [ + { + name = "CUSTOM_VAR" + value = "custom-value" + type = "PLAINTEXT" + }, + { + name = "ENVIRONMENT" + value = var.environment + type = "PLAINTEXT" + } + ] } } } diff --git a/main.tf b/main.tf index 8dcadff..3923b01 100644 --- a/main.tf +++ b/main.tf @@ -11,6 +11,7 @@ module "role" { terraform_state_s3_bucket = each.value.terraform_state_s3_bucket dynamodb_lock_table = each.value.dynamodb_lock_table additional_iam_policy_doc_json_list = each.value.additional_iam_policy_doc_json_list + enable_vpc = each.value.enable_vpc tags = var.tags } @@ -48,6 +49,8 @@ module "codebuild" { } ) + vpc_config = each.value.vpc_config + environment_variables = each.value.environment_variables tags = var.tags diff --git a/modules/codebuild/main.tf b/modules/codebuild/main.tf index a3e5b60..153a9d3 100644 --- a/modules/codebuild/main.tf +++ b/modules/codebuild/main.tf @@ -11,6 +11,7 @@ module "role" { terraform_state_s3_bucket = var.role_data.terraform_state_s3_bucket dynamodb_lock_table = var.role_data.dynamodb_lock_table additional_iam_policy_doc_json_list = var.role_data.additional_iam_policy_doc_json_list + enable_vpc = var.vpc_config != null tags = var.tags } @@ -36,6 +37,15 @@ resource "aws_codebuild_project" "this" { type = var.compute_type_container image_pull_credentials_type = var.image_pull_credentials_type privileged_mode = var.privileged_mode + + dynamic "environment_variable" { + for_each = var.environment_variables + content { + name = environment_variable.value.name + value = environment_variable.value.value + type = environment_variable.value.type + } + } } source { @@ -46,5 +56,14 @@ resource "aws_codebuild_project" "this" { }) : var.buildspec_file } + dynamic "vpc_config" { + for_each = var.vpc_config != null ? [var.vpc_config] : [] + content { + vpc_id = vpc_config.value.vpc_id + subnets = vpc_config.value.subnets + security_group_ids = vpc_config.value.security_group_ids + } + } + tags = var.tags } diff --git a/modules/codebuild/variables.tf b/modules/codebuild/variables.tf index b647389..4e9017c 100644 --- a/modules/codebuild/variables.tf +++ b/modules/codebuild/variables.tf @@ -127,3 +127,23 @@ variable "tags" { type = map(string) description = "Tags for AWS resources" } + +variable "vpc_config" { + type = object({ + vpc_id = string + subnets = list(string) + security_group_ids = list(string) + }) + description = "VPC configuration for CodeBuild project. Required to run builds in a VPC." + default = null +} + +variable "environment_variables" { + type = list(object({ + name = string + value = string + type = optional(string, "PLAINTEXT") + })) + description = "Environment variables for CodeBuild project" + default = [] +} diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index b81f1ee..7215040 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -76,6 +76,24 @@ data "aws_iam_policy_document" "codebuild" { } } + dynamic "statement" { + for_each = var.enable_vpc ? [1] : [] + content { + effect = "Allow" + actions = [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeDhcpOptions", + "ec2:DescribeVpcs", + "ec2:CreateNetworkInterfacePermission" + ] + resources = ["*"] + } + } + } resource "aws_iam_role" "this" { diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index 7f430da..31e3768 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -55,3 +55,9 @@ variable "tags" { description = "Tags for IAM role" default = {} } + +variable "enable_vpc" { + type = bool + description = "Enable VPC configuration for CodeBuild. When true, adds EC2 network interface permissions." + default = false +} diff --git a/variables.tf b/variables.tf index 9ef569b..1c6d4a5 100644 --- a/variables.tf +++ b/variables.tf @@ -43,6 +43,16 @@ variable "codebuild_projects" { dynamodb_lock_table = optional(string, null) additional_iam_policy_doc_json_list = optional(list(any), []) }), null) + vpc_config = optional(object({ + vpc_id = string + subnets = list(string) + security_group_ids = list(string) + }), null) + environment_variables = optional(list(object({ + name = string + value = string + type = optional(string, "PLAINTEXT") + })), []) })) description = "Values to create Codebuild project" default = null // null doesn't create codebuild project @@ -148,6 +158,7 @@ variable "role_data" { terraform_state_s3_bucket = optional(string, null) dynamodb_lock_table = optional(string, null) additional_iam_policy_doc_json_list = optional(list(any), []) + enable_vpc = optional(bool, false) })) description = "Roles to be created" default = {}