Resource Packs

Cloud

Example

Flavor

Feature

Config

Example: config resource the configures ECR image pull secrets

Configuration

This example configures a config Resource Definition, which injects an imagePullSecret into workloads. An imagePullSecret is required when the k8s cluster runs outside AWS and workload use private ECR images.

The image used to fetch and periodically renew the secrets can be found here https://github.com/humanitec-architecture/aws-ecr-credentials-refresh and the configs are heavily inspired by https://skryvets.com/blog/2021/03/15/kubernetes-pull-image-from-private-ecr-registry/.

Orchestrator setup

graph LR;
  workload_1 --> config["imagepullsecret, resource_type: config"]
  workload_2 --> config["imagepullsecret, resource_type: config"]

Terraform docs

Requirements

Name Version
terraform >= 1.3.0
aws ~> 5.0
humanitec ~> 1.0
random ~> 3.5

Providers

Name Version
aws ~> 5.0
humanitec ~> 1.0

Modules

Name Source Version
imagepullsecret ../../../humanitec-resource-defs/config/imagepullsecret n/a

Resources

Name Type
aws_iam_access_key.cluster_ecr_pull resource
aws_iam_user.cluster_ecr_pull resource
aws_iam_user_policy_attachment.cluster_ecr_pull resource
aws_secretsmanager_secret.ecr_pull resource
aws_secretsmanager_secret_version.ecr_pull resource
humanitec_application.example resource
humanitec_resource_definition.workload resource
humanitec_resource_definition_criteria.imagepullsecret resource
humanitec_resource_definition_criteria.workload resource
aws_caller_identity.current data source

Inputs

Name Description Type Default Required
humanitec_secret_store_id Humanitec Secret Store ID that points to AWS Secrets Manager string n/a yes
region AWS Region string n/a yes
name Name of the example application string "hum-rp-ips-example" no
prefix Prefix of the created resources string "hum-rp-ips-ex-" no

main.tf (view on GitHub) :

# AWS IAM user used by the k8s-cluster to pull images from ECR

resource "aws_iam_user" "cluster_ecr_pull" {
  name = "cluster_ecr_pull"
}

resource "aws_iam_user_policy_attachment" "cluster_ecr_pull" {
  user = aws_iam_user.cluster_ecr_pull.name
  # https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonEC2ContainerRegistryReadOnly
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
}

resource "aws_iam_access_key" "cluster_ecr_pull" {
  user = aws_iam_user.cluster_ecr_pull.name

  # Ensure that the policy is not deleted before the access key
  depends_on = [aws_iam_user_policy_attachment.cluster_ecr_pull]
}

# Store the access key and secret in AWS Secrets Manager for the Humanitec Operator to be able to fetch them.

locals {
  ecr_pull_secrets = {
    aws-access-key-id     = aws_iam_access_key.cluster_ecr_pull.id
    aws-secret-access-key = aws_iam_access_key.cluster_ecr_pull.secret
  }

  ecr_pull_secret_refs = {
    for key, value in local.ecr_pull_secrets : key => {
      ref     = aws_secretsmanager_secret.ecr_pull[key].name
      store   = var.humanitec_secret_store_id
      version = aws_secretsmanager_secret_version.ecr_pull[key].version_id
    }
  }
}

resource "aws_secretsmanager_secret" "ecr_pull" {
  for_each = local.ecr_pull_secrets
  name     = "humanitec-ecr-pull-secret-${each.key}"
}

resource "aws_secretsmanager_secret_version" "ecr_pull" {
  for_each = local.ecr_pull_secrets

  secret_id     = aws_secretsmanager_secret.ecr_pull[each.key].id
  secret_string = each.value
}

# Example application and resource definition criteria

resource "humanitec_application" "example" {
  id   = var.name
  name = var.name
}

# Current AWS Account ID
data "aws_caller_identity" "current" {}

locals {
  imagepullsecret_config_res_id = "imagepullsecret"
}

module "imagepullsecret" {
  source = "github.com/humanitec-architecture/resource-packs-aws?ref=v2024-06-14//humanitec-resource-defs/config/imagepullsecret"

  prefix = var.prefix

  account_id            = data.aws_caller_identity.current.account_id
  region                = var.region
  access_key_id_ref     = local.ecr_pull_secret_refs["aws-access-key-id"]
  secret_access_key_ref = local.ecr_pull_secret_refs["aws-secret-access-key"]
}

resource "humanitec_resource_definition_criteria" "imagepullsecret" {
  resource_definition_id = module.imagepullsecret.id
  app_id                 = humanitec_application.example.id
  res_id                 = local.imagepullsecret_config_res_id
  class                  = "default"

  force_delete = true
}

resource "humanitec_resource_definition" "workload" {
  driver_type = "humanitec/template"
  id          = "${var.prefix}workload"
  name        = "${var.prefix}workload"
  type        = "workload"

  driver_inputs = {
    values_string = jsonencode({
      templates = {
        outputs = <<EOL
update:
- op: add
  path: /spec/imagePullSecrets
  value:
    - name: $${resources["config.default#${local.imagepullsecret_config_res_id}"].outputs.secret_name}
EOL
      }
    })
  }
}

resource "humanitec_resource_definition_criteria" "workload" {
  resource_definition_id = humanitec_resource_definition.workload.id
  app_id                 = humanitec_application.example.id

  force_delete = true
}


providers.tf (view on GitHub) :

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    humanitec = {
      source  = "humanitec/humanitec"
      version = "~> 1.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.5"
    }
  }

  required_version = ">= 1.3.0"
}

provider "aws" {
  default_tags {
    tags = {
      "managed_by" = "terraform"
      "source"     = "github.com/humanitec-architecture/resource-pack-aws"
    }
  }
}

provider "humanitec" {}

provider "random" {}


terraform.tfvars.example (view on GitHub) :


# Humanitec Secret Store ID that points to AWS Secrets Manager
humanitec_secret_store_id = ""

# Name of the example application
name = "hum-rp-ips-example"

# Prefix of the created resources
prefix = "hum-rp-ips-ex-"

# AWS Region
region = ""

variables.tf (view on GitHub) :

variable "region" {
  description = "AWS Region"
  type        = string
}

variable "name" {
  description = "Name of the example application"
  type        = string
  default     = "hum-rp-ips-example"
}

variable "prefix" {
  description = "Prefix of the created resources"
  type        = string
  default     = "hum-rp-ips-ex-"
}

variable "humanitec_secret_store_id" {
  description = "Humanitec Secret Store ID that points to AWS Secrets Manager"
  type        = string
}

Top