diff --git a/Gopkg.lock b/Gopkg.lock index f9c7004717..8485cad49e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -9,6 +9,7 @@ "services/compute/mgmt/2018-04-01/compute", "services/graphrbac/1.6/graphrbac", "services/network/mgmt/2018-05-01/network", + "services/preview/msi/mgmt/2015-08-31-preview/msi", "services/resources/mgmt/2016-06-01/subscriptions", "services/resources/mgmt/2018-05-01/resources", "services/storage/mgmt/2018-02-01/storage", @@ -541,6 +542,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "09d18a302df6f52ed3650b78ba636e968b3a0565df8afdf8d2de9399aa8196fc" + inputs-digest = "0a1c6d0a2c2a76181459f185f8123bf8dec75f28f69f1da21c147a688c06df70" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/deploy.go b/cmd/deploy.go index aa311d477a..3a3f5c8f3c 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -308,8 +308,32 @@ func autofillApimodel(dc *deployCmd) error { return err } - useManagedIdentity := dc.containerService.Properties.OrchestratorProfile.KubernetesConfig != nil && - dc.containerService.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity + k8sConfig := dc.containerService.Properties.OrchestratorProfile.KubernetesConfig + + useManagedIdentity := k8sConfig != nil && + k8sConfig.UseManagedIdentity + + userAssignedID := k8sConfig != nil && + k8sConfig.UseManagedIdentity && + k8sConfig.UserAssignedID != "" + + // Note: User assigned identity can be assigned from the ARM template, but the role assigment following that will + // fail due to a bug with the service. This code is added to wait till the newly created AAD identity is properly + // propogated. + if userAssignedID { + userID, err := dc.client.CreateUserAssignedID(dc.location, dc.resourceGroup, k8sConfig.UserAssignedID) + if err != nil { + return nil + } + // Fill up the client id for creating azure.json + k8sConfig.UserAssignedClientID = (*userID.ClientID).String() + err = dc.client.CreateRoleAssignmentSimple(ctx, dc.resourceGroup, (*userID.PrincipalID).String()) + if err != nil { + return errors.Wrap(err, "apimodel: could not create role assignment for user assigned id ") + } + //TODO: Support e2e return fake user id. + return nil + } if !useManagedIdentity { spp := dc.containerService.Properties.ServicePrincipalProfile diff --git a/examples/kubernetes-msi-userassigned/kube-vma.json b/examples/kubernetes-msi-userassigned/kube-vma.json new file mode 100644 index 0000000000..70bd947e34 --- /dev/null +++ b/examples/kubernetes-msi-userassigned/kube-vma.json @@ -0,0 +1,36 @@ +{ + "apiVersion": "vlabs", + "properties": { + "orchestratorProfile": { + "orchestratorType": "Kubernetes", + "kubernetesConfig": { + "useManagedIdentity": true, + "userAssignedID": "acsenginetestid" + } + }, + "masterProfile": { + "count": 1, + "dnsPrefix": "", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agentpool1", + "count": 3, + "vmSize": "Standard_D2_v2", + "availabilityProfile": "AvailabilitySet", + "storageProfile": "ManagedDisks" + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "" + } + ] + } + } + } +} diff --git a/examples/kubernetes-msi-userassigned/kube-vmss.json b/examples/kubernetes-msi-userassigned/kube-vmss.json new file mode 100644 index 0000000000..081b5d0652 --- /dev/null +++ b/examples/kubernetes-msi-userassigned/kube-vmss.json @@ -0,0 +1,36 @@ +{ + "apiVersion": "vlabs", + "properties": { + "orchestratorProfile": { + "orchestratorType": "Kubernetes", + "kubernetesConfig": { + "useManagedIdentity": true, + "userAssignedID": "acsenginetestid" + } + }, + "masterProfile": { + "count": 1, + "dnsPrefix": "", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agentpool1", + "count": 3, + "vmSize": "Standard_D2_v2", + "availabilityProfile": "VirtualMachineScaleSets", + "storageProfile": "ManagedDisks" + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "" + } + ] + } + } + } +} diff --git a/parts/k8s/kubernetesagentresourcesvmas.t b/parts/k8s/kubernetesagentresourcesvmas.t index 2b7d938313..0993cda1b3 100644 --- a/parts/k8s/kubernetesagentresourcesvmas.t +++ b/parts/k8s/kubernetesagentresourcesvmas.t @@ -146,10 +146,14 @@ }, {{end}} { + {{if UserAssignedIDEnabled}} + "apiVersion": "[variables('apiVersionUserMSI')]", + {{else}} {{if .IsManagedDisks}} "apiVersion": "[variables('apiVersionStorageManagedDisks')]", {{else}} "apiVersion": "[variables('apiVersionDefault')]", + {{end}} {{end}} "copy": { "count": "[sub(variables('{{.Name}}Count'), variables('{{.Name}}Offset'))]", @@ -177,10 +181,19 @@ "location": "[variables('location')]", "name": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex(variables('{{.Name}}Offset')))]", {{if UseManagedIdentity}} + {{if UserAssignedIDEnabled}} + "identity": { + "type": "userAssigned", + "userAssignedIdentities": { + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/', variables('userAssignedID'))]":{} + } + }, + {{else}} "identity": { "type": "systemAssigned" }, {{end}} + {{end}} {{if and IsOpenShift (not (UseAgentCustomImage .))}} "plan": { "name": "[variables('{{.Name}}osImageSKU')]", @@ -256,6 +269,7 @@ "type": "Microsoft.Compute/virtualMachines" }, {{if UseManagedIdentity}} + {{if (not UserAssignedIDEnabled)}} { "apiVersion": "2014-10-01-preview", "copy": { @@ -269,6 +283,7 @@ "principalId": "[reference(concat('Microsoft.Compute/virtualMachines/', variables('{{.Name}}VMNamePrefix'), copyIndex()), '2017-03-30', 'Full').identity.principalId]" } }, + {{end}} { "type": "Microsoft.Compute/virtualMachines/extensions", "name": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex(), '/ManagedIdentityExtension')]", @@ -278,10 +293,16 @@ }, "apiVersion": "2015-05-01-preview", "location": "[resourceGroup().location]", + {{if UserAssignedIDEnabled}} + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', variables('{{.Name}}VMNamePrefix'), copyIndex())]" + ], + {{else}} "dependsOn": [ "[concat('Microsoft.Compute/virtualMachines/', variables('{{.Name}}VMNamePrefix'), copyIndex())]", "[concat('Microsoft.Authorization/roleAssignments/', guid(concat('Microsoft.Compute/virtualMachines/', variables('{{.Name}}VMNamePrefix'), copyIndex(), 'vmidentity')))]" ], + {{end}} "properties": { "publisher": "Microsoft.ManagedIdentity", "type": "ManagedIdentityExtensionForLinux", diff --git a/parts/k8s/kubernetesagentresourcesvmss.t b/parts/k8s/kubernetesagentresourcesvmss.t index 2f5d06b3aa..8df48b8cf6 100644 --- a/parts/k8s/kubernetesagentresourcesvmss.t +++ b/parts/k8s/kubernetesagentresourcesvmss.t @@ -1,4 +1,4 @@ -{{if UseManagedIdentity}} +{{if and UseManagedIdentity (not UserAssignedIDEnabled)}} { "apiVersion": "2014-10-01-preview", "name": "[guid(concat('Microsoft.Compute/virtualMachineScaleSets/', variables('{{.Name}}VMNamePrefix'), 'vmidentity'))]", @@ -28,10 +28,19 @@ "location": "[variables('location')]", "name": "[variables('{{.Name}}VMNamePrefix')]", {{if UseManagedIdentity}} + {{if UserAssignedIDEnabled}} + "identity": { + "type": "userAssigned", + "identityIds": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/', variables('userAssignedID'))]" + ] + }, + {{else}} "identity": { "type": "systemAssigned" }, {{end}} + {{end}} "sku": { "tier": "Standard", "capacity": "[variables('{{.Name}}Count')]", diff --git a/parts/k8s/kubernetesbase.t b/parts/k8s/kubernetesbase.t index 40bf3aebd1..d4c4aebc6a 100644 --- a/parts/k8s/kubernetesbase.t +++ b/parts/k8s/kubernetesbase.t @@ -52,6 +52,14 @@ {{template "k8s/kubernetesmastervars.t" .}} }, "resources": [ + {{if UserAssignedIDEnabled}} + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "name": "[variables('userAssignedID')]", + "apiVersion": "2015-08-31-PREVIEW", + "location": "[variables('location')]" + }, + {{end}} {{if IsOpenShift}} {{template "openshift/infraresources.t" .}} {{end}} diff --git a/parts/k8s/kubernetesconfigs.sh b/parts/k8s/kubernetesconfigs.sh index e1b70e0d78..c9c2d05a1a 100644 --- a/parts/k8s/kubernetesconfigs.sh +++ b/parts/k8s/kubernetesconfigs.sh @@ -155,6 +155,7 @@ function configureK8s() { "cloudProviderRateLimitQPS": ${CLOUDPROVIDER_RATELIMIT_QPS}, "cloudProviderRateLimitBucket": ${CLOUDPROVIDER_RATELIMIT_BUCKET}, "useManagedIdentityExtension": ${USE_MANAGED_IDENTITY_EXTENSION}, + "userAssignedIdentityID": "${USER_ASSIGNED_IDENTITY_ID}", "useInstanceMetadata": ${USE_INSTANCE_METADATA}, "loadBalancerSku": "${LOAD_BALANCER_SKU}", "excludeMasterFromStandardLB": ${EXCLUDE_MASTER_FROM_STANDARD_LB}, diff --git a/parts/k8s/kubernetesmasterresources.t b/parts/k8s/kubernetesmasterresources.t index 499feafe53..bbf93c5b56 100644 --- a/parts/k8s/kubernetesmasterresources.t +++ b/parts/k8s/kubernetesmasterresources.t @@ -720,10 +720,14 @@ }, {{end}} { + {{if UserAssignedIDEnabled}} + "apiVersion": "[variables('apiVersionUserMSI')]", + {{else}} {{if .MasterProfile.IsManagedDisks}} "apiVersion": "[variables('apiVersionStorageManagedDisks')]", {{else}} "apiVersion": "[variables('apiVersionDefault')]", + {{end}} {{end}} "copy": { "count": "[sub(variables('masterCount'), variables('masterOffset'))]", @@ -747,10 +751,19 @@ "location": "[variables('location')]", "name": "[concat(variables('masterVMNamePrefix'), copyIndex(variables('masterOffset')))]", {{if UseManagedIdentity}} + {{if UserAssignedIDEnabled}} + "identity": { + "type": "userAssigned", + "userAssignedIdentities": { + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/', variables('userAssignedID'))]":{} + } + }, + {{else}} "identity": { "type": "systemAssigned" }, {{end}} + {{end}} {{if and IsOpenShift (not UseMasterCustomImage)}} "plan": { "name": "[parameters('osImageSku')]", @@ -839,6 +852,7 @@ "type": "Microsoft.Compute/virtualMachines" }, {{if UseManagedIdentity}} + {{if (not UserAssignedIDEnabled)}} { "apiVersion": "2014-10-01-preview", "copy": { @@ -852,6 +866,7 @@ "principalId": "[reference(concat('Microsoft.Compute/virtualMachines/', variables('masterVMNamePrefix'), copyIndex()), '2017-03-30', 'Full').identity.principalId]" } }, + {{end}} { "type": "Microsoft.Compute/virtualMachines/extensions", "name": "[concat(variables('masterVMNamePrefix'), copyIndex(), '/ManagedIdentityExtension')]", @@ -861,10 +876,16 @@ }, "apiVersion": "2015-05-01-preview", "location": "[resourceGroup().location]", + {{if (not UserAssignedIDEnabled)}} "dependsOn": [ "[concat('Microsoft.Compute/virtualMachines/', variables('masterVMNamePrefix'), copyIndex())]", "[concat('Microsoft.Authorization/roleAssignments/', guid(concat('Microsoft.Compute/virtualMachines/', variables('masterVMNamePrefix'), copyIndex(), 'vmidentity')))]" ], + {{else}} + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', variables('masterVMNamePrefix'), copyIndex())]" + ], + {{end}} "properties": { "publisher": "Microsoft.ManagedIdentity", "type": "ManagedIdentityExtensionForLinux", diff --git a/parts/k8s/kubernetesmastervars.t b/parts/k8s/kubernetesmastervars.t index 65ad4b629f..780ce3d116 100644 --- a/parts/k8s/kubernetesmastervars.t +++ b/parts/k8s/kubernetesmastervars.t @@ -66,6 +66,8 @@ {{end}} {{end}} "useManagedIdentityExtension": "{{ UseManagedIdentity }}", + "userAssignedID": "{{UserAssignedID}}", + "userAssignedClientID": "{{UserAssignedClientID}}", "useInstanceMetadata": "{{ UseInstanceMetadata }}", "loadBalancerSku": "{{ LoadBalancerSku }}", "excludeMasterFromStandardLB": "{{ ExcludeMasterFromStandardLB }}", @@ -115,6 +117,9 @@ "storageAccountPrefixes": [], "storageAccountBaseName": "", {{end}} +{{if UserAssignedIDEnabled}} + "apiVersionUserMSI": "2018-06-01", +{{end}} {{if .HasManagedDisks}} "apiVersionStorageManagedDisks": "2016-04-30-preview", {{end}} @@ -134,7 +139,7 @@ "customSearchDomainsScript": "{{GetKubernetesB64CustomSearchDomainsScript}}", "sshdConfig": "{{GetB64sshdConfig}}", {{if not IsOpenShift}} - "provisionScriptParametersCommon": "[concat('ADMINUSER=',parameters('linuxAdminUsername'),' ETCD_DOWNLOAD_URL=',parameters('etcdDownloadURLBase'),' ETCD_VERSION=',parameters('etcdVersion'),' DOCKER_ENGINE_VERSION=',parameters('dockerEngineVersion'),' DOCKER_REPO=',parameters('dockerEngineDownloadRepo'),' TENANT_ID=',variables('tenantID'),' KUBERNETES_VERSION={{.OrchestratorProfile.OrchestratorVersion}} HYPERKUBE_URL=',parameters('kubernetesHyperkubeSpec'),' APISERVER_PUBLIC_KEY=',parameters('apiserverCertificate'),' SUBSCRIPTION_ID=',variables('subscriptionId'),' RESOURCE_GROUP=',variables('resourceGroup'),' LOCATION=',variables('location'),' VM_TYPE=',variables('vmType'),' SUBNET=',variables('subnetName'),' NETWORK_SECURITY_GROUP=',variables('nsgName'),' VIRTUAL_NETWORK=',variables('virtualNetworkName'),' VIRTUAL_NETWORK_RESOURCE_GROUP=',variables('virtualNetworkResourceGroupName'),' ROUTE_TABLE=',variables('routeTableName'),' PRIMARY_AVAILABILITY_SET=',variables('primaryAvailabilitySetName'),' PRIMARY_SCALE_SET=',variables('primaryScaleSetName'),' SERVICE_PRINCIPAL_CLIENT_ID=',variables('servicePrincipalClientId'),' SERVICE_PRINCIPAL_CLIENT_SECRET=',variables('singleQuote'),variables('servicePrincipalClientSecret'),variables('singleQuote'),' KUBELET_PRIVATE_KEY=',parameters('clientPrivateKey'),' TARGET_ENVIRONMENT=',parameters('targetEnvironment'),' NETWORK_PLUGIN=',parameters('networkPlugin'),' VNET_CNI_PLUGINS_URL=',parameters('vnetCniLinuxPluginsURL'),' CNI_PLUGINS_URL=',parameters('cniPluginsURL'),' CLOUDPROVIDER_BACKOFF=',parameters('cloudproviderConfig').cloudProviderBackoff,' CLOUDPROVIDER_BACKOFF_RETRIES=',parameters('cloudproviderConfig').cloudProviderBackoffRetries,' CLOUDPROVIDER_BACKOFF_EXPONENT=',parameters('cloudproviderConfig').cloudProviderBackoffExponent,' CLOUDPROVIDER_BACKOFF_DURATION=',parameters('cloudproviderConfig').cloudProviderBackoffDuration,' CLOUDPROVIDER_BACKOFF_JITTER=',parameters('cloudproviderConfig').cloudProviderBackoffJitter,' CLOUDPROVIDER_RATELIMIT=',parameters('cloudproviderConfig').cloudProviderRatelimit,' CLOUDPROVIDER_RATELIMIT_QPS=',parameters('cloudproviderConfig').cloudProviderRatelimitQPS,' CLOUDPROVIDER_RATELIMIT_BUCKET=',parameters('cloudproviderConfig').cloudProviderRatelimitBucket,' USE_MANAGED_IDENTITY_EXTENSION=',variables('useManagedIdentityExtension'),' USE_INSTANCE_METADATA=',variables('useInstanceMetadata'),' LOAD_BALANCER_SKU=',variables('loadBalancerSku'),' EXCLUDE_MASTER_FROM_STANDARD_LB=',variables('excludeMasterFromStandardLB'),' CONTAINER_RUNTIME=',parameters('containerRuntime'),' CONTAINERD_DOWNLOAD_URL_BASE=',parameters('containerdDownloadURLBase'),' POD_INFRA_CONTAINER_SPEC=',parameters('kubernetesPodInfraContainerSpec'),' KMS_PROVIDER_VAULT_NAME=',variables('clusterKeyVaultName'))]", + "provisionScriptParametersCommon": "[concat('ADMINUSER=',parameters('linuxAdminUsername'),' ETCD_DOWNLOAD_URL=',parameters('etcdDownloadURLBase'),' ETCD_VERSION=',parameters('etcdVersion'),' DOCKER_ENGINE_VERSION=',parameters('dockerEngineVersion'),' DOCKER_REPO=',parameters('dockerEngineDownloadRepo'),' TENANT_ID=',variables('tenantID'),' KUBERNETES_VERSION={{.OrchestratorProfile.OrchestratorVersion}} HYPERKUBE_URL=',parameters('kubernetesHyperkubeSpec'),' APISERVER_PUBLIC_KEY=',parameters('apiserverCertificate'),' SUBSCRIPTION_ID=',variables('subscriptionId'),' RESOURCE_GROUP=',variables('resourceGroup'),' LOCATION=',variables('location'),' VM_TYPE=',variables('vmType'),' SUBNET=',variables('subnetName'),' NETWORK_SECURITY_GROUP=',variables('nsgName'),' VIRTUAL_NETWORK=',variables('virtualNetworkName'),' VIRTUAL_NETWORK_RESOURCE_GROUP=',variables('virtualNetworkResourceGroupName'),' ROUTE_TABLE=',variables('routeTableName'),' PRIMARY_AVAILABILITY_SET=',variables('primaryAvailabilitySetName'),' PRIMARY_SCALE_SET=',variables('primaryScaleSetName'),' SERVICE_PRINCIPAL_CLIENT_ID=',variables('servicePrincipalClientId'),' SERVICE_PRINCIPAL_CLIENT_SECRET=',variables('singleQuote'),variables('servicePrincipalClientSecret'),variables('singleQuote'),' KUBELET_PRIVATE_KEY=',parameters('clientPrivateKey'),' TARGET_ENVIRONMENT=',parameters('targetEnvironment'),' NETWORK_PLUGIN=',parameters('networkPlugin'),' VNET_CNI_PLUGINS_URL=',parameters('vnetCniLinuxPluginsURL'),' CNI_PLUGINS_URL=',parameters('cniPluginsURL'),' CLOUDPROVIDER_BACKOFF=',parameters('cloudproviderConfig').cloudProviderBackoff,' CLOUDPROVIDER_BACKOFF_RETRIES=',parameters('cloudproviderConfig').cloudProviderBackoffRetries,' CLOUDPROVIDER_BACKOFF_EXPONENT=',parameters('cloudproviderConfig').cloudProviderBackoffExponent,' CLOUDPROVIDER_BACKOFF_DURATION=',parameters('cloudproviderConfig').cloudProviderBackoffDuration,' CLOUDPROVIDER_BACKOFF_JITTER=',parameters('cloudproviderConfig').cloudProviderBackoffJitter,' CLOUDPROVIDER_RATELIMIT=',parameters('cloudproviderConfig').cloudProviderRatelimit,' CLOUDPROVIDER_RATELIMIT_QPS=',parameters('cloudproviderConfig').cloudProviderRatelimitQPS,' CLOUDPROVIDER_RATELIMIT_BUCKET=',parameters('cloudproviderConfig').cloudProviderRatelimitBucket,' USE_MANAGED_IDENTITY_EXTENSION=',variables('useManagedIdentityExtension'),' USER_ASSIGNED_IDENTITY_ID=',variables('userAssignedClientID'),' USE_INSTANCE_METADATA=',variables('useInstanceMetadata'),' LOAD_BALANCER_SKU=',variables('loadBalancerSku'),' EXCLUDE_MASTER_FROM_STANDARD_LB=',variables('excludeMasterFromStandardLB'),' CONTAINER_RUNTIME=',parameters('containerRuntime'),' CONTAINERD_DOWNLOAD_URL_BASE=',parameters('containerdDownloadURLBase'),' POD_INFRA_CONTAINER_SPEC=',parameters('kubernetesPodInfraContainerSpec'),' KMS_PROVIDER_VAULT_NAME=',variables('clusterKeyVaultName'))]", {{if not IsHostedMaster}} "provisionScriptParametersMaster": "[concat('MASTER_VM_NAME=',variables('masterVMNames')[variables('masterOffset')],' ETCD_PEER_URL=',variables('masterEtcdPeerURLs')[variables('masterOffset')],' ETCD_CLIENT_URL=',variables('masterEtcdClientURLs')[variables('masterOffset')],' MASTER_NODE=true CLUSTER_AUTOSCALER_ADDON=',parameters('kubernetesClusterAutoscalerEnabled'),' ACI_CONNECTOR_ADDON=',parameters('kubernetesACIConnectorEnabled'),' APISERVER_PRIVATE_KEY=',parameters('apiServerPrivateKey'),' CA_CERTIFICATE=',parameters('caCertificate'),' CA_PRIVATE_KEY=',parameters('caPrivateKey'),' MASTER_FQDN=',variables('masterFqdnPrefix'),' KUBECONFIG_CERTIFICATE=',parameters('kubeConfigCertificate'),' KUBECONFIG_KEY=',parameters('kubeConfigPrivateKey'),' ETCD_SERVER_CERTIFICATE=',parameters('etcdServerCertificate'),' ETCD_CLIENT_CERTIFICATE=',parameters('etcdClientCertificate'),' ETCD_SERVER_PRIVATE_KEY=',parameters('etcdServerPrivateKey'),' ETCD_CLIENT_PRIVATE_KEY=',parameters('etcdClientPrivateKey'),' ETCD_PEER_CERTIFICATES=',string(variables('etcdPeerCertificates')),' ETCD_PEER_PRIVATE_KEYS=',string(variables('etcdPeerPrivateKeys')),' ENABLE_AGGREGATED_APIS=',string(parameters('enableAggregatedAPIs')),' KUBECONFIG_SERVER=',variables('kubeconfigServer'))]", {{end}} diff --git a/parts/k8s/kuberneteswindowssetup.ps1 b/parts/k8s/kuberneteswindowssetup.ps1 index 11479adfe8..a48c1bb09c 100644 --- a/parts/k8s/kuberneteswindowssetup.ps1 +++ b/parts/k8s/kuberneteswindowssetup.ps1 @@ -64,6 +64,7 @@ $global:KubeNetwork = "l2bridge" $global:KubeDnsSearchPath = "svc.cluster.local" $global:UseManagedIdentityExtension = "{{WrapAsVariable "useManagedIdentityExtension"}}" +$global:UserAssignedClientID = "{{WrapAsVariable "userAssignedClientID"}}" $global:UseInstanceMetadata = "{{WrapAsVariable "useInstanceMetadata"}}" $global:LoadBalancerSku = "{{WrapAsVariable "loadBalancerSku"}}" $global:ExcludeMasterFromStandardLB = "{{WrapAsVariable "excludeMasterFromStandardLB"}}" @@ -185,6 +186,7 @@ Write-AzureConfig() "primaryAvailabilitySetName": "$global:PrimaryAvailabilitySetName", "primaryScaleSetName": "$global:PrimaryScaleSetName", "useManagedIdentityExtension": $global:UseManagedIdentityExtension, + "userAssignedIdentityID": $global:UserAssignedClientID, "useInstanceMetadata": $global:UseInstanceMetadata, "loadBalancerSku": "$global:LoadBalancerSku", "excludeMasterFromStandardLB": $global:ExcludeMasterFromStandardLB diff --git a/pkg/acsengine/template_generator.go b/pkg/acsengine/template_generator.go index 5280e13b37..955de8e919 100644 --- a/pkg/acsengine/template_generator.go +++ b/pkg/acsengine/template_generator.go @@ -282,6 +282,27 @@ func (t *TemplateGenerator) getTemplateFuncMap(cs *api.ContainerService) templat "UseManagedIdentity": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity }, + "UserAssignedIDEnabled": func() bool { + if cs.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity && + cs.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedID != "" { + return true + } + return false + }, + "UserAssignedID": func() string { + if cs.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity && + cs.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedID != "" { + return cs.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedID + } + return "" + }, + "UserAssignedClientID": func() string { + if cs.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity && + cs.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedClientID != "" { + return cs.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedClientID + } + return "" + }, "UseAksExtension": func() bool { cloudSpecConfig := getCloudSpecConfig(cs.Location) return cloudSpecConfig.CloudName == azurePublicCloud diff --git a/pkg/api/converterfromapi.go b/pkg/api/converterfromapi.go index 6f2e8db303..e5dc0dc937 100644 --- a/pkg/api/converterfromapi.go +++ b/pkg/api/converterfromapi.go @@ -728,6 +728,8 @@ func convertKubernetesConfigToVLabs(api *KubernetesConfig, vlabs *vlabs.Kubernet vlabs.CloudProviderRateLimitBucket = api.CloudProviderRateLimitBucket vlabs.CloudProviderRateLimitQPS = api.CloudProviderRateLimitQPS vlabs.UseManagedIdentity = api.UseManagedIdentity + vlabs.UserAssignedID = api.UserAssignedID + vlabs.UserAssignedClientID = api.UserAssignedClientID vlabs.CustomHyperkubeImage = api.CustomHyperkubeImage vlabs.DockerEngineVersion = api.DockerEngineVersion vlabs.CustomCcmImage = api.CustomCcmImage diff --git a/pkg/api/convertertoapi.go b/pkg/api/convertertoapi.go index 13774d47dd..e8e0dcfd80 100644 --- a/pkg/api/convertertoapi.go +++ b/pkg/api/convertertoapi.go @@ -688,6 +688,8 @@ func convertVLabsKubernetesConfig(vlabs *vlabs.KubernetesConfig, api *Kubernetes api.CloudProviderRateLimitBucket = vlabs.CloudProviderRateLimitBucket api.CloudProviderRateLimitQPS = vlabs.CloudProviderRateLimitQPS api.UseManagedIdentity = vlabs.UseManagedIdentity + api.UserAssignedID = vlabs.UserAssignedID + api.UserAssignedClientID = vlabs.UserAssignedClientID api.CustomHyperkubeImage = vlabs.CustomHyperkubeImage api.DockerEngineVersion = vlabs.DockerEngineVersion api.CustomCcmImage = vlabs.CustomCcmImage diff --git a/pkg/api/types.go b/pkg/api/types.go index ed5f9d5b6f..e076330dca 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -287,6 +287,8 @@ type KubernetesConfig struct { DNSServiceIP string `json:"dnsServiceIP,omitempty"` ServiceCIDR string `json:"serviceCidr,omitempty"` UseManagedIdentity bool `json:"useManagedIdentity,omitempty"` + UserAssignedID string `json:"userAssignedID,omitempty"` + UserAssignedClientID string `json:"userAssignedClientID,omitempty"` //Note: cannot be provided in config. Used *only* for transferring this to azure.json. CustomHyperkubeImage string `json:"customHyperkubeImage,omitempty"` DockerEngineVersion string `json:"dockerEngineVersion,omitempty"` CustomCcmImage string `json:"customCcmImage,omitempty"` // Image for cloud-controller-manager diff --git a/pkg/api/types_test.go b/pkg/api/types_test.go index 03ee771ab9..dec90f09c2 100644 --- a/pkg/api/types_test.go +++ b/pkg/api/types_test.go @@ -28,6 +28,47 @@ const exampleAPIModel = `{ } ` +const exampleSystemMSIModel = `{ + "apiVersion": "vlabs", +"properties": { + "orchestratorProfile": { + "orchestratorType": "Kubernetes", + "kubernetesConfig": { + "useManagedIdentity": true + } + }, + "masterProfile": { "count": 1, "dnsPrefix": "", "vmSize": "Standard_D2_v2" }, + "agentPoolProfiles": [ { "name": "linuxpool1", "count": 2, "vmSize": "Standard_D2_v2", "availabilityProfile": "AvailabilitySet" } ], + "windowsProfile": { "adminUsername": "azureuser", "adminPassword": "replacepassword1234$" }, + "linuxProfile": { "adminUsername": "azureuser", "ssh": { "publicKeys": [ { "keyData": "" } ] } + }, + "servicePrincipalProfile": { "clientId": "", "secret": "" } +} +} +` + +const exampleUserMSI = "/subscriptions//resourcegroups//providers/Microsoft.ManagedIdentity/userAssignedIdentities/" + +const exampleUserMSIModel = `{ + "apiVersion": "vlabs", +"properties": { + "orchestratorProfile": { + "orchestratorType": "Kubernetes", + "kubernetesConfig": { + "useManagedIdentity": true, + "userAssignedID": "` + exampleUserMSI + `" + } + }, + "masterProfile": { "count": 1, "dnsPrefix": "", "vmSize": "Standard_D2_v2" }, + "agentPoolProfiles": [ { "name": "linuxpool1", "count": 2, "vmSize": "Standard_D2_v2", "availabilityProfile": "AvailabilitySet" } ], + "windowsProfile": { "adminUsername": "azureuser", "adminPassword": "replacepassword1234$" }, + "linuxProfile": { "adminUsername": "azureuser", "ssh": { "publicKeys": [ { "keyData": "" } ] } + }, + "servicePrincipalProfile": { "clientId": "", "secret": "" } +} +} +` + func TestOSType(t *testing.T) { p := Properties{ MasterProfile: &MasterProfile{ @@ -746,6 +787,38 @@ func TestCustomHyperkubeImageField(t *testing.T) { } } +func TestUserAssignedMSI(t *testing.T) { + // Test1: With just System MSI + log.Println(exampleSystemMSIModel) + apiloader := &Apiloader{ + Translator: nil, + } + apiModel, _, err := apiloader.DeserializeContainerService([]byte(exampleSystemMSIModel), false, false, nil) + if err != nil { + t.Fatalf("unexpected error deserailizing the example user msi api model: %s", err) + } + systemMSI := apiModel.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity + actualUserMSI := apiModel.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedID + if systemMSI != true || actualUserMSI != "" { + t.Fatalf("found user msi: %t and usermsi: %s", systemMSI, actualUserMSI) + } + + // Test2: With user assigned MSI + log.Println(exampleUserMSIModel) + apiloader = &Apiloader{ + Translator: nil, + } + apiModel, _, err = apiloader.DeserializeContainerService([]byte(exampleUserMSIModel), false, false, nil) + if err != nil { + t.Fatalf("unexpected error deserailizing the example user msi api model: %s", err) + } + systemMSI = apiModel.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity + actualUserMSI = apiModel.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedID + if systemMSI != true && actualUserMSI != exampleUserMSI { + t.Fatalf("found user msi: %t and usermsi: %s", systemMSI, actualUserMSI) + } +} + func TestKubernetesAddon(t *testing.T) { addon := getMockAddon("addon") if !addon.IsEnabled(true) { diff --git a/pkg/api/vlabs/types.go b/pkg/api/vlabs/types.go index a2c8f39bb5..95486daa5e 100644 --- a/pkg/api/vlabs/types.go +++ b/pkg/api/vlabs/types.go @@ -273,6 +273,8 @@ type KubernetesConfig struct { MaxPods int `json:"maxPods,omitempty"` DockerBridgeSubnet string `json:"dockerBridgeSubnet,omitempty"` UseManagedIdentity bool `json:"useManagedIdentity,omitempty"` + UserAssignedID string `json:"userAssignedID,omitempty"` + UserAssignedClientID string `json:"userAssignedClientID,omitempty"` //Note: cannot be provided in config. Used *only* for transferring this to azure.json. CustomHyperkubeImage string `json:"customHyperkubeImage,omitempty"` DockerEngineVersion string `json:"dockerEngineVersion,omitempty"` CustomCcmImage string `json:"customCcmImage,omitempty"` diff --git a/pkg/armhelpers/azureclient.go b/pkg/armhelpers/azureclient.go index a1cd24b597..66b4c8b752 100644 --- a/pkg/armhelpers/azureclient.go +++ b/pkg/armhelpers/azureclient.go @@ -18,6 +18,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-05-01/network" + "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-02-01/storage" "github.com/Azure/go-autorest/autorest" @@ -51,6 +52,7 @@ type AzureClient struct { authorizationClient authorization.RoleAssignmentsClient deploymentsClient resources.DeploymentsClient deploymentOperationsClient resources.DeploymentOperationsClient + msiClient msi.UserAssignedIdentitiesClient resourcesClient apimanagement.GroupClient storageAccountsClient storage.AccountsClient interfacesClient network.InterfacesClient @@ -272,6 +274,7 @@ func getClient(env azure.Environment, subscriptionID, tenantID string, armSpt *a authorizationClient: authorization.NewRoleAssignmentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), deploymentsClient: resources.NewDeploymentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), deploymentOperationsClient: resources.NewDeploymentOperationsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), + msiClient: msi.NewUserAssignedIdentitiesClient(subscriptionID), resourcesClient: apimanagement.NewGroupClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), storageAccountsClient: storage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), interfacesClient: network.NewInterfacesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), @@ -290,6 +293,7 @@ func getClient(env azure.Environment, subscriptionID, tenantID string, armSpt *a c.authorizationClient.Authorizer = authorizer c.deploymentsClient.Authorizer = authorizer c.deploymentOperationsClient.Authorizer = authorizer + c.msiClient.Authorizer = authorizer c.resourcesClient.Authorizer = authorizer c.storageAccountsClient.Authorizer = authorizer c.interfacesClient.Authorizer = authorizer diff --git a/pkg/armhelpers/graph.go b/pkg/armhelpers/graph.go index ad5053fc23..3889ec521b 100644 --- a/pkg/armhelpers/graph.go +++ b/pkg/armhelpers/graph.go @@ -127,6 +127,7 @@ func (az *AzureClient) CreateRoleAssignmentSimple(ctx context.Context, resourceG log.Debugf("Failed to create role assignment (will abort now): %q", err) return err } + // TODO: Should we handle 409 errors as well here ? log.Debugf("Failed to create role assignment (will retry): %q", err) time.Sleep(3 * time.Second) continue diff --git a/pkg/armhelpers/interfaces.go b/pkg/armhelpers/interfaces.go index d1b414162a..e903212235 100644 --- a/pkg/armhelpers/interfaces.go +++ b/pkg/armhelpers/interfaces.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/authorization/mgmt/2015-07-01/authorization" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" log "github.com/sirupsen/logrus" "k8s.io/api/core/v1" @@ -99,6 +100,10 @@ type ACSEngineClient interface { CreateGraphPrincipal(ctx context.Context, servicePrincipalCreateParameters graphrbac.ServicePrincipalCreateParameters) (graphrbac.ServicePrincipal, error) CreateApp(ctx context.Context, applicationName, applicationURL string, replyURLs *[]string, requiredResourceAccess *[]graphrbac.RequiredResourceAccess) (applicationID, servicePrincipalObjectID, secret string, err error) + // User Assigned MSI + //CreateUserAssignedID - Creates a user assigned msi. + CreateUserAssignedID(location string, resourceGroup string, userAssignedID string) (*msi.Identity, error) + // RBAC CreateRoleAssignment(ctx context.Context, scope string, roleAssignmentName string, parameters authorization.RoleAssignmentCreateParameters) (authorization.RoleAssignment, error) CreateRoleAssignmentSimple(ctx context.Context, applicationID, roleID string) error diff --git a/pkg/armhelpers/mockclients.go b/pkg/armhelpers/mockclients.go index 1022fd0b23..0c8973e3ab 100644 --- a/pkg/armhelpers/mockclients.go +++ b/pkg/armhelpers/mockclients.go @@ -12,6 +12,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/authorization/mgmt/2015-07-01/authorization" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" "github.com/Azure/go-autorest/autorest" log "github.com/sirupsen/logrus" @@ -530,6 +531,13 @@ func (mc *MockACSEngineClient) CreateApp(ctx context.Context, applicationName, a return "app-id", "client-id", "client-secret", nil } +// User Assigned MSI + +//CreateUserAssignedID - Creates a user assigned msi. +func (mc *MockACSEngineClient) CreateUserAssignedID(location string, resourceGroup string, userAssignedID string) (*msi.Identity, error) { + return &msi.Identity{}, nil +} + // RBAC Mocks // CreateRoleAssignment creates a role assignment via the authorization client diff --git a/pkg/armhelpers/msi.go b/pkg/armhelpers/msi.go new file mode 100644 index 0000000000..dce8cbb8ec --- /dev/null +++ b/pkg/armhelpers/msi.go @@ -0,0 +1,22 @@ +package armhelpers + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi" + "github.com/Azure/go-autorest/autorest/to" + log "github.com/sirupsen/logrus" +) + +//CreateUserAssignedID - Creates a user assigned msi. +func (az *AzureClient) CreateUserAssignedID(location string, resourceGroup string, userAssignedID string) (id *msi.Identity, err error) { + idCreated, err := az.msiClient.CreateOrUpdate(context.Background(), resourceGroup, userAssignedID, msi.Identity{ + Location: to.StringPtr(location), + }) + if err != nil { + log.Error(err) + return nil, err + } + log.Infof("Created %s in rg %s", userAssignedID, resourceGroup) + return &idCreated, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/client.go b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/client.go new file mode 100644 index 0000000000..a4e7c71d55 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/client.go @@ -0,0 +1,51 @@ +// Package msi implements the Azure ARM Msi service API version 2015-08-31-preview. +// +// The Managed Service Identity Client. +package msi + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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 Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/Azure/go-autorest/autorest" +) + +const ( + // DefaultBaseURI is the default URI used for the service Msi + DefaultBaseURI = "https://management.azure.com" +) + +// BaseClient is the base client for Msi. +type BaseClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the BaseClient client. +func New(subscriptionID string) BaseClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the BaseClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) BaseClient { + return BaseClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/models.go b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/models.go new file mode 100644 index 0000000000..c72acd231b --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/models.go @@ -0,0 +1,403 @@ +package msi + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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 Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "encoding/json" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/to" + "github.com/satori/go.uuid" + "net/http" +) + +// UserAssignedIdentities enumerates the values for user assigned identities. +type UserAssignedIdentities string + +const ( + // MicrosoftManagedIdentityuserAssignedIdentities ... + MicrosoftManagedIdentityuserAssignedIdentities UserAssignedIdentities = "Microsoft.ManagedIdentity/userAssignedIdentities" +) + +// PossibleUserAssignedIdentitiesValues returns an array of possible values for the UserAssignedIdentities const type. +func PossibleUserAssignedIdentitiesValues() []UserAssignedIdentities { + return []UserAssignedIdentities{MicrosoftManagedIdentityuserAssignedIdentities} +} + +// CloudError an error response from the ManagedServiceIdentity service. +type CloudError struct { + // Error - A list of additional details about the error. + Error *CloudErrorBody `json:"error,omitempty"` +} + +// CloudErrorBody an error response from the ManagedServiceIdentity service. +type CloudErrorBody struct { + // Code - An identifier for the error. + Code *string `json:"code,omitempty"` + // Message - A message describing the error, intended to be suitable for display in a user interface. + Message *string `json:"message,omitempty"` + // Target - The target of the particular error. For example, the name of the property in error. + Target *string `json:"target,omitempty"` + // Details - A list of additional details about the error. + Details *[]CloudErrorBody `json:"details,omitempty"` +} + +// Identity describes an identity resource. +type Identity struct { + autorest.Response `json:"-"` + // ID - The id of the created identity. + ID *string `json:"id,omitempty"` + // Name - The name of the created identity. + Name *string `json:"name,omitempty"` + // Location - The Azure region where the identity lives. + Location *string `json:"location,omitempty"` + // Tags - Resource tags + Tags map[string]*string `json:"tags"` + // IdentityProperties - The properties associated with the identity. + *IdentityProperties `json:"properties,omitempty"` + // Type - The type of resource i.e. Microsoft.ManagedIdentity/userAssignedIdentities. Possible values include: 'MicrosoftManagedIdentityuserAssignedIdentities' + Type UserAssignedIdentities `json:"type,omitempty"` +} + +// MarshalJSON is the custom marshaler for Identity. +func (i Identity) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]interface{}) + if i.ID != nil { + objectMap["id"] = i.ID + } + if i.Name != nil { + objectMap["name"] = i.Name + } + if i.Location != nil { + objectMap["location"] = i.Location + } + if i.Tags != nil { + objectMap["tags"] = i.Tags + } + if i.IdentityProperties != nil { + objectMap["properties"] = i.IdentityProperties + } + if i.Type != "" { + objectMap["type"] = i.Type + } + return json.Marshal(objectMap) +} + +// UnmarshalJSON is the custom unmarshaler for Identity struct. +func (i *Identity) UnmarshalJSON(body []byte) error { + var m map[string]*json.RawMessage + err := json.Unmarshal(body, &m) + if err != nil { + return err + } + for k, v := range m { + switch k { + case "id": + if v != nil { + var ID string + err = json.Unmarshal(*v, &ID) + if err != nil { + return err + } + i.ID = &ID + } + case "name": + if v != nil { + var name string + err = json.Unmarshal(*v, &name) + if err != nil { + return err + } + i.Name = &name + } + case "location": + if v != nil { + var location string + err = json.Unmarshal(*v, &location) + if err != nil { + return err + } + i.Location = &location + } + case "tags": + if v != nil { + var tags map[string]*string + err = json.Unmarshal(*v, &tags) + if err != nil { + return err + } + i.Tags = tags + } + case "properties": + if v != nil { + var identityProperties IdentityProperties + err = json.Unmarshal(*v, &identityProperties) + if err != nil { + return err + } + i.IdentityProperties = &identityProperties + } + case "type": + if v != nil { + var typeVar UserAssignedIdentities + err = json.Unmarshal(*v, &typeVar) + if err != nil { + return err + } + i.Type = typeVar + } + } + } + + return nil +} + +// IdentityProperties the properties associated with the identity. +type IdentityProperties struct { + // TenantID - The id of the tenant which the identity belongs to. + TenantID *uuid.UUID `json:"tenantId,omitempty"` + // PrincipalID - The id of the service principal object associated with the created identity. + PrincipalID *uuid.UUID `json:"principalId,omitempty"` + // ClientID - The id of the app associated with the identity. This is a random generated UUID by MSI. + ClientID *uuid.UUID `json:"clientId,omitempty"` + // ClientSecretURL - The ManagedServiceIdentity DataPlane URL that can be queried to obtain the identity credentials. + ClientSecretURL *string `json:"clientSecretUrl,omitempty"` +} + +// Operation operation supported by the Microsoft.ManagedIdentity REST API. +type Operation struct { + // Name - The name of the REST Operation. This is of the format {provider}/{resource}/{operation}. + Name *string `json:"name,omitempty"` + // Display - The object that describes the operation. + Display *OperationDisplay `json:"display,omitempty"` +} + +// OperationDisplay the object that describes the operation. +type OperationDisplay struct { + // Provider - Friendly name of the resource provider. + Provider *string `json:"provider,omitempty"` + // Operation - The type of operation. For example: read, write, delete. + Operation *string `json:"operation,omitempty"` + // Resource - The resource type on which the operation is performed. + Resource *string `json:"resource,omitempty"` + // Description - A description of the operation. + Description *string `json:"description,omitempty"` +} + +// OperationListResult a list of operations supported by Microsoft.ManagedIdentity Resource Provider. +type OperationListResult struct { + autorest.Response `json:"-"` + // Value - A list of operations supported by Microsoft.ManagedIdentity Resource Provider. + Value *[]Operation `json:"value,omitempty"` + // NextLink - The url to get the next page of results, if any. + NextLink *string `json:"nextLink,omitempty"` +} + +// OperationListResultIterator provides access to a complete listing of Operation values. +type OperationListResultIterator struct { + i int + page OperationListResultPage +} + +// Next advances to the next value. If there was an error making +// the request the iterator does not advance and the error is returned. +func (iter *OperationListResultIterator) Next() error { + iter.i++ + if iter.i < len(iter.page.Values()) { + return nil + } + err := iter.page.Next() + if err != nil { + iter.i-- + return err + } + iter.i = 0 + return nil +} + +// NotDone returns true if the enumeration should be started or is not yet complete. +func (iter OperationListResultIterator) NotDone() bool { + return iter.page.NotDone() && iter.i < len(iter.page.Values()) +} + +// Response returns the raw server response from the last page request. +func (iter OperationListResultIterator) Response() OperationListResult { + return iter.page.Response() +} + +// Value returns the current value or a zero-initialized value if the +// iterator has advanced beyond the end of the collection. +func (iter OperationListResultIterator) Value() Operation { + if !iter.page.NotDone() { + return Operation{} + } + return iter.page.Values()[iter.i] +} + +// IsEmpty returns true if the ListResult contains no values. +func (olr OperationListResult) IsEmpty() bool { + return olr.Value == nil || len(*olr.Value) == 0 +} + +// operationListResultPreparer prepares a request to retrieve the next set of results. +// It returns nil if no more results exist. +func (olr OperationListResult) operationListResultPreparer() (*http.Request, error) { + if olr.NextLink == nil || len(to.String(olr.NextLink)) < 1 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(olr.NextLink))) +} + +// OperationListResultPage contains a page of Operation values. +type OperationListResultPage struct { + fn func(OperationListResult) (OperationListResult, error) + olr OperationListResult +} + +// Next advances to the next page of values. If there was an error making +// the request the page does not advance and the error is returned. +func (page *OperationListResultPage) Next() error { + next, err := page.fn(page.olr) + if err != nil { + return err + } + page.olr = next + return nil +} + +// NotDone returns true if the page enumeration should be started or is not yet complete. +func (page OperationListResultPage) NotDone() bool { + return !page.olr.IsEmpty() +} + +// Response returns the raw server response from the last page request. +func (page OperationListResultPage) Response() OperationListResult { + return page.olr +} + +// Values returns the slice of values for the current page or nil if there are no values. +func (page OperationListResultPage) Values() []Operation { + if page.olr.IsEmpty() { + return nil + } + return *page.olr.Value +} + +// UserAssignedIdentitiesListResult values returned by the List operation. +type UserAssignedIdentitiesListResult struct { + autorest.Response `json:"-"` + // Value - The collection of userAssignedIdentities returned by the listing operation. + Value *[]Identity `json:"value,omitempty"` + // NextLink - The url to get the next page of results, if any. + NextLink *string `json:"nextLink,omitempty"` +} + +// UserAssignedIdentitiesListResultIterator provides access to a complete listing of Identity values. +type UserAssignedIdentitiesListResultIterator struct { + i int + page UserAssignedIdentitiesListResultPage +} + +// Next advances to the next value. If there was an error making +// the request the iterator does not advance and the error is returned. +func (iter *UserAssignedIdentitiesListResultIterator) Next() error { + iter.i++ + if iter.i < len(iter.page.Values()) { + return nil + } + err := iter.page.Next() + if err != nil { + iter.i-- + return err + } + iter.i = 0 + return nil +} + +// NotDone returns true if the enumeration should be started or is not yet complete. +func (iter UserAssignedIdentitiesListResultIterator) NotDone() bool { + return iter.page.NotDone() && iter.i < len(iter.page.Values()) +} + +// Response returns the raw server response from the last page request. +func (iter UserAssignedIdentitiesListResultIterator) Response() UserAssignedIdentitiesListResult { + return iter.page.Response() +} + +// Value returns the current value or a zero-initialized value if the +// iterator has advanced beyond the end of the collection. +func (iter UserAssignedIdentitiesListResultIterator) Value() Identity { + if !iter.page.NotDone() { + return Identity{} + } + return iter.page.Values()[iter.i] +} + +// IsEmpty returns true if the ListResult contains no values. +func (uailr UserAssignedIdentitiesListResult) IsEmpty() bool { + return uailr.Value == nil || len(*uailr.Value) == 0 +} + +// userAssignedIdentitiesListResultPreparer prepares a request to retrieve the next set of results. +// It returns nil if no more results exist. +func (uailr UserAssignedIdentitiesListResult) userAssignedIdentitiesListResultPreparer() (*http.Request, error) { + if uailr.NextLink == nil || len(to.String(uailr.NextLink)) < 1 { + return nil, nil + } + return autorest.Prepare(&http.Request{}, + autorest.AsJSON(), + autorest.AsGet(), + autorest.WithBaseURL(to.String(uailr.NextLink))) +} + +// UserAssignedIdentitiesListResultPage contains a page of Identity values. +type UserAssignedIdentitiesListResultPage struct { + fn func(UserAssignedIdentitiesListResult) (UserAssignedIdentitiesListResult, error) + uailr UserAssignedIdentitiesListResult +} + +// Next advances to the next page of values. If there was an error making +// the request the page does not advance and the error is returned. +func (page *UserAssignedIdentitiesListResultPage) Next() error { + next, err := page.fn(page.uailr) + if err != nil { + return err + } + page.uailr = next + return nil +} + +// NotDone returns true if the page enumeration should be started or is not yet complete. +func (page UserAssignedIdentitiesListResultPage) NotDone() bool { + return !page.uailr.IsEmpty() +} + +// Response returns the raw server response from the last page request. +func (page UserAssignedIdentitiesListResultPage) Response() UserAssignedIdentitiesListResult { + return page.uailr +} + +// Values returns the slice of values for the current page or nil if there are no values. +func (page UserAssignedIdentitiesListResultPage) Values() []Identity { + if page.uailr.IsEmpty() { + return nil + } + return *page.uailr.Value +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/operations.go b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/operations.go new file mode 100644 index 0000000000..aff0090736 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/operations.go @@ -0,0 +1,126 @@ +package msi + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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 Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "context" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "net/http" +) + +// OperationsClient is the the Managed Service Identity Client. +type OperationsClient struct { + BaseClient +} + +// NewOperationsClient creates an instance of the OperationsClient client. +func NewOperationsClient(subscriptionID string) OperationsClient { + return NewOperationsClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewOperationsClientWithBaseURI creates an instance of the OperationsClient client. +func NewOperationsClientWithBaseURI(baseURI string, subscriptionID string) OperationsClient { + return OperationsClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// List lists available operations for the Microsoft.ManagedIdentity provider +func (client OperationsClient) List(ctx context.Context) (result OperationListResultPage, err error) { + result.fn = client.listNextResults + req, err := client.ListPreparer(ctx) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.OperationsClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.olr.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "msi.OperationsClient", "List", resp, "Failure sending request") + return + } + + result.olr, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.OperationsClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client OperationsClient) ListPreparer(ctx context.Context) (*http.Request, error) { + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPath("/providers/Microsoft.ManagedIdentity/operations"), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client OperationsClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client OperationsClient) ListResponder(resp *http.Response) (result OperationListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// listNextResults retrieves the next set of results, if any. +func (client OperationsClient) listNextResults(lastResults OperationListResult) (result OperationListResult, err error) { + req, err := lastResults.operationListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "msi.OperationsClient", "listNextResults", nil, "Failure preparing next results request") + } + if req == nil { + return + } + resp, err := client.ListSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "msi.OperationsClient", "listNextResults", resp, "Failure sending next results request") + } + result, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.OperationsClient", "listNextResults", resp, "Failure responding to next results request") + } + return +} + +// ListComplete enumerates all values, automatically crossing page boundaries as required. +func (client OperationsClient) ListComplete(ctx context.Context) (result OperationListResultIterator, err error) { + result.page, err = client.List(ctx) + return +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/userassignedidentities.go b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/userassignedidentities.go new file mode 100644 index 0000000000..06c7a49452 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/userassignedidentities.go @@ -0,0 +1,496 @@ +package msi + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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 Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "context" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "net/http" +) + +// UserAssignedIdentitiesClient is the the Managed Service Identity Client. +type UserAssignedIdentitiesClient struct { + BaseClient +} + +// NewUserAssignedIdentitiesClient creates an instance of the UserAssignedIdentitiesClient client. +func NewUserAssignedIdentitiesClient(subscriptionID string) UserAssignedIdentitiesClient { + return NewUserAssignedIdentitiesClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewUserAssignedIdentitiesClientWithBaseURI creates an instance of the UserAssignedIdentitiesClient client. +func NewUserAssignedIdentitiesClientWithBaseURI(baseURI string, subscriptionID string) UserAssignedIdentitiesClient { + return UserAssignedIdentitiesClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +// CreateOrUpdate create or update an identity in the specified subscription and resource group. +// Parameters: +// 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 +func (client UserAssignedIdentitiesClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity) (result Identity, err error) { + req, err := client.CreateOrUpdatePreparer(ctx, resourceGroupName, resourceName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "CreateOrUpdate", resp, "Failure responding to request") + } + + return +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client UserAssignedIdentitiesClient) CreateOrUpdatePreparer(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client UserAssignedIdentitiesClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client UserAssignedIdentitiesClient) CreateOrUpdateResponder(resp *http.Response) (result Identity, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete deletes the identity. +// Parameters: +// resourceGroupName - the name of the Resource Group to which the identity belongs. +// resourceName - the name of the identity resource. +func (client UserAssignedIdentitiesClient) Delete(ctx context.Context, resourceGroupName string, resourceName string) (result autorest.Response, err error) { + req, err := client.DeletePreparer(ctx, resourceGroupName, resourceName) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Delete", resp, "Failure responding to request") + } + + return +} + +// DeletePreparer prepares the Delete request. +func (client UserAssignedIdentitiesClient) DeletePreparer(ctx context.Context, resourceGroupName string, resourceName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client UserAssignedIdentitiesClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client UserAssignedIdentitiesClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// Get gets the identity. +// Parameters: +// resourceGroupName - the name of the Resource Group to which the identity belongs. +// resourceName - the name of the identity resource. +func (client UserAssignedIdentitiesClient) Get(ctx context.Context, resourceGroupName string, resourceName string) (result Identity, err error) { + req, err := client.GetPreparer(ctx, resourceGroupName, resourceName) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Get", resp, "Failure responding to request") + } + + return +} + +// GetPreparer prepares the Get request. +func (client UserAssignedIdentitiesClient) GetPreparer(ctx context.Context, resourceGroupName string, resourceName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// GetSender sends the Get request. The method will close the +// http.Response Body if it receives an error. +func (client UserAssignedIdentitiesClient) GetSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// GetResponder handles the response to the Get request. The method always +// closes the http.Response Body. +func (client UserAssignedIdentitiesClient) GetResponder(resp *http.Response) (result Identity, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListByResourceGroup lists all the userAssignedIdentities available under the specified ResourceGroup. +// Parameters: +// resourceGroupName - the name of the Resource Group to which the identity belongs. +func (client UserAssignedIdentitiesClient) ListByResourceGroup(ctx context.Context, resourceGroupName string) (result UserAssignedIdentitiesListResultPage, err error) { + result.fn = client.listByResourceGroupNextResults + req, err := client.ListByResourceGroupPreparer(ctx, resourceGroupName) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "ListByResourceGroup", nil, "Failure preparing request") + return + } + + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.uailr.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "ListByResourceGroup", resp, "Failure sending request") + return + } + + result.uailr, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "ListByResourceGroup", resp, "Failure responding to request") + } + + return +} + +// ListByResourceGroupPreparer prepares the ListByResourceGroup request. +func (client UserAssignedIdentitiesClient) ListByResourceGroupPreparer(ctx context.Context, resourceGroupName string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the +// http.Response Body if it receives an error. +func (client UserAssignedIdentitiesClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always +// closes the http.Response Body. +func (client UserAssignedIdentitiesClient) ListByResourceGroupResponder(resp *http.Response) (result UserAssignedIdentitiesListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// listByResourceGroupNextResults retrieves the next set of results, if any. +func (client UserAssignedIdentitiesClient) listByResourceGroupNextResults(lastResults UserAssignedIdentitiesListResult) (result UserAssignedIdentitiesListResult, err error) { + req, err := lastResults.userAssignedIdentitiesListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "listByResourceGroupNextResults", nil, "Failure preparing next results request") + } + if req == nil { + return + } + resp, err := client.ListByResourceGroupSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "listByResourceGroupNextResults", resp, "Failure sending next results request") + } + result, err = client.ListByResourceGroupResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "listByResourceGroupNextResults", resp, "Failure responding to next results request") + } + return +} + +// ListByResourceGroupComplete enumerates all values, automatically crossing page boundaries as required. +func (client UserAssignedIdentitiesClient) ListByResourceGroupComplete(ctx context.Context, resourceGroupName string) (result UserAssignedIdentitiesListResultIterator, err error) { + result.page, err = client.ListByResourceGroup(ctx, resourceGroupName) + return +} + +// ListBySubscription lists all the userAssignedIdentities available under the specified subscription. +func (client UserAssignedIdentitiesClient) ListBySubscription(ctx context.Context) (result UserAssignedIdentitiesListResultPage, err error) { + result.fn = client.listBySubscriptionNextResults + req, err := client.ListBySubscriptionPreparer(ctx) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "ListBySubscription", nil, "Failure preparing request") + return + } + + resp, err := client.ListBySubscriptionSender(req) + if err != nil { + result.uailr.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "ListBySubscription", resp, "Failure sending request") + return + } + + result.uailr, err = client.ListBySubscriptionResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "ListBySubscription", resp, "Failure responding to request") + } + + return +} + +// ListBySubscriptionPreparer prepares the ListBySubscription request. +func (client UserAssignedIdentitiesClient) ListBySubscriptionPreparer(ctx context.Context) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.ManagedIdentity/userAssignedIdentities", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// ListBySubscriptionSender sends the ListBySubscription request. The method will close the +// http.Response Body if it receives an error. +func (client UserAssignedIdentitiesClient) ListBySubscriptionSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// ListBySubscriptionResponder handles the response to the ListBySubscription request. The method always +// closes the http.Response Body. +func (client UserAssignedIdentitiesClient) ListBySubscriptionResponder(resp *http.Response) (result UserAssignedIdentitiesListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// listBySubscriptionNextResults retrieves the next set of results, if any. +func (client UserAssignedIdentitiesClient) listBySubscriptionNextResults(lastResults UserAssignedIdentitiesListResult) (result UserAssignedIdentitiesListResult, err error) { + req, err := lastResults.userAssignedIdentitiesListResultPreparer() + if err != nil { + return result, autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "listBySubscriptionNextResults", nil, "Failure preparing next results request") + } + if req == nil { + return + } + resp, err := client.ListBySubscriptionSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + return result, autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "listBySubscriptionNextResults", resp, "Failure sending next results request") + } + result, err = client.ListBySubscriptionResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "listBySubscriptionNextResults", resp, "Failure responding to next results request") + } + return +} + +// ListBySubscriptionComplete enumerates all values, automatically crossing page boundaries as required. +func (client UserAssignedIdentitiesClient) ListBySubscriptionComplete(ctx context.Context) (result UserAssignedIdentitiesListResultIterator, err error) { + result.page, err = client.ListBySubscription(ctx) + return +} + +// Update update an identity in the specified subscription and resource group. +// Parameters: +// 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 +func (client UserAssignedIdentitiesClient) Update(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity) (result Identity, err error) { + req, err := client.UpdatePreparer(ctx, resourceGroupName, resourceName, parameters) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Update", nil, "Failure preparing request") + return + } + + resp, err := client.UpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Update", resp, "Failure sending request") + return + } + + result, err = client.UpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "msi.UserAssignedIdentitiesClient", "Update", resp, "Failure responding to request") + } + + return +} + +// UpdatePreparer prepares the Update request. +func (client UserAssignedIdentitiesClient) UpdatePreparer(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "resourceName": autorest.Encode("path", resourceName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + } + + const APIVersion = "2015-08-31-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsPatch(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}", pathParameters), + autorest.WithJSON(parameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// UpdateSender sends the Update request. The method will close the +// http.Response Body if it receives an error. +func (client UserAssignedIdentitiesClient) UpdateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + azure.DoRetryWithRegistration(client.Client)) +} + +// UpdateResponder handles the response to the Update request. The method always +// closes the http.Response Body. +func (client UserAssignedIdentitiesClient) UpdateResponder(resp *http.Response) (result Identity, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/version.go b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/version.go new file mode 100644 index 0000000000..7d3f9ab502 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi/version.go @@ -0,0 +1,30 @@ +package msi + +import "github.com/Azure/azure-sdk-for-go/version" + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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 Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/" + version.Number + " msi/2015-08-31-preview" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return version.Number +}