diff --git a/azurerm/resource_arm_container_group.go b/azurerm/resource_arm_container_group.go index 5e11908d934d..0bf7b99a0f72 100644 --- a/azurerm/resource_arm_container_group.go +++ b/azurerm/resource_arm_container_group.go @@ -49,6 +49,37 @@ func resourceArmContainerGroup() *schema.Resource { }, true), }, + "image_registry_credential": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "server": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + ForceNew: true, + }, + + "username": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + ForceNew: true, + }, + + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.NoZeroValues, + ForceNew: true, + }, + }, + }, + }, + "tags": tagsForceNewSchema(), "restart_policy": { @@ -218,8 +249,9 @@ func resourceArmContainerGroupCreate(d *schema.ResourceData, meta interface{}) e Type: &IPAddressType, Ports: containerGroupPorts, }, - OsType: containerinstance.OperatingSystemTypes(OSType), - Volumes: containerGroupVolumes, + OsType: containerinstance.OperatingSystemTypes(OSType), + Volumes: containerGroupVolumes, + ImageRegistryCredentials: expandContainerImageRegistryCredentials(d), }, } @@ -277,6 +309,10 @@ func resourceArmContainerGroupRead(d *schema.ResourceData, meta interface{}) err } flattenAndSetTags(d, resp.Tags) + if err := d.Set("image_registry_credential", flattenContainerImageRegistryCredentials(d, resp.ImageRegistryCredentials)); err != nil { + return fmt.Errorf("Error setting `capabilities`: %+v", err) + } + d.Set("os_type", string(resp.OsType)) if address := resp.IPAddress; address != nil { d.Set("ip_address_type", address.Type) @@ -531,6 +567,57 @@ func expandContainerEnvironmentVariables(input interface{}) *[]containerinstance return &output } +func expandContainerImageRegistryCredentials(d *schema.ResourceData) *[]containerinstance.ImageRegistryCredential { + credsRaw := d.Get("image_registry_credential").([]interface{}) + if len(credsRaw) == 0 { + return nil + } + + output := make([]containerinstance.ImageRegistryCredential, 0, len(credsRaw)) + + for _, c := range credsRaw { + credConfig := c.(map[string]interface{}) + + output = append(output, containerinstance.ImageRegistryCredential{ + Server: utils.String(credConfig["server"].(string)), + Password: utils.String(credConfig["password"].(string)), + Username: utils.String(credConfig["username"].(string)), + }) + } + + return &output +} + +func flattenContainerImageRegistryCredentials(d *schema.ResourceData, credsPtr *[]containerinstance.ImageRegistryCredential) []interface{} { + if credsPtr == nil { + return nil + } + configsOld := d.Get("image_registry_credential").([]interface{}) + + creds := *credsPtr + output := make([]interface{}, 0, len(creds)) + for i, cred := range creds { + credConfig := make(map[string]interface{}) + if cred.Server != nil { + credConfig["server"] = *cred.Server + } + if cred.Username != nil { + credConfig["username"] = *cred.Username + } + + data := configsOld[i].(map[string]interface{}) + oldServer := data["server"].(string) + if cred.Server != nil && *cred.Server == oldServer { + if v, ok := d.GetOk(fmt.Sprintf("image_registry_credential.%d.password", i)); ok { + credConfig["password"] = v.(string) + } + } + + output = append(output, credConfig) + } + return output +} + func expandContainerVolumes(input interface{}) (*[]containerinstance.VolumeMount, *[]containerinstance.Volume) { volumesRaw := input.([]interface{}) diff --git a/azurerm/resource_arm_container_group_test.go b/azurerm/resource_arm_container_group_test.go index 5b9e655e6e98..742151705837 100644 --- a/azurerm/resource_arm_container_group_test.go +++ b/azurerm/resource_arm_container_group_test.go @@ -11,6 +11,73 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) +func TestAccAzureRMContainerGroup_imageRegistryCredentials(t *testing.T) { + resourceName := "azurerm_container_group.test" + ri := acctest.RandInt() + + config := testAccAzureRMContainerGroup_imageRegistryCredentials(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.#", "2"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.server", "hub.docker.com"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.username", "yourusername"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.password", "yourpassword"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.1.server", "mine.acr.io"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.1.username", "acrusername"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.1.password", "acrpassword"), + ), + }, + }, + }) +} + +func TestAccAzureRMContainerGroup_imageRegistryCredentialsUpdate(t *testing.T) { + resourceName := "azurerm_container_group.test" + ri := acctest.RandInt() + + config := testAccAzureRMContainerGroup_imageRegistryCredentials(ri, testLocation()) + updated := testAccAzureRMContainerGroup_imageRegistryCredentialsUpdated(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMContainerGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.#", "2"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.server", "hub.docker.com"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.username", "yourusername"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.password", "yourpassword"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.1.server", "mine.acr.io"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.1.username", "acrusername"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.1.password", "acrpassword"), + ), + }, + { + Config: updated, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMContainerGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.#", "1"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.server", "hub.docker.com"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.username", "updatedusername"), + resource.TestCheckResourceAttr(resourceName, "image_registry_credential.0.password", "updatedpassword"), + ), + }, + }, + }) +} + func TestAccAzureRMContainerGroup_linuxBasic(t *testing.T) { resourceName := "azurerm_container_group.test" ri := acctest.RandInt() @@ -176,6 +243,96 @@ resource "azurerm_container_group" "test" { `, ri, location, ri) } +func testAccAzureRMContainerGroup_imageRegistryCredentials(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" + } + + image_registry_credential { + server = "hub.docker.com" + username = "yourusername" + password = "yourpassword" + } + + image_registry_credential { + server = "mine.acr.io" + username = "acrusername" + password = "acrpassword" + } + + container { + name = "sidecar" + image = "microsoft/aci-tutorial-sidecar" + cpu = "0.5" + memory = "0.5" + } + + tags { + environment = "Testing" + } +} +`, ri, location, ri) +} + +func testAccAzureRMContainerGroup_imageRegistryCredentialsUpdated(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" + } + + image_registry_credential { + server = "hub.docker.com" + username = "updatedusername" + password = "updatedpassword" + } + + container { + name = "sidecar" + image = "microsoft/aci-tutorial-sidecar" + cpu = "0.5" + memory = "0.5" + } + + tags { + environment = "Testing" + } +} +`, ri, location, ri) +} + func testAccAzureRMContainerGroup_linuxBasicUpdated(ri int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/examples/aci-image-registry-credentials/main.tf b/examples/aci-image-registry-credentials/main.tf new file mode 100644 index 000000000000..97f2f868e7f4 --- /dev/null +++ b/examples/aci-image-registry-credentials/main.tf @@ -0,0 +1,43 @@ +resource "azurerm_resource_group" "aci-rg" { + name = "aci-test-creds" + location = "west us" +} + +resource "azurerm_container_group" "aci-test" { + name = "my-aci-hw" + location = "${azurerm_resource_group.aci-rg.location}" + resource_group_name = "${azurerm_resource_group.aci-rg.name}" + ip_address_type = "public" + os_type = "linux" + + image_registry_credential { + server = "hub.docker.com" + username = "yourusername1" + password = "yourpassword" + } + + image_registry_credential { + server = "2hub.docker.com" + username = "2yourusername1" + password = "2yourpassword" + } + + container { + name = "hw" + image = "microsoft/aci-helloworld:latest" + cpu = "0.5" + memory = "1.5" + port = "80" + } + + container { + name = "sidecar" + image = "microsoft/aci-tutorial-sidecar" + cpu = "0.5" + memory = "1.5" + } + + tags { + environment = "testing" + } +} diff --git a/website/docs/r/container_group.html.markdown b/website/docs/r/container_group.html.markdown index 2f98942e95a9..d95fd25e7cf4 100644 --- a/website/docs/r/container_group.html.markdown +++ b/website/docs/r/container_group.html.markdown @@ -99,6 +99,8 @@ The following arguments are supported: * `restart_policy` - (Optional) Restart policy for the container group. Allowed values are `Always`, `Never`, `OnFailure`. Defaults to `Always`. +* `image_registry_credential` - (Optional) Set image registry credentials for the group as documented in the `image_registry_credential` block below + * `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. @@ -135,6 +137,14 @@ The `volume` block supports: * `share_name` - (Required) The Azure storage share that is to be mounted as a volume. This must be created on the storage account specified as above. Changing this forces a new resource to be created. +The `image_registry_credential` block supports: + +* `username` - (Required) The username with which to connect to the registry. + +* `password` - (Required) The password with which to connect to the registry. + +* `server` - (Required) The address to use to connect to the registry without protocol ("https"/"http"). For example: "myacr.acr.io" + ## Attributes Reference The following attributes are exported: