diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml deleted file mode 100644 index cf3f91c..0000000 --- a/.github/workflows/terraform-ci.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: 'Terraform Module CI' - -on: - push: - branches: - - main - paths: - - '**/*.tf' - pull_request: - branches: - - main - paths: - - '**/*.tf' - workflow_dispatch: - -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 - - - name: Run Checkov - uses: bridgecrewio/checkov-action@master - with: - directory: . - framework: terraform - skip_check: CKV_AWS_115,CKV_AWS_116 - quiet: true - soft_fail: true - output_format: sarif - output_file: checkov-results.sarif - - release: - name: 'Create Release' - needs: validate - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - runs-on: self-hosted - permissions: - contents: write - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.9' - - - name: Install Commitizen - run: | - pip install commitizen - - - 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/.github/workflows/terraform-release.yaml b/.github/workflows/terraform-release.yaml new file mode 100644 index 0000000..04b96db --- /dev/null +++ b/.github/workflows/terraform-release.yaml @@ -0,0 +1,40 @@ +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 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: 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: Run Release Action + uses: CSVD/releaser@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/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d247a5..7ef969f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: # Terraform Hooks - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.97.3 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases + rev: v1.98.0 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: - id: terraform_fmt args: @@ -104,6 +104,6 @@ repos: # - --hook-config=--parallelism-ci-cpu-cores=2 - repo: https://github.com/ljnsn/cz-conventional-gitmoji - rev: v0.6.1 + rev: v0.7.0 hooks: - id: conventional-gitmoji diff --git a/README.md b/README.md index 9e26efa..95208a2 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Common issues and solutions: | [terraform](#requirement\_terraform) | >= 0.13 | | [aws](#requirement\_aws) | ~> 5.14 | | [helm](#requirement\_helm) | ~> 2.11 | +| [keycloak](#requirement\_keycloak) | >= 5.0.0 | | [kubernetes](#requirement\_kubernetes) | ~> 2.23 | | [null](#requirement\_null) | ~> 3.2 | | [random](#requirement\_random) | ~> 3.6 | @@ -76,8 +77,8 @@ Common issues and solutions: | Name | Source | Version | |------|--------|---------| -| [images](#module\_images) | git@github.e.it.census.gov:terraform-modules/aws-ecr-copy-images.git/ | tf-upgrade | -| [ingress\_resources](#module\_ingress\_resources) | git@github.e.it.census.gov:SCT-Engineering/tfmod-istio-service-ingress.git | main | +| [images](#module\_images) | git::https://github.e.it.census.gov/terraform-modules/aws-ecr-copy-images.git/ | tf-upgrade | +| [ingress\_resources](#module\_ingress\_resources) | git::https://github.e.it.census.gov/SCT-Engineering/tfmod-istio-service-ingress.git | main | ## Resources @@ -92,22 +93,27 @@ Common issues and solutions: | [cluster\_domain](#input\_cluster\_domain) | The domain name used to reference ingresses for the cluster. | `string` | n/a | yes | | [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 | | [default\_storage\_class](#input\_default\_storage\_class) | The default storage class to use for persistent volumes | `string` | `"gp3-encrypted"` | no | -| [keycloak\_database](#input\_keycloak\_database) | The name of the database to create in the RDS instance. | `string` | `"bitnami_keycloak"` | no | -| [keycloak\_password](#input\_keycloak\_password) | The initial password for the database. | `string` | `"bn_keycloak"` | no | +| [keycloak\_database](#input\_keycloak\_database) | The name of the database to create in the RDS instance. | `string` | `"keycloak"` | no | +| [keycloak\_password](#input\_keycloak\_password) | The initial password for the database. | `string` | `"keycloak"` | no | | [keycloak\_tag](#input\_keycloak\_tag) | The image tag associated with the keycloak\_chart\_version | `string` | `"22.0.1-debian-11-r30"` | no | -| [keycloak\_user](#input\_keycloak\_user) | The username for the database. | `string` | `"bn_keycloak"` | no | +| [keycloak\_user](#input\_keycloak\_user) | The username for the database. | `string` | `"keycloak"` | no | | [namespace](#input\_namespace) | The namespace which will be created and into which keycloak will be deployed. | `string` | `"keycloak"` | no | | [postgresql\_tag](#input\_postgresql\_tag) | The tag of the postgresql image to use. | `string` | `"17.4.0"` | no | | [profile](#input\_profile) | AWS config profile used to upload images into ECR | `string` | `""` | no | -| [service\_name](#input\_service\_name) | The name of the service to be deployed in the EKS cluster. | `string` | `"keycloak-service"` | no | +| [service\_name](#input\_service\_name) | The name of the service to be deployed in the EKS cluster. | `string` | `"service"` | no | | [telemetry\_namespace](#input\_telemetry\_namespace) | The namespace used for telemetry. | `string` | `"namespace"` | no | ## Outputs | Name | Description | |------|-------------| +| [admin\_internal\_endpoint](#output\_admin\_internal\_endpoint) | internal endpoint for admin | +| [client\_id](#output\_client\_id) | The client ID for accessing keycloak. | +| [client\_secret](#output\_client\_secret) | The client secret for accessing keycloak. | +| [discovery\_url](#output\_discovery\_url) | The URL for discovering keycloak services. | | [module\_name](#output\_module\_name) | The name of this module. | | [module\_version](#output\_module\_version) | The version of this module. | | [namespace](#output\_namespace) | The namespace in which keycloak gets installed in. | +| [provider\_config](#output\_provider\_config) | Add provider configuration block for easier consumption | | [public\_endpoint](#output\_public\_endpoint) | The endpoint at which keycloak can be reached from outside the cluster. | diff --git a/copy_images.tf b/copy_images.tf index 86ee348..9ea2ccd 100644 --- a/copy_images.tf +++ b/copy_images.tf @@ -36,7 +36,7 @@ locals { } module "images" { - source = "git@github.e.it.census.gov:terraform-modules/aws-ecr-copy-images.git/?ref=tf-upgrade" + source = "git::https://github.e.it.census.gov/terraform-modules/aws-ecr-copy-images.git/?ref=tf-upgrade" profile = var.profile application_name = var.cluster_name diff --git a/keycloak-values.yaml.tmpl b/keycloak-values.yaml.tmpl index beaa0c1..a17a86b 100644 --- a/keycloak-values.yaml.tmpl +++ b/keycloak-values.yaml.tmpl @@ -3,45 +3,64 @@ replicaCount: 3 global: security: allowInsecureImages: true - imageRegistry: "${image_registry}" - defaultStorageClass: "${default_storage_class}" + imageRegistry: ${image_registry} + defaultStorageClass: ${default_storage_class} image: - registry: "${image_registry}" - repository: "${image_repository}" - tag: "${image_tag}" + registry: ${image_registry} + repository: ${image_repository} + tag: ${image_tag} autoscaling: enabled: true minReplicas: 1 maxReplicas: 5 - targetCPU: "80" + targetCPU: 80 resources: requests: cpu: 100m - memory: 256Mi + memory: 512Mi limits: cpu: 2000m - memory: 2Gi + memory: 1024Mi metrics: enabled: true serviceMonitor: enabled: true + prometheusRule: + enabled: true service: - type: "ClusterIP" + type: ClusterIP production: true -proxy: "edge" +proxy: edge + +# Bootstrap configuration +auth: + adminUser: ${keycloak_user} + adminPassword: ${keycloak_password} + +# Initial realm configuration +initConfiguration: + createInitialAdmin: true + importRealms: false + +# Realms import configuration (optional) +# initConfigurationFile: /configuration/realm-config.json +# extraVolumes: +# - name: realm-config +# configMap: +# name: keycloak-realm-config postgresql: architecture: replication image: registry: ${image_registry} - repository: "${postgresql_image_repository}" - tag: "${postgresql_image_tag}" + repository: ${postgresql_image_repository} + tag: ${postgresql_image_tag} enabled: true auth: username: ${keycloak_user} @@ -51,24 +70,45 @@ postgresql: resources: cpu: 100m memory: 128Mi + limits: + cpu: 2000m + memory: 2Gi readReplicas: resources: cpu: 100m memory: 128Mi + limits: + cpu: 2000m + memory: 2Gi metrics: enabled: true +tracing: + enabled: true extraEnv: | - name: JGROUPS_DISCOVERY_PROTOCOL value: dns.DNS_PING - name: JGROUPS_DISCOVERY_PROPERTIES - value: "dns_query=${serviceDnsName}" + value: 'dns_query={{ include "keycloak.serviceDnsName" . }}' - name: CACHE_OWNERS_COUNT - value: "3" + value: 3 - name: CACHE_OWNERS_AUTH_SESSIONS_COUNT - value: "3" + value: 3 - name: PROXY_ADDRESS_FORWARDING - value: "true" + value: true - name: KC_CONFIG_FILE - value: "/opt/bitnami/keycloak/kcadm.config" + value: /opt/bitnami/keycloak/kcadm.config + - name: KEYCLOAK_USER + value: ${keycloak_user} + - name: KEYCLOAK_PASSWORD + value: ${keycloak_password} + - name: KEYCLOAK_STATISTICS + value: all + - name: JAVA_OPTS + value: >- + -XX:+UseContainerSupport + -XX:MaxRAMPercentage=50.0 + -Djava.net.preferIPv4Stack=true + -Djboss.modules.system.pkgs=$JBOSS_MODULES_SYSTEM_PKGS + -Djava.awt.headless=true diff --git a/main.tf b/main.tf index 0cf5121..1d320cd 100644 --- a/main.tf +++ b/main.tf @@ -1,9 +1,7 @@ # main.tf locals { - internal_port_number = "80" - public_domain = var.cluster_domain - - service_name = var.service_name + internal_port_number = "8080" + admin_internal_port_number = "9000" } resource "helm_release" "keycloak" { @@ -11,23 +9,24 @@ resource "helm_release" "keycloak" { name = var.service_name repository = "${path.module}/charts" create_namespace = true - timeout = 120 + timeout = 300 wait = true namespace = var.namespace values = [templatefile("${path.module}/keycloak-values.yaml.tmpl", { + cluster_name = var.cluster_name, + default_storage_class = var.default_storage_class, image_registry = module.images.images[local.keycloak_key].dest_registry, image_repository = module.images.images[local.keycloak_key].dest_repository, image_tag = module.images.images[local.keycloak_key].tag, - default_storage_class = var.default_storage_class, - serviceDnsName = format("https://%s.%s", var.service_name, local.public_domain), - telemetry_namespace = var.telemetry_namespace, + keycloak_database = var.keycloak_database, + keycloak_password = var.keycloak_password, + keycloak_user = var.keycloak_user, postgresql_image_registry = module.images.images[local.postgresql_key].dest_registry, postgresql_image_repository = module.images.images[local.postgresql_key].dest_repository, postgresql_image_tag = module.images.images[local.postgresql_key].tag, - keycloak_database = var.keycloak_database, - keycloak_user = var.keycloak_user, - keycloak_password = var.keycloak_password, + serviceDnsName = format("https://%s.%s", var.service_name, var.cluster_domain), + telemetry_namespace = var.telemetry_namespace, })] depends_on = [ @@ -38,10 +37,10 @@ resource "helm_release" "keycloak" { module "ingress_resources" { depends_on = [helm_release.keycloak] # tflint-ignore: terraform_module_pinned_source - source = "git@github.e.it.census.gov:SCT-Engineering/tfmod-istio-service-ingress.git?ref=main" - public_hostname = local.service_name - public_domain = local.public_domain - service_name = local.service_name + source = "git::https://github.e.it.census.gov/SCT-Engineering/tfmod-istio-service-ingress.git?ref=main" + public_hostname = var.service_name + public_domain = var.cluster_domain + service_name = var.service_name service_namespace = var.namespace service_port = local.internal_port_number } diff --git a/outputs.tf b/outputs.tf index 9a125f9..998f7a7 100644 --- a/outputs.tf +++ b/outputs.tf @@ -3,15 +3,39 @@ output "public_endpoint" { value = module.ingress_resources.service_url } -# output "client_id" { -# description = "The client ID for accessing keycloak." -# value = dependency.eks_keycloak.outputs.client_id -# } - -# output "client_secret" { -# description = "The client secret for accessing keycloak." -# value = dependency.eks_keycloak.outputs.client_secret -# } +output "admin_internal_endpoint" { + description = "internal endpoint for admin" + value = { + hostname = format("%v.%v.svc.cluster.local", var.service_name, var.namespace) + port_number = local.admin_internal_port_number + url = format("http://%v.%v.svc.cluster.local:%v", var.service_name, var.namespace, local.admin_internal_port_number) + } +} + +# Add provider configuration block for easier consumption +output "provider_config" { + value = { + url = "${module.ingress_resources.service_url}/auth" + realm = "master" + client_id = var.keycloak_user + } + sensitive = true +} + +output "discovery_url" { + description = "The URL for discovering keycloak services." + value = "${module.ingress_resources.service_url}/auth/realms/master" +} + +output "client_id" { + description = "The client ID for accessing keycloak." + value = var.keycloak_user +} + +output "client_secret" { + description = "The client secret for accessing keycloak." + value = var.keycloak_password +} output "namespace" { description = "The namespace in which keycloak gets installed in." diff --git a/requirements.tf b/requirements.tf index 9276c48..a9f3181 100644 --- a/requirements.tf +++ b/requirements.tf @@ -10,6 +10,10 @@ terraform { source = "hashicorp/helm" version = "~> 2.11" } + keycloak = { + source = "keycloak/keycloak" + version = ">= 5.0.0" + } kubernetes = { source = "hashicorp/kubernetes" version = "~> 2.23" diff --git a/variables.tf b/variables.tf index c53d4f5..194fdbc 100644 --- a/variables.tf +++ b/variables.tf @@ -29,19 +29,19 @@ variable "cluster_domain" { variable "keycloak_database" { description = "The name of the database to create in the RDS instance." type = string - default = "bitnami_keycloak" + default = "keycloak" } variable "keycloak_user" { description = "The username for the database." type = string - default = "bn_keycloak" + default = "keycloak" } variable "keycloak_password" { description = "The initial password for the database." type = string - default = "bn_keycloak" + default = "keycloak" } variable "default_storage_class" { @@ -53,7 +53,7 @@ variable "default_storage_class" { variable "service_name" { description = "The name of the service to be deployed in the EKS cluster." type = string - default = "keycloak-service" + default = "service" } variable "telemetry_namespace" {