Skip to content

Commit

Permalink
Merge pull request #24913 from daftkid/cloudfront_response_headers_po…
Browse files Browse the repository at this point in the history
…licy_server_timing

r/aws_cloudfront_response_headers_policy added `server_timing_headers_config` attribute
  • Loading branch information
ewbankkit authored May 25, 2022
2 parents eb182a5 + 73cbaa2 commit aa7a442
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 3 deletions.
7 changes: 7 additions & 0 deletions .changelog/24913.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_cloudfront_response_headers_policy: Add `server_timing_headers_config` argument
```

```release-note:enhancement
datasource/aws_cloudfront_response_headers_policy: Add `server_timing_headers_config` attribute
```
81 changes: 78 additions & 3 deletions internal/service/cloudfront/response_headers_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func ResourceResponseHeadersPolicy() *schema.Resource {
},
},
},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config"},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config", "server_timing_headers_config"},
},
"custom_headers_config": {
Type: schema.TypeList,
Expand Down Expand Up @@ -134,7 +134,7 @@ func ResourceResponseHeadersPolicy() *schema.Resource {
},
},
},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config"},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config", "server_timing_headers_config"},
},
"etag": {
Type: schema.TypeString,
Expand Down Expand Up @@ -269,7 +269,26 @@ func ResourceResponseHeadersPolicy() *schema.Resource {
},
},
},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config"},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config", "server_timing_headers_config"},
},
"server_timing_headers_config": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Required: true,
},
"sampling_rate": {
Type: schema.TypeFloat,
Required: true,
ValidateFunc: validation.FloatBetween(0.0, 100.0),
},
},
},
AtLeastOneOf: []string{"cors_config", "custom_headers_config", "security_headers_config", "server_timing_headers_config"},
},
},
}
Expand Down Expand Up @@ -299,6 +318,10 @@ func resourceResponseHeadersPolicyCreate(d *schema.ResourceData, meta interface{
apiObject.SecurityHeadersConfig = expandResponseHeadersPolicySecurityHeadersConfig(v.([]interface{})[0].(map[string]interface{}))
}

if v, ok := d.GetOk("server_timing_headers_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
apiObject.ServerTimingHeadersConfig = expandResponseHeadersPolicyServerTimingHeadersConfig(v.([]interface{})[0].(map[string]interface{}))
}

input := &cloudfront.CreateResponseHeadersPolicyInput{
ResponseHeadersPolicyConfig: apiObject,
}
Expand Down Expand Up @@ -356,6 +379,14 @@ func resourceResponseHeadersPolicyRead(d *schema.ResourceData, meta interface{})
d.Set("security_headers_config", nil)
}

if apiObject.ServerTimingHeadersConfig != nil {
if err := d.Set("server_timing_headers_config", []interface{}{flattenResponseHeadersPolicyServerTimingHeadersConfig(apiObject.ServerTimingHeadersConfig)}); err != nil {
return fmt.Errorf("error setting server_timing_headers_config: %w", err)
}
} else {
d.Set("server_timing_headers_config", nil)
}

return nil
}

Expand Down Expand Up @@ -386,6 +417,10 @@ func resourceResponseHeadersPolicyUpdate(d *schema.ResourceData, meta interface{
apiObject.SecurityHeadersConfig = expandResponseHeadersPolicySecurityHeadersConfig(v.([]interface{})[0].(map[string]interface{}))
}

if v, ok := d.GetOk("server_timing_headers_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
apiObject.ServerTimingHeadersConfig = expandResponseHeadersPolicyServerTimingHeadersConfig(v.([]interface{})[0].(map[string]interface{}))
}

input := &cloudfront.UpdateResponseHeadersPolicyInput{
Id: aws.String(d.Id()),
IfMatch: aws.String(d.Get("etag").(string)),
Expand Down Expand Up @@ -1057,3 +1092,43 @@ func flattenResponseHeadersPolicyXSSProtection(apiObject *cloudfront.ResponseHea

return tfMap
}

//
// server_timing_headers_config:
//

func expandResponseHeadersPolicyServerTimingHeadersConfig(tfMap map[string]interface{}) *cloudfront.ResponseHeadersPolicyServerTimingHeadersConfig {
if tfMap == nil {
return nil
}

apiObject := &cloudfront.ResponseHeadersPolicyServerTimingHeadersConfig{}

if v, ok := tfMap["enabled"].(bool); ok {
apiObject.Enabled = aws.Bool(v)
}

if v, ok := tfMap["sampling_rate"].(float64); ok && v != 0 {
apiObject.SamplingRate = aws.Float64(v)
}

return apiObject
}

func flattenResponseHeadersPolicyServerTimingHeadersConfig(apiObject *cloudfront.ResponseHeadersPolicyServerTimingHeadersConfig) map[string]interface{} {
if apiObject == nil {
return nil
}

tfMap := map[string]interface{}{}

if v := apiObject.Enabled; v != nil {
tfMap["enabled"] = aws.BoolValue(v)
}

if v := apiObject.SamplingRate; v != nil {
tfMap["sampling_rate"] = aws.Float64Value(v)
}

return tfMap
}
23 changes: 23 additions & 0 deletions internal/service/cloudfront/response_headers_policy_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ func DataSourceResponseHeadersPolicy() *schema.Resource {
},
},
},
"server_timing_headers_config": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Computed: true,
},
"sampling_rate": {
Type: schema.TypeFloat,
Computed: true,
},
}},
},
},
}
}
Expand Down Expand Up @@ -324,5 +339,13 @@ func dataSourceResponseHeadersPolicyRead(d *schema.ResourceData, meta interface{
d.Set("security_headers_config", nil)
}

if apiObject.ServerTimingHeadersConfig != nil {
if err := d.Set("server_timing_headers_config", []interface{}{flattenResponseHeadersPolicyServerTimingHeadersConfig(apiObject.ServerTimingHeadersConfig)}); err != nil {
return fmt.Errorf("error setting server_timing_headers_config: %w", err)
}
} else {
d.Set("server_timing_headers_config", nil)
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ func TestAccCloudFrontResponseHeadersPolicyDataSource_basic(t *testing.T) {
resource.TestCheckResourceAttrPair(dataSource1Name, "security_headers_config.0.referrer_policy.#", resourceName, "security_headers_config.0.referrer_policy.#"),
resource.TestCheckResourceAttrPair(dataSource1Name, "security_headers_config.0.strict_transport_security.#", resourceName, "security_headers_config.0.strict_transport_security.#"),
resource.TestCheckResourceAttrPair(dataSource1Name, "security_headers_config.0.xss_protection.#", resourceName, "security_headers_config.0.xss_protection.#"),
resource.TestCheckResourceAttrPair(dataSource1Name, "server_timing_headers_config.#", resourceName, "server_timing_headers_config.#"),
resource.TestCheckResourceAttrPair(dataSource1Name, "server_timing_headers_config.0.enabled", resourceName, "server_timing_headers_config.0.enabled"),
resource.TestCheckResourceAttrPair(dataSource1Name, "server_timing_headers_config.0.sampling_rate", resourceName, "server_timing_headers_config.0.sampling_rate"),

resource.TestCheckResourceAttrPair(dataSource2Name, "comment", resourceName, "comment"),
resource.TestCheckResourceAttrPair(dataSource2Name, "cors_config.#", resourceName, "cors_config.#"),
Expand All @@ -74,6 +77,9 @@ func TestAccCloudFrontResponseHeadersPolicyDataSource_basic(t *testing.T) {
resource.TestCheckResourceAttrPair(dataSource2Name, "security_headers_config.0.referrer_policy.#", resourceName, "security_headers_config.0.referrer_policy.#"),
resource.TestCheckResourceAttrPair(dataSource2Name, "security_headers_config.0.strict_transport_security.#", resourceName, "security_headers_config.0.strict_transport_security.#"),
resource.TestCheckResourceAttrPair(dataSource2Name, "security_headers_config.0.xss_protection.#", resourceName, "security_headers_config.0.xss_protection.#"),
resource.TestCheckResourceAttrPair(dataSource2Name, "server_timing_headers_config.#", resourceName, "server_timing_headers_config.#"),
resource.TestCheckResourceAttrPair(dataSource2Name, "server_timing_headers_config.0.enabled", resourceName, "server_timing_headers_config.0.enabled"),
resource.TestCheckResourceAttrPair(dataSource2Name, "server_timing_headers_config.0.sampling_rate", resourceName, "server_timing_headers_config.0.sampling_rate"),
),
},
},
Expand Down Expand Up @@ -143,6 +149,11 @@ resource "aws_cloudfront_response_headers_policy" "test" {
preload = true
}
}
server_timing_headers_config {
enabled = true
sampling_rate = 10
}
}
`, rName)
}
83 changes: 83 additions & 0 deletions internal/service/cloudfront/response_headers_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func TestAccCloudFrontResponseHeadersPolicy_CorsConfig(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "etag"),
resource.TestCheckResourceAttr(resourceName, "name", rName1),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "0"),
),
},
{
Expand Down Expand Up @@ -84,6 +85,7 @@ func TestAccCloudFrontResponseHeadersPolicy_CorsConfig(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "etag"),
resource.TestCheckResourceAttr(resourceName, "name", rName2),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "0"),
),
},
},
Expand Down Expand Up @@ -121,6 +123,7 @@ func TestAccCloudFrontResponseHeadersPolicy_CustomHeadersConfig(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "etag"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "0"),
),
},
{
Expand Down Expand Up @@ -173,6 +176,7 @@ func TestAccCloudFrontResponseHeadersPolicy_SecurityHeadersConfig(t *testing.T)
resource.TestCheckResourceAttr(resourceName, "security_headers_config.0.strict_transport_security.0.override", "true"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.0.strict_transport_security.0.preload", "true"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.0.xss_protection.#", "0"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "0"),
),
},
{
Expand Down Expand Up @@ -204,6 +208,72 @@ func TestAccCloudFrontResponseHeadersPolicy_SecurityHeadersConfig(t *testing.T)
resource.TestCheckResourceAttr(resourceName, "security_headers_config.0.xss_protection.0.override", "true"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.0.xss_protection.0.protection", "true"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.0.xss_protection.0.report_uri", "https://example.com/"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "0"),
),
},
},
})
}

func TestAccCloudFrontResponseHeadersPolicy_ServerTimingHeadersConfig(t *testing.T) {
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_cloudfront_response_headers_policy.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(cloudfront.EndpointsID, t) },
ErrorCheck: acctest.ErrorCheck(t, cloudfront.EndpointsID),
ProviderFactories: acctest.ProviderFactories,
CheckDestroy: testAccCheckResponseHeadersPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccResponseHeadersPolicyServerTimingHeadersConfigConfig(rName, true, 10),
Check: resource.ComposeTestCheckFunc(
testAccCheckResponseHeadersPolicyExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "comment", ""),
resource.TestCheckResourceAttr(resourceName, "cors_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "custom_headers_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.#", "0"),
resource.TestCheckResourceAttrSet(resourceName, "etag"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.0.sampling_rate", "10"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{},
},
{
Config: testAccResponseHeadersPolicyServerTimingHeadersConfigConfig(rName, true, 90),
Check: resource.ComposeTestCheckFunc(
testAccCheckResponseHeadersPolicyExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "comment", ""),
resource.TestCheckResourceAttr(resourceName, "cors_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "custom_headers_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.#", "0"),
resource.TestCheckResourceAttrSet(resourceName, "etag"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.0.sampling_rate", "90"),
),
},
{
Config: testAccResponseHeadersPolicyServerTimingHeadersConfigConfig(rName, false, 0),
Check: resource.ComposeTestCheckFunc(
testAccCheckResponseHeadersPolicyExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "comment", ""),
resource.TestCheckResourceAttr(resourceName, "cors_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "custom_headers_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "security_headers_config.#", "0"),
resource.TestCheckResourceAttrSet(resourceName, "etag"),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.0.enabled", "false"),
resource.TestCheckResourceAttr(resourceName, "server_timing_headers_config.0.sampling_rate", "0"),
),
},
},
Expand Down Expand Up @@ -419,3 +489,16 @@ resource "aws_cloudfront_response_headers_policy" "test" {
}
`, rName)
}

func testAccResponseHeadersPolicyServerTimingHeadersConfigConfig(rName string, enabled bool, rate float64) string {
return fmt.Sprintf(`
resource "aws_cloudfront_response_headers_policy" "test" {
name = %[1]q
server_timing_headers_config {
enabled = %[2]t
sampling_rate = %[3]f
}
}
`, rName, enabled, rate)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ In addition to all arguments above, the following attributes are exported:
* `cors_config` - A configuration for a set of HTTP response headers that are used for Cross-Origin Resource Sharing (CORS). See [Cors Config](#cors-config) for more information.
* `custom_headers_config` - Object that contains an attribute `items` that contains a list of Custom Headers See [Custom Header](#custom-header) for more information.
* `security_headers_config` - A configuration for a set of security-related HTTP response headers. See [Security Headers Config](#security-headers-config) for more information.
* `server_timing_headers_config` - (Optional) A configuration for enabling the Server-Timing header in HTTP responses sent from CloudFront. See [Server Timing Headers Config](#server-timing-headers-config) for more information.

### Cors Config

Expand Down Expand Up @@ -91,3 +92,8 @@ In addition to all arguments above, the following attributes are exported:
* `override` - A Boolean value that determines whether CloudFront overrides the X-XSS-Protection HTTP response header received from the origin with the one specified in this response headers policy.
* `protection` - A Boolean value that determines the value of the X-XSS-Protection HTTP response header. When this setting is true, the value of the X-XSS-Protection header is 1. When this setting is false, the value of the X-XSS-Protection header is 0.
* `report_uri` - A Boolean value that determines whether CloudFront sets a reporting URI in the X-XSS-Protection header.

### Server Timing Headers Config

* `enabled` - A Boolean that determines whether CloudFront adds the `Server-Timing` header to HTTP responses that it sends in response to requests that match a cache behavior that's associated with this response headers policy.
* `sampling_rate` - A number 0–100 (inclusive) that specifies the percentage of responses that you want CloudFront to add the Server-Timing header to.
27 changes: 27 additions & 0 deletions website/docs/r/cloudfront_response_headers_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@ resource "aws_cloudfront_response_headers_policy" "example" {
}
```

The example below creates a CloudFront response headers policy with a custom headers config and server timing headers config.

```terraform
resource "aws_cloudfront_response_headers_policy" "example" {
name = "example-headers-policy"
custom_headers_config {
items {
header = "X-Permitted-Cross-Domain-Policies"
override = true
value = "none"
}
}
server_timing_headers_config {
enabled = true
sampling_rate = 50
}
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -73,6 +94,7 @@ The following arguments are supported:
* `cors_config` - (Optional) A configuration for a set of HTTP response headers that are used for Cross-Origin Resource Sharing (CORS). See [Cors Config](#cors-config) for more information.
* `custom_headers_config` - (Optional) Object that contains an attribute `items` that contains a list of custom headers. See [Custom Header](#custom-header) for more information.
* `security_headers_config` - (Optional) A configuration for a set of security-related HTTP response headers. See [Security Headers Config](#security-headers-config) for more information.
* `server_timing_headers_config` - (Optional) A configuration for enabling the Server-Timing header in HTTP responses sent from CloudFront. See [Server Timing Headers Config](#server-timing-headers-config) for more information.

### Cors Config

Expand Down Expand Up @@ -132,6 +154,11 @@ The following arguments are supported:
* `protection` - (Required) A Boolean value that determines the value of the `X-XSS-Protection` HTTP response header. When this setting is `true`, the value of the `X-XSS-Protection` header is `1`. When this setting is `false`, the value of the `X-XSS-Protection` header is `0`.
* `report_uri` - (Optional) A reporting URI, which CloudFront uses as the value of the report directive in the `X-XSS-Protection` header. You cannot specify a `report_uri` when `mode_block` is `true`.

### Server Timing Headers Config

* `enabled` - (Required) A Boolean that determines whether CloudFront adds the `Server-Timing` header to HTTP responses that it sends in response to requests that match a cache behavior that's associated with this response headers policy.
* `sampling_rate` - (Required) A number 0–100 (inclusive) that specifies the percentage of responses that you want CloudFront to add the Server-Timing header to. Valid range: Minimum value of 0.0. Maximum value of 100.0.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:
Expand Down

0 comments on commit aa7a442

Please sign in to comment.