
## AWS Certificate Manager

External Secrets Operator integrates with [AWS Certificate Manager (ACM)](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html) to support:

- **Exporting** certificates from ACM into Kubernetes via `ExternalSecret`.
- **Importing** TLS certificates into ACM via `PushSecret` (e.g., certificates issued by [cert-manager](https://cert-manager.io/)).

Both public and private ACM certificates can be exported, provided the export option was enabled when the certificate was requested. See [Exportable Certificates](#exportable-certificates) below for details.

### SecretStore

``` yaml
{% include 'aws-acm-store.yaml' %}
```

**NOTE:** In case of a `ClusterSecretStore`, be sure to provide `namespace` in `accessKeyIDSecretRef` and `secretAccessKeySecretRef` with the namespaces where the secrets reside.

### IAM Policy

The required IAM permissions depend on whether you use `ExternalSecret` (export), `PushSecret` (import), or both.

#### Export only (`ExternalSecret`)

``` json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "acm:DescribeCertificate",
        "acm:ExportCertificate",
        "acm:GetCertificate"
      ],
      "Resource": "*"
    }
  ]
}
```

#### Import only (`PushSecret`)

``` json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "acm:GetCertificate",
        "acm:ImportCertificate",
        "acm:DeleteCertificate",
        "acm:AddTagsToCertificate",
        "acm:ListTagsForCertificate",
        "acm:RemoveTagsFromCertificate",
        "tag:GetResources"
      ],
      "Resource": "*"
    }
  ]
}
```

The `tag:GetResources` permission (Resource Groups Tagging API) is used to locate certificates by their ESO management tags with server-side filtering. `tag:GetResources` does not support resource-level ARNs and must be granted on `"Resource": "*"`.

#### Both export and import

``` json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "acm:GetCertificate",
        "acm:ImportCertificate",
        "acm:DeleteCertificate",
        "acm:DescribeCertificate",
        "acm:ExportCertificate",
        "acm:AddTagsToCertificate",
        "acm:ListTagsForCertificate",
        "acm:RemoveTagsFromCertificate",
        "tag:GetResources"
      ],
      "Resource": "*"
    }
  ]
}
```

You can scope the `acm:*` actions to specific certificate ARNs or use conditions to restrict access to certain regions or accounts. `tag:GetResources` must remain on `"Resource": "*"`.

### ExternalSecret

#### Exportable Certificates

Both **public** and **private** ACM certificates can be exported, but the certificate must have been requested with the export option **enabled**. This option cannot be changed after the certificate is created — if it was not enabled at request time, the certificate is not exportable.

Requesting a public certificate with the export option enabled is a **paid** feature — AWS charges a fee upon issuance and again on each renewal. Separately, the `ExportCertificate` API is free for the first 10,000 calls per month per account; exceeding this threshold incurs additional per-call charges. See the [ACM pricing page](https://aws.amazon.com/certificate-manager/pricing/) for current details.

#### Export Caching

To minimize calls to the paid `ExportCertificate` API, the provider caches exported PEM bundles in memory, keyed by certificate ARN. On each access the provider calls the free `GetCertificate` and compares the certificate's fingerprint against the cached entry, which prevents serving stale data; `ExportCertificate` is only called on the first access, when the fingerprint changes (certificate renewal), or when the cached entry has been evicted. The cache is bounded to 128 entries with a 24-hour TTL per entry (LRU eviction above that). It is held on the long-lived AWS provider instance and survives across reconciles, but is in-memory only and is reset when the operator pod restarts or when the underlying `SecretStore` is updated.

#### Usage

Use `remoteRef.key` to specify the certificate ARN. The provider returns the certificate, chain, and decrypted private key as a single PEM bundle. Use [template functions](../guides/templating.md) such as `filterPEM` and `filterCertChain` to extract individual components:

``` yaml
{% include 'aws-acm-external-secret.yaml' %}
```

You can also extract just the leaf or intermediate certificates using `filterCertChain`:

``` yaml
{% include 'aws-acm-external-secret-chain.yaml' %}
```

### PushSecret

The ACM provider expects the source Kubernetes secret to be a standard `kubernetes.io/tls` secret containing:

- `tls.crt` — PEM-encoded leaf certificate, optionally followed by intermediate CA certificates.
- `tls.key` — PEM-encoded private key.

The `spec.data` entry must **not** set `secretKey` — the provider always reads the full secret in whole-secret mode because ACM's `ImportCertificate` API requires both the certificate and private key in a single call.

The `remoteKey` field is used as a tag value (`external-secrets-remote-key`) to identify and track the imported certificate in ACM.

``` yaml
{% include 'aws-acm-push-secret.yaml' %}
```

#### Certificate Chain Handling

The provider automatically splits the `tls.crt` PEM bundle:

- The **first** PEM block is treated as the leaf certificate and passed as the `Certificate` field.
- Any subsequent PEM blocks are passed as-is in the `CertificateChain` field.

This matches the output format of cert-manager, which places the leaf certificate first, followed by intermediates.

#### Custom Tags (Metadata)

You can apply custom AWS resource tags to the imported certificate using PushSecret metadata:

``` yaml
{% include 'aws-acm-push-secret-with-metadata.yaml' %}
```

The tags `managed-by` and `external-secrets-remote-key` are reserved for ESO internal tracking and cannot be overridden via metadata.

### Usage with cert-manager

A common pattern is to use [cert-manager](https://cert-manager.io/) to provision TLS certificates and then push them to ACM via `PushSecret`. cert-manager stores issued certificates as `kubernetes.io/tls` secrets, which is exactly the format expected by the ACM provider.

**1. Create a `Certificate` resource:**

``` yaml
{% include 'aws-acm-cert-manager-certificate.yaml' %}
```

**2. Create a `PushSecret` to import the certificate into ACM:**

``` yaml
{% include 'aws-acm-cert-manager-push-secret.yaml' %}
```

When cert-manager renews the certificate, the `PushSecret` controller will detect the change and re-import the updated certificate into ACM using the same ARN (identified by the `external-secrets-remote-key` tag). This keeps the ACM certificate in sync without creating duplicates.

If the `PushSecret` is deleted and `deletionPolicy` is set to `Delete`, the imported certificate will be removed from ACM (provided it is not in use).
