diff --git a/.github/workflows/terraform-release.yaml b/.github/workflows/terraform-release.yaml new file mode 100644 index 0000000..90910bc --- /dev/null +++ b/.github/workflows/terraform-release.yaml @@ -0,0 +1,73 @@ +name: Terraform CI/CD +on: + workflow_dispatch: + pull_request: + types: [closed] + branches: + - main +jobs: + terraform-ci-cd: + runs-on: 229685449397 + permissions: + contents: write + + steps: + - name: Checkout code + uses: CSVD/gh-actions-checkout@v4 + + - name: Setup Terraform + uses: CSVD/gh-actions-setup-terraform@v3 + with: + terraform_version: "1.9.1" + + - name: Setup GITHUB Credentials + id: github_credentials + uses: CSVD/gh-auth@main + with: + github_app_pem_file: ${{ secrets.GH_APP_PEM_FILE }} + github_app_installation_id: ${{ vars.GH_APP_INSTALLATION_ID }} + github_app_id: ${{ vars.GH_APP_ID }} + + + - name: Debug Authentication + run: | + # Print the GitHub server URL + echo "GitHub Server URL: ${{ github.server_url }}" + + # Extract the host from the URL + HOST="${{ github.server_url }}" + HOST="${HOST#*//}" + HOST="${HOST%%/*}" + echo "GitHub Host: $HOST" + + # Check if token exists + if [[ -n "${{ steps.github_credentials.outputs.github_token }}" ]]; then + echo "Token generated successfully" + # Test the token with a simple GitHub API call (without exposing the token) + STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${{ steps.github_credentials.outputs.github_token }}" "${{ github.server_url }}/api/v3/user") + echo "API Test Status Code: $STATUS" + else + echo "No token was generated!" + fi + + - name: Setup GitHub CLI + run: | + # Force manual authentication since setup-git might not work with GitHub Enterprise + echo "${{ steps.github_credentials.outputs.github_token }}" > /tmp/token.txt + gh auth login --with-token --hostname "github.e.it.census.gov" < /tmp/token.txt + rm /tmp/token.txt + + # Test GitHub CLI auth status + gh auth status || echo "GitHub CLI authentication failed" + + - name: AWS Auth + id: aws_auth + uses: CSVD/aws-auth@main + with: + ecs: true + + - name: Run Terraform Module Release Action + uses: CSVD/terraform-module-release@main + with: + github-token: ${{ steps.github_credentials.outputs.github_token }} + working-directory: '.' diff --git a/.github/workflows/terraform-validate.yaml b/.github/workflows/terraform-validate.yaml new file mode 100644 index 0000000..ac349eb --- /dev/null +++ b/.github/workflows/terraform-validate.yaml @@ -0,0 +1,42 @@ +name: Terraform Validate +on: + pull_request: + workflow_dispatch: + +jobs: + + terraform-validate: + runs-on: "229685449397" + permissions: + contents: write + steps: + - name: Checkout code + uses: CSVD/gh-actions-checkout@v4 + + - name: Setup Terraform + uses: CSVD/gh-actions-setup-terraform@v2 + with: + terraform_version: '1.10.5' + + - name: Validate Terraform Configuration + id: validate + uses: CSVD/terraform-validate@main + + - name: Check Validation/Test Results + if: always() + run: | + # Set default values if outputs are empty + IS_VALID="${{ steps.validate.outputs.is_valid }}" + TESTS_PASSED="${{ steps.validate.outputs.tests_passed }}" + + # If outputs are empty, set them to false + [ -z "$IS_VALID" ] && IS_VALID="false" + [ -z "$TESTS_PASSED" ] && TESTS_PASSED="false" + + if [[ "$IS_VALID" != "true" || "$TESTS_PASSED" != "true" ]]; then + echo "Validation or test errors found:" + echo "${{ steps.validate.outputs.stderr }}" + exit 1 + else + echo "All validations and tests passed successfully!" + fi diff --git a/.github/workflows/terragrunt-cicd.yml b/.github/workflows/terragrunt-cicd.yml deleted file mode 100644 index a78523e..0000000 --- a/.github/workflows/terragrunt-cicd.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: 'Terraform Module CI' - -on: - push: - branches: - - main - paths: - - '**/*.hcl' - - '**/*.tf' - pull_request: - branches: - - main - paths: - - '**/*.hcl' - - '**/*.tf' - -permissions: - contents: read - pull-requests: write - -jobs: - validate: - name: 'Validate Module' - runs-on: self-hosted - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v2 - with: - terraform_version: 1.5.0 - - - name: Terraform Init - run: | - terraform init -backend=false - - - name: Terraform Format - run: | - terraform fmt -check - - - name: Terraform Validate - run: | - terraform validate - - - name: Run tflint - uses: terraform-linters/setup-tflint@v3 - if: github.event_name == 'pull_request' - - - name: Lint Terraform - if: github.event_name == 'pull_request' - run: | - tflint --format compact - - release: - name: 'Create Release' - needs: validate - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - runs-on: self-hosted - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.9' - - - name: Install Commitizen - run: | - pip install commitizen - - - name: Configure Git - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - - name: Bump Version and Generate Changelog - id: cz - run: | - cz bump --yes - echo "new_version=$(cz version --project)" >> $GITHUB_OUTPUT - echo "changelog=$(cz changelog --dry-run)" >> $GITHUB_OUTPUT - - - name: Create Release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: v${{ steps.cz.outputs.new_version }} - release_name: Release v${{ steps.cz.outputs.new_version }} - draft: false - prerelease: false - body: ${{ steps.cz.outputs.changelog }} diff --git a/README.md b/README.md index a450348..c9da336 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The following addons are automatically installed and configured: ```hcl module "eks" { - source = "git@github.e.it.census.gov:SCT-Engineering/tfmod-eks.git" + source = "https://github.e.it.census.gov/SCT-Engineering/terraform-aws-eks.git?ref=v20.35.0" cluster_name = "my-cluster" cluster_version = "1.28" @@ -111,7 +111,7 @@ efs-csi-controller 0 5m | Name | Source | Version | |------|--------|---------| | [cloudwatch\_observability\_irsa\_role](#module\_cloudwatch\_observability\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [cluster](#module\_cluster) | git@github.e.it.census.gov:SCT-Engineering/terraform-aws-eks.git | v20.33.1 | +| [cluster](#module\_cluster) | git::https://github.e.it.census.gov/SCT-Engineering/terraform-aws-eks/ | v20.35.0 | | [ebs\_csi\_irsa\_role](#module\_ebs\_csi\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | | [efs\_csi\_irsa\_role](#module\_efs\_csi\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | | [vpc\_cni\_irsa\_role](#module\_vpc\_cni\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | @@ -123,6 +123,7 @@ efs-csi-controller 0 5m | [aws_ec2_tag.container_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource | | [aws_security_group.additional_eks_cluster_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group.all_worker_mgmt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.extra_cluster_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group_rule.allow_sidecar_injection](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [terraform_data.subnet_validation](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | | [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | @@ -142,16 +143,18 @@ efs-csi-controller 0 5m | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [access\_entries](#input\_access\_entries) | Map of access entries to add to the cluster | `any` | `{}` | no | -| [census\_private\_cidr](#input\_census\_private\_cidr) | Census Private CIR Blocks | `list(string)` |
[
"148.129.0.0/16",
"172.16.0.0/12",
"192.168.0.0/16"
]
| no | -| [cluster\_endpoint\_public\_access](#input\_cluster\_endpoint\_public\_access) | Whether the EKS cluster API server endpoint is publicly accessible | `bool` | `false` | no | +| [census\_private\_cidr](#input\_census\_private\_cidr) | Census Private CIR Blocks | `list(string)` |
[
"148.129.0.0/16",
"172.16.0.0/12",
"192.168.0.0/16",
"10.0.0.0/16"
]
| no | +| [cloudwatch\_retention\_days](#input\_cloudwatch\_retention\_days) | number of days to retain logs in cloudwatch | `string` | `"14"` | no | +| [cluster\_endpoint\_private\_access](#input\_cluster\_endpoint\_private\_access) | Whether the EKS cluster API server endpoint is privately accessible | `bool` | `true` | no | +| [cluster\_endpoint\_public\_access](#input\_cluster\_endpoint\_public\_access) | Whether the EKS cluster API server endpoint is publicly accessible | `bool` | `true` | no | | [cluster\_name](#input\_cluster\_name) | EKS cluster name name component used through out the EKS cluster describing its purpose (ex: dice-dev) | `string` | n/a | yes | | [cluster\_version](#input\_cluster\_version) | Kubernetes version to use for the EKS cluster | `string` | n/a | yes | | [eks\_instance\_disk\_size](#input\_eks\_instance\_disk\_size) | Size of the EKS node disk in GB | `number` | `80` | no | -| [eks\_instance\_types](#input\_eks\_instance\_types) | List of EC2 instance types for the EKS node group | `list(string)` |
[
"t3a.large"
]
| no | +| [eks\_instance\_types](#input\_eks\_instance\_types) | List of EC2 instance types for the EKS node group | `list(string)` |
[
"t3a.medium"
]
| no | | [eks\_ng\_desired\_size](#input\_eks\_ng\_desired\_size) | Desired size of the EKS node group | `number` | `4` | no | | [eks\_ng\_max\_size](#input\_eks\_ng\_max\_size) | Maximum size of the EKS node group | `number` | `15` | no | | [eks\_ng\_min\_size](#input\_eks\_ng\_min\_size) | Minimum size of the EKS node group | `number` | `4` | no | -| [enable\_cluster\_creator\_admin\_permissions](#input\_enable\_cluster\_creator\_admin\_permissions) | Grant admin permissions to the cluster creator | `bool` | `false` | no | +| [enable\_cluster\_creator\_admin\_permissions](#input\_enable\_cluster\_creator\_admin\_permissions) | Grant admin permissions to the cluster creator | `bool` | `true` | no | | [subnets\_name](#input\_subnets\_name) | Name pattern for subnets to be used by EKS cluster | `string` | `"*-container-*"` | no | | [tags](#input\_tags) | Additional tags to apply to all resources | `map(string)` | `{}` | no | | [vpc\_name](#input\_vpc\_name) | Name of the VPC where EKS cluster will be created | `string` | n/a | yes | diff --git a/aws_data.tf b/aws_data.tf index 2c6aade..1402bc0 100644 --- a/aws_data.tf +++ b/aws_data.tf @@ -3,3 +3,31 @@ data "aws_caller_identity" "current" {} data "aws_arn" "current" { arn = data.aws_caller_identity.current.arn } +data "aws_vpc" "eks_vpc" { + filter { + name = "tag:Name" + values = [var.vpc_name] + } +} + +data "aws_subnets" "subnets" { + filter { + name = "tag:Name" + values = [var.subnets_name] + } + filter { + name = "vpc-id" + values = [data.aws_vpc.eks_vpc.id] + } +} + +data "aws_subnet" "subnets" { + for_each = toset(data.aws_subnets.subnets.ids) + id = each.key +} + +data "aws_ebs_default_kms_key" "current" {} + +data "aws_kms_key" "ebs_key" { + key_id = data.aws_ebs_default_kms_key.current.key_arn +} diff --git a/irsa_roles.tf b/irsa_roles.tf index a1129b5..ee75c98 100644 --- a/irsa_roles.tf +++ b/irsa_roles.tf @@ -2,7 +2,7 @@ module "vpc_cni_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "${var.cluster_name}-vpc-cni" + role_name = format("%v%v-%v", local.prefixes["eks-role"], var.cluster_name, "vpc-cni") attach_vpc_cni_policy = true vpc_cni_enable_ipv4 = true @@ -20,7 +20,7 @@ module "vpc_cni_irsa_role" { module "ebs_csi_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "${var.cluster_name}-ebs-csi-driver" + role_name = format("%v%v-%v", local.prefixes["eks-role"], var.cluster_name, "ebs-csi-driver") attach_ebs_csi_policy = true oidc_providers = { @@ -36,7 +36,7 @@ module "ebs_csi_irsa_role" { module "efs_csi_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "${var.cluster_name}-efs-csi-driver" + role_name = format("%v%v-%v", local.prefixes["eks-role"], var.cluster_name, "efs-csi-driver") attach_efs_csi_policy = true oidc_providers = { @@ -52,7 +52,7 @@ module "efs_csi_irsa_role" { module "cloudwatch_observability_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "${var.cluster_name}-cloudwatch-observability" + role_name = format("%v%v-%v", local.prefixes["eks-role"], var.cluster_name, "cloudwatch-observability") attach_cloudwatch_observability_policy = true oidc_providers = { diff --git a/main.tf b/main.tf index 709cb98..cf353f9 100644 --- a/main.tf +++ b/main.tf @@ -1,32 +1,3 @@ -data "aws_vpc" "eks_vpc" { - filter { - name = "tag:Name" - values = [var.vpc_name] - } -} - -data "aws_subnets" "subnets" { - filter { - name = "tag:Name" - values = [var.subnets_name] - } - filter { - name = "vpc-id" - values = [data.aws_vpc.eks_vpc.id] - } -} - -data "aws_subnet" "subnets" { - for_each = toset(data.aws_subnets.subnets.ids) - id = each.key -} - -data "aws_ebs_default_kms_key" "current" {} - -data "aws_kms_key" "ebs_key" { - key_id = data.aws_ebs_default_kms_key.current.key_arn -} - locals { additional_policies = {} base_tags = { @@ -55,13 +26,16 @@ resource "terraform_data" "subnet_validation" { } module "cluster" { - source = "git@github.e.it.census.gov:SCT-Engineering/terraform-aws-eks.git?ref=v20.33.1" + source = "git::https://github.e.it.census.gov/SCT-Engineering/terraform-aws-eks/?ref=v20.35.0" + access_entries = local.access_entries + cloudwatch_log_group_retention_in_days = var.cloudwatch_retention_days + cluster_endpoint_private_access = var.cluster_endpoint_private_access + cluster_endpoint_public_access = var.cluster_endpoint_public_access cluster_name = var.cluster_name + cluster_upgrade_policy = { support_type = "STANDARD" } cluster_version = var.cluster_version - cluster_endpoint_public_access = var.cluster_endpoint_public_access enable_cluster_creator_admin_permissions = var.enable_cluster_creator_admin_permissions - access_entries = local.access_entries cluster_enabled_log_types = [ "api", @@ -124,7 +98,7 @@ module "cluster" { max_size = var.eks_ng_max_size desired_size = var.eks_ng_desired_size - iam_role_name = local.ng_name + iam_role_name = format("%v%v-nodegroup", local.prefixes["eks-role"], var.cluster_name) iam_role_additional_policies = local.additional_policies block_device_mappings = { diff --git a/prefixes.tf b/prefixes.tf index f677e0a..4e2709e 100644 --- a/prefixes.tf +++ b/prefixes.tf @@ -29,6 +29,6 @@ locals { "eks-user" = "s-eks-" "eks-role" = "r-eks-" "eks-policy" = "p-eks-" - "eks-security-group" = "eks-" # "sg-eks-" + "eks-security-group" = "eks-sg-" # "sg-eks-" } } diff --git a/security_groups.tf b/security_groups.tf index 6683944..e62c641 100644 --- a/security_groups.tf +++ b/security_groups.tf @@ -4,13 +4,54 @@ locals { additional_eks_cluster_sg_name = format("%v%v-cluster", local.prefixes["eks-security-group"], var.cluster_name) } +resource "aws_security_group" "additional_eks_cluster_sg" { + name = local.additional_eks_cluster_sg_name + + tags = merge( + local.base_tags, + var.tags, + { "Name" = local.additional_eks_cluster_sg_name }, + ) + + vpc_id = data.aws_vpc.eks_vpc.id + + ingress { + from_port = 0 + to_port = 0 + protocol = -1 + + security_groups = [ + aws_security_group.all_worker_mgmt.id, + ] + } + + # in-VPC access to K8s API + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = concat(var.census_private_cidr, ["10.0.0.0/8"]) + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + lifecycle { + ignore_changes = [ingress, egress] + } +} + + resource "aws_security_group" "all_worker_mgmt" { name = local.all_worker_mgmt_name tags = merge( local.base_tags, var.tags, - tomap({ "Name" = local.all_worker_mgmt_name }), + { "Name" = local.all_worker_mgmt_name }, ) vpc_id = local.vpc_id @@ -28,15 +69,19 @@ resource "aws_security_group" "all_worker_mgmt" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } + lifecycle { + ignore_changes = [ingress, egress] + } } -resource "aws_security_group" "additional_eks_cluster_sg" { - name = local.additional_eks_cluster_sg_name +resource "aws_security_group" "extra_cluster_sg" { + name = format("%v%v-extra", local.prefixes["eks-security-group"], var.cluster_name) + description = format("Security group for additional access for EKS cluster %v", var.cluster_name) tags = merge( local.base_tags, var.tags, - tomap({ "Name" = local.additional_eks_cluster_sg_name }), + { "Name" = format("%v%v-extra", local.prefixes["eks-security-group"], var.cluster_name) }, ) vpc_id = data.aws_vpc.eks_vpc.id @@ -45,20 +90,21 @@ resource "aws_security_group" "additional_eks_cluster_sg" { from_port = 0 to_port = 0 protocol = -1 + self = true + } - security_groups = [ - aws_security_group.all_worker_mgmt.id, - ] + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = concat(var.census_private_cidr, ["10.0.0.0/8"]) } - # this grants in-VPC access to the K8S api - # updated to get all census private cidrs to get on-prem, as we are now sending the interface traffic over - # a private IP only (disabling public access). This is to reach a cluster api from another account and VPC - # so we open all the cloud accounts too + + # kubectl logs ingress { - from_port = 443 - to_port = 443 - protocol = "tcp" - # cidr_blocks = [ var.vpc_cidr_block ] + from_port = 10250 + to_port = 10250 + protocol = "tcp" cidr_blocks = concat(var.census_private_cidr, ["10.0.0.0/8"]) } @@ -68,4 +114,7 @@ resource "aws_security_group" "additional_eks_cluster_sg" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } + lifecycle { + ignore_changes = [ingress, egress] + } } diff --git a/variables.tf b/variables.tf index 4153048..8a9fb29 100644 --- a/variables.tf +++ b/variables.tf @@ -16,16 +16,22 @@ variable "cluster_version" { } } +variable "cluster_endpoint_private_access" { + description = "Whether the EKS cluster API server endpoint is privately accessible" + type = bool + default = true +} + variable "cluster_endpoint_public_access" { description = "Whether the EKS cluster API server endpoint is publicly accessible" type = bool - default = false + default = true } variable "enable_cluster_creator_admin_permissions" { description = "Grant admin permissions to the cluster creator" type = bool - default = false + default = true } variable "vpc_name" { @@ -61,7 +67,7 @@ variable "eks_instance_types" { description = "List of EC2 instance types for the EKS node group" type = list(string) default = [ - "t3a.large" + "t3a.medium" ] validation { condition = length(var.eks_instance_types) > 0 @@ -127,7 +133,7 @@ variable "access_entries" { variable "census_private_cidr" { description = "Census Private CIR Blocks" type = list(string) - default = ["148.129.0.0/16", "172.16.0.0/12", "192.168.0.0/16"] + default = ["148.129.0.0/16", "172.16.0.0/12", "192.168.0.0/16", "10.0.0.0/16"] validation { condition = alltrue([ for cidr in var.census_private_cidr : can(cidrhost(cidr, 0)) @@ -149,3 +155,9 @@ variable "tags" { error_message = "Tag keys must be <= 128 chars, values <= 256 chars, and both can only contain alphanumeric characters, spaces, and '.+-=@:_'." } } + +variable "cloudwatch_retention_days" { + description = "number of days to retain logs in cloudwatch" + type = string + default = "14" +}