In this lab you will add an Azure Container Registry (ACR) to the Kubernetes component and
grant the cluster’s kubelet identity permission to pull images from it.
Step 1 — Install the Random Provider
ACR names must be globally unique and contain only alphanumeric characters. To guarantee
uniqueness without manual coordination, add the pulumi-random provider to requirements.txt:
pulumi-random==4.21.0
Then install it:
pip install -r requirements.txt
Explanation
pulumi-random wraps HashiCorp’s random provider and exposes resources such as RandomInteger
and RandomString. Unlike Python’s random module, values are generated once and stored in
the Pulumi state file — they remain stable across subsequent pulumi up runs. Every participant
in the workshop generates a different random suffix, avoiding global naming conflicts in Azure.
Step 2 — Add add_registry to the Component
Add import pulumi_random at the top of kubernetes.py, then add the add_registry method
after grant_role:
RandomInteger with min=10000000, max=99999999 produces exactly 8 digits. Combined with the
cr prefix the registry name is 10 characters — well within the 5–50 alphanumeric-only
constraint ACR enforces.
Property
Value
registry_name
Globally unique, 5–50 alphanumeric characters, no hyphens
sku
Basic for development; Standard/Premium for geo-replication
admin_user_enabled
Enables password-based login for local docker push
The role assignment grants AcrPull to the AKS kubelet identity — the identity used by
each node agent when pulling images at pod startup.
Kubelet identity vs cluster identity
AKS creates two managed identities:
Identity
Used by
Required for
Cluster identity
AKS control plane
Managing Azure resources (LBs, disks)
Kubelet identity
Each node (kubelet)
Pulling images from ACR
identity_profile["kubeletidentity"].object_id is the kubelet identity. The AcrPull role
assignment must target this identity — assigning it to the cluster identity alone will not allow
nodes to pull images.
The role assignment is scoped to the ACR resource ID, not the subscription, following the same
least-privilege principle used for the cluster admin role in the previous lab.
Step 3 — Wire up __main__.py
Call add_registry after the node pool loop and add an export:
"acr" is the logical Pulumi resource name used to track the registry in state. The physical
ACR name (cr{random}) is generated internally by the component. Keeping the logical name
short and stable means the resource identity in state does not change if the naming scheme is
ever adjusted.
Step 4 — Deploy
The preview will show three new resources: a RandomInteger, the Registry, and a
RoleAssignment. Confirm with yes to deploy.
pulumi up
Explanation
RandomInteger appears as a first-class resource in the Pulumi graph. Pulumi creates it on the
first pulumi up, stores the result in state, and reuses the same value on every subsequent
run — the registry name never changes after initial creation.
Step 5 — Verify
Confirm the registry was created and the kubelet identity holds the AcrPull role:
az acr show \
--name $(pulumi stack output registry_name)\
--query "{name:name, loginServer:loginServer, sku:sku.name}"\
-o table
Confirm that the kubelet identity has the AcrPull role:
az role assignment list \
--scope $(az acr show --name $(pulumi stack output registry_name) --query id -o tsv)\
--query "[].{principal:principalName, role:roleDefinitionName}"\
-o table
Explanation
The first command confirms the registry was created and shows its login server address
(cr{random}.azurecr.io). The second lists role assignments on the ACR resource and should
show the AKS kubelet managed identity holding AcrPull.