> For the complete documentation index, see [llms.txt](https://hahoangv.gitbook.io/azure-kubernetes-service/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hahoangv.gitbook.io/azure-kubernetes-service/hands-on-provisioning-and-configuring-an-aks-cluster-on-azure-with-pulumi/deploy-ingress-controller-and-configure-ssl-tls/deploy-cert-manager.md).

# Deploy cert-manager

To secure your NGINX ingress controller, you'll need SSL/TLS certificates. You can either:

* Use a **Let's Encrypt** certificate (via **Cert-Manager**).
* Use a custom certificate (if you already have an SSL certificate and private key)

You use a **Let's Encrypt** certificate via cert-manager to do the work with Kubernetes to request a certificate and respond to the challenge to validate it.&#x20;

You can install **Cert-Manager** using Helm and configure it to issue SSL certificates from **Let's Encrypt**.

In `resources` folder, create a new `certmanager.ts` and add the below code:

```typescript
import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";
import { config } from "../config";

export const aksClusterIssuer = (k8sProvider) => {
    // Create a namespace for Cert-manager
    const certManagerNamespace = new k8s.core.v1.Namespace("cert-manager", {
        metadata: {
            name: "cert-manager",
        },
    }, { provider: k8sProvider });

    // Install Cert-manager using Helm
    const cert = new k8s.helm.v4.Chart("cert-manager", {
        chart: "cert-manager",
        version: "1.15.3",
        repositoryOpts: {
            repo: "https://charts.jetstack.io",
        },
        namespace: certManagerNamespace.metadata.name,
        values: {
            installCRDs: true
        },
    }, { transformations: [
        // Ignore changes that will be overwritten by the deployment.
        // https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/managing-resources-with-server-side-apply/#handle-field-conflicts-on-existing-resources
        args => {
            if (args.type === "kubernetes:admissionregistration.k8s.io/v1:ValidatingWebhookConfiguration" ||
                args.type === "kubernetes:admissionregistration.k8s.io/v1:MutatingWebhookConfiguration") {
                return {
                    props: args.props,
                    opts: pulumi.mergeOptions(args.opts, {
                        ignoreChanges: ["metadata.annotations.template", "webhooks[*].clientConfig"],
                    })
                }
            }
            return undefined;
        }
    ], provider: k8sProvider });

    // Create a ClusterIssuer for Let's Encrypt
    const letsEncryptIssuer = new k8s.apiextensions.CustomResource("letsencrypt-issuer", {
        apiVersion: "cert-manager.io/v1",
        kind: "Issuer",
        metadata: {
            name: "letsencrypt-staging",
            namespace: config.appNamespace
        },
        spec: {
            acme: {
                server: "https://acme-staging-v02.api.letsencrypt.org/directory",
                email: config.letenscriptEmail,
                privateKeySecretRef: {
                    name: "letsencrypt-staging",
                },
                solvers: [{
                    http01: {
                        ingress: {
                            ingressClassName: "nginx",
                        },
                    },
                }],
            },
        },
    }, { provider: k8sProvider, dependsOn: cert });

    return letsEncryptIssuer;
}
```

Make the changes from `index.ts` file that includes the code to create cert manager and the cluster issuer:

```typescript
...
import { aksClusterIssuer } from "./resources/certmanager";
...

// Set Up Cert-Manager for Automatic Let's Encrypt Certificates
const letsEncryptIssuer = aksClusterIssuer(provider);

export const resourceGroupName = resourceGroup.name;
```

Deploy the resource again with the command:

```
pulumi up
```

### Verify the Cluster Issuer

When it is complete, you can check if the (Cluster)Issuer you're using is in a ready state:

```
$kubectl get issuers  -n apps
NAME                  READY   AGE
letsencrypt-staging   True    109m
```

And check the status using `kubectl describe:`

```
$kubectl describe issuer letsencrypt-staging -n apps
ame:         letsencrypt-staging
Namespace:    apps
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1
Kind:         Issuer
Metadata:
  Creation Timestamp:  2024-09-09T03:13:54Z
  Generation:          1
  Resource Version:    2957
  UID:                 f5469724-51fd-4f70-9d31-e91f0f45bb52
Spec:
  Acme:
    Email:  email@example.com
    Private Key Secret Ref:
      Name:  letsencrypt-staging
    Server:  https://acme-staging-v02.api.letsencrypt.org/directory
    Solvers:
      http01:
        Ingress:
          Ingress Class Name:  nginx
Status:
  Acme:
    Last Private Key Hash:  zGE6xeUaHb5WXL6CymJK8sHTuLsFYlHPHHM61+IinoQ=
    Last Registered Email:  email@example.com
    Uri:                    https://acme-staging-v02.api.letsencrypt.org/acme/acct/164108913
  Conditions:
    Last Transition Time:  2024-09-09T03:13:56Z
    Message:               The ACME account was registered with the ACME server
    Observed Generation:   1
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>
```
