diff --git a/.changelog/31561.txt b/.changelog/31561.txt new file mode 100644 index 00000000000..e33278dccfd --- /dev/null +++ b/.changelog/31561.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_amplify_app: Add `custom_headers` argument +``` diff --git a/internal/acctest/acctest.go b/internal/acctest/acctest.go index b4674aaa9d0..11ea3b3bf88 100644 --- a/internal/acctest/acctest.go +++ b/internal/acctest/acctest.go @@ -744,6 +744,16 @@ func CheckResourceAttrJMESPair(nameFirst, keyFirst, jmesPath, nameSecond, keySec } } +// CheckResourceAttrHasPrefix ensures the Terraform state value has the specified prefix. +func CheckResourceAttrHasPrefix(name, key, prefix string) resource.TestCheckFunc { + return resource.TestCheckResourceAttrWith(name, key, func(value string) error { + if strings.HasPrefix(value, prefix) { + return nil + } + return fmt.Errorf("%s: Attribute '%s' expected prefix %#v, got %#v", name, key, prefix, value) + }) +} + // Copied and inlined from the SDK testing code func PrimaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { rs, ok := s.RootModule().Resources[name] diff --git a/internal/service/amplify/app.go b/internal/service/amplify/app.go index 9fb96cdc324..1ff60e79268 100644 --- a/internal/service/amplify/app.go +++ b/internal/service/amplify/app.go @@ -32,6 +32,7 @@ func ResourceApp() *schema.Resource { ReadWithoutTimeout: resourceAppRead, UpdateWithoutTimeout: resourceAppUpdate, DeleteWithoutTimeout: resourceAppDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -55,12 +56,10 @@ func ResourceApp() *schema.Resource { Sensitive: true, ValidateFunc: validation.StringLenBetween(1, 255), }, - "arn": { Type: schema.TypeString, Computed: true, }, - "auto_branch_creation_config": { Type: schema.TypeList, Optional: true, @@ -82,52 +81,43 @@ func ResourceApp() *schema.Resource { return true }, }, - "build_spec": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(1, 25000), }, - "enable_auto_build": { Type: schema.TypeBool, Optional: true, }, - "enable_basic_auth": { Type: schema.TypeBool, Optional: true, }, - "enable_performance_mode": { Type: schema.TypeBool, Optional: true, ForceNew: true, }, - "enable_pull_request_preview": { Type: schema.TypeBool, Optional: true, }, - "environment_variables": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "framework": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(1, 255), }, - "pull_request_environment_name": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(1, 255), }, - "stage": { Type: schema.TypeString, Optional: true, @@ -144,7 +134,6 @@ func ResourceApp() *schema.Resource { }, }, }, - "auto_branch_creation_patterns": { Type: schema.TypeSet, Optional: true, @@ -158,7 +147,6 @@ func ResourceApp() *schema.Resource { return true }, }, - "basic_auth_credentials": { Type: schema.TypeString, Optional: true, @@ -173,14 +161,18 @@ func ResourceApp() *schema.Resource { return true }, }, - "build_spec": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringLenBetween(1, 25000), }, - + "custom_headers": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringLenBetween(1, 25000), + }, "custom_rule": { Type: schema.TypeList, Optional: true, @@ -191,13 +183,11 @@ func ResourceApp() *schema.Resource { Optional: true, ValidateFunc: validation.StringLenBetween(1, 2048), }, - "source": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(1, 2048), }, - "status": { Type: schema.TypeString, Optional: true, @@ -209,7 +199,6 @@ func ResourceApp() *schema.Resource { "404-200", }, false), }, - "target": { Type: schema.TypeString, Required: true, @@ -218,70 +207,58 @@ func ResourceApp() *schema.Resource { }, }, }, - "default_domain": { Type: schema.TypeString, Computed: true, }, - "description": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(1, 1000), }, - "enable_auto_branch_creation": { Type: schema.TypeBool, Optional: true, }, - "enable_basic_auth": { Type: schema.TypeBool, Optional: true, }, - "enable_branch_auto_build": { Type: schema.TypeBool, Optional: true, }, - "enable_branch_auto_deletion": { Type: schema.TypeBool, Optional: true, }, - "environment_variables": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "iam_service_role_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - "name": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(1, 255), }, - "oauth_token": { Type: schema.TypeString, Optional: true, Sensitive: true, ValidateFunc: validation.StringLenBetween(1, 1000), }, - "platform": { Type: schema.TypeString, Optional: true, Default: amplify.PlatformWeb, ValidateFunc: validation.StringInSlice(amplify.Platform_Values(), false), }, - "production_branch": { Type: schema.TypeList, Computed: true, @@ -291,17 +268,14 @@ func ResourceApp() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "last_deploy_time": { Type: schema.TypeString, Computed: true, }, - "status": { Type: schema.TypeString, Computed: true, }, - "thumbnail_url": { Type: schema.TypeString, Computed: true, @@ -309,13 +283,11 @@ func ResourceApp() *schema.Resource { }, }, }, - "repository": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(1, 1000), }, - names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), }, @@ -353,6 +325,10 @@ func resourceAppCreate(ctx context.Context, d *schema.ResourceData, meta interfa input.BuildSpec = aws.String(v.(string)) } + if v, ok := d.GetOk("custom_headers"); ok { + input.CustomHeaders = aws.String(v.(string)) + } + if v, ok := d.GetOk("custom_rule"); ok && len(v.([]interface{})) > 0 { input.CustomRules = expandCustomRules(v.([]interface{})) } @@ -436,6 +412,7 @@ func resourceAppRead(ctx context.Context, d *schema.ResourceData, meta interface d.Set("auto_branch_creation_patterns", aws.StringValueSlice(app.AutoBranchCreationPatterns)) d.Set("basic_auth_credentials", app.BasicAuthCredentials) d.Set("build_spec", app.BuildSpec) + d.Set("custom_headers", app.CustomHeaders) if err := d.Set("custom_rule", flattenCustomRules(app.CustomRules)); err != nil { return sdkdiag.AppendErrorf(diags, "setting custom_rule: %s", err) } @@ -498,6 +475,10 @@ func resourceAppUpdate(ctx context.Context, d *schema.ResourceData, meta interfa input.BuildSpec = aws.String(d.Get("build_spec").(string)) } + if d.HasChange("custom_headers") { + input.CustomHeaders = aws.String(d.Get("custom_headers").(string)) + } + if d.HasChange("custom_rule") { if v := d.Get("custom_rule").([]interface{}); len(v) > 0 { input.CustomRules = expandCustomRules(v) diff --git a/internal/service/amplify/app_test.go b/internal/service/amplify/app_test.go index f8365ceb680..c721456801f 100644 --- a/internal/service/amplify/app_test.go +++ b/internal/service/amplify/app_test.go @@ -45,6 +45,7 @@ func testAccApp_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "auto_branch_creation_patterns.#", "0"), resource.TestCheckResourceAttr(resourceName, "basic_auth_credentials", ""), resource.TestCheckResourceAttr(resourceName, "build_spec", ""), + resource.TestCheckResourceAttr(resourceName, "custom_headers", ""), resource.TestCheckResourceAttr(resourceName, "custom_rule.#", "0"), resource.TestMatchResourceAttr(resourceName, "default_domain", regexache.MustCompile(`\.amplifyapp\.com$`)), resource.TestCheckResourceAttr(resourceName, "description", ""), diff --git a/website/docs/r/amplify_app.html.markdown b/website/docs/r/amplify_app.html.markdown index f891f85cde4..bef72b52f8b 100644 --- a/website/docs/r/amplify_app.html.markdown +++ b/website/docs/r/amplify_app.html.markdown @@ -136,6 +136,30 @@ resource "aws_amplify_app" "example" { } ``` +### Custom Headers + +```terraform +resource "aws_amplify_app" "example" { + name = "example" + + custom_headers = <<-EOT + customHeaders: + - pattern: '**' + headers: + - key: 'Strict-Transport-Security' + value: 'max-age=31536000; includeSubDomains' + - key: 'X-Frame-Options' + value: 'SAMEORIGIN' + - key: 'X-XSS-Protection' + value: '1; mode=block' + - key: 'X-Content-Type-Options' + value: 'nosniff' + - key: 'Content-Security-Policy' + value: "default-src 'self'" + EOT +} +``` + ## Argument Reference This resource supports the following arguments: @@ -146,6 +170,7 @@ This resource supports the following arguments: * `auto_branch_creation_patterns` - (Optional) Automated branch creation glob patterns for an Amplify app. * `basic_auth_credentials` - (Optional) Credentials for basic authorization for an Amplify app. * `build_spec` - (Optional) The [build specification](https://docs.aws.amazon.com/amplify/latest/userguide/build-settings.html) (build spec) for an Amplify app. +* `custom_headers` - (Optional) The [custom HTTP headers](https://docs.aws.amazon.com/amplify/latest/userguide/custom-headers.html) for an Amplify app. * `custom_rule` - (Optional) Custom rewrite and redirect rules for an Amplify app. A `custom_rule` block is documented below. * `description` - (Optional) Description for an Amplify app. * `enable_auto_branch_creation` - (Optional) Enables automated branch creation for an Amplify app.