Skip to content

Commit

Permalink
Merge pull request #3243 from jplane/aci-identity
Browse files Browse the repository at this point in the history
added support for identity attribute on container groups
  • Loading branch information
tombuildsstuff authored Apr 18, 2019
2 parents f29d9ce + 25d54b9 commit eae51bf
Show file tree
Hide file tree
Showing 3 changed files with 312 additions and 0 deletions.
93 changes: 93 additions & 0 deletions azurerm/resource_arm_container_group.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,39 @@ func resourceArmContainerGroup() *schema.Resource {
},
},

"identity": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"SystemAssigned",
"UserAssigned",
"SystemAssigned, UserAssigned",
}, false),
},
"principal_id": {
Type: schema.TypeString,
Computed: true,
},
"identity_ids": {
Type: schema.TypeList,
Optional: true,
MinItems: 1,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.NoZeroValues,
},
},
},
},
},

"tags": tagsForceNewSchema(),

"restart_policy": {
Expand Down Expand Up @@ -416,6 +449,7 @@ func resourceArmContainerGroupCreate(d *schema.ResourceData, meta interface{}) e
Name: &name,
Location: &location,
Tags: expandTags(tags),
Identity: expandContainerGroupIdentity(d),
ContainerGroupProperties: &containerinstance.ContainerGroupProperties{
Containers: containers,
Diagnostics: diagnostics,
Expand Down Expand Up @@ -486,6 +520,10 @@ func resourceArmContainerGroupRead(d *schema.ResourceData, meta interface{}) err
d.Set("location", azureRMNormalizeLocation(*location))
}

if err := d.Set("identity", flattenContainerGroupIdentity(d, resp.Identity)); err != nil {
return fmt.Errorf("Error setting `identity`: %+v", err)
}

if props := resp.ContainerGroupProperties; props != nil {
containerConfigs := flattenContainerGroupContainers(d, resp.Containers, props.IPAddress.Ports, props.Volumes)
if err := d.Set("container", containerConfigs); err != nil {
Expand Down Expand Up @@ -709,6 +747,31 @@ func expandContainerEnvironmentVariables(input interface{}, secure bool) *[]cont
return &output
}

func expandContainerGroupIdentity(d *schema.ResourceData) *containerinstance.ContainerGroupIdentity {
v := d.Get("identity")
identities := v.([]interface{})
if len(identities) == 0 {
return nil
}
identity := identities[0].(map[string]interface{})
identityType := containerinstance.ResourceIdentityType(identity["type"].(string))

identityIds := make(map[string]*containerinstance.ContainerGroupIdentityUserAssignedIdentitiesValue)
for _, id := range identity["identity_ids"].([]interface{}) {
identityIds[id.(string)] = &containerinstance.ContainerGroupIdentityUserAssignedIdentitiesValue{}
}

cgIdentity := containerinstance.ContainerGroupIdentity{
Type: identityType,
}

if cgIdentity.Type == containerinstance.UserAssigned || cgIdentity.Type == containerinstance.SystemAssignedUserAssigned {
cgIdentity.UserAssignedIdentities = identityIds
}

return &cgIdentity
}

func expandContainerImageRegistryCredentials(d *schema.ResourceData) *[]containerinstance.ImageRegistryCredential {
credsRaw := d.Get("image_registry_credential").([]interface{})
if len(credsRaw) == 0 {
Expand Down Expand Up @@ -834,6 +897,36 @@ func expandContainerProbe(input interface{}) *containerinstance.ContainerProbe {
return &probe
}

func flattenContainerGroupIdentity(d *schema.ResourceData, identity *containerinstance.ContainerGroupIdentity) []interface{} {
if identity == nil {
return make([]interface{}, 0)
}

result := make(map[string]interface{})
result["type"] = string(identity.Type)
if identity.PrincipalID != nil {
result["principal_id"] = *identity.PrincipalID
}

identityIds := make([]string, 0)
if identity.UserAssignedIdentities != nil {
/*
"userAssignedIdentities": {
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/tomdevidentity/providers/Microsoft.ManagedIdentity/userAssignedIdentities/tom123": {
"principalId": "00000000-0000-0000-0000-000000000000",
"clientId": "00000000-0000-0000-0000-000000000000"
}
}
*/
for key := range identity.UserAssignedIdentities {
identityIds = append(identityIds, key)
}
}
result["identity_ids"] = identityIds

return []interface{}{result}
}

func flattenContainerImageRegistryCredentials(d *schema.ResourceData, input *[]containerinstance.ImageRegistryCredential) []interface{} {
if input == nil {
return nil
Expand Down
207 changes: 207 additions & 0 deletions azurerm/resource_arm_container_group_test.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,104 @@ import (
"net/http"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func TestAccAzureRMContainerGroup_SystemAssignedIdentity(t *testing.T) {
resourceName := "azurerm_container_group.test"
ri := tf.AccRandTimeInt()
config := testAccAzureRMContainerGroup_SystemAssignedIdentity(ri, testLocation())
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMContainerGroupDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMContainerGroupExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned"),
resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "0"),
resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", validate.UUIDRegExp),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"identity.0.principal_id",
},
},
},
})
}

func TestAccAzureRMContainerGroup_UserAssignedIdentity(t *testing.T) {
resourceName := "azurerm_container_group.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(14)
config := testAccAzureRMContainerGroup_UserAssignedIdentity(ri, testLocation(), rs)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMContainerGroupDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMContainerGroupExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "identity.0.type", "UserAssigned"),
resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "1"),
resource.TestCheckResourceAttr(resourceName, "identity.0.principal_id", ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMContainerGroup_multipleAssignedIdentities(t *testing.T) {
resourceName := "azurerm_container_group.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(14)
config := testAccAzureRMContainerGroup_MultipleAssignedIdentities(ri, testLocation(), rs)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMContainerGroupDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMContainerGroupExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned, UserAssigned"),
resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "1"),
resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", validate.UUIDRegExp),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"identity.0.principal_id",
},
},
},
})
}

func TestAccAzureRMContainerGroup_imageRegistryCredentials(t *testing.T) {
resourceName := "azurerm_container_group.test"
ri := tf.AccRandTimeInt()
Expand Down Expand Up @@ -364,6 +456,121 @@ func TestAccAzureRMContainerGroup_windowsComplete(t *testing.T) {
})
}

func testAccAzureRMContainerGroup_SystemAssignedIdentity(ri int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_container_group" "test" {
name = "acctestcontainergroup-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
ip_address_type = "public"
os_type = "Linux"
container {
name = "hw"
image = "microsoft/aci-helloworld:latest"
cpu = "0.5"
memory = "0.5"
port = 80
}
identity {
type = "SystemAssigned"
}
tags = {
environment = "Testing"
}
}
`, ri, location, ri)
}

func testAccAzureRMContainerGroup_UserAssignedIdentity(ri int, location string, rString 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 = "acctest%s"
}
resource "azurerm_container_group" "test" {
name = "acctestcontainergroup-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
ip_address_type = "public"
os_type = "Linux"
container {
name = "hw"
image = "microsoft/aci-helloworld:latest"
cpu = "0.5"
memory = "0.5"
port = 80
}
identity {
type = "UserAssigned"
identity_ids = ["${azurerm_user_assigned_identity.test.id}"]
}
tags = {
environment = "Testing"
}
}
`, ri, location, rString, ri)
}

func testAccAzureRMContainerGroup_MultipleAssignedIdentities(ri int, location string, rString 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 = "acctest%s"
}
resource "azurerm_container_group" "test" {
name = "acctestcontainergroup-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
ip_address_type = "public"
os_type = "Linux"
container {
name = "hw"
image = "microsoft/aci-helloworld:latest"
cpu = "0.5"
memory = "0.5"
port = 80
}
identity {
type = "SystemAssigned, UserAssigned"
identity_ids = ["${azurerm_user_assigned_identity.test.id}"]
}
tags = {
environment = "Testing"
}
}
`, ri, location, rString, ri)
}

func testAccAzureRMContainerGroup_linuxBasic(ri int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down
12 changes: 12 additions & 0 deletions website/docs/r/container_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ The following arguments are supported:

* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created.

* `identity` - (Optional) An `identity` block.

* `container` - (Required) The definition of a container that is part of the group as documented in the `container` block below. Changing this forces a new resource to be created.

~> **Note:** if `os_type` is set to `Windows` currently only a single `container` block is supported.
Expand All @@ -132,6 +134,16 @@ The following arguments are supported:

---

An `identity` block supports the following:

* `type` - (Required) The Managed Service Identity Type of this container group. Possible values are `SystemAssigned` (where Azure will generate a Service Principal for you), `UserAssigned` where you can specify the Service Principal IDs in the `identity_ids` field, and `SystemAssigned, UserAssigned` which assigns both a system managed identity as well as the specified user assigned identities.

~> **NOTE:** When `type` is set to `SystemAssigned`, identity the Principal ID can be retrieved after the container group has been created. See [documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview) for more information.

* `identity_ids` - (Optional) Specifies a list of user managed identity ids to be assigned. Required if `type` is `UserAssigned`.

---

A `container` block supports:

* `name` - (Required) Specifies the name of the Container. Changing this forces a new resource to be created.
Expand Down

0 comments on commit eae51bf

Please sign in to comment.