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

added support for identity attribute on container groups #3243

Merged
merged 8 commits into from
Apr 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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{})
jplane marked this conversation as resolved.
Show resolved Hide resolved
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"
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
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.
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved

~> **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