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
Changes from 1 commit
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
Next Next commit
added support for identity attribute on container groups
Josh Lane committed Apr 14, 2019
commit a52a332b00d842465f4169f61fde855bdca6d4ff
91 changes: 91 additions & 0 deletions azurerm/resource_arm_container_group.go
100644 → 100755
Original file line number Diff line number Diff line change
@@ -92,6 +92,40 @@ 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,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
jplane marked this conversation as resolved.
Show resolved Hide resolved
ValidateFunc: validation.StringInSlice([]string{
"SystemAssigned",
"UserAssigned",
"SystemAssignedUserAssigned",
}, 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": {
@@ -416,6 +450,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,
@@ -486,6 +521,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 {
@@ -709,6 +748,28 @@ func expandContainerEnvironmentVariables(input interface{}, secure bool) *[]cont
return &output
}

func expandContainerGroupIdentity(d *schema.ResourceData) *containerinstance.ContainerGroupIdentity {
v := d.Get("identity")
identities := v.([]interface{})
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 {
@@ -834,6 +895,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
206 changes: 206 additions & 0 deletions azurerm/resource_arm_container_group_test.go
Original file line number Diff line number Diff line change
@@ -5,12 +5,103 @@ import (
"net/http"
"testing"

"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 := testAccAzureRMContainerGroupSystemAssignedIdentity(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 := testAccAzureRMContainerGroupUserAssignedIdentity(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_multipleAssignedIdentity(t *testing.T) {
resourceName := "azurerm_container_group.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(14)
config := testAccAzureRMContainerGroupMultipleAssignedIdentity(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()
@@ -364,6 +455,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" {
12 changes: 12 additions & 0 deletions website/docs/r/container_group.html.markdown
Original file line number Diff line number Diff line change
@@ -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.
@@ -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.