Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/user managed identity #1

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
8 changes: 8 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2017-09-01/network"
"github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql"
"github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization"
"github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi"
"github.com/Azure/azure-sdk-for-go/services/preview/operationalinsights/mgmt/2015-11-01-preview/operationalinsights"
"github.com/Azure/azure-sdk-for-go/services/preview/operationsmanagement/mgmt/2015-11-01-preview/operationsmanagement"
"github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql"
Expand Down Expand Up @@ -149,6 +150,9 @@ type ArmClient struct {
// Monitor
monitorAlertRulesClient insights.AlertRulesClient

// MSI
userAssignedIdentitiesClient msi.UserAssignedIdentitiesClient

// Networking
applicationGatewayClient network.ApplicationGatewaysClient
applicationSecurityGroupsClient network.ApplicationSecurityGroupsClient
Expand Down Expand Up @@ -793,6 +797,10 @@ func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, a
c.configureClient(&subnetsClient.Client, auth)
c.subnetClient = subnetsClient

userAssignedIdentitiesClient := msi.NewUserAssignedIdentitiesClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&userAssignedIdentitiesClient.Client, auth)
c.userAssignedIdentitiesClient = userAssignedIdentitiesClient

watchersClient := network.NewWatchersClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&watchersClient.Client, auth)
c.watcherClient = watchersClient
Expand Down
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_template_deployment": resourceArmTemplateDeployment(),
"azurerm_traffic_manager_endpoint": resourceArmTrafficManagerEndpoint(),
"azurerm_traffic_manager_profile": resourceArmTrafficManagerProfile(),
"azurerm_user_assigned_identity": resourceArmUserAssignedIdentity(),
"azurerm_virtual_machine_extension": resourceArmVirtualMachineExtensions(),
"azurerm_virtual_machine": resourceArmVirtualMachine(),
"azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(),
Expand Down
122 changes: 122 additions & 0 deletions azurerm/resource_arm_user_assigned_identity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package azurerm

import (
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi"
"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmUserAssignedIdentity() *schema.Resource {
return &schema.Resource{
Create: resourceArmUserAssignedIdentityCreate,
Read: resourceArmUserAssignedIdentityRead,
Update: resourceArmUserAssignedIdentityCreate,
Delete: resourceArmUserAssignedIdentityDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"resource_group_name": resourceGroupNameSchema(),
"location": locationSchema(),

"name": {
Type: schema.TypeString,
Required: true,
},

"principal_id": {
Type: schema.TypeString,
Computed: true,
},

"tags": tagsSchema(),
},
}
}

func resourceArmUserAssignedIdentityCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).userAssignedIdentitiesClient
ctx := meta.(*ArmClient).StopContext

log.Printf("[INFO] preparing arguments for Azure ARM user identity creation.")

resourceName := d.Get("name").(string)
location := d.Get("location").(string)
resGroup := d.Get("resource_group_name").(string)
tags := d.Get("tags").(map[string]interface{})
identity := msi.Identity{
Name: &resourceName,
Location: &location,
Tags: expandTags(tags),
}

identity, err := client.CreateOrUpdate(ctx, resGroup, resourceName, identity)
if err != nil {
return fmt.Errorf("Error Creating/Updating User Assigned Identity %q (Resource Group %q): %+v", resourceName, resGroup, err)
}

if identity.ID == nil {
return fmt.Errorf("Cannot read User Assigned Identity %q ID (resource group %q) ID", resourceName, resGroup)
}

d.SetId(*identity.ID)

return resourceArmUserAssignedIdentityRead(d, meta)
}

func resourceArmUserAssignedIdentityRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).userAssignedIdentitiesClient
ctx := meta.(*ArmClient).StopContext

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
resourceName := id.Path["userAssignedIdentities"]

resp, err := client.Get(ctx, resGroup, resourceName)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
d.SetId("")
return nil
}
return fmt.Errorf("Error making Read request on User Assigned Identity %q (Resource Group %q): %+v", resourceName, resGroup, err)
}

if resp.IdentityProperties == nil || resp.IdentityProperties.PrincipalID == nil {
return fmt.Errorf("Error PrincipalID can't be null for User Assigned Identity %q (Resource Group %q): %+v", resourceName, resGroup, err)
}

d.Set("resource_group_name", resGroup)
d.Set("location", resp.Location)
d.Set("name", resp.Name)
d.Set("principal_id", resp.IdentityProperties.PrincipalID.String())

flattenAndSetTags(d, resp.Tags)

return nil
}

func resourceArmUserAssignedIdentityDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).userAssignedIdentitiesClient
ctx := meta.(*ArmClient).StopContext

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
resourceName := id.Path["userAssignedIdentities"]

_, err = client.Delete(ctx, resGroup, resourceName)
if err != nil {
return fmt.Errorf("Error deleting User Assigned Identity %q (Resource Group %q): %+v", resourceName, resGroup, err)
}

return nil
}
48 changes: 48 additions & 0 deletions azurerm/resource_arm_user_assigned_identity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package azurerm

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAzureRMUserAssignedIdentity_create(t *testing.T) {
resourceIdRegex := "/subscriptions/.+/resourcegroups/.+/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test"
principalIdRegex := "^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$"
resourceName := "azurerm_user_assigned_identity.test"
ri := acctest.RandInt()
config := testAccAzureRMUserAssignedIdentityCreateTemplate(ri, testLocation())
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(resourceName, "id", regexp.MustCompile(resourceIdRegex)),
resource.TestMatchResourceAttr(resourceName, "principal_id", regexp.MustCompile(principalIdRegex)),
),
},
},
})
}

func testAccAzureRMUserAssignedIdentityCreateTemplate(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_user_assigned_identity" "test" {
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"

name = "test"
}
`, rInt, location)
}
42 changes: 39 additions & 3 deletions azurerm/resource_arm_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,20 @@ func resourceArmVirtualMachine() *schema.Resource {
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
ValidateFunc: validation.StringInSlice([]string{
"SystemAssigned",
"UserAssigned",
}, true),
},
"principal_id": {
Type: schema.TypeString,
Computed: true,
},
"identity_ids": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
Expand Down Expand Up @@ -933,6 +941,14 @@ func flattenAzureRmVirtualMachineIdentity(identity *compute.VirtualMachineIdenti
result["principal_id"] = *identity.PrincipalID
}

if identity.IdentityIds != nil {
identity_ids := make([]string, 0)
for _, id := range *identity.IdentityIds {
identity_ids = append(identity_ids, id)
}
result["identity_ids"] = identity_ids
}

return []interface{}{result}
}

Expand Down Expand Up @@ -1152,13 +1168,33 @@ func expandAzureRmVirtualMachinePlan(d *schema.ResourceData) (*compute.Plan, err
}

func expandAzureRmVirtualMachineIdentity(d *schema.ResourceData) *compute.VirtualMachineIdentity {
var identityType compute.ResourceIdentityType

v := d.Get("identity")
identities := v.([]interface{})
identity := identities[0].(map[string]interface{})
identityType := identity["type"].(string)
return &compute.VirtualMachineIdentity{
Type: compute.ResourceIdentityType(identityType),

switch strings.ToLower(identity["type"].(string)) {
case strings.ToLower(string(compute.ResourceIdentityTypeUserAssigned)):
identityType = compute.ResourceIdentityTypeUserAssigned
case strings.ToLower(string(compute.ResourceIdentityTypeSystemAssigned)):
identityType = compute.ResourceIdentityTypeSystemAssigned
}

identityIds := []string{}
for _, id := range identity["identity_ids"].([]interface{}) {
identityIds = append(identityIds, id.(string))
}

vmIdentity := compute.VirtualMachineIdentity{
Type: identityType,
}

if vmIdentity.Type == compute.ResourceIdentityTypeUserAssigned {
vmIdentity.IdentityIds = &identityIds
}

return &vmIdentity
}

func expandAzureRmVirtualMachineOsProfile(d *schema.ResourceData) (*compute.OSProfile, error) {
Expand Down
42 changes: 39 additions & 3 deletions azurerm/resource_arm_virtual_machine_scale_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,16 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource {
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
ValidateFunc: validation.StringInSlice([]string{
"SystemAssigned",
"UserAssigned",
}, true),
},
"identity_ids": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"principal_id": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -932,6 +940,14 @@ func flattenAzureRmVirtualMachineScaleSetIdentity(identity *compute.VirtualMachi
result["principal_id"] = *identity.PrincipalID
}

if identity.IdentityIds != nil {
identity_ids := make([]string, 0)
for _, id := range *identity.IdentityIds {
identity_ids = append(identity_ids, id)
}
result["identity_ids"] = identity_ids
}

return []interface{}{result}
}

Expand Down Expand Up @@ -1632,13 +1648,33 @@ func expandAzureRMVirtualMachineScaleSetsDiagnosticProfile(d *schema.ResourceDat
}

func expandAzureRmVirtualMachineScaleSetIdentity(d *schema.ResourceData) *compute.VirtualMachineScaleSetIdentity {
var identityType compute.ResourceIdentityType

v := d.Get("identity")
identities := v.([]interface{})
identity := identities[0].(map[string]interface{})
identityType := identity["type"].(string)
return &compute.VirtualMachineScaleSetIdentity{
Type: compute.ResourceIdentityType(identityType),

switch strings.ToLower(identity["type"].(string)) {
case strings.ToLower(string(compute.ResourceIdentityTypeUserAssigned)):
identityType = compute.ResourceIdentityTypeUserAssigned
case strings.ToLower(string(compute.ResourceIdentityTypeSystemAssigned)):
identityType = compute.ResourceIdentityTypeSystemAssigned
}

identityIds := []string{}
for _, id := range identity["identity_ids"].([]interface{}) {
identityIds = append(identityIds, id.(string))
}

vmssIdentity := compute.VirtualMachineScaleSetIdentity{
Type: identityType,
}

if vmssIdentity.Type == compute.ResourceIdentityTypeUserAssigned {
vmssIdentity.IdentityIds = &identityIds
}

return &vmssIdentity
}

func expandAzureRMVirtualMachineScaleSetsStorageProfileOsDisk(d *schema.ResourceData) (*compute.VirtualMachineScaleSetOSDisk, error) {
Expand Down
Loading