// Module included in the following assemblies:
//
// * nodes/pods/nodes-pods-secrets-store.adoc

:_mod-docs-content-type: PROCEDURE
[id="secrets-store-azure_{context}"]
= Mounting secrets from {azure-short} Key Vault

[role="_abstract"]
You can use the {secrets-store-operator} to mount secrets from {azure-first} Key Vault to a Container Storage Interface (CSI) volume in {product-title}. Using an external secret store protects information that you do not want developers to have and can be more secure than `secret` objects.

.Prerequisites

* Your have installed a cluster on {azure-short}.
* You have access to the cluster as a user with the `cluster-admin` role.
* You have installed the {azure-short} CLI (`az`).
* You have installed the {secrets-store-operator}. See "Installing the {secrets-store-driver}" for instructions.
* You have configured {azure-short} Key Vault to store the required secrets.

.Procedure

. Install the {azure-short} Key Vault provider:

.. Create a YAML file named `azure-provider.yaml` that defines the `ServiceAccount` resource configuration. See the following example configuration:
+
[IMPORTANT]
====
The {azure-short} Key Vault provider for the {secrets-store-driver} is an upstream provider.

This configuration is modified from the configuration provided in the upstream link:https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/installation/[{azure-short} documentation] so that it works properly with {product-title}. Changes to this configuration might impact functionality.
====
+
.Example `azure-provider.yaml` file
[source,yaml]
----
apiVersion: v1
kind: ServiceAccount
metadata:
  name: csi-secrets-store-provider-azure
  namespace: openshift-cluster-csi-drivers
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csi-secrets-store-provider-azure-cluster-role
rules:
- apiGroups: [""]
  resources: ["serviceaccounts/token"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["serviceaccounts"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: csi-secrets-store-provider-azure-cluster-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: csi-secrets-store-provider-azure-cluster-role
subjects:
- kind: ServiceAccount
  name: csi-secrets-store-provider-azure
  namespace: openshift-cluster-csi-drivers
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  namespace: openshift-cluster-csi-drivers
  name: csi-secrets-store-provider-azure
  labels:
    app: csi-secrets-store-provider-azure
spec:
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: csi-secrets-store-provider-azure
  template:
    metadata:
      labels:
        app: csi-secrets-store-provider-azure
    spec:
      serviceAccountName: csi-secrets-store-provider-azure
      hostNetwork: true
      containers:
        - name: provider-azure-installer
          image: mcr.microsoft.com/oss/azure/secrets-store/provider-azure:v1.4.1
          imagePullPolicy: IfNotPresent
          args:
            - --endpoint=unix:///provider/azure.sock
            - --construct-pem-chain=true
            - --healthz-port=8989
            - --healthz-path=/healthz
            - --healthz-timeout=5s
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8989
            failureThreshold: 3
            initialDelaySeconds: 5
            timeoutSeconds: 10
            periodSeconds: 30
          resources:
            requests:
              cpu: 50m
              memory: 100Mi
            limits:
              cpu: 50m
              memory: 100Mi
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsUser: 0
            capabilities:
              drop:
              - ALL
          volumeMounts:
            - mountPath: "/provider"
              name: providervol
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: type
                operator: NotIn
                values:
                - virtual-kubelet
      volumes:
        - name: providervol
          hostPath:
            path: "/var/run/secrets-store-csi-providers"
      tolerations:
      - operator: Exists
      nodeSelector:
        kubernetes.io/os: linux
----

.. Grant privileged access to the `csi-secrets-store-provider-azure` service account by running the following command:
+
[source,terminal]
----
$ oc adm policy add-scc-to-user privileged -z csi-secrets-store-provider-azure -n openshift-cluster-csi-drivers
----

.. Create the provider resources by running the following command:
+
[source,terminal]
----
$ oc apply -f azure-provider.yaml
----

. Create a service principal to access the key vault:

.. Set the service principal client secret as an environment variable by running the following command:
+
[source,terminal]
----
$ SERVICE_PRINCIPAL_CLIENT_SECRET="$(az ad sp create-for-rbac --name https://$KEYVAULT_NAME --query 'password' -otsv)"
----

.. Set the service principal client ID as an environment variable by running the following command:
+
[source,terminal]
----
$ SERVICE_PRINCIPAL_CLIENT_ID="$(az ad sp list --display-name https://$KEYVAULT_NAME --query '[0].appId' -otsv)"
----

.. Create a generic secret with the service principal client secret and ID by running the following command:
+
[source,terminal]
----
$ oc create secret generic secrets-store-creds -n my-namespace --from-literal clientid=${SERVICE_PRINCIPAL_CLIENT_ID} --from-literal clientsecret=${SERVICE_PRINCIPAL_CLIENT_SECRET}
----

.. Apply the `secrets-store.csi.k8s.io/used=true` label to allow the provider to find this `nodePublishSecretRef` secret:
+
[source,terminal]
----
$ oc -n my-namespace label secret secrets-store-creds secrets-store.csi.k8s.io/used=true
----

. Create a secret provider class to define your secrets store provider:

.. Create a YAML file that defines the `SecretProviderClass` object:
+
.Example `secret-provider-class-azure.yaml`
[source,yaml]
----
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: my-azure-provider
  namespace: my-namespace
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    userAssignedIdentityID: ""
    keyvaultName: "kvname"
    objects: |
      array:
        - |
          objectName: secret1
          objectType: secret
    tenantId: "tid"
----
+
where:

`metadata.name`:: Specifies the name for the secret provider class.
`metadata.namespace`:: Specifies the namespace for the secret provider class.
`spec.provider`:: Specifies the provider as `azure`.
`spec.parameters`:: Specifies the provider-specific configuration parameters.

.. Create the `SecretProviderClass` object by running the following command:
+
[source,terminal]
----
$ oc create -f secret-provider-class-azure.yaml
----

. Create a deployment to use this secret provider class:

.. Create a YAML file that defines the `Deployment` object:
+
.Example `deployment.yaml`
[source,yaml]
----
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-azure-deployment
  namespace: my-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-storage
  template:
    metadata:
      labels:
        app: my-storage
    spec:
      containers:
      - name: busybox
        image: k8s.gcr.io/e2e-test-images/busybox:1.29
        command:
          - "/bin/sleep"
          - "10000"
        volumeMounts:
        - name: secrets-store-inline
          mountPath: "/mnt/secrets-store"
          readOnly: true
      volumes:
        - name: secrets-store-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "my-azure-provider"
            nodePublishSecretRef:
              name: secrets-store-creds
----
+
where:

`metadata.name`:: Specifies the name for the deployment.
`metadata.namespace`:: Specifies the namespace for the deployment. This must be the same namespace as the secret provider class.
`spec.template.spec.volumes.csi.volumeAttributes.secretProviderClass`:: Specifies the name of the secret provider class.
`spec.template.spec.volumes.csi.nodePublishSecretRef.name`:: Specifies the name of the Kubernetes secret that contains the service principal credentials to access {azure-short} Key Vault.

.. Create the `Deployment` object by running the following command:
+
[source,terminal]
----
$ oc create -f deployment.yaml
----

.Verification

* Verify that you can access the secrets from {azure-short} Key Vault in the pod volume mount:

.. List the secrets in the pod mount by running the following command:
+
[source,terminal]
----
$ oc exec my-azure-deployment-<hash> -n my-namespace -- ls /mnt/secrets-store/
----
+
.Example output
[source,terminal]
----
secret1
----

.. View a secret in the pod mount by running the following command:
+
[source,terminal]
----
$ oc exec my-azure-deployment-<hash> -n my-namespace -- cat /mnt/secrets-store/secret1
----
+
.Example output
[source,terminal]
----
my-secret-value
----
