Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions api/hypershift/v1beta1/azure.go
Comment thread
twolff-gh marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,15 @@ type AzurePlatformSpec struct {
// +kubebuilder:validation:MaxLength=255
TenantID string `json:"tenantID"`

// azureContainerRegistryCredentials configures authentication for worker nodes pulling images
// from Azure Container Registry (ACR) using a user-assigned managed identity.
// When set, the identity is attached to worker VM scale sets and its resource ID is written
// into the worker cloud provider config so kubelet's ACR credential provider can authenticate
// without image pull secrets.
//
// +optional
AzureContainerRegistryCredentials AzureContainerRegistryCredentials `json:"azureContainerRegistryCredentials,omitzero"`

// topology specifies the network topology of the API server endpoint for the hosted cluster.
// - Public: The API server is accessible only via a public endpoint.
// - PublicAndPrivate: The API server is accessible via both public and private endpoints.
Expand Down Expand Up @@ -546,6 +555,35 @@ type AzureResourceManagedIdentities struct {
// +kubebuilder:validation:Pattern=`^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$`
type AzureClientID string

// AzureManagedIdentityResourceID is an ARM resource ID for a user-assigned managed identity
// in the format /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{name}.
//
// +kubebuilder:validation:XValidation:rule="self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\\\.managedidentity/userassignedidentities/[^/]+$')",message="must be a user-assigned managed identity ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}"
// +kubebuilder:validation:MinLength=131
// +kubebuilder:validation:MaxLength=345
type AzureManagedIdentityResourceID string

// UserAssignedManagedIdentity identifies a user-assigned managed identity by its ARM resource ID.
type UserAssignedManagedIdentity struct {
// resourceID is the ARM resource ID of the user-assigned managed identity.
//
// +required
ResourceID AzureManagedIdentityResourceID `json:"resourceID,omitempty"`
}

// AzureContainerRegistryCredentials configures authentication for worker nodes
// pulling images from Azure Container Registry (ACR) using a user-assigned managed identity.
// The identity does not need to be in the same subscription or resource group as the
// HostedCluster, but it must be in the same Azure AD tenant. The management cluster's
// CAPZ identity must have Microsoft.ManagedIdentity/userAssignedIdentities/*/assign/action
// on the identity's scope to attach it to worker VM scale sets at creation time.
type AzureContainerRegistryCredentials struct {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to have taken a step backwards since the last review? Why did we lose the intermediate ManagedIdentity struct? Why did we lose the type alias with the validations assigned?

Also, on the thread where I mentioned splitting the subscriptionID, resourceGroupName and identityName into three separate fields, there was no actual response to say why we shouldn't do that

It seemed like @bryan-cox was in favour of that?

The current field has a hard UX for the end user, when most of the string is static, lets build that for them

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's how we did it in other places for ARO HCP. However, I am amenable to keeping it like this if that is what the ARO HCP team wants.

This part of the API is not external as far as I know, and the only consumers are the ARO HCP folks themselves—unless that is incorrect, @bennerv?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to have taken a step backwards since the last review? Why did we lose the intermediate ManagedIdentity struct? Why did we lose the type alias with the validations assigned?

Also, on the thread where I mentioned splitting the subscriptionID, resourceGroupName and identityName into three separate fields, there was no actual response to say why we shouldn't do that

I had attempted to accommodate by iterating on the PR locally and ended up walking back those changes. The fields got eaten during that. I'll be restoring them.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there was no actual response to say why we shouldn't do that

Apologies, I conflated the responses.

I mentioned splitting the subscriptionID, resourceGroupName and identityName

There are several places where the ARM API takes a resource ID directly - VNets, NSGs, others.

In this case it makes sense because it gets passed down to CAPZ that uses it directly. Decomposing it and then recomposing makes sense when they are independently meaningful to the API, but here I think they make more sense attached to the context of the full string being used as-is down further.

I'd argue that here it makes it harder for the customer because they have access to the full string directly with the proper sub and rg for the managed ID, and they would instead need to copy/paste the components out of the string or use code to present the fields properly. I don't know how most customers do this now though - copy/paste?

Copy link
Copy Markdown
Author

@twolff-gh twolff-gh May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I'll need clarification from @bennerv

// managedIdentity identifies the user-assigned managed identity used for ACR image pulls.
//
// +required
ManagedIdentity UserAssignedManagedIdentity `json:"managedIdentity,omitzero"`
}

// AzureWorkloadIdentities is a struct that contains the client IDs of all the managed identities in self-managed Azure
// needing to authenticate with Azure's API.
type AzureWorkloadIdentities struct {
Expand Down
32 changes: 32 additions & 0 deletions api/hypershift/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5112,6 +5112,34 @@ spec:
is WorkloadIdentities, and forbidden otherwise
rule: 'self.azureAuthenticationConfigType == ''WorkloadIdentities''
? has(self.workloadIdentities) : !has(self.workloadIdentities)'
azureContainerRegistryCredentials:
description: |-
azureContainerRegistryCredentials configures authentication for worker nodes pulling images
from Azure Container Registry (ACR) using a user-assigned managed identity.
When set, the identity is attached to worker VM scale sets and its resource ID is written
into the worker cloud provider config so kubelet's ACR credential provider can authenticate
without image pull secrets.
properties:
managedIdentity:
description: managedIdentity identifies the user-assigned
managed identity used for ACR image pulls.
properties:
resourceID:
description: resourceID is the ARM resource ID of
the user-assigned managed identity.
maxLength: 345
minLength: 131
type: string
x-kubernetes-validations:
- message: must be a user-assigned managed identity
ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}
rule: self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\.managedidentity/userassignedidentities/[^/]+$')
required:
- resourceID
type: object
required:
- managedIdentity
type: object
cloud:
default: AzurePublicCloud
description: 'cloud is the cloud environment identifier, valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5103,6 +5103,34 @@ spec:
is WorkloadIdentities, and forbidden otherwise
rule: 'self.azureAuthenticationConfigType == ''WorkloadIdentities''
? has(self.workloadIdentities) : !has(self.workloadIdentities)'
azureContainerRegistryCredentials:
description: |-
azureContainerRegistryCredentials configures authentication for worker nodes pulling images
from Azure Container Registry (ACR) using a user-assigned managed identity.
When set, the identity is attached to worker VM scale sets and its resource ID is written
into the worker cloud provider config so kubelet's ACR credential provider can authenticate
without image pull secrets.
properties:
managedIdentity:
description: managedIdentity identifies the user-assigned
managed identity used for ACR image pulls.
properties:
resourceID:
description: resourceID is the ARM resource ID of
the user-assigned managed identity.
maxLength: 345
minLength: 131
type: string
x-kubernetes-validations:
- message: must be a user-assigned managed identity
ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}
rule: self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\.managedidentity/userassignedidentities/[^/]+$')
required:
- resourceID
type: object
required:
- managedIdentity
type: object
cloud:
default: AzurePublicCloud
description: 'cloud is the cloud environment identifier, valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5123,6 +5123,34 @@ spec:
is WorkloadIdentities, and forbidden otherwise
rule: 'self.azureAuthenticationConfigType == ''WorkloadIdentities''
? has(self.workloadIdentities) : !has(self.workloadIdentities)'
azureContainerRegistryCredentials:
description: |-
azureContainerRegistryCredentials configures authentication for worker nodes pulling images
from Azure Container Registry (ACR) using a user-assigned managed identity.
When set, the identity is attached to worker VM scale sets and its resource ID is written
into the worker cloud provider config so kubelet's ACR credential provider can authenticate
without image pull secrets.
properties:
managedIdentity:
description: managedIdentity identifies the user-assigned
managed identity used for ACR image pulls.
properties:
resourceID:
description: resourceID is the ARM resource ID of
the user-assigned managed identity.
maxLength: 345
minLength: 131
type: string
x-kubernetes-validations:
- message: must be a user-assigned managed identity
ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}
rule: self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\.managedidentity/userassignedidentities/[^/]+$')
required:
- resourceID
type: object
required:
- managedIdentity
type: object
cloud:
default: AzurePublicCloud
description: 'cloud is the cloud environment identifier, valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5435,6 +5435,34 @@ spec:
is WorkloadIdentities, and forbidden otherwise
rule: 'self.azureAuthenticationConfigType == ''WorkloadIdentities''
? has(self.workloadIdentities) : !has(self.workloadIdentities)'
azureContainerRegistryCredentials:
description: |-
azureContainerRegistryCredentials configures authentication for worker nodes pulling images
from Azure Container Registry (ACR) using a user-assigned managed identity.
When set, the identity is attached to worker VM scale sets and its resource ID is written
into the worker cloud provider config so kubelet's ACR credential provider can authenticate
without image pull secrets.
properties:
managedIdentity:
description: managedIdentity identifies the user-assigned
managed identity used for ACR image pulls.
properties:
resourceID:
description: resourceID is the ARM resource ID of
the user-assigned managed identity.
maxLength: 345
minLength: 131
type: string
x-kubernetes-validations:
- message: must be a user-assigned managed identity
ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}
rule: self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\.managedidentity/userassignedidentities/[^/]+$')
required:
- resourceID
type: object
required:
- managedIdentity
type: object
cloud:
default: AzurePublicCloud
description: 'cloud is the cloud environment identifier, valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5575,6 +5575,34 @@ spec:
is WorkloadIdentities, and forbidden otherwise
rule: 'self.azureAuthenticationConfigType == ''WorkloadIdentities''
? has(self.workloadIdentities) : !has(self.workloadIdentities)'
azureContainerRegistryCredentials:
description: |-
azureContainerRegistryCredentials configures authentication for worker nodes pulling images
from Azure Container Registry (ACR) using a user-assigned managed identity.
When set, the identity is attached to worker VM scale sets and its resource ID is written
into the worker cloud provider config so kubelet's ACR credential provider can authenticate
without image pull secrets.
properties:
managedIdentity:
description: managedIdentity identifies the user-assigned
managed identity used for ACR image pulls.
properties:
resourceID:
description: resourceID is the ARM resource ID of
the user-assigned managed identity.
maxLength: 345
minLength: 131
type: string
x-kubernetes-validations:
- message: must be a user-assigned managed identity
ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}
rule: self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\.managedidentity/userassignedidentities/[^/]+$')
required:
- resourceID
type: object
required:
- managedIdentity
type: object
cloud:
default: AzurePublicCloud
description: 'cloud is the cloud environment identifier, valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5566,6 +5566,34 @@ spec:
is WorkloadIdentities, and forbidden otherwise
rule: 'self.azureAuthenticationConfigType == ''WorkloadIdentities''
? has(self.workloadIdentities) : !has(self.workloadIdentities)'
azureContainerRegistryCredentials:
description: |-
azureContainerRegistryCredentials configures authentication for worker nodes pulling images
from Azure Container Registry (ACR) using a user-assigned managed identity.
When set, the identity is attached to worker VM scale sets and its resource ID is written
into the worker cloud provider config so kubelet's ACR credential provider can authenticate
without image pull secrets.
properties:
managedIdentity:
description: managedIdentity identifies the user-assigned
managed identity used for ACR image pulls.
properties:
resourceID:
description: resourceID is the ARM resource ID of
the user-assigned managed identity.
maxLength: 345
minLength: 131
type: string
x-kubernetes-validations:
- message: must be a user-assigned managed identity
ARM resource ID in the format /subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}
rule: self.lowerAscii().matches('^/subscriptions/[^/]+/resourcegroups/[^/]+/providers/microsoft\\.managedidentity/userassignedidentities/[^/]+$')
required:
- resourceID
type: object
required:
- managedIdentity
type: object
cloud:
default: AzurePublicCloud
description: 'cloud is the cloud environment identifier, valid
Expand Down
Loading