This Terraform module is designed to efficiently manage secrets within an Elastic Kubernetes Service (EKS) cluster through the use of External Secrets Operator (ESO). The module ensures secrets in your EKS cluster are continuously synced with AWS Secret Manager, providing a centralized access point for your secret backend.

Key features:

  • Deploys External Secrets Operator and cluster-level SecretStore
  • Sets an IAM policy for specific access permissions to AWS Secrets Manager resources
  • Establishes an IAM role linked with the service accounts for AWS authentication
  • Creates a Kubernetes Custom Resource Definition (CRD) for AWS Secrets management

NOTE: After deploying the module, create a Kubernetes ExternalSecret to point the Secrets Manager secret to be synchronized.

If reloader.namespace_selector parameter is set, Namespace manifests are required to have specific labels configured to match Reloader namespace selector.
If reloader.resource_label_selector parameter is set, Secrets\Config manifests are required to have specific labels configured to match Reloader resource selector.

For example, to match documentation, the Secret's labels must contain: app-secret: "yes"

Deployment resources that reference a secret in their configuration must have an annotation set: as mostly suitable, reloader.stakater.com/auto: "true"

More options can be found in Reloader documentation.

Above instruction result in the following Reloader behavior:

  • Reloader watches all resources inside a Namespace;
  • Reloader watches all secrets with specific labels;
  • Reloader is allowed to reload pod configuration once these secrets are updated.

Kubernetes ESO for AWS SM

External Secrets Operator integration with AWS Secrets Manager in EKS
$400
Dependencies included: $350
BUY
45
Log in to Corewide IaC registry

Once you have a Corewide Solutions Portal account, this one-time action will use your browser session to retrieve credentials:

 shellterraform login solutions.corewide.com
Provision instructions

Initialize mandatory providers:

Copy and paste into your Terraform configuration and insert the variables:

 hclmodule "tf_k8s_eso_aws" {
  source  = "solutions.corewide.com/kubernetes/tf-k8s-eso-aws/aws"
  version = "~> 1.0.0"

  # specify module inputs here or try one of the examples below
  ...
}

Initialize the setup:

 shellterraform init
Define update strategy

Corewide DevOps team strictly follows Semantic Versioning Specification to provide our clients with products that have predictable upgrades between versions. We recommend pinning patch versions of our modules using pessimistic constraint operator (~>) to prevent breaking changes during upgrades.

To get new features during the upgrades (without breaking compatibility), use ~> 1.0 and run terraform init -upgrade

For the safest setup, use strict pinning with version = "1.0.0"

v1.0.0 released 1 year ago
New version approx. every 10 weeks

Full ExternalSecret API documentation can be found here

Minimal Configuration:

 yamlapiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secret
  namespace: secrets-operator
  # All labels and annotations will be copied to newly-created Kubernetes secret
  labels:
    # resource_label_selector from reloader
    app-secret: "yes"
spec:
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  # Data defines the connection between the Kubernetes Secret keys and the Provider data
  data:
  - remoteRef:
      # Secrets Manager's secret name
      key: test
      # JSON path. E.g. if ASM secret contains {'foo': 'bar'}, the result will be 'bar'
      property: foo
    # Key of Kubernetes secret to append value to
    secretKey: foo
  # Used to fetch all properties from the Provider key
  dataFrom:
  - extract:
      # Secrets Manager's secret name
      key: test

Full configuration:

 yamlapiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secret
  namespace: secrets-operator
  # All labels and annotations will be copied to newly-created Kubernetes secret
  labels:
    # resource_label_selector from reloader
    app-secret: "yes"
spec:
  refreshInterval: 1m
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    # Name of future K8S secret
    name: app-secret
    # DeletionPolicy defines how/when to delete the Secret in Kubernetes
    # if the provider secret gets deleted.
    deletionPolicy: Retain
    # Secret creation policy
    creationPolicy: Owner
  # Data defines the connection between the Kubernetes Secret keys and the Provider data
  data:
  - remoteRef:
      # Secrets Manager's secret name
      key: test
      # JSON path. E.g. if ASM secret contains {'foo': 'bar'}, the result will be 'bar'
      property: foo
    # Key of Kubernetes secret to append value to
    secretKey: foo
  # Used to fetch all properties from the Provider key
  dataFrom:
  - extract:
      # Secrets Manager's secret name
      key: test

Deploy module with the default parameters:

 hclmodule "eso_aws" {
  source  = "solutions.corewide.com/kubernetes/tf-k8s-eso-aws/aws"
  version = "~> 1.0"

  name_prefix       = "foo"
  oidc_provider_url = aws_iam_openid_connect_provider.example.url
  secret_names      = ["frontend", "backend"]
}

Deploy module with custom Reloader parameters - set resource and namespace selectors:

 hclmodule "eso_aws" {
  source  = "solutions.corewide.com/kubernetes/tf-k8s-eso-aws/aws"
  version = "~> 1.0"

  name_prefix       = "foo"
  namespace         = "secrets-store"
  secret_names      = ["frontend"]
  oidc_provider_url = aws_iam_openid_connect_provider.example.url

  reloader = {
    namespace_selector = ["secrets-store"]
    resource_selector  = ["app-secret"]
  }
}

Deploy module with full custom configuration:

 hclmodule "eso_aws" {
  source  = "solutions.corewide.com/kubernetes/tf-k8s-eso-aws/aws"
  version = "~> 1.0"

  name_prefix          = "foo"
  region               = "us-east-1"
  namespace            = "secrets-store"
  secret_names         = ["frontend"]
  oidc_provider_url    = aws_iam_openid_connect_provider.example.url
  service_account_name = "super-secrets-store"

  node_selector = {
    "eks\\.amazonaws\\.com/nodegroup" = "maintenance"
  }

  eso = {
    chart_version = "yyyy"
    app_version   = "xxxx"

    custom_values = {
      foo = "bar"
    }
  }

  reloader = {
    chart_version      = "yyyy"
    app_version        = "xxxx"
    namespace_selector = ["secrets-store"]
    resource_selector  = ["app-secret"]
  }
}
Variable Description Type Default Required Sensitive
name_prefix Name prefix for the created resources string yes no
oidc_provider_url URL of AWS OIDC provider to authorize Kubernetes service account string yes no
secret_names AWS Secret Manager secret names that should be synchronized with EKS set(string) yes no
create_namespace Indicates creation of dedicated namespace for the stack deployment bool true no no
eso External Secrets Operator parameters. This parameter is passed to tf-k8s-eso module's eso parameter. eso.service_account parameter is ignored as it is managed by tf-k8s-eso-aws module itself any {} no no
namespace The namespace to install the stack into string external-secrets-operator no no
node_selector Node selector for the stack components map(string) {} no no
region AWS default region to deploy the secret manager into. If not set, takes provider-level location as default string no no
reloader Reloader parameters. This parameter is passed to tf-k8s-eso module's reloader parameter any {} no no
service_account_name Kubernetes ServiceAccount name string external-secrets-operator no no
Output Description Type Sensitive
cluster_secret_store_name Name of Kubernetes ClusterSecretStore resource to be used in creation of ExternalSecret resources attribute no
namespace The namespace name where External Secrets Operator and Reloader were installed resource no
Dependency Version Kind
terraform >= 1.3 CLI
hashicorp/aws ~> 5.0 provider
hashicorp/kubernetes ~> 2.9 provider
tf-aws-iam-role ~> 1.0 module
tf-k8s-crd ~> 2.0 module
tf-k8s-eso ~> 1.0 module

Not sure where to start?
Let's find your perfect match.