diff --git a/go.mod b/go.mod index 3bccf8ee50df6..c82087ae845da 100644 --- a/go.mod +++ b/go.mod @@ -105,6 +105,7 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect dario.cat/mergo v1.0.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/GoogleCloudPlatform/k8s-cloud-provider v1.25.0 // indirect diff --git a/go.sum b/go.sum index 2611f05ff1681..360b1d6d7da69 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsI github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 h1:L7G3dExHBgUxsO3qpTGhk/P2dgnYyW48yn7AO33Tbek= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0/go.mod h1:Ms6gYEy0+A2knfKrwdatsggTXYA2+ICKug8w7STorFw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= diff --git a/pkg/model/azuremodel/context.go b/pkg/model/azuremodel/context.go index 6ee0a7078a7e4..86dc2c3096acf 100644 --- a/pkg/model/azuremodel/context.go +++ b/pkg/model/azuremodel/context.go @@ -97,22 +97,32 @@ func (c *AzureModelContext) LinkToApplicationSecurityGroupNodes() *azuretasks.Ap return &azuretasks.ApplicationSecurityGroup{Name: fi.PtrTo(c.NameForApplicationSecurityGroupNodes())} } -// CloudTagsForInstanceGroup computes the tags to apply to instances in the specified InstanceGroup -// Mostly copied from pkg/model/context.go, but "/" in tag keys are replaced with "_" as Azure -// doesn't allow "/" in tag keys. -func (c *AzureModelContext) CloudTagsForInstanceGroup(ig *kops.InstanceGroup) map[string]*string { +// CloudTagsForClusterResource returns tags for a cluster-scoped resource +// (one that is not tied to a specific instance group). +func (c *AzureModelContext) CloudTagsForClusterResource() map[string]*string { + labels := make(map[string]string) + for k, v := range c.Cluster.Spec.CloudLabels { + labels[k] = v + } + labels[azure.TagClusterName] = c.ClusterName() + return toAzureTags(labels) +} + +// CloudTagsForInstanceGroupResource computes the tags to apply to instances in the specified InstanceGroup. +// "/" in tag keys are replaced with "_" as Azure doesn't allow "/" in tag keys. +func (c *AzureModelContext) CloudTagsForInstanceGroupResource(ig *kops.InstanceGroup) map[string]*string { const ( clusterNodeTemplateLabel = "k8s.io_cluster_node-template_label_" clusterNodeTemplateTaint = "k8s.io_cluster_node-template_taint_" ) labels := make(map[string]string) - // Apply any user-specified global labels first so they can be overridden by IG-specific labels. + // Apply any user-specified global labels first (may be overridden by IG-level labels). for k, v := range c.Cluster.Spec.CloudLabels { labels[k] = v } - // Apply any user-specified labels. + // Apply any user-specified IG labels (may override cluster-level labels). for k, v := range ig.Spec.CloudLabels { labels[k] = v } @@ -134,7 +144,8 @@ func (c *AzureModelContext) CloudTagsForInstanceGroup(ig *kops.InstanceGroup) ma } } - // The system tags take priority because the cluster likely breaks without them... + // System tags take priority because the cluster likely breaks without them. + labels[azure.TagClusterName] = c.ClusterName() labels[azure.TagNameRolePrefix+ig.Spec.Role.ToLowerString()] = "1" if ig.Spec.Role == kops.InstanceGroupRoleControlPlane { labels[azure.TagNameRolePrefix+"master"] = "1" @@ -143,7 +154,10 @@ func (c *AzureModelContext) CloudTagsForInstanceGroup(ig *kops.InstanceGroup) ma // Set the tag used by kops-controller to identify the instance group to which the VM ScaleSet belongs. labels[nodeidentityazure.InstanceGroupNameTag] = ig.Name - // Replace all "/" with "_" as "/" is not an allowed key character in Azure. + return toAzureTags(labels) +} + +func toAzureTags(labels map[string]string) map[string]*string { m := make(map[string]*string) for k, v := range labels { m[strings.ReplaceAll(k, "/", "_")] = fi.PtrTo(v) diff --git a/pkg/model/azuremodel/context_test.go b/pkg/model/azuremodel/context_test.go index 57cddef24bb3d..580e34af85e2b 100644 --- a/pkg/model/azuremodel/context_test.go +++ b/pkg/model/azuremodel/context_test.go @@ -18,12 +18,13 @@ package azuremodel import ( "reflect" + "strings" "testing" "k8s.io/kops/upup/pkg/fi" ) -func TestCloudTagsForInstanceGroup(t *testing.T) { +func TestCloudTagsForInstanceGroupResource(t *testing.T) { c := newTestAzureModelContext() c.Cluster.Spec.CloudLabels = map[string]string{ "cluster_label_key": "cluster_label_value", @@ -41,7 +42,7 @@ func TestCloudTagsForInstanceGroup(t *testing.T) { "taint_key=taint_value", } - actual := c.CloudTagsForInstanceGroup(c.InstanceGroups[0]) + actual := c.CloudTagsForInstanceGroupResource(c.InstanceGroups[0]) expected := map[string]*string{ "cluster_label_key": fi.PtrTo("cluster_label_value"), "ig_label_key": fi.PtrTo("ig_label_value"), @@ -50,9 +51,57 @@ func TestCloudTagsForInstanceGroup(t *testing.T) { "k8s.io_cluster_node-template_taint_taint_key": fi.PtrTo("taint_value"), "k8s.io_role_node": fi.PtrTo("1"), "kops.k8s.io_instancegroup": fi.PtrTo("nodes"), + "KubernetesCluster": fi.PtrTo("testcluster.test.com"), } if !reflect.DeepEqual(actual, expected) { t.Errorf("expected tags %+v, but got %+v", expected, actual) } } + +func TestCloudTagsForClusterResource(t *testing.T) { + c := newTestAzureModelContext() + c.Cluster.ObjectMeta.Name = "my.k8s" + c.Cluster.Spec.CloudLabels = map[string]string{ + "cluster_label_key": "cluster_label_value", + "node_label/key": "node_label_value", + } + + actual := c.CloudTagsForClusterResource() + expected := map[string]*string{ + "cluster_label_key": fi.PtrTo("cluster_label_value"), + "node_label_key": fi.PtrTo("node_label_value"), + "KubernetesCluster": fi.PtrTo("my.k8s"), + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("expected tags %+v, but got %+v", expected, actual) + } +} + +func TestSanitizeUserAssignedManagedIdentityName(t *testing.T) { + t.Run("preserves valid names", func(t *testing.T) { + const name = "nodepool-1" + if got := sanitizeUserAssignedManagedIdentityName(name); got != name { + t.Fatalf("expected %q, but got %q", name, got) + } + }) + + t.Run("replaces invalid characters", func(t *testing.T) { + got := sanitizeUserAssignedManagedIdentityName("my.cluster.example.com") + if strings.Contains(got, ".") { + t.Fatalf("expected dots to be replaced, got %q", got) + } + if got != "my-cluster-example-com" { + t.Fatalf("expected %q, but got %q", "my-cluster-example-com", got) + } + }) + + t.Run("truncates long names", func(t *testing.T) { + name := strings.Repeat("a", maxUserAssignedManagedIdentityNameLength+1) + got := sanitizeUserAssignedManagedIdentityName(name) + if len(got) > maxUserAssignedManagedIdentityNameLength { + t.Fatalf("expected name length <= %d, got %d (%q)", maxUserAssignedManagedIdentityNameLength, len(got), got) + } + }) +} diff --git a/pkg/model/azuremodel/names.go b/pkg/model/azuremodel/names.go new file mode 100644 index 0000000000000..c435d899ba8d9 --- /dev/null +++ b/pkg/model/azuremodel/names.go @@ -0,0 +1,41 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuremodel + +import ( + "regexp" + + "k8s.io/kops/pkg/truncate" +) + +// Azure user-assigned identity names must start with a letter or number, contain only alphanumerics, hyphens, and underscores, +// and are limited to 128 characters. See: +// https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/managed-identity-best-practice-recommendations +const maxUserAssignedManagedIdentityNameLength = 128 + +var userAssignedManagedIdentityNameInvalidCharacters = regexp.MustCompile(`[^A-Za-z0-9_-]+`) + +func sanitizeUserAssignedManagedIdentityName(name string) string { + sanitized := userAssignedManagedIdentityNameInvalidCharacters.ReplaceAllString(name, "-") + return truncate.TruncateString(sanitized, truncate.TruncateStringOptions{ + MaxLength: maxUserAssignedManagedIdentityNameLength, + }) +} + +func (c *AzureModelContext) NameForUserAssignedManagedIdentityControlPlane() string { + return sanitizeUserAssignedManagedIdentityName(c.ClusterName()) +} diff --git a/pkg/model/azuremodel/vmscaleset.go b/pkg/model/azuremodel/vmscaleset.go index 2e462cbf652d6..b8033c90d8fcd 100644 --- a/pkg/model/azuremodel/vmscaleset.go +++ b/pkg/model/azuremodel/vmscaleset.go @@ -54,43 +54,60 @@ func (b *VMScaleSetModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { Tags: map[string]*string{}, }) + controlPlaneIdentity := b.addControlPlaneManagedIdentityTasks(c) + for _, ig := range b.InstanceGroups { name := b.AutoscalingGroupName(ig) vmss, err := b.buildVMScaleSetTask(c, name, ig) if err != nil { return err } - c.AddTask(vmss) if ig.IsControlPlane() || b.Cluster.UsesLegacyGossip() { - // Create tasks for assigning built-in roles to VM Scale Sets. - // See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles - resourceGroupID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", - b.Cluster.Spec.CloudProvider.Azure.SubscriptionID, - b.Cluster.AzureResourceGroupName(), - ) - c.AddTask(&azuretasks.RoleAssignment{ - Name: to.Ptr(fmt.Sprintf("%s-%s", *vmss.Name, "owner")), - Lifecycle: b.Lifecycle, - Scope: to.Ptr(resourceGroupID), - VMScaleSet: vmss, - // Owner - RoleDefID: to.Ptr("8e3af657-a8ff-443c-a75c-2fe8c4bcb635"), - }) - c.AddTask(&azuretasks.RoleAssignment{ - Name: to.Ptr(fmt.Sprintf("%s-%s", *vmss.Name, "blob")), - Lifecycle: b.Lifecycle, - Scope: to.Ptr(b.Cluster.Spec.CloudProvider.Azure.StorageAccountID), - VMScaleSet: vmss, - // Storage Blob Data Contributor - RoleDefID: to.Ptr("ba92f5b4-2d11-453d-a403-e96b0029c9fe"), - }) + vmss.ManagedIdentities = []*azuretasks.ManagedIdentity{controlPlaneIdentity} } + + c.AddTask(vmss) } return nil } +func (b *VMScaleSetModelBuilder) addControlPlaneManagedIdentityTasks(c *fi.CloudupModelBuilderContext) *azuretasks.ManagedIdentity { + // Create tasks for assigning built-in roles to the shared control-plane managed identity. + // See https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles + controlPlaneIdentity := &azuretasks.ManagedIdentity{ + Name: fi.PtrTo(b.NameForUserAssignedManagedIdentityControlPlane()), + Lifecycle: b.Lifecycle, + ResourceGroup: b.LinkToResourceGroup(), + Tags: b.CloudTagsForClusterResource(), + } + c.AddTask(controlPlaneIdentity) + + resourceGroupID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", + b.Cluster.Spec.CloudProvider.Azure.SubscriptionID, + b.Cluster.AzureResourceGroupName(), + ) + // Owner role on the resource group. + c.AddTask(&azuretasks.RoleAssignment{ + Name: to.Ptr(fmt.Sprintf("owner-%s", *controlPlaneIdentity.Name)), + Lifecycle: b.Lifecycle, + Scope: to.Ptr(resourceGroupID), + ManagedIdentity: controlPlaneIdentity, + RoleDefID: to.Ptr("8e3af657-a8ff-443c-a75c-2fe8c4bcb635"), + }) + // Storage Blob Data Contributor role on the storage account. + c.AddTask(&azuretasks.RoleAssignment{ + Name: to.Ptr(fmt.Sprintf("blob-%s", *controlPlaneIdentity.Name)), + Lifecycle: b.Lifecycle, + Scope: to.Ptr(b.Cluster.Spec.CloudProvider.Azure.StorageAccountID), + ManagedIdentity: controlPlaneIdentity, + RoleDefID: to.Ptr("ba92f5b4-2d11-453d-a403-e96b0029c9fe"), + }) + + return controlPlaneIdentity +} + func (b *VMScaleSetModelBuilder) buildVMScaleSetTask( c *fi.CloudupModelBuilderContext, name string, @@ -176,7 +193,7 @@ func (b *VMScaleSetModelBuilder) buildVMScaleSetTask( } } - t.Tags = b.CloudTagsForInstanceGroup(ig) + t.Tags = b.CloudTagsForInstanceGroupResource(ig) return t, nil } diff --git a/pkg/model/azuremodel/vmscaleset_test.go b/pkg/model/azuremodel/vmscaleset_test.go index 3d8a08430c9f8..05c3b47d0286a 100644 --- a/pkg/model/azuremodel/vmscaleset_test.go +++ b/pkg/model/azuremodel/vmscaleset_test.go @@ -79,6 +79,16 @@ func TestVMScaleSetModelBuilder_Build(t *testing.T) { if err != nil { t.Errorf("unexpected error %s", err) } + expectedName := b.NameForUserAssignedManagedIdentityControlPlane() + for _, taskKey := range []string{ + "ManagedIdentity/" + expectedName, + "RoleAssignment/owner-" + expectedName, + "RoleAssignment/blob-" + expectedName, + } { + if _, ok := c.Tasks[taskKey]; !ok { + t.Fatalf("expected task %q", taskKey) + } + } } func TestGetCapacity(t *testing.T) { diff --git a/pkg/resources/azure/azure.go b/pkg/resources/azure/azure.go index b4e159e5d7c0d..7875107a72456 100644 --- a/pkg/resources/azure/azure.go +++ b/pkg/resources/azure/azure.go @@ -45,6 +45,7 @@ const ( typeLoadBalancer = "LoadBalancer" typePublicIPAddress = "PublicIPAddress" typeNatGateway = "NatGateway" + typeManagedIdentity = "ManagedIdentity" ) // ListResourcesAzure lists all resources for the cluster by quering Azure. @@ -409,8 +410,33 @@ func (g *resourceGetter) listVMScaleSetsAndRoleAssignments(ctx context.Context) } rs = append(rs, r) - principalIDs[*vmss.Identity.PrincipalID] = vmss + if vmss.Identity != nil { + // Collect principal IDs from both system-assigned and user-assigned identities. + if vmss.Identity.PrincipalID != nil { + principalIDs[*vmss.Identity.PrincipalID] = vmss + } + for _, uai := range vmss.Identity.UserAssignedIdentities { + if uai != nil && uai.PrincipalID != nil { + principalIDs[*uai.PrincipalID] = vmss + } + } + } + } + + // Collect VMSS IDs so that managed identities are not deleted before all VMSS are gone. + var blocked []string + for _, r := range rs { + if r.Type == typeVMScaleSet { + blocked = append(blocked, toKey(typeVMScaleSet, r.ID)) + } + } + + // Also list and delete managed identities owned by the cluster. + miResources, err := g.listManagedIdentities(ctx, blocked) + if err != nil { + return nil, err } + rs = append(rs, miResources...) resourceGroupRAs, err := g.listRoleAssignments(ctx, principalIDs, g.resourceGroupID()) if err != nil { @@ -751,6 +777,32 @@ func (g *resourceGetter) deleteNatGateway(_ fi.Cloud, r *resources.Resource) err return g.cloud.NatGateway().Delete(context.TODO(), g.resourceGroupName(), r.Name) } +func (g *resourceGetter) listManagedIdentities(ctx context.Context, blocked []string) ([]*resources.Resource, error) { + mis, err := g.cloud.ManagedIdentity().List(ctx, g.resourceGroupName()) + if err != nil { + return nil, err + } + + var rs []*resources.Resource + for _, mi := range mis { + if !g.isOwnedByCluster(mi.Tags) { + continue + } + rs = append(rs, &resources.Resource{ + Obj: mi, + Type: typeManagedIdentity, + ID: *mi.ID, + Name: *mi.Name, + Deleter: func(_ fi.Cloud, r *resources.Resource) error { + return g.cloud.ManagedIdentity().Delete(context.TODO(), g.resourceGroupName(), r.Name) + }, + Blocks: []string{toKey(typeResourceGroup, g.resourceGroupID())}, + Blocked: blocked, + }) + } + return rs, nil +} + // isOwnedByCluster returns true if the resource is owned by the cluster. func (g *resourceGetter) isOwnedByCluster(tags map[string]*string) bool { for k, v := range tags { diff --git a/tests/integration/update_cluster/minimal_azure/kubernetes.tf b/tests/integration/update_cluster/minimal_azure/kubernetes.tf index c6411bb71778a..756da21706405 100644 --- a/tests/integration/update_cluster/minimal_azure/kubernetes.tf +++ b/tests/integration/update_cluster/minimal_azure/kubernetes.tf @@ -116,7 +116,8 @@ resource "azurerm_linux_virtual_machine_scale_set" "control-plane-eastus-1-maste computer_name_prefix = "control-plane-eastus-1" disable_password_authentication = true identity { - type = "SystemAssigned" + identity_ids = [azurerm_user_assigned_identity.minimal-azure-example-com.id] + type = "UserAssigned" } instances = 1 location = "eastus" @@ -169,12 +170,9 @@ resource "azurerm_linux_virtual_machine_scale_set" "nodes-minimal-azure-example- admin_username = "admin-user" computer_name_prefix = "nodes" disable_password_authentication = true - identity { - type = "SystemAssigned" - } - instances = 1 - location = "eastus" - name = "nodes.minimal-azure.example.com" + instances = 1 + location = "eastus" + name = "nodes.minimal-azure.example.com" network_interface { enable_ip_forwarding = true ip_configuration { @@ -468,15 +466,15 @@ resource "azurerm_resource_group" "minimal-azure-example-com" { } } -resource "azurerm_role_assignment" "control-plane-eastus-1-masters-minimal-azure-example-com-blob" { - principal_id = azurerm_linux_virtual_machine_scale_set.control-plane-eastus-1-masters-minimal-azure-example-com.identity[0].principal_id +resource "azurerm_role_assignment" "blob-minimal-azure-example-com" { + principal_id = azurerm_user_assigned_identity.minimal-azure-example-com.principal_id role_definition_id = "/subscriptions/sub-321/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/teststorage/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe" scope = "/subscriptions/sub-321/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/teststorage" skip_service_principal_aad_check = true } -resource "azurerm_role_assignment" "control-plane-eastus-1-masters-minimal-azure-example-com-owner" { - principal_id = azurerm_linux_virtual_machine_scale_set.control-plane-eastus-1-masters-minimal-azure-example-com.identity[0].principal_id +resource "azurerm_role_assignment" "owner-minimal-azure-example-com" { + principal_id = azurerm_user_assigned_identity.minimal-azure-example-com.principal_id role_definition_id = "/subscriptions/sub-123/resourceGroups/minimal-azure.example.com/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635" scope = "/subscriptions/sub-123/resourceGroups/minimal-azure.example.com" skip_service_principal_aad_check = true @@ -666,6 +664,15 @@ resource "azurerm_subnet_route_table_association" "minimal-azure-example-com-eas subnet_id = azurerm_subnet.eastus.id } +resource "azurerm_user_assigned_identity" "minimal-azure-example-com" { + location = "eastus" + name = "minimal-azure-example-com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + tags = { + "KubernetesCluster" = "minimal-azure.example.com" + } +} + resource "azurerm_virtual_network" "minimal-azure-example-com" { address_space = ["10.0.0.0/16"] location = "eastus" diff --git a/upup/pkg/fi/cloudup/azure/azure_cloud.go b/upup/pkg/fi/cloudup/azure/azure_cloud.go index 7c81a99e1c695..46f880c958895 100644 --- a/upup/pkg/fi/cloudup/azure/azure_cloud.go +++ b/upup/pkg/fi/cloudup/azure/azure_cloud.go @@ -63,6 +63,7 @@ type AzureCloud interface { LoadBalancer() LoadBalancersClient PublicIPAddress() PublicIPAddressesClient NatGateway() NatGatewaysClient + ManagedIdentity() ManagedIdentitiesClient } type azureCloudImplementation struct { @@ -84,6 +85,7 @@ type azureCloudImplementation struct { loadBalancersClient LoadBalancersClient publicIPAddressesClient PublicIPAddressesClient natGatewaysClient NatGatewaysClient + managedIdentitiesClient ManagedIdentitiesClient storageAccountsClient StorageAccountsClient } @@ -165,6 +167,9 @@ func newAzureCloud(subscriptionID, resourceGroupName, location string, tags map[ if azureCloudImpl.natGatewaysClient, err = newNatGatewaysClientImpl(subscriptionID, cred); err != nil { return nil, err } + if azureCloudImpl.managedIdentitiesClient, err = newManagedIdentitiesClientImpl(subscriptionID, cred); err != nil { + return nil, err + } if azureCloudImpl.storageAccountsClient, err = newStorageAccountsClientImpl(subscriptionID, cred); err != nil { return nil, err } @@ -421,3 +426,7 @@ func (c *azureCloudImplementation) PublicIPAddress() PublicIPAddressesClient { func (c *azureCloudImplementation) NatGateway() NatGatewaysClient { return c.natGatewaysClient } + +func (c *azureCloudImplementation) ManagedIdentity() ManagedIdentitiesClient { + return c.managedIdentitiesClient +} diff --git a/upup/pkg/fi/cloudup/azure/managedidentity.go b/upup/pkg/fi/cloudup/azure/managedidentity.go new file mode 100644 index 0000000000000..bb60528f8670b --- /dev/null +++ b/upup/pkg/fi/cloudup/azure/managedidentity.go @@ -0,0 +1,83 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azure + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" +) + +// ManagedIdentitiesClient is a client for managing user-assigned managed identities. +type ManagedIdentitiesClient interface { + CreateOrUpdate(ctx context.Context, resourceGroupName, name string, parameters armmsi.Identity) (*armmsi.Identity, error) + Get(ctx context.Context, resourceGroupName, name string) (*armmsi.Identity, error) + List(ctx context.Context, resourceGroupName string) ([]*armmsi.Identity, error) + Delete(ctx context.Context, resourceGroupName, name string) error +} + +type managedIdentitiesClientImpl struct { + c *armmsi.UserAssignedIdentitiesClient +} + +var _ ManagedIdentitiesClient = (*managedIdentitiesClientImpl)(nil) + +func (c *managedIdentitiesClientImpl) CreateOrUpdate(ctx context.Context, resourceGroupName, name string, parameters armmsi.Identity) (*armmsi.Identity, error) { + resp, err := c.c.CreateOrUpdate(ctx, resourceGroupName, name, parameters, nil) + if err != nil { + return nil, err + } + return &resp.Identity, nil +} + +func (c *managedIdentitiesClientImpl) Get(ctx context.Context, resourceGroupName, name string) (*armmsi.Identity, error) { + resp, err := c.c.Get(ctx, resourceGroupName, name, nil) + if err != nil { + return nil, err + } + return &resp.Identity, nil +} + +func (c *managedIdentitiesClientImpl) List(ctx context.Context, resourceGroupName string) ([]*armmsi.Identity, error) { + var l []*armmsi.Identity + pager := c.c.NewListByResourceGroupPager(resourceGroupName, nil) + for pager.More() { + resp, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("listing managed identities: %w", err) + } + l = append(l, resp.Value...) + } + return l, nil +} + +func (c *managedIdentitiesClientImpl) Delete(ctx context.Context, resourceGroupName, name string) error { + _, err := c.c.Delete(ctx, resourceGroupName, name, nil) + return err +} + +func newManagedIdentitiesClientImpl(subscriptionID string, cred *azidentity.DefaultAzureCredential) (*managedIdentitiesClientImpl, error) { + c, err := armmsi.NewUserAssignedIdentitiesClient(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("creating managed identities client: %w", err) + } + return &managedIdentitiesClientImpl{ + c: c, + }, nil +} diff --git a/upup/pkg/fi/cloudup/azuretasks/managedidentity.go b/upup/pkg/fi/cloudup/azuretasks/managedidentity.go new file mode 100644 index 0000000000000..1ff41e4a9e1c3 --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/managedidentity.go @@ -0,0 +1,130 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuretasks + +import ( + "context" + "errors" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" + "k8s.io/klog/v2" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/azure" +) + +// ManagedIdentity is an Azure user-assigned managed identity. +// +kops:fitask +type ManagedIdentity struct { + Name *string + Lifecycle fi.Lifecycle + + ResourceGroup *ResourceGroup + Tags map[string]*string + + // PrincipalID is populated after creation and used by RoleAssignment. + PrincipalID *string + // ResourceID is populated after creation and used by VMScaleSet. + ResourceID *string +} + +var ( + _ fi.CloudupTask = &ManagedIdentity{} + _ fi.CompareWithID = &ManagedIdentity{} +) + +// CompareWithID returns the Name of the ManagedIdentity. +func (m *ManagedIdentity) CompareWithID() *string { + return m.Name +} + +// Find discovers the ManagedIdentity in the cloud provider. +func (m *ManagedIdentity) Find(c *fi.CloudupContext) (*ManagedIdentity, error) { + cloud := c.T.Cloud.(azure.AzureCloud) + found, err := cloud.ManagedIdentity().Get(context.TODO(), *m.ResourceGroup.Name, *m.Name) + if err != nil { + var azErr *azcore.ResponseError + if errors.As(err, &azErr) { + if azErr.ErrorCode == "ResourceNotFound" || azErr.ErrorCode == "ResourceGroupNotFound" { + return nil, nil + } + } + return nil, err + } + + // Populate PrincipalID and ResourceID on the source object so that + // dependent tasks (RoleAssignment, VMScaleSet) can reference them. + m.PrincipalID = found.Properties.PrincipalID + m.ResourceID = found.ID + + return &ManagedIdentity{ + Name: found.Name, + Lifecycle: m.Lifecycle, + ResourceGroup: &ResourceGroup{ + Name: m.ResourceGroup.Name, + }, + Tags: found.Tags, + PrincipalID: found.Properties.PrincipalID, + ResourceID: found.ID, + }, nil +} + +// Run implements fi.Task.Run. +func (m *ManagedIdentity) Run(c *fi.CloudupContext) error { + return fi.CloudupDefaultDeltaRunMethod(m, c) +} + +// CheckChanges returns an error if a change is not allowed. +func (*ManagedIdentity) CheckChanges(a, e, changes *ManagedIdentity) error { + if a == nil { + if e.Name == nil { + return fi.RequiredField("Name") + } + return nil + } + if changes.Name != nil { + return fi.CannotChangeField("Name") + } + return nil +} + +// RenderAzure creates or updates a user-assigned managed identity. +func (*ManagedIdentity) RenderAzure(t *azure.AzureAPITarget, a, e, changes *ManagedIdentity) error { + if a == nil { + klog.Infof("Creating a new Managed Identity with name: %s", fi.ValueOf(e.Name)) + } else { + klog.Infof("Updating a Managed Identity with name: %s", fi.ValueOf(e.Name)) + } + + result, err := t.Cloud.ManagedIdentity().CreateOrUpdate( + context.TODO(), + *e.ResourceGroup.Name, + *e.Name, + armmsi.Identity{ + Location: to.Ptr(t.Cloud.Region()), + Tags: e.Tags, + }, + ) + if err != nil { + return err + } + + e.PrincipalID = result.Properties.PrincipalID + e.ResourceID = result.ID + return nil +} diff --git a/upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go b/upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go new file mode 100644 index 0000000000000..44e6fcc6b006f --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go @@ -0,0 +1,52 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by fitask. DO NOT EDIT. + +package azuretasks + +import ( + "k8s.io/kops/upup/pkg/fi" +) + +// ManagedIdentity + +var _ fi.HasLifecycle = (*ManagedIdentity)(nil) + +// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle +func (o *ManagedIdentity) GetLifecycle() fi.Lifecycle { + return o.Lifecycle +} + +// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle +func (o *ManagedIdentity) SetLifecycle(lifecycle fi.Lifecycle) { + o.Lifecycle = lifecycle +} + +var _ fi.HasName = (*ManagedIdentity)(nil) + +// GetName returns the Name of the object, implementing fi.HasName +func (o *ManagedIdentity) GetName() *string { + return o.Name +} + +// String is the stringer function for the task, producing readable output using fi.TaskAsString +func (o *ManagedIdentity) String() string { + return fi.CloudupTaskAsString(o) +} diff --git a/upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go b/upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go new file mode 100644 index 0000000000000..2f49063f4c554 --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go @@ -0,0 +1,48 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuretasks + +import ( + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" + "k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter" +) + +type terraformAzureUserAssignedIdentity struct { + Name *string `cty:"name"` + ResourceGroupName *terraformWriter.Literal `cty:"resource_group_name"` + Location *string `cty:"location"` + Tags map[string]string `cty:"tags"` +} + +func (*ManagedIdentity) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *ManagedIdentity) error { + tf := &terraformAzureUserAssignedIdentity{ + Name: e.Name, + ResourceGroupName: e.ResourceGroup.terraformName(), + Location: fi.PtrTo(t.Cloud.Region()), + Tags: stringMap(e.Tags), + } + return t.RenderResource("azurerm_user_assigned_identity", fi.ValueOf(e.Name), tf) +} + +func (m *ManagedIdentity) terraformID() *terraformWriter.Literal { + return terraformWriter.LiteralProperty("azurerm_user_assigned_identity", fi.ValueOf(m.Name), "id") +} + +func (m *ManagedIdentity) terraformPrincipalID() *terraformWriter.Literal { + return terraformWriter.LiteralProperty("azurerm_user_assigned_identity", fi.ValueOf(m.Name), "principal_id") +} diff --git a/upup/pkg/fi/cloudup/azuretasks/roleassignment.go b/upup/pkg/fi/cloudup/azuretasks/roleassignment.go index 9a2e8cea8d51a..d9a6b738c532d 100644 --- a/upup/pkg/fi/cloudup/azuretasks/roleassignment.go +++ b/upup/pkg/fi/cloudup/azuretasks/roleassignment.go @@ -20,15 +20,13 @@ import ( "context" "errors" "fmt" - "path/filepath" - "strings" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/azure" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" authz "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3" - compute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "github.com/google/uuid" ) @@ -43,10 +41,10 @@ type RoleAssignment struct { Name *string Lifecycle fi.Lifecycle - Scope *string - VMScaleSet *VMScaleSet - ID *string - RoleDefID *string + Scope *string + ManagedIdentity *ManagedIdentity + ID *string + RoleDefID *string } var ( @@ -54,17 +52,17 @@ var ( _ fi.CompareWithID = &RoleAssignment{} ) -// CompareWithID returns the Name of the VM Scale Set. +// CompareWithID returns the Name of the RoleAssignment. func (r *RoleAssignment) CompareWithID() *string { return r.Name } // Find discovers the RoleAssignment in the cloud provider. func (r *RoleAssignment) Find(c *fi.CloudupContext) (*RoleAssignment, error) { - if r.VMScaleSet.PrincipalID == nil { - // PrincipalID of the VM Scale Set hasn't yet been + if r.ManagedIdentity.PrincipalID == nil { + // PrincipalID of the Managed Identity hasn't yet been // populated. No corresponding Role Assignment - // shouldn't exist in Cloud. + // should exist in Cloud. return nil, nil } @@ -74,18 +72,18 @@ func (r *RoleAssignment) Find(c *fi.CloudupContext) (*RoleAssignment, error) { return nil, err } - principalID := *r.VMScaleSet.PrincipalID + principalID := *r.ManagedIdentity.PrincipalID var found *authz.RoleAssignment for i := range rs { ra := rs[i] - if ra.Properties == nil { + if ra.Properties == nil || ra.Properties.RoleDefinitionID == nil || ra.Properties.PrincipalID == nil { continue } - // Use a name constructed by VMSS and Role definition ID to find a Role Assignment. We cannot use ra.Name - // as it is set to a randomly generated GUID. - l := strings.Split(*ra.Properties.RoleDefinitionID, "/") - roleDefID := l[len(l)-1] - if *ra.Properties.PrincipalID == principalID && roleDefID == *r.RoleDefID { + parsed, err := arm.ParseResourceID(*ra.Properties.RoleDefinitionID) + if err != nil { + continue + } + if *ra.Properties.PrincipalID == principalID && parsed.Name == *r.RoleDefID { found = ra break } @@ -94,35 +92,16 @@ func (r *RoleAssignment) Find(c *fi.CloudupContext) (*RoleAssignment, error) { return nil, nil } - // Query VM Scale Sets and find one that has matching Principal ID. - vs, err := cloud.VMScaleSet().List(context.TODO(), *r.VMScaleSet.ResourceGroup.Name) - if err != nil { - return nil, err - } - var foundVMSS *compute.VirtualMachineScaleSet - for _, v := range vs { - if v.Identity == nil { - continue - } - if *v.Identity.PrincipalID == principalID { - foundVMSS = v - break - } - } - if foundVMSS == nil { - return nil, fmt.Errorf("corresponding VM Scale Set not found for Role Assignment: %s", *found.ID) - } - r.ID = found.ID return &RoleAssignment{ Name: r.Name, Lifecycle: r.Lifecycle, Scope: found.Properties.Scope, - VMScaleSet: &VMScaleSet{ - Name: foundVMSS.Name, + ManagedIdentity: &ManagedIdentity{ + Name: r.ManagedIdentity.Name, }, ID: found.ID, - RoleDefID: to.Ptr(filepath.Base(*found.Properties.RoleDefinitionID)), + RoleDefID: r.RoleDefID, }, nil } @@ -168,7 +147,10 @@ func createNewRoleAssignment(t *azure.AzureAPITarget, e *RoleAssignment) error { roleAssignment := authz.RoleAssignmentCreateParameters{ Properties: &authz.RoleAssignmentProperties{ RoleDefinitionID: to.Ptr(roleDefID), - PrincipalID: e.VMScaleSet.PrincipalID, + PrincipalID: e.ManagedIdentity.PrincipalID, + // PrincipalType must be set to avoid PrincipalNotFound errors caused by + // Entra ID replication delay after managed identity creation. + PrincipalType: to.Ptr(authz.PrincipalTypeServicePrincipal), }, } ra, err := t.Cloud.RoleAssignment().Create(context.TODO(), scope, roleAssignmentName, roleAssignment) diff --git a/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go b/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go index 639d64e6399f6..707cab7bf5c58 100644 --- a/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go +++ b/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go @@ -36,7 +36,7 @@ func (*RoleAssignment) RenderTerraform(t *terraform.TerraformTarget, a, e, chang tf := &terraformAzureRoleAssignment{ Scope: e.Scope, RoleDefinitionID: &roleDefinitionID, - PrincipalID: e.VMScaleSet.terraformPrincipalID(), + PrincipalID: e.ManagedIdentity.terraformPrincipalID(), SkipServicePrincipalAADCheck: fi.PtrTo(true), } return t.RenderResource("azurerm_role_assignment", fi.ValueOf(e.Name), tf) diff --git a/upup/pkg/fi/cloudup/azuretasks/roleassignment_test.go b/upup/pkg/fi/cloudup/azuretasks/roleassignment_test.go index ca7f90958495d..24059c55a1313 100644 --- a/upup/pkg/fi/cloudup/azuretasks/roleassignment_test.go +++ b/upup/pkg/fi/cloudup/azuretasks/roleassignment_test.go @@ -23,7 +23,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" authz "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3" - compute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" "github.com/google/uuid" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/azure" @@ -36,8 +36,8 @@ func TestRoleAssignmentRenderAzure(t *testing.T) { expected := &RoleAssignment{ Name: to.Ptr("ra"), Scope: to.Ptr("scope"), - VMScaleSet: &VMScaleSet{ - Name: to.Ptr("vmss"), + ManagedIdentity: &ManagedIdentity{ + Name: to.Ptr("mi"), PrincipalID: to.Ptr("pid"), }, RoleDefID: to.Ptr("rdid0"), @@ -51,8 +51,8 @@ func TestRoleAssignmentRenderAzure(t *testing.T) { t.Fatalf("id must be set") } actual := cloud.RoleAssignmentsClient.RAs[*expected.ID] - if a, e := *actual.Properties.PrincipalID, *expected.VMScaleSet.PrincipalID; a != e { - t.Errorf("unexpected role definition ID: expected %s, but got %s", e, a) + if a, e := *actual.Properties.PrincipalID, *expected.ManagedIdentity.PrincipalID; a != e { + t.Errorf("unexpected principal ID: expected %s, but got %s", e, a) } } @@ -64,27 +64,25 @@ func TestRoleAssignmentFind(t *testing.T) { }, } - rg := &ResourceGroup{ - Name: to.Ptr("rg"), - } - vmssName := "vmss" - resp, err := cloud.VMScaleSet().CreateOrUpdate(context.TODO(), *rg.Name, vmssName, newTestVMSSParameters()) + // Create a managed identity. + miName := "test-identity" + miResult, err := cloud.ManagedIdentity().CreateOrUpdate(context.TODO(), "rg", miName, armmsi.Identity{ + Location: to.Ptr("eastus"), + }) if err != nil { - t.Fatalf("failed to create: %s", err) - } - vmss := &VMScaleSet{ - Name: to.Ptr(vmssName), - PrincipalID: resp.Identity.PrincipalID, - ResourceGroup: rg, + t.Fatalf("failed to create managed identity: %s", err) } - scope := "scope" + scope := "/subscriptions/sub/resourceGroups/rg" roleDefID := "rdid0" ra := &RoleAssignment{ - Name: vmss.Name, - Scope: &scope, - VMScaleSet: vmss, - RoleDefID: &roleDefID, + Name: to.Ptr(miName), + Scope: &scope, + ManagedIdentity: &ManagedIdentity{ + Name: to.Ptr(miName), + PrincipalID: miResult.Properties.PrincipalID, + }, + RoleDefID: &roleDefID, } // Find will return nothing if there is no Role Assignment created. actual, err := ra.Find(ctx) @@ -92,7 +90,7 @@ func TestRoleAssignmentFind(t *testing.T) { t.Fatalf("unexpected error: %s", err) } if actual != nil { - t.Errorf("unexpected vmss found: %+v", actual) + t.Errorf("unexpected role assignment found: %+v", actual) } // Create Role Assignments. One of them has irrelevant (different role definition ID). @@ -100,7 +98,7 @@ func TestRoleAssignmentFind(t *testing.T) { roleAssignment := authz.RoleAssignmentCreateParameters{ Properties: &authz.RoleAssignmentProperties{ RoleDefinitionID: to.Ptr(roleDefID), - PrincipalID: vmss.PrincipalID, + PrincipalID: miResult.Properties.PrincipalID, }, } if _, err := cloud.RoleAssignment().Create(context.TODO(), scope, roleAssignmentName, roleAssignment); err != nil { @@ -110,7 +108,7 @@ func TestRoleAssignmentFind(t *testing.T) { irrelevant := authz.RoleAssignmentCreateParameters{ Properties: &authz.RoleAssignmentProperties{ RoleDefinitionID: to.Ptr("irrelevant"), - PrincipalID: vmss.PrincipalID, + PrincipalID: miResult.Properties.PrincipalID, }, } if _, err := cloud.RoleAssignment().Create(context.TODO(), scope, uuid.New().String(), irrelevant); err != nil { @@ -123,7 +121,7 @@ func TestRoleAssignmentFind(t *testing.T) { t.Fatalf("unexpected error: %s", err) } if actual == nil { - t.Fatalf("unexpected vmss not found: %+v", actual) + t.Fatalf("expected role assignment to be found") } if a, e := *actual.Name, *ra.Name; a != e { t.Errorf("unexpected Role name Assignment: expected %+v, but got %+v", e, a) @@ -134,7 +132,7 @@ func TestRoleAssignmentFind(t *testing.T) { } // TestRoleAssignmentFind_NoPrincipalID verifies that Find doesn't find any Role Assignment -// when the principal ID of VM Scale Set hasn't yet been set. +// when the principal ID of the Managed Identity hasn't yet been set. func TestRoleAssignmentFind_NoPrincipalID(t *testing.T) { cloud := NewMockAzureCloud("eastus") ctx := &fi.CloudupContext{ @@ -143,15 +141,6 @@ func TestRoleAssignmentFind_NoPrincipalID(t *testing.T) { }, } - // Create a VM Scale Set. - rg := &ResourceGroup{ - Name: to.Ptr("rg"), - } - vmssName := "vmss" - if _, err := cloud.VMScaleSet().CreateOrUpdate(context.TODO(), *rg.Name, vmssName, newTestVMSSParameters()); err != nil { - t.Fatalf("failed to create VM Scale Set: %s", err) - } - // Create a dummy Role Assignment to ensure that this won't be returned by Find. roleAssignment := authz.RoleAssignmentCreateParameters{ Properties: &authz.RoleAssignmentProperties{ @@ -164,10 +153,10 @@ func TestRoleAssignmentFind_NoPrincipalID(t *testing.T) { scope := "scope" ra := &RoleAssignment{ - Name: to.Ptr(vmssName), + Name: to.Ptr("test"), Scope: to.Ptr(scope), - VMScaleSet: &VMScaleSet{ - Name: to.Ptr(vmssName), + ManagedIdentity: &ManagedIdentity{ + Name: to.Ptr("mi"), // Do not set principal ID. }, } @@ -218,11 +207,3 @@ func TestRoleAssignmentCheckChanges(t *testing.T) { }) } } - -func newTestVMSSParameters() compute.VirtualMachineScaleSet { - return compute.VirtualMachineScaleSet{ - Identity: &compute.VirtualMachineScaleSetIdentity{ - Type: to.Ptr(compute.ResourceIdentityTypeSystemAssigned), - }, - } -} diff --git a/upup/pkg/fi/cloudup/azuretasks/subnet.go b/upup/pkg/fi/cloudup/azuretasks/subnet.go index 6f16b058047b3..43f900140bac0 100644 --- a/upup/pkg/fi/cloudup/azuretasks/subnet.go +++ b/upup/pkg/fi/cloudup/azuretasks/subnet.go @@ -64,12 +64,9 @@ func (s *Subnet) Find(c *fi.CloudupContext) (*Subnet, error) { if errors.As(err, &azErr) { if azErr.ErrorCode == "ResourceNotFound" || azErr.ErrorCode == "ResourceGroupNotFound" { return nil, nil - } else { - return nil, azErr } - } else { - return nil, err } + return nil, err } var found *network.Subnet diff --git a/upup/pkg/fi/cloudup/azuretasks/testing.go b/upup/pkg/fi/cloudup/azuretasks/testing.go index 98e464f1620a3..b33e892a7e2af 100644 --- a/upup/pkg/fi/cloudup/azuretasks/testing.go +++ b/upup/pkg/fi/cloudup/azuretasks/testing.go @@ -21,9 +21,11 @@ import ( "errors" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" authz "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3" compute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" network "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" resources "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" @@ -58,6 +60,7 @@ type MockAzureCloud struct { LoadBalancersClient *MockLoadBalancersClient PublicIPAddressesClient *MockPublicIPAddressesClient NatGatewaysClient *MockNatGatewaysClient + ManagedIdentitiesClient *MockManagedIdentitiesClient StorageAccountsClient *MockStorageAccountsClient } @@ -117,6 +120,9 @@ func NewMockAzureCloud(location string) *MockAzureCloud { NatGatewaysClient: &MockNatGatewaysClient{ NGWs: map[string]*network.NatGateway{}, }, + ManagedIdentitiesClient: &MockManagedIdentitiesClient{ + MIs: map[string]*armmsi.Identity{}, + }, StorageAccountsClient: &MockStorageAccountsClient{ SAs: map[string]*armstorage.Account{}, }, @@ -272,6 +278,11 @@ func (c *MockAzureCloud) NatGateway() azure.NatGatewaysClient { return c.NatGatewaysClient } +// ManagedIdentity returns the managed identity client. +func (c *MockAzureCloud) ManagedIdentity() azure.ManagedIdentitiesClient { + return c.ManagedIdentitiesClient +} + // MockResourceGroupsClient is a mock implementation of resource group client. type MockResourceGroupsClient struct { RGs map[string]*resources.ResourceGroup @@ -433,7 +444,9 @@ func (c *MockVMScaleSetsClient) CreateOrUpdate(ctx context.Context, resourceGrou } parameters.Name = &vmScaleSetName parameters.ID = &vmScaleSetName - parameters.Identity.PrincipalID = to.Ptr(uuid.New().String()) + if parameters.Identity != nil { + parameters.Identity.PrincipalID = to.Ptr(uuid.New().String()) + } c.VMSSes[vmScaleSetName] = ¶meters return ¶meters, nil } @@ -545,12 +558,13 @@ func (c *MockRoleAssignmentsClient) Create( if _, ok := c.RAs[roleAssignmentName]; ok { return nil, fmt.Errorf("update not supported") } + roleDefID := fmt.Sprintf("%s/providers/Microsoft.Authorization/roleDefinitions/%s", scope, *parameters.Properties.RoleDefinitionID) ra := &authz.RoleAssignment{ ID: to.Ptr(roleAssignmentName), Name: to.Ptr(roleAssignmentName), Properties: &authz.RoleAssignmentProperties{ Scope: to.Ptr(scope), - RoleDefinitionID: parameters.Properties.RoleDefinitionID, + RoleDefinitionID: to.Ptr(roleDefID), PrincipalID: parameters.Properties.PrincipalID, }, } @@ -819,6 +833,58 @@ func (c *MockNatGatewaysClient) Delete(ctx context.Context, resourceGroupName, n return nil } +// MockManagedIdentitiesClient is a mock implementation of managed identities client. +type MockManagedIdentitiesClient struct { + MIs map[string]*armmsi.Identity +} + +var _ azure.ManagedIdentitiesClient = (*MockManagedIdentitiesClient)(nil) + +// CreateOrUpdate creates or updates a managed identity. +func (c *MockManagedIdentitiesClient) CreateOrUpdate(ctx context.Context, resourceGroupName, name string, parameters armmsi.Identity) (*armmsi.Identity, error) { + principalID := uuid.New().String() + resourceID := fmt.Sprintf("/subscriptions/sub/resourceGroups/%s/providers/Microsoft.ManagedIdentity/userAssignedIdentities/%s", resourceGroupName, name) + mi := &armmsi.Identity{ + Name: to.Ptr(name), + ID: to.Ptr(resourceID), + Location: parameters.Location, + Tags: parameters.Tags, + Properties: &armmsi.UserAssignedIdentityProperties{ + PrincipalID: to.Ptr(principalID), + ClientID: to.Ptr(uuid.New().String()), + }, + } + c.MIs[name] = mi + return mi, nil +} + +// Get returns a managed identity by name. +func (c *MockManagedIdentitiesClient) Get(ctx context.Context, resourceGroupName, name string) (*armmsi.Identity, error) { + mi, ok := c.MIs[name] + if !ok { + return nil, &azcore.ResponseError{ErrorCode: "ResourceNotFound"} + } + return mi, nil +} + +// List returns a slice of managed identities. +func (c *MockManagedIdentitiesClient) List(ctx context.Context, resourceGroupName string) ([]*armmsi.Identity, error) { + var l []*armmsi.Identity + for _, mi := range c.MIs { + l = append(l, mi) + } + return l, nil +} + +// Delete deletes a managed identity. +func (c *MockManagedIdentitiesClient) Delete(ctx context.Context, resourceGroupName, name string) error { + if _, ok := c.MIs[name]; !ok { + return fmt.Errorf("%s does not exist", name) + } + delete(c.MIs, name) + return nil +} + // MockStorageAccountsClient is a mock implementation of Nat Gateway client. type MockStorageAccountsClient struct { SAs map[string]*armstorage.Account diff --git a/upup/pkg/fi/cloudup/azuretasks/vmscaleset.go b/upup/pkg/fi/cloudup/azuretasks/vmscaleset.go index e806e91e2fb43..ab47d4162a03c 100644 --- a/upup/pkg/fi/cloudup/azuretasks/vmscaleset.go +++ b/upup/pkg/fi/cloudup/azuretasks/vmscaleset.go @@ -20,8 +20,10 @@ import ( "context" "encoding/base64" "fmt" + "sort" "strings" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" compute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "k8s.io/klog/v2" @@ -55,10 +57,11 @@ type VMScaleSet struct { AdminUser *string SSHPublicKey *string // UserData is the user data configuration - UserData fi.Resource - Tags map[string]*string - Zones []*string - PrincipalID *string + UserData fi.Resource + Tags map[string]*string + Zones []*string + // ManagedIdentities are the user-assigned managed identities for this VMSS. + ManagedIdentities []*ManagedIdentity } var _ fi.CloudupTaskNormalize = (*VMScaleSet)(nil) @@ -203,7 +206,24 @@ func (s *VMScaleSet) Find(c *fi.CloudupContext) (*VMScaleSet, error) { SSHPublicKey: sshKeys[0].KeyData, UserData: fi.NewBytesResource(userData), Tags: found.Tags, - PrincipalID: found.Identity.PrincipalID, + } + // Extract user-assigned managed identities if present. + if found.Identity != nil && found.Identity.UserAssignedIdentities != nil { + var resourceIDs []string + for resourceID := range found.Identity.UserAssignedIdentities { + resourceIDs = append(resourceIDs, resourceID) + } + sort.Strings(resourceIDs) + for _, resourceID := range resourceIDs { + parsedID, err := arm.ParseResourceID(resourceID) + if err != nil { + return nil, fmt.Errorf("failed to parse managed identity ID %s: %w", resourceID, err) + } + vmss.ManagedIdentities = append(vmss.ManagedIdentities, &ManagedIdentity{ + Name: to.Ptr(parsedID.Name), + ResourceID: to.Ptr(resourceID), + }) + } } if ipConfig.Properties != nil && ipConfig.Properties.ApplicationSecurityGroups != nil { for _, asg := range ipConfig.Properties.ApplicationSecurityGroups { @@ -220,7 +240,6 @@ func (s *VMScaleSet) Find(c *fi.CloudupContext) (*VMScaleSet, error) { if found.Zones != nil { vmss.Zones = found.Zones } - s.PrincipalID = found.Identity.PrincipalID return vmss, nil } @@ -362,24 +381,27 @@ func (s *VMScaleSet) RenderAzure(t *azure.AzureAPITarget, a, e, changes *VMScale }, }, }, - // Assign a system-assigned managed identity so that - // Azure creates an identity for VMs and provision - // its credentials on the VMs. - Identity: &compute.VirtualMachineScaleSetIdentity{ - Type: to.Ptr(compute.ResourceIdentityTypeSystemAssigned), - }, Tags: e.Tags, Zones: e.Zones, } - result, err := t.Cloud.VMScaleSet().CreateOrUpdate( + if len(e.ManagedIdentities) > 0 { + vmss.Identity = &compute.VirtualMachineScaleSetIdentity{ + Type: to.Ptr(compute.ResourceIdentityTypeUserAssigned), + UserAssignedIdentities: make(map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, len(e.ManagedIdentities)), + } + for _, managedIdentity := range e.ManagedIdentities { + if managedIdentity == nil || managedIdentity.ResourceID == nil { + return fmt.Errorf("user-assigned identities require managed identity resource IDs") + } + vmss.Identity.UserAssignedIdentities[*managedIdentity.ResourceID] = &compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue{} + } + } + + _, err := t.Cloud.VMScaleSet().CreateOrUpdate( context.TODO(), *e.ResourceGroup.Name, name, vmss) - if err != nil { - return err - } - e.PrincipalID = result.Identity.PrincipalID - return nil + return err } diff --git a/upup/pkg/fi/cloudup/azuretasks/vmscaleset_terraform.go b/upup/pkg/fi/cloudup/azuretasks/vmscaleset_terraform.go index 405c3230451cb..481ee123df02a 100644 --- a/upup/pkg/fi/cloudup/azuretasks/vmscaleset_terraform.go +++ b/upup/pkg/fi/cloudup/azuretasks/vmscaleset_terraform.go @@ -65,7 +65,8 @@ type terraformAzureVMScaleSetNetworkInterface struct { } type terraformAzureVMScaleSetIdentity struct { - Type *string `cty:"type"` + Type *string `cty:"type"` + IdentityIDs []*terraformWriter.Literal `cty:"identity_ids"` } type terraformAzureVMScaleSet struct { @@ -123,12 +124,20 @@ func (*VMScaleSet) RenderTerraform(t *terraform.TerraformTarget, a, e, changes * StorageAccountType: storageAccountType(storageProfile.OSDisk.ManagedDisk), DiskSizeGB: storageProfile.OSDisk.DiskSizeGB, }, - Identity: &terraformAzureVMScaleSetIdentity{ - Type: fi.PtrTo("SystemAssigned"), - }, Tags: stringMap(e.Tags), } + if len(e.ManagedIdentities) > 0 { + var identityIDs []*terraformWriter.Literal + for _, mi := range e.ManagedIdentities { + identityIDs = append(identityIDs, mi.terraformID()) + } + tf.Identity = &terraformAzureVMScaleSetIdentity{ + Type: fi.PtrTo("UserAssigned"), + IdentityIDs: identityIDs, + } + } + if e.UserData != nil { userData, err := t.AddFileResource("azurerm_linux_virtual_machine_scale_set", fi.ValueOf(e.Name), "user_data", e.UserData, true) if err != nil { diff --git a/upup/pkg/fi/cloudup/azuretasks/vmscaleset_test.go b/upup/pkg/fi/cloudup/azuretasks/vmscaleset_test.go index 71ff198a7a127..50ea2f58cee1b 100644 --- a/upup/pkg/fi/cloudup/azuretasks/vmscaleset_test.go +++ b/upup/pkg/fi/cloudup/azuretasks/vmscaleset_test.go @@ -26,6 +26,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" compute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/azure" ) @@ -126,13 +127,12 @@ func TestVMScaleSetRenderAzure(t *testing.T) { t.Errorf("unexpected user data: expected %v, but got %v", expectedUserData, actualUserData) } - if expected.PrincipalID == nil { - t.Errorf("unexpected nil principalID") - } - if a, e := actual.Zones, expected.Zones; !reflect.DeepEqual(a, e) { t.Errorf("unexpected Zone: expected %+v, but got %+v", e, a) } + if actual.Identity != nil { + t.Fatalf("expected VMSS identity to be omitted, got %+v", actual.Identity) + } } func TestVMScaleSetFind(t *testing.T) { @@ -301,6 +301,46 @@ func TestVMScaleSetFind(t *testing.T) { } } +func TestVMScaleSetFindPreservesManagedIdentityName(t *testing.T) { + cloud := NewMockAzureCloud("eastus") + ctx := &fi.CloudupContext{ + T: fi.CloudupSubContext{ + Cloud: cloud, + }, + } + + vmss := newTestVMScaleSet() + for _, managedIdentityName := range []string{"vmss-mi-b", "vmss-mi-a"} { + managedIdentity, err := cloud.ManagedIdentity().CreateOrUpdate(context.Background(), *vmss.ResourceGroup.Name, managedIdentityName, armmsi.Identity{ + Location: to.Ptr(cloud.Location), + }) + if err != nil { + t.Fatalf("failed to create managed identity: %s", err) + } + vmss.ManagedIdentities = append(vmss.ManagedIdentities, &ManagedIdentity{ + Name: to.Ptr(managedIdentityName), + ResourceID: managedIdentity.ID, + }) + } + if err := (&VMScaleSet{}).RenderAzure(azure.NewAzureAPITarget(cloud), nil, vmss, nil); err != nil { + t.Fatalf("failed to create vmss: %s", err) + } + + actual, err := vmss.Find(ctx) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if actual == nil || len(actual.ManagedIdentities) != 2 { + t.Fatalf("expected managed identities to be discovered, got %+v", actual.ManagedIdentities) + } + expected := *actual + + changes := &VMScaleSet{} + if changed := fi.BuildChanges(actual, &expected, changes); changed { + t.Fatalf("expected no changes when managed identities match, got %+v", changes.ManagedIdentities) + } +} + func TestVMScaleSetRun(t *testing.T) { cloud := NewMockAzureCloud("eastus") ctx := &fi.CloudupContext{ diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md new file mode 100644 index 0000000000000..34085f0955218 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md @@ -0,0 +1,68 @@ +# Release History + +## 1.3.0 (2025-07-25) +### Features Added + +- New enum type `IsolationScope` with values `IsolationScopeNone`, `IsolationScopeRegional` +- New field `IsolationScope` in struct `UserAssignedIdentityProperties` + + +## 1.2.0 (2023-11-24) +### Features Added + +- Support for test fakes and OpenTelemetry trace spans. + + +## 1.1.0 (2023-03-31) +### Features Added + +- New struct `ClientFactory` which is a client factory used to create any client in this module + + +## 1.0.0 (2023-02-24) +### Breaking Changes + +- Function `*UserAssignedIdentitiesClient.NewListAssociatedResourcesPager` has been removed + +### Features Added + +- New type alias `CreatedByType` with values `CreatedByTypeApplication`, `CreatedByTypeKey`, `CreatedByTypeManagedIdentity`, `CreatedByTypeUser` +- New function `timeRFC3339.MarshalText() ([]byte, error)` +- New function `*timeRFC3339.Parse(string) error` +- New function `*timeRFC3339.UnmarshalText([]byte) error` +- New struct `SystemData` +- New field `SystemData` in struct `FederatedIdentityCredential` +- New field `SystemData` in struct `Identity` +- New field `SystemData` in struct `IdentityUpdate` +- New field `SystemData` in struct `SystemAssignedIdentity` + + +## 0.7.0 (2022-06-27) +### Features Added + +- New function `*FederatedIdentityCredentialsClient.Delete(context.Context, string, string, string, *FederatedIdentityCredentialsClientDeleteOptions) (FederatedIdentityCredentialsClientDeleteResponse, error)` +- New function `*FederatedIdentityCredentialsClient.CreateOrUpdate(context.Context, string, string, string, FederatedIdentityCredential, *FederatedIdentityCredentialsClientCreateOrUpdateOptions) (FederatedIdentityCredentialsClientCreateOrUpdateResponse, error)` +- New function `*FederatedIdentityCredentialsClient.NewListPager(string, string, *FederatedIdentityCredentialsClientListOptions) *runtime.Pager[FederatedIdentityCredentialsClientListResponse]` +- New function `NewFederatedIdentityCredentialsClient(string, azcore.TokenCredential, *arm.ClientOptions) (*FederatedIdentityCredentialsClient, error)` +- New function `*FederatedIdentityCredentialsClient.Get(context.Context, string, string, string, *FederatedIdentityCredentialsClientGetOptions) (FederatedIdentityCredentialsClientGetResponse, error)` +- New struct `FederatedIdentityCredential` +- New struct `FederatedIdentityCredentialProperties` +- New struct `FederatedIdentityCredentialsClient` +- New struct `FederatedIdentityCredentialsClientCreateOrUpdateOptions` +- New struct `FederatedIdentityCredentialsClientCreateOrUpdateResponse` +- New struct `FederatedIdentityCredentialsClientDeleteOptions` +- New struct `FederatedIdentityCredentialsClientDeleteResponse` +- New struct `FederatedIdentityCredentialsClientGetOptions` +- New struct `FederatedIdentityCredentialsClientGetResponse` +- New struct `FederatedIdentityCredentialsClientListOptions` +- New struct `FederatedIdentityCredentialsClientListResponse` +- New struct `FederatedIdentityCredentialsListResult` + + +## 0.6.0 (2022-05-17) + +The package of `github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi` is using our [next generation design principles](https://azure.github.io/azure-sdk/general_introduction.html) since version 0.6.0, which contains breaking changes. + +To migrate the existing applications to the latest version, please refer to [Migration Guide](https://aka.ms/azsdk/go/mgmt/migration). + +To learn more, please refer to our documentation [Quick Start](https://aka.ms/azsdk/go/mgmt). \ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt new file mode 100644 index 0000000000000..dc0c2ffb3dc15 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md new file mode 100644 index 0000000000000..8c86245d85a18 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md @@ -0,0 +1,90 @@ +# Azure Managed Service Identity Module for Go + +The `armmsi` module provides operations for working with Azure Managed Service Identity. + +[Source code](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/resourcemanager/msi/armmsi) + +# Getting started + +## Prerequisites + +- an [Azure subscription](https://azure.microsoft.com/free/) +- [Supported](https://aka.ms/azsdk/go/supported-versions) version of Go (You could download and install the latest version of Go from [here](https://go.dev/doc/install). It will replace the existing Go on your machine. If you want to install multiple Go versions on the same machine, you could refer this [doc](https://go.dev/doc/manage-install).) + +## Install the package + +This project uses [Go modules](https://github.com/golang/go/wiki/Modules) for versioning and dependency management. + +Install the Azure Managed Service Identity module: + +```sh +go get github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi +``` + +## Authorization + +When creating a client, you will need to provide a credential for authenticating with Azure Managed Service Identity. The `azidentity` module provides facilities for various ways of authenticating with Azure including client/secret, certificate, managed identity, and more. + +```go +cred, err := azidentity.NewDefaultAzureCredential(nil) +``` + +For more information on authentication, please see the documentation for `azidentity` at [pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity). + +## Client Factory + +Azure Managed Service Identity module consists of one or more clients. We provide a client factory which could be used to create any client in this module. + +```go +clientFactory, err := armmsi.NewClientFactory(, cred, nil) +``` + +You can use `ClientOptions` in package `github.com/Azure/azure-sdk-for-go/sdk/azcore/arm` to set endpoint to connect with public and sovereign clouds as well as Azure Stack. For more information, please see the documentation for `azcore` at [pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore). + +```go +options := arm.ClientOptions { + ClientOptions: azcore.ClientOptions { + Cloud: cloud.AzureChina, + }, +} +clientFactory, err := armmsi.NewClientFactory(, cred, &options) +``` + +## Clients + +A client groups a set of related APIs, providing access to its functionality. Create one or more clients to access the APIs you require using client factory. + +```go +client := clientFactory.NewFederatedIdentityCredentialsClient() +``` + +## Fakes + +The fake package contains types used for constructing in-memory fake servers used in unit tests. +This allows writing tests to cover various success/error conditions without the need for connecting to a live service. + +Please see https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/samples/fakes for details and examples on how to use fakes. + +## Provide Feedback + +If you encounter bugs or have suggestions, please +[open an issue](https://github.com/Azure/azure-sdk-for-go/issues) and assign the `Managed Service Identity` label. + +# Contributing + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. +For details, visit [https://cla.microsoft.com](https://cla.microsoft.com). + +When you submit a pull request, a CLA-bot will automatically determine whether +you need to provide a CLA and decorate the PR appropriately (e.g., label, +comment). Simply follow the instructions provided by the bot. You will only +need to do this once across all repos using our CLA. + +This project has adopted the +[Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information, see the +[Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any +additional questions or comments. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json new file mode 100644 index 0000000000000..84324b8b7300e --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json @@ -0,0 +1,6 @@ +{ + "AssetsRepo": "Azure/azure-sdk-assets", + "AssetsRepoPrefixPath": "go", + "TagPrefix": "go/resourcemanager/msi/armmsi", + "Tag": "go/resourcemanager/msi/armmsi_dbfb7feb00" +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md new file mode 100644 index 0000000000000..c735b46f0ab12 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md @@ -0,0 +1,13 @@ +### AutoRest Configuration + +> see https://aka.ms/autorest + +``` yaml +azure-arm: true +require: +- https://github.com/Azure/azure-rest-api-specs/blob/44319b51c6f952fdc9543d3dc4fdd9959350d102/specification/msi/resource-manager/readme.md +- https://github.com/Azure/azure-rest-api-specs/blob/44319b51c6f952fdc9543d3dc4fdd9959350d102/specification/msi/resource-manager/readme.go.md +license-header: MICROSOFT_MIT_NO_VERSION +module-version: 1.3.0 +tag: package-2024-11-30 +``` \ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go new file mode 100644 index 0000000000000..1c3e780b1829b --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +// This file enables 'go generate' to regenerate this specific SDK +//go:generate pwsh ../../../../eng/scripts/build.ps1 -skipBuild -cleanGenerated -format -tidy -generate -alwaysSetBodyParamRequired -removeUnreferencedTypes resourcemanager/msi/armmsi + +package armmsi diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml new file mode 100644 index 0000000000000..c897a929e2db1 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml @@ -0,0 +1,28 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. +trigger: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/resourcemanager/msi/armmsi/ + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/resourcemanager/msi/armmsi/ + +extends: + template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml + parameters: + ServiceDirectory: 'resourcemanager/msi/armmsi' + UsePipelineProxy: false diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go new file mode 100644 index 0000000000000..fd7d09d85fa64 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" +) + +// ClientFactory is a client factory used to create any client in this module. +// Don't use this type directly, use NewClientFactory instead. +type ClientFactory struct { + subscriptionID string + internal *arm.Client +} + +// NewClientFactory creates a new instance of ClientFactory with the specified values. +// The parameter values will be propagated to any client created from this factory. +// - subscriptionID - The Id of the Subscription to which the identity belongs. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewClientFactory(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (*ClientFactory, error) { + internal, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + return &ClientFactory{ + subscriptionID: subscriptionID, + internal: internal, + }, nil +} + +// NewFederatedIdentityCredentialsClient creates a new instance of FederatedIdentityCredentialsClient. +func (c *ClientFactory) NewFederatedIdentityCredentialsClient() *FederatedIdentityCredentialsClient { + return &FederatedIdentityCredentialsClient{ + subscriptionID: c.subscriptionID, + internal: c.internal, + } +} + +// NewOperationsClient creates a new instance of OperationsClient. +func (c *ClientFactory) NewOperationsClient() *OperationsClient { + return &OperationsClient{ + internal: c.internal, + } +} + +// NewSystemAssignedIdentitiesClient creates a new instance of SystemAssignedIdentitiesClient. +func (c *ClientFactory) NewSystemAssignedIdentitiesClient() *SystemAssignedIdentitiesClient { + return &SystemAssignedIdentitiesClient{ + internal: c.internal, + } +} + +// NewUserAssignedIdentitiesClient creates a new instance of UserAssignedIdentitiesClient. +func (c *ClientFactory) NewUserAssignedIdentitiesClient() *UserAssignedIdentitiesClient { + return &UserAssignedIdentitiesClient{ + subscriptionID: c.subscriptionID, + internal: c.internal, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go new file mode 100644 index 0000000000000..d33c5e1e4cc17 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +const ( + moduleName = "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" + moduleVersion = "v1.3.0" +) + +// CreatedByType - The type of identity that created the resource. +type CreatedByType string + +const ( + CreatedByTypeApplication CreatedByType = "Application" + CreatedByTypeKey CreatedByType = "Key" + CreatedByTypeManagedIdentity CreatedByType = "ManagedIdentity" + CreatedByTypeUser CreatedByType = "User" +) + +// PossibleCreatedByTypeValues returns the possible values for the CreatedByType const type. +func PossibleCreatedByTypeValues() []CreatedByType { + return []CreatedByType{ + CreatedByTypeApplication, + CreatedByTypeKey, + CreatedByTypeManagedIdentity, + CreatedByTypeUser, + } +} + +// IsolationScope - Enum to configure regional restrictions on identity assignment, as necessary. +type IsolationScope string + +const ( + IsolationScopeNone IsolationScope = "None" + IsolationScopeRegional IsolationScope = "Regional" +) + +// PossibleIsolationScopeValues returns the possible values for the IsolationScope const type. +func PossibleIsolationScopeValues() []IsolationScope { + return []IsolationScope{ + IsolationScopeNone, + IsolationScopeRegional, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go new file mode 100644 index 0000000000000..05f06385c5e38 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go @@ -0,0 +1,317 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "net/url" + "strconv" + "strings" +) + +// FederatedIdentityCredentialsClient contains the methods for the FederatedIdentityCredentials group. +// Don't use this type directly, use NewFederatedIdentityCredentialsClient() instead. +type FederatedIdentityCredentialsClient struct { + internal *arm.Client + subscriptionID string +} + +// NewFederatedIdentityCredentialsClient creates a new instance of FederatedIdentityCredentialsClient with the specified values. +// - subscriptionID - The Id of the Subscription to which the identity belongs. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewFederatedIdentityCredentialsClient(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (*FederatedIdentityCredentialsClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &FederatedIdentityCredentialsClient{ + subscriptionID: subscriptionID, + internal: cl, + } + return client, nil +} + +// CreateOrUpdate - Create or update a federated identity credential under the specified user assigned identity. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - federatedIdentityCredentialResourceName - The name of the federated identity credential resource. +// - parameters - Parameters to create or update the federated identity credential. +// - options - FederatedIdentityCredentialsClientCreateOrUpdateOptions contains the optional parameters for the FederatedIdentityCredentialsClient.CreateOrUpdate +// method. +func (client *FederatedIdentityCredentialsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, parameters FederatedIdentityCredential, options *FederatedIdentityCredentialsClientCreateOrUpdateOptions) (FederatedIdentityCredentialsClientCreateOrUpdateResponse, error) { + var err error + const operationName = "FederatedIdentityCredentialsClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.createOrUpdateCreateRequest(ctx, resourceGroupName, resourceName, federatedIdentityCredentialResourceName, parameters, options) + if err != nil { + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusCreated) { + err = runtime.NewResponseError(httpResp) + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + resp, err := client.createOrUpdateHandleResponse(httpResp) + return resp, err +} + +// createOrUpdateCreateRequest creates the CreateOrUpdate request. +func (client *FederatedIdentityCredentialsClient) createOrUpdateCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, parameters FederatedIdentityCredential, _ *FederatedIdentityCredentialsClientCreateOrUpdateOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials/{federatedIdentityCredentialResourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + if federatedIdentityCredentialResourceName == "" { + return nil, errors.New("parameter federatedIdentityCredentialResourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{federatedIdentityCredentialResourceName}", url.PathEscape(federatedIdentityCredentialResourceName)) + req, err := runtime.NewRequest(ctx, http.MethodPut, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, parameters); err != nil { + return nil, err + } + return req, nil +} + +// createOrUpdateHandleResponse handles the CreateOrUpdate response. +func (client *FederatedIdentityCredentialsClient) createOrUpdateHandleResponse(resp *http.Response) (FederatedIdentityCredentialsClientCreateOrUpdateResponse, error) { + result := FederatedIdentityCredentialsClientCreateOrUpdateResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FederatedIdentityCredential); err != nil { + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + return result, nil +} + +// Delete - Deletes the federated identity credential. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - federatedIdentityCredentialResourceName - The name of the federated identity credential resource. +// - options - FederatedIdentityCredentialsClientDeleteOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Delete +// method. +func (client *FederatedIdentityCredentialsClient) Delete(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, options *FederatedIdentityCredentialsClientDeleteOptions) (FederatedIdentityCredentialsClientDeleteResponse, error) { + var err error + const operationName = "FederatedIdentityCredentialsClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.deleteCreateRequest(ctx, resourceGroupName, resourceName, federatedIdentityCredentialResourceName, options) + if err != nil { + return FederatedIdentityCredentialsClientDeleteResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return FederatedIdentityCredentialsClientDeleteResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusNoContent) { + err = runtime.NewResponseError(httpResp) + return FederatedIdentityCredentialsClientDeleteResponse{}, err + } + return FederatedIdentityCredentialsClientDeleteResponse{}, nil +} + +// deleteCreateRequest creates the Delete request. +func (client *FederatedIdentityCredentialsClient) deleteCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, _ *FederatedIdentityCredentialsClientDeleteOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials/{federatedIdentityCredentialResourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + if federatedIdentityCredentialResourceName == "" { + return nil, errors.New("parameter federatedIdentityCredentialResourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{federatedIdentityCredentialResourceName}", url.PathEscape(federatedIdentityCredentialResourceName)) + req, err := runtime.NewRequest(ctx, http.MethodDelete, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// Get - Gets the federated identity credential. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - federatedIdentityCredentialResourceName - The name of the federated identity credential resource. +// - options - FederatedIdentityCredentialsClientGetOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Get +// method. +func (client *FederatedIdentityCredentialsClient) Get(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, options *FederatedIdentityCredentialsClientGetOptions) (FederatedIdentityCredentialsClientGetResponse, error) { + var err error + const operationName = "FederatedIdentityCredentialsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.getCreateRequest(ctx, resourceGroupName, resourceName, federatedIdentityCredentialResourceName, options) + if err != nil { + return FederatedIdentityCredentialsClientGetResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return FederatedIdentityCredentialsClientGetResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return FederatedIdentityCredentialsClientGetResponse{}, err + } + resp, err := client.getHandleResponse(httpResp) + return resp, err +} + +// getCreateRequest creates the Get request. +func (client *FederatedIdentityCredentialsClient) getCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, _ *FederatedIdentityCredentialsClientGetOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials/{federatedIdentityCredentialResourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + if federatedIdentityCredentialResourceName == "" { + return nil, errors.New("parameter federatedIdentityCredentialResourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{federatedIdentityCredentialResourceName}", url.PathEscape(federatedIdentityCredentialResourceName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getHandleResponse handles the Get response. +func (client *FederatedIdentityCredentialsClient) getHandleResponse(resp *http.Response) (FederatedIdentityCredentialsClientGetResponse, error) { + result := FederatedIdentityCredentialsClientGetResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FederatedIdentityCredential); err != nil { + return FederatedIdentityCredentialsClientGetResponse{}, err + } + return result, nil +} + +// NewListPager - Lists all the federated identity credentials under the specified user assigned identity. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - options - FederatedIdentityCredentialsClientListOptions contains the optional parameters for the FederatedIdentityCredentialsClient.NewListPager +// method. +func (client *FederatedIdentityCredentialsClient) NewListPager(resourceGroupName string, resourceName string, options *FederatedIdentityCredentialsClientListOptions) *runtime.Pager[FederatedIdentityCredentialsClientListResponse] { + return runtime.NewPager(runtime.PagingHandler[FederatedIdentityCredentialsClientListResponse]{ + More: func(page FederatedIdentityCredentialsClientListResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *FederatedIdentityCredentialsClientListResponse) (FederatedIdentityCredentialsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "FederatedIdentityCredentialsClient.NewListPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listCreateRequest(ctx, resourceGroupName, resourceName, options) + }, nil) + if err != nil { + return FederatedIdentityCredentialsClientListResponse{}, err + } + return client.listHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listCreateRequest creates the List request. +func (client *FederatedIdentityCredentialsClient) listCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, options *FederatedIdentityCredentialsClientListOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + if options != nil && options.Skiptoken != nil { + reqQP.Set("$skiptoken", *options.Skiptoken) + } + if options != nil && options.Top != nil { + reqQP.Set("$top", strconv.FormatInt(int64(*options.Top), 10)) + } + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listHandleResponse handles the List response. +func (client *FederatedIdentityCredentialsClient) listHandleResponse(resp *http.Response) (FederatedIdentityCredentialsClientListResponse, error) { + result := FederatedIdentityCredentialsClientListResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FederatedIdentityCredentialsListResult); err != nil { + return FederatedIdentityCredentialsClientListResponse{}, err + } + return result, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go new file mode 100644 index 0000000000000..6bf4970953996 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import "time" + +// FederatedIdentityCredential - Describes a federated identity credential. +type FederatedIdentityCredential struct { + // The properties associated with the federated identity credential. + Properties *FederatedIdentityCredentialProperties + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// FederatedIdentityCredentialProperties - The properties associated with a federated identity credential. +type FederatedIdentityCredentialProperties struct { + // REQUIRED; The list of audiences that can appear in the issued token. + Audiences []*string + + // REQUIRED; The URL of the issuer to be trusted. + Issuer *string + + // REQUIRED; The identifier of the external identity. + Subject *string +} + +// FederatedIdentityCredentialsListResult - Values returned by the List operation for federated identity credentials. +type FederatedIdentityCredentialsListResult struct { + // The url to get the next page of results, if any. + NextLink *string + + // The collection of federated identity credentials returned by the listing operation. + Value []*FederatedIdentityCredential +} + +// Identity - Describes an identity resource. +type Identity struct { + // REQUIRED; The geo-location where the resource lives + Location *string + + // Resource tags. + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; The properties associated with the identity. + Properties *UserAssignedIdentityProperties + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// IdentityUpdate - Describes an identity resource. +type IdentityUpdate struct { + // The geo-location where the resource lives + Location *string + + // Resource tags + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; The properties associated with the identity. + Properties *UserAssignedIdentityProperties + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// Operation supported by the Microsoft.ManagedIdentity REST API. +type Operation struct { + // The object that describes the operation. + Display *OperationDisplay + + // The name of the REST Operation. This is of the format {provider}/{resource}/{operation}. + Name *string +} + +// OperationDisplay - The object that describes the operation. +type OperationDisplay struct { + // A description of the operation. + Description *string + + // The type of operation. For example: read, write, delete. + Operation *string + + // Friendly name of the resource provider. + Provider *string + + // The resource type on which the operation is performed. + Resource *string +} + +// OperationListResult - A list of operations supported by Microsoft.ManagedIdentity Resource Provider. +type OperationListResult struct { + // The url to get the next page of results, if any. + NextLink *string + + // A list of operations supported by Microsoft.ManagedIdentity Resource Provider. + Value []*Operation +} + +// SystemAssignedIdentity - Describes a system assigned identity resource. +type SystemAssignedIdentity struct { + // REQUIRED; The geo-location where the resource lives + Location *string + + // Resource tags + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; The properties associated with the identity. + Properties *SystemAssignedIdentityProperties + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// SystemAssignedIdentityProperties - The properties associated with the system assigned identity. +type SystemAssignedIdentityProperties struct { + // READ-ONLY; The id of the app associated with the identity. This is a random generated UUID by MSI. + ClientID *string + + // READ-ONLY; The ManagedServiceIdentity DataPlane URL that can be queried to obtain the identity credentials. + ClientSecretURL *string + + // READ-ONLY; The id of the service principal object associated with the created identity. + PrincipalID *string + + // READ-ONLY; The id of the tenant which the identity belongs to. + TenantID *string +} + +// SystemData - Metadata pertaining to creation and last modification of the resource. +type SystemData struct { + // The timestamp of resource creation (UTC). + CreatedAt *time.Time + + // The identity that created the resource. + CreatedBy *string + + // The type of identity that created the resource. + CreatedByType *CreatedByType + + // The timestamp of resource last modification (UTC) + LastModifiedAt *time.Time + + // The identity that last modified the resource. + LastModifiedBy *string + + // The type of identity that last modified the resource. + LastModifiedByType *CreatedByType +} + +// UserAssignedIdentitiesListResult - Values returned by the List operation. +type UserAssignedIdentitiesListResult struct { + // The url to get the next page of results, if any. + NextLink *string + + // The collection of userAssignedIdentities returned by the listing operation. + Value []*Identity +} + +// UserAssignedIdentityProperties - The properties associated with the user assigned identity. +type UserAssignedIdentityProperties struct { + // Enum to configure regional restrictions on identity assignment, as necessary. + IsolationScope *IsolationScope + + // READ-ONLY; The id of the app associated with the identity. This is a random generated UUID by MSI. + ClientID *string + + // READ-ONLY; The id of the service principal object associated with the created identity. + PrincipalID *string + + // READ-ONLY; The id of the tenant which the identity belongs to. + TenantID *string +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go new file mode 100644 index 0000000000000..323bfb1031d5e --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go @@ -0,0 +1,552 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" +) + +// MarshalJSON implements the json.Marshaller interface for type FederatedIdentityCredential. +func (f FederatedIdentityCredential) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", f.ID) + populate(objectMap, "name", f.Name) + populate(objectMap, "properties", f.Properties) + populate(objectMap, "systemData", f.SystemData) + populate(objectMap, "type", f.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FederatedIdentityCredential. +func (f *FederatedIdentityCredential) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &f.ID) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &f.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &f.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &f.SystemData) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &f.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type FederatedIdentityCredentialProperties. +func (f FederatedIdentityCredentialProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "audiences", f.Audiences) + populate(objectMap, "issuer", f.Issuer) + populate(objectMap, "subject", f.Subject) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FederatedIdentityCredentialProperties. +func (f *FederatedIdentityCredentialProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "audiences": + err = unpopulate(val, "Audiences", &f.Audiences) + delete(rawMsg, key) + case "issuer": + err = unpopulate(val, "Issuer", &f.Issuer) + delete(rawMsg, key) + case "subject": + err = unpopulate(val, "Subject", &f.Subject) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type FederatedIdentityCredentialsListResult. +func (f FederatedIdentityCredentialsListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", f.NextLink) + populate(objectMap, "value", f.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FederatedIdentityCredentialsListResult. +func (f *FederatedIdentityCredentialsListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &f.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &f.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type Identity. +func (i Identity) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", i.ID) + populate(objectMap, "location", i.Location) + populate(objectMap, "name", i.Name) + populate(objectMap, "properties", i.Properties) + populate(objectMap, "systemData", i.SystemData) + populate(objectMap, "tags", i.Tags) + populate(objectMap, "type", i.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Identity. +func (i *Identity) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &i.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &i.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &i.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &i.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &i.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &i.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &i.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type IdentityUpdate. +func (i IdentityUpdate) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", i.ID) + populate(objectMap, "location", i.Location) + populate(objectMap, "name", i.Name) + populate(objectMap, "properties", i.Properties) + populate(objectMap, "systemData", i.SystemData) + populate(objectMap, "tags", i.Tags) + populate(objectMap, "type", i.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type IdentityUpdate. +func (i *IdentityUpdate) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &i.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &i.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &i.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &i.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &i.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &i.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &i.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type Operation. +func (o Operation) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "display", o.Display) + populate(objectMap, "name", o.Name) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Operation. +func (o *Operation) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "display": + err = unpopulate(val, "Display", &o.Display) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &o.Name) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type OperationDisplay. +func (o OperationDisplay) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "description", o.Description) + populate(objectMap, "operation", o.Operation) + populate(objectMap, "provider", o.Provider) + populate(objectMap, "resource", o.Resource) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type OperationDisplay. +func (o *OperationDisplay) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "description": + err = unpopulate(val, "Description", &o.Description) + delete(rawMsg, key) + case "operation": + err = unpopulate(val, "Operation", &o.Operation) + delete(rawMsg, key) + case "provider": + err = unpopulate(val, "Provider", &o.Provider) + delete(rawMsg, key) + case "resource": + err = unpopulate(val, "Resource", &o.Resource) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type OperationListResult. +func (o OperationListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", o.NextLink) + populate(objectMap, "value", o.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type OperationListResult. +func (o *OperationListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &o.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &o.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type SystemAssignedIdentity. +func (s SystemAssignedIdentity) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", s.ID) + populate(objectMap, "location", s.Location) + populate(objectMap, "name", s.Name) + populate(objectMap, "properties", s.Properties) + populate(objectMap, "systemData", s.SystemData) + populate(objectMap, "tags", s.Tags) + populate(objectMap, "type", s.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SystemAssignedIdentity. +func (s *SystemAssignedIdentity) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &s.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &s.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &s.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &s.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &s.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &s.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &s.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type SystemAssignedIdentityProperties. +func (s SystemAssignedIdentityProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "clientId", s.ClientID) + populate(objectMap, "clientSecretUrl", s.ClientSecretURL) + populate(objectMap, "principalId", s.PrincipalID) + populate(objectMap, "tenantId", s.TenantID) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SystemAssignedIdentityProperties. +func (s *SystemAssignedIdentityProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "clientId": + err = unpopulate(val, "ClientID", &s.ClientID) + delete(rawMsg, key) + case "clientSecretUrl": + err = unpopulate(val, "ClientSecretURL", &s.ClientSecretURL) + delete(rawMsg, key) + case "principalId": + err = unpopulate(val, "PrincipalID", &s.PrincipalID) + delete(rawMsg, key) + case "tenantId": + err = unpopulate(val, "TenantID", &s.TenantID) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type SystemData. +func (s SystemData) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populateDateTimeRFC3339(objectMap, "createdAt", s.CreatedAt) + populate(objectMap, "createdBy", s.CreatedBy) + populate(objectMap, "createdByType", s.CreatedByType) + populateDateTimeRFC3339(objectMap, "lastModifiedAt", s.LastModifiedAt) + populate(objectMap, "lastModifiedBy", s.LastModifiedBy) + populate(objectMap, "lastModifiedByType", s.LastModifiedByType) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SystemData. +func (s *SystemData) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "createdAt": + err = unpopulateDateTimeRFC3339(val, "CreatedAt", &s.CreatedAt) + delete(rawMsg, key) + case "createdBy": + err = unpopulate(val, "CreatedBy", &s.CreatedBy) + delete(rawMsg, key) + case "createdByType": + err = unpopulate(val, "CreatedByType", &s.CreatedByType) + delete(rawMsg, key) + case "lastModifiedAt": + err = unpopulateDateTimeRFC3339(val, "LastModifiedAt", &s.LastModifiedAt) + delete(rawMsg, key) + case "lastModifiedBy": + err = unpopulate(val, "LastModifiedBy", &s.LastModifiedBy) + delete(rawMsg, key) + case "lastModifiedByType": + err = unpopulate(val, "LastModifiedByType", &s.LastModifiedByType) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type UserAssignedIdentitiesListResult. +func (u UserAssignedIdentitiesListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", u.NextLink) + populate(objectMap, "value", u.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type UserAssignedIdentitiesListResult. +func (u *UserAssignedIdentitiesListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &u.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &u.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type UserAssignedIdentityProperties. +func (u UserAssignedIdentityProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "clientId", u.ClientID) + populate(objectMap, "isolationScope", u.IsolationScope) + populate(objectMap, "principalId", u.PrincipalID) + populate(objectMap, "tenantId", u.TenantID) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type UserAssignedIdentityProperties. +func (u *UserAssignedIdentityProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "clientId": + err = unpopulate(val, "ClientID", &u.ClientID) + delete(rawMsg, key) + case "isolationScope": + err = unpopulate(val, "IsolationScope", &u.IsolationScope) + delete(rawMsg, key) + case "principalId": + err = unpopulate(val, "PrincipalID", &u.PrincipalID) + delete(rawMsg, key) + case "tenantId": + err = unpopulate(val, "TenantID", &u.TenantID) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + } + return nil +} + +func populate(m map[string]any, k string, v any) { + if v == nil { + return + } else if azcore.IsNullValue(v) { + m[k] = nil + } else if !reflect.ValueOf(v).IsNil() { + m[k] = v + } +} + +func unpopulate(data json.RawMessage, fn string, v any) error { + if data == nil || string(data) == "null" { + return nil + } + if err := json.Unmarshal(data, v); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + return nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go new file mode 100644 index 0000000000000..c16b57ca2fad8 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" +) + +// OperationsClient contains the methods for the Operations group. +// Don't use this type directly, use NewOperationsClient() instead. +type OperationsClient struct { + internal *arm.Client +} + +// NewOperationsClient creates a new instance of OperationsClient with the specified values. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewOperationsClient(credential azcore.TokenCredential, options *arm.ClientOptions) (*OperationsClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &OperationsClient{ + internal: cl, + } + return client, nil +} + +// NewListPager - Lists available operations for the Microsoft.ManagedIdentity provider +// +// Generated from API version 2024-11-30 +// - options - OperationsClientListOptions contains the optional parameters for the OperationsClient.NewListPager method. +func (client *OperationsClient) NewListPager(options *OperationsClientListOptions) *runtime.Pager[OperationsClientListResponse] { + return runtime.NewPager(runtime.PagingHandler[OperationsClientListResponse]{ + More: func(page OperationsClientListResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *OperationsClientListResponse) (OperationsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "OperationsClient.NewListPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listCreateRequest(ctx, options) + }, nil) + if err != nil { + return OperationsClientListResponse{}, err + } + return client.listHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listCreateRequest creates the List request. +func (client *OperationsClient) listCreateRequest(ctx context.Context, _ *OperationsClientListOptions) (*policy.Request, error) { + urlPath := "/providers/Microsoft.ManagedIdentity/operations" + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listHandleResponse handles the List response. +func (client *OperationsClient) listHandleResponse(resp *http.Response) (OperationsClientListResponse, error) { + result := OperationsClientListResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.OperationListResult); err != nil { + return OperationsClientListResponse{}, err + } + return result, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go new file mode 100644 index 0000000000000..6b67faa9a8e10 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +// FederatedIdentityCredentialsClientCreateOrUpdateOptions contains the optional parameters for the FederatedIdentityCredentialsClient.CreateOrUpdate +// method. +type FederatedIdentityCredentialsClientCreateOrUpdateOptions struct { + // placeholder for future optional parameters +} + +// FederatedIdentityCredentialsClientDeleteOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Delete +// method. +type FederatedIdentityCredentialsClientDeleteOptions struct { + // placeholder for future optional parameters +} + +// FederatedIdentityCredentialsClientGetOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Get +// method. +type FederatedIdentityCredentialsClientGetOptions struct { + // placeholder for future optional parameters +} + +// FederatedIdentityCredentialsClientListOptions contains the optional parameters for the FederatedIdentityCredentialsClient.NewListPager +// method. +type FederatedIdentityCredentialsClientListOptions struct { + // A skip token is used to continue retrieving items after an operation returns a partial result. If a previous response contains + // a nextLink element, the value of the nextLink element will include a + // skipToken parameter that specifies a starting point to use for subsequent calls. + Skiptoken *string + + // Number of records to return. + Top *int32 +} + +// OperationsClientListOptions contains the optional parameters for the OperationsClient.NewListPager method. +type OperationsClientListOptions struct { + // placeholder for future optional parameters +} + +// SystemAssignedIdentitiesClientGetByScopeOptions contains the optional parameters for the SystemAssignedIdentitiesClient.GetByScope +// method. +type SystemAssignedIdentitiesClientGetByScopeOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientCreateOrUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.CreateOrUpdate +// method. +type UserAssignedIdentitiesClientCreateOrUpdateOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientDeleteOptions contains the optional parameters for the UserAssignedIdentitiesClient.Delete +// method. +type UserAssignedIdentitiesClientDeleteOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientGetOptions contains the optional parameters for the UserAssignedIdentitiesClient.Get method. +type UserAssignedIdentitiesClientGetOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientListByResourceGroupOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListByResourceGroupPager +// method. +type UserAssignedIdentitiesClientListByResourceGroupOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientListBySubscriptionOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListBySubscriptionPager +// method. +type UserAssignedIdentitiesClientListBySubscriptionOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.Update +// method. +type UserAssignedIdentitiesClientUpdateOptions struct { + // placeholder for future optional parameters +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go new file mode 100644 index 0000000000000..420661fd25fec --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +// FederatedIdentityCredentialsClientCreateOrUpdateResponse contains the response from method FederatedIdentityCredentialsClient.CreateOrUpdate. +type FederatedIdentityCredentialsClientCreateOrUpdateResponse struct { + // Describes a federated identity credential. + FederatedIdentityCredential +} + +// FederatedIdentityCredentialsClientDeleteResponse contains the response from method FederatedIdentityCredentialsClient.Delete. +type FederatedIdentityCredentialsClientDeleteResponse struct { + // placeholder for future response values +} + +// FederatedIdentityCredentialsClientGetResponse contains the response from method FederatedIdentityCredentialsClient.Get. +type FederatedIdentityCredentialsClientGetResponse struct { + // Describes a federated identity credential. + FederatedIdentityCredential +} + +// FederatedIdentityCredentialsClientListResponse contains the response from method FederatedIdentityCredentialsClient.NewListPager. +type FederatedIdentityCredentialsClientListResponse struct { + // Values returned by the List operation for federated identity credentials. + FederatedIdentityCredentialsListResult +} + +// OperationsClientListResponse contains the response from method OperationsClient.NewListPager. +type OperationsClientListResponse struct { + // A list of operations supported by Microsoft.ManagedIdentity Resource Provider. + OperationListResult +} + +// SystemAssignedIdentitiesClientGetByScopeResponse contains the response from method SystemAssignedIdentitiesClient.GetByScope. +type SystemAssignedIdentitiesClientGetByScopeResponse struct { + // Describes a system assigned identity resource. + SystemAssignedIdentity +} + +// UserAssignedIdentitiesClientCreateOrUpdateResponse contains the response from method UserAssignedIdentitiesClient.CreateOrUpdate. +type UserAssignedIdentitiesClientCreateOrUpdateResponse struct { + // Describes an identity resource. + Identity +} + +// UserAssignedIdentitiesClientDeleteResponse contains the response from method UserAssignedIdentitiesClient.Delete. +type UserAssignedIdentitiesClientDeleteResponse struct { + // placeholder for future response values +} + +// UserAssignedIdentitiesClientGetResponse contains the response from method UserAssignedIdentitiesClient.Get. +type UserAssignedIdentitiesClientGetResponse struct { + // Describes an identity resource. + Identity +} + +// UserAssignedIdentitiesClientListByResourceGroupResponse contains the response from method UserAssignedIdentitiesClient.NewListByResourceGroupPager. +type UserAssignedIdentitiesClientListByResourceGroupResponse struct { + // Values returned by the List operation. + UserAssignedIdentitiesListResult +} + +// UserAssignedIdentitiesClientListBySubscriptionResponse contains the response from method UserAssignedIdentitiesClient.NewListBySubscriptionPager. +type UserAssignedIdentitiesClientListBySubscriptionResponse struct { + // Values returned by the List operation. + UserAssignedIdentitiesListResult +} + +// UserAssignedIdentitiesClientUpdateResponse contains the response from method UserAssignedIdentitiesClient.Update. +type UserAssignedIdentitiesClientUpdateResponse struct { + // Describes an identity resource. + Identity +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go new file mode 100644 index 0000000000000..f87357240f5f9 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "strings" +) + +// SystemAssignedIdentitiesClient contains the methods for the SystemAssignedIdentities group. +// Don't use this type directly, use NewSystemAssignedIdentitiesClient() instead. +type SystemAssignedIdentitiesClient struct { + internal *arm.Client +} + +// NewSystemAssignedIdentitiesClient creates a new instance of SystemAssignedIdentitiesClient with the specified values. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewSystemAssignedIdentitiesClient(credential azcore.TokenCredential, options *arm.ClientOptions) (*SystemAssignedIdentitiesClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &SystemAssignedIdentitiesClient{ + internal: cl, + } + return client, nil +} + +// GetByScope - Gets the systemAssignedIdentity available under the specified RP scope. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - scope - The resource provider scope of the resource. Parent resource being extended by Managed Identities. +// - options - SystemAssignedIdentitiesClientGetByScopeOptions contains the optional parameters for the SystemAssignedIdentitiesClient.GetByScope +// method. +func (client *SystemAssignedIdentitiesClient) GetByScope(ctx context.Context, scope string, options *SystemAssignedIdentitiesClientGetByScopeOptions) (SystemAssignedIdentitiesClientGetByScopeResponse, error) { + var err error + const operationName = "SystemAssignedIdentitiesClient.GetByScope" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.getByScopeCreateRequest(ctx, scope, options) + if err != nil { + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + resp, err := client.getByScopeHandleResponse(httpResp) + return resp, err +} + +// getByScopeCreateRequest creates the GetByScope request. +func (client *SystemAssignedIdentitiesClient) getByScopeCreateRequest(ctx context.Context, scope string, _ *SystemAssignedIdentitiesClientGetByScopeOptions) (*policy.Request, error) { + urlPath := "/{scope}/providers/Microsoft.ManagedIdentity/identities/default" + urlPath = strings.ReplaceAll(urlPath, "{scope}", scope) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getByScopeHandleResponse handles the GetByScope response. +func (client *SystemAssignedIdentitiesClient) getByScopeHandleResponse(resp *http.Response) (SystemAssignedIdentitiesClientGetByScopeResponse, error) { + result := SystemAssignedIdentitiesClientGetByScopeResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.SystemAssignedIdentity); err != nil { + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + return result, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go new file mode 100644 index 0000000000000..141269078a878 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" + "regexp" + "strings" + "time" +) + +// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. +var tzOffsetRegex = regexp.MustCompile(`(?:Z|z|\+|-)(?:\d+:\d+)*"*$`) + +const ( + utcDateTime = "2006-01-02T15:04:05.999999999" + utcDateTimeJSON = `"` + utcDateTime + `"` + utcDateTimeNoT = "2006-01-02 15:04:05.999999999" + utcDateTimeJSONNoT = `"` + utcDateTimeNoT + `"` + dateTimeNoT = `2006-01-02 15:04:05.999999999Z07:00` + dateTimeJSON = `"` + time.RFC3339Nano + `"` + dateTimeJSONNoT = `"` + dateTimeNoT + `"` +) + +type dateTimeRFC3339 time.Time + +func (t dateTimeRFC3339) MarshalJSON() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalJSON() +} + +func (t dateTimeRFC3339) MarshalText() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalText() +} + +func (t *dateTimeRFC3339) UnmarshalJSON(data []byte) error { + tzOffset := tzOffsetRegex.Match(data) + hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t") + var layout string + if tzOffset && hasT { + layout = dateTimeJSON + } else if tzOffset { + layout = dateTimeJSONNoT + } else if hasT { + layout = utcDateTimeJSON + } else { + layout = utcDateTimeJSONNoT + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) UnmarshalText(data []byte) error { + if len(data) == 0 { + return nil + } + tzOffset := tzOffsetRegex.Match(data) + hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t") + var layout string + if tzOffset && hasT { + layout = time.RFC3339Nano + } else if tzOffset { + layout = dateTimeNoT + } else if hasT { + layout = utcDateTime + } else { + layout = utcDateTimeNoT + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) Parse(layout, value string) error { + p, err := time.Parse(layout, strings.ToUpper(value)) + *t = dateTimeRFC3339(p) + return err +} + +func (t dateTimeRFC3339) String() string { + return time.Time(t).Format(time.RFC3339Nano) +} + +func populateDateTimeRFC3339(m map[string]any, k string, t *time.Time) { + if t == nil { + return + } else if azcore.IsNullValue(t) { + m[k] = nil + return + } else if reflect.ValueOf(t).IsNil() { + return + } + m[k] = (*dateTimeRFC3339)(t) +} + +func unpopulateDateTimeRFC3339(data json.RawMessage, fn string, t **time.Time) error { + if data == nil || string(data) == "null" { + return nil + } + var aux dateTimeRFC3339 + if err := json.Unmarshal(data, &aux); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + *t = (*time.Time)(&aux) + return nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go new file mode 100644 index 0000000000000..454e4c0c35f22 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go @@ -0,0 +1,414 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "net/url" + "strings" +) + +// UserAssignedIdentitiesClient contains the methods for the UserAssignedIdentities group. +// Don't use this type directly, use NewUserAssignedIdentitiesClient() instead. +type UserAssignedIdentitiesClient struct { + internal *arm.Client + subscriptionID string +} + +// NewUserAssignedIdentitiesClient creates a new instance of UserAssignedIdentitiesClient with the specified values. +// - subscriptionID - The Id of the Subscription to which the identity belongs. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewUserAssignedIdentitiesClient(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (*UserAssignedIdentitiesClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &UserAssignedIdentitiesClient{ + subscriptionID: subscriptionID, + internal: cl, + } + return client, nil +} + +// CreateOrUpdate - Create or update an identity in the specified subscription and resource group. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - parameters - Parameters to create or update the identity +// - options - UserAssignedIdentitiesClientCreateOrUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.CreateOrUpdate +// method. +func (client *UserAssignedIdentitiesClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity, options *UserAssignedIdentitiesClientCreateOrUpdateOptions) (UserAssignedIdentitiesClientCreateOrUpdateResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.createOrUpdateCreateRequest(ctx, resourceGroupName, resourceName, parameters, options) + if err != nil { + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusCreated) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + resp, err := client.createOrUpdateHandleResponse(httpResp) + return resp, err +} + +// createOrUpdateCreateRequest creates the CreateOrUpdate request. +func (client *UserAssignedIdentitiesClient) createOrUpdateCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity, _ *UserAssignedIdentitiesClientCreateOrUpdateOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodPut, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, parameters); err != nil { + return nil, err + } + return req, nil +} + +// createOrUpdateHandleResponse handles the CreateOrUpdate response. +func (client *UserAssignedIdentitiesClient) createOrUpdateHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientCreateOrUpdateResponse, error) { + result := UserAssignedIdentitiesClientCreateOrUpdateResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Identity); err != nil { + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + return result, nil +} + +// Delete - Deletes the identity. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - options - UserAssignedIdentitiesClientDeleteOptions contains the optional parameters for the UserAssignedIdentitiesClient.Delete +// method. +func (client *UserAssignedIdentitiesClient) Delete(ctx context.Context, resourceGroupName string, resourceName string, options *UserAssignedIdentitiesClientDeleteOptions) (UserAssignedIdentitiesClientDeleteResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.deleteCreateRequest(ctx, resourceGroupName, resourceName, options) + if err != nil { + return UserAssignedIdentitiesClientDeleteResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientDeleteResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusNoContent) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientDeleteResponse{}, err + } + return UserAssignedIdentitiesClientDeleteResponse{}, nil +} + +// deleteCreateRequest creates the Delete request. +func (client *UserAssignedIdentitiesClient) deleteCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, _ *UserAssignedIdentitiesClientDeleteOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodDelete, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// Get - Gets the identity. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - options - UserAssignedIdentitiesClientGetOptions contains the optional parameters for the UserAssignedIdentitiesClient.Get +// method. +func (client *UserAssignedIdentitiesClient) Get(ctx context.Context, resourceGroupName string, resourceName string, options *UserAssignedIdentitiesClientGetOptions) (UserAssignedIdentitiesClientGetResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.getCreateRequest(ctx, resourceGroupName, resourceName, options) + if err != nil { + return UserAssignedIdentitiesClientGetResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientGetResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientGetResponse{}, err + } + resp, err := client.getHandleResponse(httpResp) + return resp, err +} + +// getCreateRequest creates the Get request. +func (client *UserAssignedIdentitiesClient) getCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, _ *UserAssignedIdentitiesClientGetOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getHandleResponse handles the Get response. +func (client *UserAssignedIdentitiesClient) getHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientGetResponse, error) { + result := UserAssignedIdentitiesClientGetResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Identity); err != nil { + return UserAssignedIdentitiesClientGetResponse{}, err + } + return result, nil +} + +// NewListByResourceGroupPager - Lists all the userAssignedIdentities available under the specified ResourceGroup. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - options - UserAssignedIdentitiesClientListByResourceGroupOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListByResourceGroupPager +// method. +func (client *UserAssignedIdentitiesClient) NewListByResourceGroupPager(resourceGroupName string, options *UserAssignedIdentitiesClientListByResourceGroupOptions) *runtime.Pager[UserAssignedIdentitiesClientListByResourceGroupResponse] { + return runtime.NewPager(runtime.PagingHandler[UserAssignedIdentitiesClientListByResourceGroupResponse]{ + More: func(page UserAssignedIdentitiesClientListByResourceGroupResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *UserAssignedIdentitiesClientListByResourceGroupResponse) (UserAssignedIdentitiesClientListByResourceGroupResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "UserAssignedIdentitiesClient.NewListByResourceGroupPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listByResourceGroupCreateRequest(ctx, resourceGroupName, options) + }, nil) + if err != nil { + return UserAssignedIdentitiesClientListByResourceGroupResponse{}, err + } + return client.listByResourceGroupHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listByResourceGroupCreateRequest creates the ListByResourceGroup request. +func (client *UserAssignedIdentitiesClient) listByResourceGroupCreateRequest(ctx context.Context, resourceGroupName string, _ *UserAssignedIdentitiesClientListByResourceGroupOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listByResourceGroupHandleResponse handles the ListByResourceGroup response. +func (client *UserAssignedIdentitiesClient) listByResourceGroupHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientListByResourceGroupResponse, error) { + result := UserAssignedIdentitiesClientListByResourceGroupResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.UserAssignedIdentitiesListResult); err != nil { + return UserAssignedIdentitiesClientListByResourceGroupResponse{}, err + } + return result, nil +} + +// NewListBySubscriptionPager - Lists all the userAssignedIdentities available under the specified subscription. +// +// Generated from API version 2024-11-30 +// - options - UserAssignedIdentitiesClientListBySubscriptionOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListBySubscriptionPager +// method. +func (client *UserAssignedIdentitiesClient) NewListBySubscriptionPager(options *UserAssignedIdentitiesClientListBySubscriptionOptions) *runtime.Pager[UserAssignedIdentitiesClientListBySubscriptionResponse] { + return runtime.NewPager(runtime.PagingHandler[UserAssignedIdentitiesClientListBySubscriptionResponse]{ + More: func(page UserAssignedIdentitiesClientListBySubscriptionResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *UserAssignedIdentitiesClientListBySubscriptionResponse) (UserAssignedIdentitiesClientListBySubscriptionResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "UserAssignedIdentitiesClient.NewListBySubscriptionPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listBySubscriptionCreateRequest(ctx, options) + }, nil) + if err != nil { + return UserAssignedIdentitiesClientListBySubscriptionResponse{}, err + } + return client.listBySubscriptionHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listBySubscriptionCreateRequest creates the ListBySubscription request. +func (client *UserAssignedIdentitiesClient) listBySubscriptionCreateRequest(ctx context.Context, _ *UserAssignedIdentitiesClientListBySubscriptionOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/providers/Microsoft.ManagedIdentity/userAssignedIdentities" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listBySubscriptionHandleResponse handles the ListBySubscription response. +func (client *UserAssignedIdentitiesClient) listBySubscriptionHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientListBySubscriptionResponse, error) { + result := UserAssignedIdentitiesClientListBySubscriptionResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.UserAssignedIdentitiesListResult); err != nil { + return UserAssignedIdentitiesClientListBySubscriptionResponse{}, err + } + return result, nil +} + +// Update - Update an identity in the specified subscription and resource group. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - parameters - Parameters to update the identity +// - options - UserAssignedIdentitiesClientUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.Update +// method. +func (client *UserAssignedIdentitiesClient) Update(ctx context.Context, resourceGroupName string, resourceName string, parameters IdentityUpdate, options *UserAssignedIdentitiesClientUpdateOptions) (UserAssignedIdentitiesClientUpdateResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.Update" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.updateCreateRequest(ctx, resourceGroupName, resourceName, parameters, options) + if err != nil { + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + resp, err := client.updateHandleResponse(httpResp) + return resp, err +} + +// updateCreateRequest creates the Update request. +func (client *UserAssignedIdentitiesClient) updateCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, parameters IdentityUpdate, _ *UserAssignedIdentitiesClientUpdateOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodPatch, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, parameters); err != nil { + return nil, err + } + return req, nil +} + +// updateHandleResponse handles the Update response. +func (client *UserAssignedIdentitiesClient) updateHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientUpdateResponse, error) { + result := UserAssignedIdentitiesClientUpdateResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Identity); err != nil { + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + return result, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0210cda41a224..85799da3d8981 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -67,6 +67,9 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthoriza # github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 ## explicit; go 1.18 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute +# github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 +## explicit; go 1.23.0 +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi # github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 ## explicit; go 1.18 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork