diff --git a/aws/resource_aws_ecr_repository.go b/aws/resource_aws_ecr_repository.go index dca6b4c332b2..ac9ad614b6bc 100644 --- a/aws/resource_aws_ecr_repository.go +++ b/aws/resource_aws_ecr_repository.go @@ -42,6 +42,20 @@ func resourceAwsEcrRepository() *schema.Resource { ecr.ImageTagMutabilityImmutable, }, false), }, + "image_scanning_configuration": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "scan_on_push": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + DiffSuppressFunc: suppressMissingOptionalConfigurationBlock, + }, "tags": tagsSchema(), "arn": { Type: schema.TypeString, @@ -68,6 +82,17 @@ func resourceAwsEcrRepositoryCreate(d *schema.ResourceData, meta interface{}) er Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().EcrTags(), } + imageScanningConfigs := d.Get("image_scanning_configuration").([]interface{}) + if len(imageScanningConfigs) > 0 { + imageScanningConfig := imageScanningConfigs[0] + if imageScanningConfig != nil { + configMap := imageScanningConfig.(map[string]interface{}) + input.ImageScanningConfiguration = &ecr.ImageScanningConfiguration{ + ScanOnPush: aws.Bool(configMap["scan_on_push"].(bool)), + } + } + } + log.Printf("[DEBUG] Creating ECR repository: %#v", input) out, err := conn.CreateRepository(&input) if err != nil { @@ -127,6 +152,10 @@ func resourceAwsEcrRepositoryRead(d *schema.ResourceData, meta interface{}) erro d.Set("repository_url", repository.RepositoryUri) d.Set("image_tag_mutability", repository.ImageTagMutability) + if err := d.Set("image_scanning_configuration", flattenImageScanningConfiguration(repository.ImageScanningConfiguration)); err != nil { + return fmt.Errorf("error setting image_scanning_configuration: %s", err) + } + tags, err := keyvaluetags.EcrListTags(conn, arn) if err != nil { @@ -140,6 +169,19 @@ func resourceAwsEcrRepositoryRead(d *schema.ResourceData, meta interface{}) erro return nil } +func flattenImageScanningConfiguration(isc *ecr.ImageScanningConfiguration) []map[string]interface{} { + if isc == nil { + return nil + } + + config := make(map[string]interface{}) + config["scan_on_push"] = aws.BoolValue(isc.ScanOnPush) + + return []map[string]interface{}{ + config, + } +} + func resourceAwsEcrRepositoryUpdate(d *schema.ResourceData, meta interface{}) error { arn := d.Get("arn").(string) conn := meta.(*AWSClient).ecrconn @@ -150,6 +192,12 @@ func resourceAwsEcrRepositoryUpdate(d *schema.ResourceData, meta interface{}) er } } + if d.HasChange("image_scanning_configuration") { + if err := resourceAwsEcrRepositoryUpdateImageScanningConfiguration(conn, d); err != nil { + return err + } + } + if d.HasChange("tags") { o, n := d.GetChange("tags") @@ -222,3 +270,28 @@ func resourceAwsEcrRepositoryUpdateImageTagMutability(conn *ecr.ECR, d *schema.R return nil } +func resourceAwsEcrRepositoryUpdateImageScanningConfiguration(conn *ecr.ECR, d *schema.ResourceData) error { + + var ecrImageScanningConfig ecr.ImageScanningConfiguration + imageScanningConfigs := d.Get("image_scanning_configuration").([]interface{}) + if len(imageScanningConfigs) > 0 { + imageScanningConfig := imageScanningConfigs[0] + if imageScanningConfig != nil { + configMap := imageScanningConfig.(map[string]interface{}) + ecrImageScanningConfig.ScanOnPush = aws.Bool(configMap["scan_on_push"].(bool)) + } + } + + input := &ecr.PutImageScanningConfigurationInput{ + ImageScanningConfiguration: &ecrImageScanningConfig, + RepositoryName: aws.String(d.Id()), + RegistryId: aws.String(d.Get("registry_id").(string)), + } + + _, err := conn.PutImageScanningConfiguration(input) + if err != nil { + return fmt.Errorf("Error setting image scanning configuration: %s", err.Error()) + } + + return nil +} diff --git a/aws/resource_aws_ecr_repository_test.go b/aws/resource_aws_ecr_repository_test.go index c999b9e0b0c3..3c54a6476cbe 100644 --- a/aws/resource_aws_ecr_repository_test.go +++ b/aws/resource_aws_ecr_repository_test.go @@ -96,6 +96,54 @@ func TestAccAWSEcrRepository_immutability(t *testing.T) { }) } +func TestAccAWSEcrRepository_image_scanning_configuration(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_ecr_repository.default" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcrRepositoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEcrRepositoryConfig_image_scanning_configuration(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcrRepositoryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "image_scanning_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "image_scanning_configuration.0.scan_on_push", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test that the removal of the non-default image_scanning_configuration causes plan changes + Config: testAccAWSEcrRepositoryConfig(rName), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + { + // Test attribute update + Config: testAccAWSEcrRepositoryConfig_image_scanning_configuration(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcrRepositoryExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "image_scanning_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "image_scanning_configuration.0.scan_on_push", "false"), + ), + }, + { + // Test that the removal of the default image_scanning_configuration doesn't cause any plan changes + Config: testAccAWSEcrRepositoryConfig(rName), + PlanOnly: true, + ExpectNonEmptyPlan: false, + }, + }, + }) +} + func testAccCheckAWSEcrRepositoryDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ecrconn @@ -194,3 +242,14 @@ resource "aws_ecr_repository" "default" { } `, rName) } + +func testAccAWSEcrRepositoryConfig_image_scanning_configuration(rName string, scanOnPush bool) string { + return fmt.Sprintf(` +resource "aws_ecr_repository" "default" { + name = %q + image_scanning_configuration { + scan_on_push = %t + } +} +`, rName, scanOnPush) +} diff --git a/website/docs/r/ecr_repository.html.markdown b/website/docs/r/ecr_repository.html.markdown index 54b40443c569..47a6927ff94d 100644 --- a/website/docs/r/ecr_repository.html.markdown +++ b/website/docs/r/ecr_repository.html.markdown @@ -13,7 +13,12 @@ Provides an Elastic Container Registry Repository. ```hcl resource "aws_ecr_repository" "foo" { - name = "bar" + name = "bar" + image_tag_mutability = "MUTABLE" + + image_scanning_configuration { + scan_on_push = true + } } ``` @@ -23,6 +28,8 @@ The following arguments are supported: * `name` - (Required) Name of the repository. * `image_tag_mutability` - (Optional) The tag mutability setting for the repository. Must be one of: `MUTABLE` or `IMMUTABLE`. Defaults to `MUTABLE`. +* `image_scanning_configuration` - (Optional) Configuration block that defines image scanning configuration for the repository. By default, image scanning must be manually triggered. See the [ECR User Guide](https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html) for more information about image scanning. + * `scan_on_push` - (Required) Indicates whether images are scanned after being pushed to the repository (true) or not scanned (false). * `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference