Recommended resources
The freely available resources this lesson will direct you to.
Hello. In the previous lesson, you created a short-lived AKS cluster and gave its kubelet identity permission to pull images from your private Azure Container Registry. That solved image-pull authentication. Your application has a different configuration problem: its backend needs settings, and it may need a database credential. Neither belongs in the container image.
This lesson separates those two kinds of data with Kubernetes ConfigMaps and Secrets. You will create one of each for the FastAPI backend, then see how a future Deployment will reference them without containing their values. The practical steps create no Pods, database, or public endpoint.
Allow about 40 minutes. If the AKS cluster is running, its existing node, disk, and networking charges continue throughout the lesson; the ConfigMap, Secret, and namespace do not add a separate Azure resource charge.
Draw the boundary before writing YAML
A ConfigMap holds non-confidential configuration. For example, a log level or an application environment name can vary between a local Docker run and AKS without requiring a different image. A Secret holds a password, token, connection string containing a password, or other confidential value. Both are Kubernetes objects that a Pod can consume; choosing a Secret does not, by itself, make a value safe to publish.
Read the Kubernetes documentation with that distinction in mind.
Read the Kubernetes documentation to see why configuration is separated from an image and how a Pod selects particular settings.
In “ConfigMaps,” read from the introduction, including “Motivation” and its warning about confidential data. Then find “Using Configmaps as environment variables.” Start at the warning immediately above that subsection and read through the environment examples. Focus on the difference between importing every key with envFrom and selecting one key with configMapKeyRef.
For our backend, LOG_LEVEL: "INFO" is a reasonable ConfigMap entry. A DATABASE_URL that embeds a database password is not: although it looks like an application setting, its value is a credential. The same rule applies to a frontend ConfigMap: anything delivered to browser-side JavaScript is visible to the browser user, regardless of where you stored it in Kubernetes.
Secrets require a more careful threat model than their name suggests. Kubernetes commonly represents Secret values as base64-encoded data; base64 is encoding, not encryption. The upstream documentation warns that Secrets are not encrypted in the API server’s underlying store by default. Do not assume a particular AKS encryption configuration from the word “Secret”; control who can access the Kubernetes API and verify the protections required for any real environment.
Read the Kubernetes documentation for what Secrets protect, how containers consume them, and where their protection ends.
In “Secrets,” read the introduction and caution. In “Using a Secret,” start at the consumption guidance; continue through “Using Secrets as environment variables.” Finally, in “Information security for Secrets,” read the access discussion. Notice especially that permission to create Pods in a namespace can give someone an indirect way to access Secrets there.
The ACR permission from the previous lesson is separate from all this. Kubelet AcrPull allows the node to fetch an image. A backend Secret supplies data to the running application. Granting one does not provide the other, and you do not need an ACR password Secret for the cluster configured previously.
Create a namespace and a versionable ConfigMap
Use a dedicated namespace named app-lab so the objects for this application are grouped together. A namespace is an organizational and authorization scope, not an automatic guarantee that other cluster users cannot read its Secrets.
First, confirm that kubectl still points to your lab cluster:
kubectl config current-context
kubectl get nodes
Check the context before creating anything, particularly if you also work with other Kubernetes clusters. If you destroyed AKS after the previous lesson, you can study the examples without running them, or recreate that cluster using its reviewed Terraform workflow. Recreating it resumes its node and supporting-infrastructure charges.
Create the namespace:
kubectl create namespace app-lab
If it already exists, do not create another one. In your application repository, create k8s/backend-configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: backend-config
namespace: app-lab
data:
APP_ENV: "lab"
LOG_LEVEL: "INFO"
These keys illustrate the pattern; before deploying your FastAPI image, use the actual environment-variable names its code reads. Adding a ConfigMap key does not make an application recognize it. YAML strings are quoted here so values such as INFO are unambiguous strings.
Apply and inspect this non-secret object:
kubectl apply -f k8s/backend-configmap.yaml
kubectl -n app-lab get configmap backend-config
kubectl -n app-lab describe configmap backend-config
It is acceptable for this ConfigMap manifest and its values to be reviewed in Git only while every value remains non-confidential. If a future setting contains a token or password, moving it to a Secret is necessary; merely renaming the key or base64-encoding it is not.
Cost of this practical step: creating the namespace and ConfigMap has no separate charge. The AKS node continues to run at the regional rate discussed previously—roughly USD 0.04–0.08 per hour for the example VM, plus disk and networking—until you destroy the cluster.
Create a Secret without writing its value to a manifest
For a backend that accepts DATABASE_URL, use a general-purpose Kubernetes Secret with a key of that name. If your application instead expects separate database-host, user, and password variables, adapt the keys to its existing configuration rather than inventing an unused DATABASE_URL.
Do not write a Secret YAML file with stringData and commit it: stringData contains plaintext. Do not commit a YAML file with base64 under data either: it is readily decoded. Avoid passing a real password as a --from-literal command argument, which may be captured by shell history or process inspection.
The following Bash commands prompt for a value without echoing it, pass it to kubectl through standard input, and clear the shell variable afterward. You may enter a disposable test value if the PostgreSQL lab is not currently running; it will demonstrate the Kubernetes configuration, but it will not let the backend connect to a database. Run this in a shell without command tracing (set -x) or a terminal recorder that captures input.
read -r -s -p "Enter DATABASE_URL or a disposable test value: " DATABASE_URL
printf '\n'
printf '%s' "$DATABASE_URL" |
kubectl -n app-lab create secret generic backend-credentials \
--from-file=DATABASE_URL=/dev/stdin
unset DATABASE_URL
The prompt itself contains no value. --from-file=DATABASE_URL=/dev/stdin tells kubectl to read the piped bytes as the Secret key DATABASE_URL; no local credential file or Secret manifest is created. unset removes the variable from the shell after the command, but it cannot remove the value from Kubernetes or from any terminal-recording system you may be using. If creation fails, check the error and run the prompt again rather than printing the value to diagnose it.
Verify existence and key name, not content:
kubectl -n app-lab get secret backend-credentials
kubectl -n app-lab describe secret backend-credentials
describe secret shows metadata and key sizes, not the value. Avoid kubectl get secret backend-credentials -o yaml, decoding its data, printing container environment variables, or putting a credential into logs. Treat your kubeconfig and Kubernetes API permissions carefully as well: someone authorized to read Secrets—or to create a Pod that consumes one in this namespace—may be able to obtain the value.
Keep the Secret out of Terraform configuration and Terraform-managed Kubernetes resources for this lab. Terraform state can retain managed secret values even when an output is marked sensitive. The imperative creation above deliberately avoids placing the credential in that state, though you must recreate the Secret after recreating the cluster.
Cost of this practical step: creating and inspecting the Secret adds no separate Azure charge. It does not start PostgreSQL or any application Pod; only the already-running AKS infrastructure continues to accrue cost.
Reference values without placing them in a Deployment
A Deployment can name the ConfigMap key and Secret key it needs. Here is the relevant part of a future backend container specification, not a complete Deployment to apply today:
containers:
- name: backend
image: your-registry.azurecr.io/backend:your-version
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: backend-config
key: LOG_LEVEL
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: backend-credentials
key: DATABASE_URL
env[].name is the variable the container receives. Each valueFrom entry identifies the Kubernetes object and its key; it does not copy the value into the Deployment manifest. The Deployment, ConfigMap, and Secret must be in the same namespace. A missing required Secret or key prevents the dependent container from starting, so create and check them before deploying the backend.
You could use envFrom to import every ConfigMap key, but selecting keys explicitly makes it clearer which settings this backend needs. It is especially useful for Secret references: give each container only the credential it requires. A Secret referenced as an environment variable is still present in that container’s process environment, so avoid debugging commands that print environments or application code that logs configuration wholesale.
There is also an operational consequence. Environment-variable values are fixed when the container starts. Editing the ConfigMap or rotating the Secret does not update those variables in an existing Pod; arrange a Pod restart or rollout after a change. Kubernetes can also mount ConfigMaps and Secrets as files, with different update behavior, but environment variables fit the backend configuration used here.
Cost of this practical step: reading and preparing this YAML fragment costs nothing and creates no workload. In the next lesson, actually running the backend will use capacity on your already-billable AKS node.
Leave a safe handoff—or clean up
Before moving on, you should have backend-config and backend-credentials in app-lab, with no secret value in a repository file, Terraform state, or displayed command output. You can check object names without revealing values:
kubectl -n app-lab get configmap backend-config
kubectl -n app-lab get secret backend-credentials
Leave them in place if you will deploy the backend soon. If the Secret contains a disposable test value, replace it with an appropriate real credential before expecting database connectivity; never treat the placeholder as production configuration. Avoid deleting and recreating an in-use Secret casually, because dependent Pods may be affected.
If you are pausing, remove these objects and destroy the AKS cluster using the Terraform destroy workflow from the previous lesson:
kubectl -n app-lab delete secret backend-credentials
kubectl -n app-lab delete configmap backend-config
Delete app-lab as well only if it contains nothing else you intend to keep. Removing Kubernetes objects does not stop the node charge. Destroying the AKS cluster is the budget-relevant action; your separately managed ACR continues to incur its own charges until you remove it.
Cost of verification and cleanup: the kubectl operations have no separate charge. AKS infrastructure remains billable until Azure completes cluster deletion; no new database, load balancer, or public endpoint was created in this lesson.
Key takeaways
ConfigMaps keep non-secret settings separate from an image. Secrets let a Pod refer to credentials without embedding values in its Deployment, but base64 is not encryption and Kubernetes access remains a security boundary. Use explicit key references, keep real values out of Git and Terraform state, and restart Pods when environment-sourced values change.
Next, you will put these references into a complete FastAPI Deployment, add its Service, resource limits, and health probes, and check whether the backend actually starts on AKS.