diff --git a/docs/guides/image_optimizer_default_settings.md b/docs/guides/image_optimizer_default_settings.md
new file mode 100644
index 000000000..4dbd636d3
--- /dev/null
+++ b/docs/guides/image_optimizer_default_settings.md
@@ -0,0 +1,65 @@
+---
+page_title: image_optimizer_default_settings
+subcategory: "Guides"
+---
+
+## Image Optimizer Default Settings
+
+[Fastly Image Optimizer](https://docs.fastly.com/products/image-optimizer) (Fastly IO) is an [image optimization](https://www.fastly.com/learning/what-is-image-optimization) service that manipulates and transforms your images in real time and caches optimized versions of them.
+
+Fastly Image Optimizer supports a variety of image formats and applies specific settings to all images by default. These can be controlled with this API or the [web interface](https://docs.fastly.com/en/guides/about-fastly-image-optimizer#configuring-default-image-settings). Changes to other image settings, including most image transformations, require using query string parameters on individual requests.
+
+The [Image Optimizer default settings](https://developer.fastly.com/reference/api/services/image-optimizer-default-settings/) API allows customers to configure
+default settings for Image Optimizer requests, configuring the way images are optimized when not overridden by URL parameters on specific requests.
+
+The service must have the Image Optimizer product enabled using the Product Enablement API, UI, or Terraform block to use the `image_optimizer` block.
+
+## Example Usage
+
+Basic usage:
+
+```terraform
+resource "fastly_service_vcl" "demo" {
+ name = "demofastly"
+
+ domain {
+ name = "demo.notexample.com"
+ comment = "demo"
+ }
+
+ backend {
+ address = "127.0.0.1"
+ name = "localhost"
+ port = 80
+ }
+
+ product_enablement {
+ image_optimizer = true
+ }
+
+ image_optimizer_default_settings {
+ resize_filter = "lanczos3"
+ webp = false
+ webp_quality = 85
+ jpeg_type = "auto"
+ jpeg_quality = 85
+ upscale = false
+ allow_video = false
+ }
+
+ force_destroy = true
+}
+```
+
+All fields in `image_optimizer_default_settings` are optional.
+
+NOTE: When added, `image_optimizer_default_settings` will always set all default settings. This will replace any settings previously changed in the UI or API.
+
+## Delete
+
+Deleting the resource will reset all Image Optimizer default settings to their default values.
+
+If deleting the resource errors due to Image Optimizer no longer being enabled on the service, then this error will be ignored.
+
+When Image Optimizer is next re-enabled on a service, that service's Image Optimizer default settings will be reset - so a disabled service effectively already
+has deleted/default Image Optimizer default settings.
diff --git a/docs/resources/service_compute.md b/docs/resources/service_compute.md
index 2da76f696..a601edf40 100644
--- a/docs/resources/service_compute.md
+++ b/docs/resources/service_compute.md
@@ -84,6 +84,7 @@ $ terraform import fastly_service_compute.demo xxxxxxxxxxxxxxxxxxxx@2
- `comment` (String) Description field for the service. Default `Managed by Terraform`
- `dictionary` (Block Set) (see [below for nested schema](#nestedblock--dictionary))
- `force_destroy` (Boolean) Services that are active cannot be destroyed. In order to destroy the Service, set `force_destroy` to `true`. Default `false`
+- `image_optimizer_default_settings` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--image_optimizer_default_settings))
- `logging_bigquery` (Block Set) (see [below for nested schema](#nestedblock--logging_bigquery))
- `logging_blobstorage` (Block Set) (see [below for nested schema](#nestedblock--logging_blobstorage))
- `logging_cloudfiles` (Block Set) (see [below for nested schema](#nestedblock--logging_cloudfiles))
@@ -187,6 +188,29 @@ Read-Only:
- `dictionary_id` (String) The ID of the dictionary
+
+### Nested Schema for `image_optimizer_default_settings`
+
+Optional:
+
+- `allow_video` (Boolean) Enables GIF to MP4 transformations on this service.
+- `jpeg_quality` (Number) The default quality to use with JPEG output. This can be overridden with the "quality" parameter on specific image optimizer requests.
+- `jpeg_type` (String) The default type of JPEG output to use. This can be overridden with "format=bjpeg" and "format=pjpeg" on specific image optimizer requests. Valid values are `auto`, `baseline` and `progressive`.
+ - auto: Match the input JPEG type, or baseline if transforming from a non-JPEG input.
+ - baseline: Output baseline JPEG images
+ - progressive: Output progressive JPEG images
+- `name` (String) Used by the provider to identify modified settings. Changing this value will force the entire block to be deleted, then recreated.
+- `resize_filter` (String) The type of filter to use while resizing an image. Valid values are `lanczos3`, `lanczos2`, `bicubic`, `bilinear` and `nearest`.
+ - lanczos3: A Lanczos filter with a kernel size of 3. Lanczos filters can detect edges and linear features within an image, providing the best possible reconstruction.
+ - lanczos2: A Lanczos filter with a kernel size of 2.
+ - bicubic: A filter using an average of a 4x4 environment of pixels, weighing the innermost pixels higher.
+ - bilinear: A filter using an average of a 2x2 environment of pixels.
+ - nearest: A filter using the value of nearby translated pixel values. Preserves hard edges.
+- `upscale` (Boolean) Whether or not we should allow output images to render at sizes larger than input.
+- `webp` (Boolean) Controls whether or not to default to WebP output when the client supports it. This is equivalent to adding "auto=webp" to all image optimizer requests.
+- `webp_quality` (Number) The default quality to use with WebP output. This can be overridden with the second option in the "quality" URL parameter on specific image optimizer requests.
+
+
### Nested Schema for `logging_bigquery`
diff --git a/docs/resources/service_vcl.md b/docs/resources/service_vcl.md
index 85f92e78f..a91ed3a42 100644
--- a/docs/resources/service_vcl.md
+++ b/docs/resources/service_vcl.md
@@ -260,6 +260,7 @@ $ terraform import fastly_service_vcl.demo xxxxxxxxxxxxxxxxxxxx@2
- `header` (Block Set) (see [below for nested schema](#nestedblock--header))
- `healthcheck` (Block Set) (see [below for nested schema](#nestedblock--healthcheck))
- `http3` (Boolean) Enables support for the HTTP/3 (QUIC) protocol
+- `image_optimizer_default_settings` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--image_optimizer_default_settings))
- `logging_bigquery` (Block Set) (see [below for nested schema](#nestedblock--logging_bigquery))
- `logging_blobstorage` (Block Set) (see [below for nested schema](#nestedblock--logging_blobstorage))
- `logging_cloudfiles` (Block Set) (see [below for nested schema](#nestedblock--logging_cloudfiles))
@@ -510,6 +511,29 @@ Optional:
- `window` (Number) The number of most recent Healthcheck queries to keep for this Healthcheck. Default `5`
+
+### Nested Schema for `image_optimizer_default_settings`
+
+Optional:
+
+- `allow_video` (Boolean) Enables GIF to MP4 transformations on this service.
+- `jpeg_quality` (Number) The default quality to use with JPEG output. This can be overridden with the "quality" parameter on specific image optimizer requests.
+- `jpeg_type` (String) The default type of JPEG output to use. This can be overridden with "format=bjpeg" and "format=pjpeg" on specific image optimizer requests. Valid values are `auto`, `baseline` and `progressive`.
+ - auto: Match the input JPEG type, or baseline if transforming from a non-JPEG input.
+ - baseline: Output baseline JPEG images
+ - progressive: Output progressive JPEG images
+- `name` (String) Used by the provider to identify modified settings. Changing this value will force the entire block to be deleted, then recreated.
+- `resize_filter` (String) The type of filter to use while resizing an image. Valid values are `lanczos3`, `lanczos2`, `bicubic`, `bilinear` and `nearest`.
+ - lanczos3: A Lanczos filter with a kernel size of 3. Lanczos filters can detect edges and linear features within an image, providing the best possible reconstruction.
+ - lanczos2: A Lanczos filter with a kernel size of 2.
+ - bicubic: A filter using an average of a 4x4 environment of pixels, weighing the innermost pixels higher.
+ - bilinear: A filter using an average of a 2x2 environment of pixels.
+ - nearest: A filter using the value of nearby translated pixel values. Preserves hard edges.
+- `upscale` (Boolean) Whether or not we should allow output images to render at sizes larger than input.
+- `webp` (Boolean) Controls whether or not to default to WebP output when the client supports it. This is equivalent to adding "auto=webp" to all image optimizer requests.
+- `webp_quality` (Number) The default quality to use with WebP output. This can be overridden with the second option in the "quality" URL parameter on specific image optimizer requests.
+
+
### Nested Schema for `logging_bigquery`
diff --git a/fastly/block_fastly_service_image_optimizer_default_settings.go b/fastly/block_fastly_service_image_optimizer_default_settings.go
new file mode 100644
index 000000000..246712411
--- /dev/null
+++ b/fastly/block_fastly_service_image_optimizer_default_settings.go
@@ -0,0 +1,337 @@
+package fastly
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "net/http"
+ "strings"
+
+ gofastly "github.com/fastly/go-fastly/v9/fastly"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+// ProductEnablementServiceAttributeHandler provides a base implementation for ServiceAttributeDefinition.
+type ImageOptimizerDefaultSettingsServiceAttributeHandler struct {
+ *DefaultServiceAttributeHandler
+}
+
+// NewServiceProductEnablement returns a new resource.
+func NewServiceImageOptimizerDefaultSettings(sa ServiceMetadata) ServiceAttributeDefinition {
+ return ToServiceAttributeDefinition(&ImageOptimizerDefaultSettingsServiceAttributeHandler{
+ &DefaultServiceAttributeHandler{
+ key: "image_optimizer_default_settings",
+ serviceMetadata: sa,
+ },
+ })
+}
+
+// Key returns the resource key.
+func (h *ImageOptimizerDefaultSettingsServiceAttributeHandler) Key() string {
+ return h.key
+}
+
+// GetSchema returns the resource schema.
+func (h *ImageOptimizerDefaultSettingsServiceAttributeHandler) GetSchema() *schema.Schema {
+ attributes := map[string]*schema.Schema{
+ "allow_video": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "Enables GIF to MP4 transformations on this service.",
+ },
+ "jpeg_quality": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: 85,
+ Description: "The default quality to use with JPEG output. This can be overridden with the \"quality\" parameter on specific image optimizer requests.",
+ ValidateFunc: validation.IntBetween(1, 100),
+ },
+ "jpeg_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "auto",
+ Description: "The default type of JPEG output to use. This can be overridden with \"format=bjpeg\" and \"format=pjpeg\" on specific image optimizer requests. Valid values are `auto`, `baseline` and `progressive`." + `
+ - auto: Match the input JPEG type, or baseline if transforming from a non-JPEG input.
+ - baseline: Output baseline JPEG images
+ - progressive: Output progressive JPEG images`,
+ ValidateFunc: validation.StringInSlice([]string{"auto", "baseline", "progressive"}, false),
+ },
+ "name": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "image_optimizer_default_settings",
+ Description: "Used by the provider to identify modified settings. Changing this value will force the entire block to be deleted, then recreated.",
+ },
+ "resize_filter": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "lanczos3",
+ Description: "The type of filter to use while resizing an image. Valid values are `lanczos3`, `lanczos2`, `bicubic`, `bilinear` and `nearest`." + `
+ - lanczos3: A Lanczos filter with a kernel size of 3. Lanczos filters can detect edges and linear features within an image, providing the best possible reconstruction.
+ - lanczos2: A Lanczos filter with a kernel size of 2.
+ - bicubic: A filter using an average of a 4x4 environment of pixels, weighing the innermost pixels higher.
+ - bilinear: A filter using an average of a 2x2 environment of pixels.
+ - nearest: A filter using the value of nearby translated pixel values. Preserves hard edges.`,
+ ValidateFunc: validation.StringInSlice([]string{"lanczos3", "lanczos2", "bicubic", "bilinear", "nearest"}, false),
+ },
+ "upscale": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "Whether or not we should allow output images to render at sizes larger than input.",
+ },
+ "webp": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "Controls whether or not to default to WebP output when the client supports it. This is equivalent to adding \"auto=webp\" to all image optimizer requests.",
+ },
+ "webp_quality": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: 85,
+ Description: "The default quality to use with WebP output. This can be overridden with the second option in the \"quality\" URL parameter on specific image optimizer requests.",
+ ValidateFunc: validation.IntBetween(1, 100),
+ },
+ }
+
+ // NOTE: MaxItems: 1 (to enforce only one image_optimizer_default_settings per service).
+ // lintignore:S018
+ return &schema.Schema{
+ Type: schema.TypeSet,
+ Optional: true,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: attributes,
+ },
+ }
+}
+
+// Create creates the resource.
+//
+// If a user has Image Optimizer enabled, they will always have some default settings. So, creation and updating are synonymous.
+func (h *ImageOptimizerDefaultSettingsServiceAttributeHandler) Create(c context.Context, d *schema.ResourceData, resource map[string]any, serviceVersion int, conn *gofastly.Client) error {
+ return h.Update(c, d, resource, resource, serviceVersion, conn)
+}
+
+// Read refreshes the resource.
+func (h *ImageOptimizerDefaultSettingsServiceAttributeHandler) Read(_ context.Context, d *schema.ResourceData, resource map[string]any, serviceVersion int, conn *gofastly.Client) error {
+ localState := d.Get(h.Key()).(*schema.Set).List()
+
+ if len(localState) > 0 || d.Get("imported").(bool) || d.Get("force_refresh").(bool) {
+ serviceID := d.Id()
+
+ log.Printf("[DEBUG] Refreshing Image Optimizer default settings for (%s)", serviceID)
+
+ remoteState, err := conn.GetImageOptimizerDefaultSettings(&gofastly.GetImageOptimizerDefaultSettingsInput{
+ ServiceID: serviceID,
+ ServiceVersion: serviceVersion,
+ })
+ if err != nil {
+ return err
+ }
+ // Handle the case where the service has no Image Optimizer default settings configured (for example, if it has never had Image
+ // Optimizer enabled.)
+ if remoteState == nil {
+ return nil
+ }
+
+ result := map[string]any{
+ "allow_video": remoteState.AllowVideo,
+ "jpeg_type": remoteState.JpegType,
+ "jpeg_quality": remoteState.JpegQuality,
+ "resize_filter": remoteState.ResizeFilter,
+ "upscale": remoteState.Upscale,
+ "webp": remoteState.Webp,
+ "webp_quality": remoteState.WebpQuality,
+ }
+
+ // The `name` attribute in this resource is used by default as a key for calculating diffs.
+ // This is handled as part of the internal abstraction logic.
+ //
+ // See the call ToServiceAttributeDefinition() inside NewServiceProductEnablement()
+ // See also the diffing logic:
+ // - https://github.com/fastly/terraform-provider-fastly/blob/4b9506fba1fd17e2bf760f447cbd8c394bb1e153/fastly/service_crud_attribute_definition.go#L94
+ // - https://github.com/fastly/terraform-provider-fastly/blob/4b9506fba1fd17e2bf760f447cbd8c394bb1e153/fastly/diff.go#L108-L117
+ //
+ // Because the name can be set by a user, we first check if the resource
+ // exists in their state, and if so we'll use the value assigned there. If
+ // they've not explicitly defined a name in their config, then the default
+ // value will be returned.
+ if len(localState) > 0 {
+ name := localState[0].(map[string]any)["name"].(string)
+ result["name"] = name
+ }
+
+ results := []map[string]any{result}
+
+ if err := d.Set(h.Key(), results); err != nil {
+ log.Printf("[WARN] Error setting Image Optimizer default setting for (%s): %s", d.Id(), err)
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Update updates the resource.
+func (h *ImageOptimizerDefaultSettingsServiceAttributeHandler) Update(_ context.Context, d *schema.ResourceData, _, modified map[string]any, serviceVersion int, conn *gofastly.Client) error {
+ serviceID := d.Id()
+ log.Println("[DEBUG] Update Image Optimizer default settings")
+
+ if len(modified) == 0 {
+ return nil
+ }
+
+ apiInput := gofastly.UpdateImageOptimizerDefaultSettingsInput{
+ ServiceID: serviceID,
+ ServiceVersion: serviceVersion,
+ }
+
+ for key, value := range modified {
+ switch key {
+ case "resize_filter":
+ var resizeFilter gofastly.ImageOptimizerResizeFilter
+ switch value.(string) {
+ case "lanczos3":
+ resizeFilter = gofastly.ImageOptimizerLanczos3
+ case "lanczos2":
+ resizeFilter = gofastly.ImageOptimizerLanczos2
+ case "bicubic":
+ resizeFilter = gofastly.ImageOptimizerBicubic
+ case "bilinear":
+ resizeFilter = gofastly.ImageOptimizerBilinear
+ case "nearest":
+ resizeFilter = gofastly.ImageOptimizerNearest
+ default:
+ return fmt.Errorf("got unexpected resize_filter: %v", value)
+ }
+ apiInput.ResizeFilter = &resizeFilter
+ case "webp":
+ apiInput.Webp = gofastly.ToPointer(value.(bool))
+ case "webp_quality":
+ apiInput.WebpQuality = gofastly.ToPointer(value.(int))
+ case "jpeg_type":
+ var jpegType gofastly.ImageOptimizerJpegType
+ switch value.(string) {
+ case "auto":
+ jpegType = gofastly.ImageOptimizerAuto
+ case "baseline":
+ jpegType = gofastly.ImageOptimizerBaseline
+ case "progressive":
+ jpegType = gofastly.ImageOptimizerProgressive
+ default:
+ return fmt.Errorf("got unexpected jpeg_type: %v", value)
+ }
+ apiInput.JpegType = &jpegType
+ case "jpeg_quality":
+ apiInput.JpegQuality = gofastly.ToPointer(value.(int))
+ case "upscale":
+ apiInput.Upscale = gofastly.ToPointer(value.(bool))
+ case "allow_video":
+ apiInput.AllowVideo = gofastly.ToPointer(value.(bool))
+ case "name":
+ continue
+ default:
+ return fmt.Errorf("got unexpected image_optimizer_default_settings key: %v", key)
+ }
+ }
+
+ log.Printf("[DEBUG] Calling Image Optimizer default settings update API with parameters: %+v", apiInput)
+
+ if _, err := conn.UpdateImageOptimizerDefaultSettings(&apiInput); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Delete deletes the resource.
+//
+// This resets Image Optimizer default settings to their defaults, to make it possible to easily undo any effect this block had.
+//
+// This assumes the service wasn't modified with the UI or any other non-terraform method. Given terraform's regular mode of operating within the world is to
+// assume its in control of everything, I think that's quite a reasonable assumption.
+func (h *ImageOptimizerDefaultSettingsServiceAttributeHandler) Delete(_ context.Context, d *schema.ResourceData, resource map[string]any, serviceVersion int, conn *gofastly.Client) error {
+ serviceID := d.Id()
+ log.Println("[DEBUG] Update Image Optimizer default settings")
+
+ apiInput := gofastly.UpdateImageOptimizerDefaultSettingsInput{
+ ServiceID: serviceID,
+ ServiceVersion: serviceVersion,
+ }
+
+ for key, value := range resource {
+ switch key {
+ case "resize_filter":
+ var resizeFilter gofastly.ImageOptimizerResizeFilter
+ switch value.(string) {
+ case "lanczos3":
+ resizeFilter = gofastly.ImageOptimizerLanczos3
+ case "lanczos2":
+ resizeFilter = gofastly.ImageOptimizerLanczos2
+ case "bicubic":
+ resizeFilter = gofastly.ImageOptimizerBicubic
+ case "bilinear":
+ resizeFilter = gofastly.ImageOptimizerBilinear
+ case "nearest":
+ resizeFilter = gofastly.ImageOptimizerNearest
+ default:
+ return fmt.Errorf("got unexpected resize_filter: %v", value)
+ }
+ apiInput.ResizeFilter = &resizeFilter
+ case "webp":
+ apiInput.Webp = gofastly.ToPointer(value.(bool))
+ case "webp_quality":
+ apiInput.WebpQuality = gofastly.ToPointer(value.(int))
+ case "jpeg_type":
+ var jpegType gofastly.ImageOptimizerJpegType
+ switch value.(string) {
+ case "auto":
+ jpegType = gofastly.ImageOptimizerAuto
+ case "baseline":
+ jpegType = gofastly.ImageOptimizerBaseline
+ case "progressive":
+ jpegType = gofastly.ImageOptimizerProgressive
+ default:
+ return fmt.Errorf("got unexpected jpeg_type: %v", value)
+ }
+ apiInput.JpegType = &jpegType
+ case "jpeg_quality":
+ apiInput.JpegQuality = gofastly.ToPointer(value.(int))
+ case "upscale":
+ apiInput.Upscale = gofastly.ToPointer(value.(bool))
+ case "allow_video":
+ apiInput.AllowVideo = gofastly.ToPointer(value.(bool))
+ case "name":
+ continue
+ default:
+ return fmt.Errorf("got unexpected image_optimizer_default_settings key: %v", key)
+ }
+ }
+
+ log.Printf("[DEBUG] Calling Image Optimizer default settings update API with parameters: %+v", apiInput)
+
+ if _, err := conn.UpdateImageOptimizerDefaultSettings(&apiInput); err != nil {
+ // inspect the error type for a title that has a message indicating the user cannot call the API because they do not have Image Optimizer
+ // enabled. For these users we want to skip the error so that we can allow them to clean up their Terraform state. (also, because the Image Optimizer
+ // default settings for services with Image Optimizer are effectively cleared by disabling Image Optimizer.)
+ if he, ok := err.(*gofastly.HTTPError); ok {
+ if he.StatusCode == http.StatusBadRequest {
+ for _, e := range he.Errors {
+ if strings.Contains(e.Detail, "Image Optimizer is not enabled on this service") {
+ log.Printf("[DEBUG] Ignoring error %v, as a service without Image Optimizer enabled already effectively has no default settings.", e.Detail)
+ return nil
+ }
+ }
+ }
+ }
+
+ return err
+
+ }
+
+ return nil
+}
diff --git a/fastly/block_fastly_service_image_optimizer_default_settings_test.go b/fastly/block_fastly_service_image_optimizer_default_settings_test.go
new file mode 100644
index 000000000..c181f2309
--- /dev/null
+++ b/fastly/block_fastly_service_image_optimizer_default_settings_test.go
@@ -0,0 +1,137 @@
+package fastly
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ gofastly "github.com/fastly/go-fastly/v9/fastly"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+)
+
+func TestAccFastlyServiceImageOptimizerDefaultSettings_basic(t *testing.T) {
+ var service gofastly.ServiceDetail
+ serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
+ domainName := fmt.Sprintf("fastly-test.tf-%s.com", acctest.RandString(10))
+ backendName := fmt.Sprintf("backend-tf-%s", acctest.RandString(10))
+ backendAddress := "httpbin.org"
+
+ block1 := `
+ resize_filter = "lanczos2"
+ webp = true
+ webp_quality = 100
+ jpeg_type = "progressive"
+ jpeg_quality = 100
+ upscale = true
+ allow_video = false
+ `
+
+ default_settings1 := gofastly.ImageOptimizerDefaultSettings{
+ ResizeFilter: "lanczos2",
+ Webp: true,
+ WebpQuality: 100,
+ JpegType: "progressive",
+ JpegQuality: 100,
+ Upscale: true,
+ AllowVideo: false,
+ }
+
+ block2 := `
+ resize_filter = "bicubic"
+ webp = false
+ webp_quality = 30
+ jpeg_type = "baseline"
+ jpeg_quality = 20
+ upscale = true
+ allow_video = true
+ `
+
+ def_settings2 := gofastly.ImageOptimizerDefaultSettings{
+ ResizeFilter: "bicubic",
+ Webp: false,
+ WebpQuality: 30,
+ JpegType: "baseline",
+ JpegQuality: 20,
+ Upscale: true,
+ AllowVideo: true,
+ }
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ ProviderFactories: testAccProviders,
+ CheckDestroy: testAccCheckServiceVCLDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccImageOptimizerDefaultSettingsVCLConfig(serviceName, domainName, backendAddress, backendName, block1),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckServiceExists("fastly_service_vcl.foo", &service),
+ resource.TestCheckResourceAttr("fastly_service_vcl.foo", "name", serviceName),
+ resource.TestCheckResourceAttr("fastly_service_vcl.foo", "image_optimizer_default_settings.#", "1"),
+ testAccCheckFastlyServiceImageOptimizerDefaultSettingsAttributes(&service, &default_settings1),
+ ),
+ },
+
+ {
+ Config: testAccImageOptimizerDefaultSettingsVCLConfig(serviceName, domainName, backendAddress, backendName, block2),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckServiceExists("fastly_service_vcl.foo", &service),
+ resource.TestCheckResourceAttr("fastly_service_vcl.foo", "name", serviceName),
+ resource.TestCheckResourceAttr("fastly_service_vcl.foo", "image_optimizer_default_settings.#", "1"),
+ testAccCheckFastlyServiceImageOptimizerDefaultSettingsAttributes(&service, &def_settings2),
+ ),
+ },
+ },
+ })
+}
+
+func testAccCheckFastlyServiceImageOptimizerDefaultSettingsAttributes(service *gofastly.ServiceDetail, want *gofastly.ImageOptimizerDefaultSettings) resource.TestCheckFunc {
+ return func(_ *terraform.State) error {
+ conn := testAccProvider.Meta().(*APIClient).conn
+ have, err := conn.GetImageOptimizerDefaultSettings(&gofastly.GetImageOptimizerDefaultSettingsInput{
+ ServiceID: gofastly.ToValue(service.ServiceID),
+ ServiceVersion: gofastly.ToValue(service.ActiveVersion.Number),
+ })
+ if err != nil {
+ return fmt.Errorf("error looking up Image Optimizer default settings for (%s), version (%v): %s", gofastly.ToValue(service.Name), gofastly.ToValue(service.ActiveVersion.Number), err)
+ }
+
+ if !reflect.DeepEqual(want, have) {
+ return fmt.Errorf("bad Image Optimizer default settings, expected (%#v), got (%#v)", want, have)
+ }
+
+ return nil
+ }
+}
+
+func testAccImageOptimizerDefaultSettingsVCLConfig(serviceName, domainName, backendAddress, backendName, image_optimizer_settings string) string {
+ return fmt.Sprintf(`
+ resource "fastly_service_vcl" "foo" {
+ name = "%s"
+
+ domain {
+ name = "%s"
+ comment = "demo"
+ }
+
+ backend {
+ address = "%s"
+ name = "%s"
+ port = 443
+ shield = "amsterdam-nl"
+ }
+
+ image_optimizer_default_settings {
+ %s
+ }
+
+ product_enablement {
+ image_optimizer = true
+ }
+
+ force_destroy = true
+ }`, serviceName, domainName, backendAddress, backendName, image_optimizer_settings)
+}
diff --git a/fastly/resource_fastly_service_compute.go b/fastly/resource_fastly_service_compute.go
index 309fc9b52..5d6d16ac7 100644
--- a/fastly/resource_fastly_service_compute.go
+++ b/fastly/resource_fastly_service_compute.go
@@ -17,6 +17,7 @@ var computeService = &BaseServiceDefinition{
NewServiceDomain(computeAttributes),
NewServiceBackend(computeAttributes),
NewServiceProductEnablement(computeAttributes),
+ NewServiceImageOptimizerDefaultSettings(vclAttributes),
NewServiceLoggingS3(computeAttributes),
NewServiceLoggingPaperTrail(computeAttributes),
NewServiceLoggingSumologic(computeAttributes),
diff --git a/fastly/resource_fastly_service_vcl.go b/fastly/resource_fastly_service_vcl.go
index 5419b20fb..3e4d7262e 100644
--- a/fastly/resource_fastly_service_vcl.go
+++ b/fastly/resource_fastly_service_vcl.go
@@ -20,6 +20,7 @@ var vclService = &BaseServiceDefinition{
NewServiceHealthCheck(vclAttributes),
NewServiceBackend(vclAttributes),
NewServiceProductEnablement(vclAttributes),
+ NewServiceImageOptimizerDefaultSettings(vclAttributes),
NewServiceDirector(vclAttributes),
NewServiceHeader(vclAttributes),
NewServiceGzip(vclAttributes),
diff --git a/go.mod b/go.mod
index 6bb459000..22505edf5 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.20
require (
github.com/bflad/tfproviderlint v0.29.0
- github.com/fastly/go-fastly/v9 v9.3.1
+ github.com/fastly/go-fastly/v9 v9.4.0
github.com/google/go-cmp v0.6.0
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/terraform-plugin-docs v0.19.2
diff --git a/go.sum b/go.sum
index 54a36709b..27f451099 100644
--- a/go.sum
+++ b/go.sum
@@ -36,8 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
-github.com/fastly/go-fastly/v9 v9.3.1 h1:2UZUe8Kkz60aPrkDV82dpHkEqsT/J2qxOZxFhWEJJ2k=
-github.com/fastly/go-fastly/v9 v9.3.1/go.mod h1:5w2jgJBZqQEebOwM/rRg7wutAcpDTziiMYWb/6qdM7U=
+github.com/fastly/go-fastly/v9 v9.4.0 h1:yUuj1Wy2kpK0sdB+OAxXAY54qhH8GerM7BeSngKfNRY=
+github.com/fastly/go-fastly/v9 v9.4.0/go.mod h1:5w2jgJBZqQEebOwM/rRg7wutAcpDTziiMYWb/6qdM7U=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
diff --git a/templates/guides/image_optimizer_default_settings.md b/templates/guides/image_optimizer_default_settings.md
new file mode 100644
index 000000000..4dbd636d3
--- /dev/null
+++ b/templates/guides/image_optimizer_default_settings.md
@@ -0,0 +1,65 @@
+---
+page_title: image_optimizer_default_settings
+subcategory: "Guides"
+---
+
+## Image Optimizer Default Settings
+
+[Fastly Image Optimizer](https://docs.fastly.com/products/image-optimizer) (Fastly IO) is an [image optimization](https://www.fastly.com/learning/what-is-image-optimization) service that manipulates and transforms your images in real time and caches optimized versions of them.
+
+Fastly Image Optimizer supports a variety of image formats and applies specific settings to all images by default. These can be controlled with this API or the [web interface](https://docs.fastly.com/en/guides/about-fastly-image-optimizer#configuring-default-image-settings). Changes to other image settings, including most image transformations, require using query string parameters on individual requests.
+
+The [Image Optimizer default settings](https://developer.fastly.com/reference/api/services/image-optimizer-default-settings/) API allows customers to configure
+default settings for Image Optimizer requests, configuring the way images are optimized when not overridden by URL parameters on specific requests.
+
+The service must have the Image Optimizer product enabled using the Product Enablement API, UI, or Terraform block to use the `image_optimizer` block.
+
+## Example Usage
+
+Basic usage:
+
+```terraform
+resource "fastly_service_vcl" "demo" {
+ name = "demofastly"
+
+ domain {
+ name = "demo.notexample.com"
+ comment = "demo"
+ }
+
+ backend {
+ address = "127.0.0.1"
+ name = "localhost"
+ port = 80
+ }
+
+ product_enablement {
+ image_optimizer = true
+ }
+
+ image_optimizer_default_settings {
+ resize_filter = "lanczos3"
+ webp = false
+ webp_quality = 85
+ jpeg_type = "auto"
+ jpeg_quality = 85
+ upscale = false
+ allow_video = false
+ }
+
+ force_destroy = true
+}
+```
+
+All fields in `image_optimizer_default_settings` are optional.
+
+NOTE: When added, `image_optimizer_default_settings` will always set all default settings. This will replace any settings previously changed in the UI or API.
+
+## Delete
+
+Deleting the resource will reset all Image Optimizer default settings to their default values.
+
+If deleting the resource errors due to Image Optimizer no longer being enabled on the service, then this error will be ignored.
+
+When Image Optimizer is next re-enabled on a service, that service's Image Optimizer default settings will be reset - so a disabled service effectively already
+has deleted/default Image Optimizer default settings.
diff --git a/vendor/github.com/fastly/go-fastly/v9/fastly/client.go b/vendor/github.com/fastly/go-fastly/v9/fastly/client.go
index 64a7df318..d14475557 100644
--- a/vendor/github.com/fastly/go-fastly/v9/fastly/client.go
+++ b/vendor/github.com/fastly/go-fastly/v9/fastly/client.go
@@ -58,7 +58,7 @@ const JSONMimeType = "application/json"
var ProjectURL = "github.com/fastly/go-fastly"
// ProjectVersion is the version of this library.
-var ProjectVersion = "9.3.1"
+var ProjectVersion = "9.4.0"
// UserAgent is the user agent for this particular client.
var UserAgent = fmt.Sprintf("FastlyGo/%s (+%s; %s)",
diff --git a/vendor/github.com/fastly/go-fastly/v9/fastly/errors.go b/vendor/github.com/fastly/go-fastly/v9/fastly/errors.go
index 06e1a3761..c622f9380 100644
--- a/vendor/github.com/fastly/go-fastly/v9/fastly/errors.go
+++ b/vendor/github.com/fastly/go-fastly/v9/fastly/errors.go
@@ -332,6 +332,10 @@ var ErrMissingCertificateMTLS = NewFieldError("Certificate, MutualAuthentication
// requires a "IntegrationID" key, but one was not set.
var ErrMissingIntegrationID = NewFieldError("IntegrationID")
+// ErrMissingImageOptimizerSettings is an error that is returned when an input struct
+// requires one of the optional Image Optimizer default settings, but none are set.
+var ErrMissingImageOptimizerDefaultSetting = NewFieldError("ResizeFilter, Webp, WebpQuality, JpegType, JpegQuality, Upscale, AllowVideo").Message("at least one of the available optional fields is required")
+
// Ensure HTTPError is, in fact, an error.
var _ error = (*HTTPError)(nil)
diff --git a/vendor/github.com/fastly/go-fastly/v9/fastly/helpers.go b/vendor/github.com/fastly/go-fastly/v9/fastly/helpers.go
index 966cf4c28..3b5bb684c 100644
--- a/vendor/github.com/fastly/go-fastly/v9/fastly/helpers.go
+++ b/vendor/github.com/fastly/go-fastly/v9/fastly/helpers.go
@@ -2,7 +2,7 @@ package fastly
// MultiConstraint is a generic constraint for ToPointer/ToValue.
type MultiConstraint interface {
- ~string | ~int | int32 | ~int64 | uint | uint8 | uint32 | uint64 | float64 | ~bool
+ []string | ~string | ~int | int32 | ~int64 | uint | uint8 | uint32 | uint64 | float64 | ~bool
}
// ToPointer converts T to *T.
diff --git a/vendor/github.com/fastly/go-fastly/v9/fastly/image_optimizer_default_settings.go b/vendor/github.com/fastly/go-fastly/v9/fastly/image_optimizer_default_settings.go
new file mode 100644
index 000000000..0e352547c
--- /dev/null
+++ b/vendor/github.com/fastly/go-fastly/v9/fastly/image_optimizer_default_settings.go
@@ -0,0 +1,187 @@
+package fastly
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+// ImageOptimizerResizeFilter is a base for the different ImageOptimizerResizeFilter variants.
+type ImageOptimizerResizeFilter int64
+
+func (r ImageOptimizerResizeFilter) String() string {
+ switch r {
+ case ImageOptimizerLanczos3:
+ return "lanczos3"
+ case ImageOptimizerLanczos2:
+ return "lanczos2"
+ case ImageOptimizerBicubic:
+ return "bicubic"
+ case ImageOptimizerBilinear:
+ return "bilinear"
+ case ImageOptimizerNearest:
+ return "nearest"
+ }
+ return "lanczos3" // default
+}
+
+func (r ImageOptimizerResizeFilter) MarshalJSON() ([]byte, error) {
+ return json.Marshal(r.String())
+}
+
+const (
+ // A Lanczos filter with a kernel size of 3. Lanczos filters can detect edges and linear features within an image, providing the best possible reconstruction.
+ ImageOptimizerLanczos3 ImageOptimizerResizeFilter = iota
+ // A Lanczos filter with a kernel size of 2.
+ ImageOptimizerLanczos2
+ // A filter using an average of a 4x4 environment of pixels, weighing the innermost pixels higher.
+ ImageOptimizerBicubic
+ // A filter using an average of a 2x2 environment of pixels.
+ ImageOptimizerBilinear
+ // A filter using the value of nearby translated pixel values. Preserves hard edges.
+ ImageOptimizerNearest
+)
+
+// ImageOptimizerJpegType is a base for different ImageOptimizerJpegType variants
+type ImageOptimizerJpegType int64
+
+func (r ImageOptimizerJpegType) String() string {
+ switch r {
+ case ImageOptimizerAuto:
+ return "auto"
+ case ImageOptimizerBaseline:
+ return "baseline"
+ case ImageOptimizerProgressive:
+ return "progressive"
+ }
+ return "auto" // default
+}
+
+func (r ImageOptimizerJpegType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(r.String())
+}
+
+const (
+ // Match the input JPEG type, or baseline if transforming from a non-JPEG input.
+ ImageOptimizerAuto ImageOptimizerJpegType = iota
+ // Output baseline JPEG images
+ ImageOptimizerBaseline
+ // Output progressive JPEG images
+ ImageOptimizerProgressive
+)
+
+// ImageOptimizerDefaultSettings represents the returned Image Optimizer default settings for a given service.
+type ImageOptimizerDefaultSettings struct {
+ // The type of filter to use while resizing an image.
+ ResizeFilter string `json:"resize_filter"`
+ // Controls whether or not to default to WebP output when the client supports it. This is equivalent to adding "auto=webp" to all image optimizer requests.
+ Webp bool `json:"webp"`
+ // The default quality to use with WebP output. This can be overridden with the second option in the "quality" URL parameter on specific image optimizer requests.
+ WebpQuality int `json:"webp_quality"`
+ // The default type of JPEG output to use. This can be overridden with "format=bjpeg" and "format=pjpeg" on specific image optimizer requests.
+ JpegType string `json:"jpeg_type"`
+ // The default quality to use with JPEG output. This can be overridden with the "quality" parameter on specific image optimizer requests.
+ JpegQuality int `json:"jpeg_quality"`
+ // Whether or not we should allow output images to render at sizes larger than input.
+ Upscale bool `json:"upscale"`
+ // Enables GIF to MP4 transformations on this service.
+ AllowVideo bool `json:"allow_video"`
+}
+
+// GetImageOptimizerDefaultSettingsInput is used as input to the
+// GetImageOptimizerDefaultSettings function.
+type GetImageOptimizerDefaultSettingsInput struct {
+ // ServiceID is the ID of the service (required).
+ ServiceID string
+ // ServiceVersion is the specific configuration version (required).
+ ServiceVersion int
+}
+
+// UpdateImageOptimizerDefaultSettingsInput is used as input to the
+// UpdateImageOptimizerDefaultSettings function.
+//
+// A minimum of one optional field is required.
+type UpdateImageOptimizerDefaultSettingsInput struct {
+ // ServiceID is the ID of the service (required).
+ ServiceID string `json:"-"`
+ // ServiceVersion is the specific configuration version (required).
+ ServiceVersion int `json:"-"`
+ // The type of filter to use while resizing an image.
+ ResizeFilter *ImageOptimizerResizeFilter `json:"resize_filter,omitempty"`
+ // Controls whether or not to default to WebP output when the client supports it. This is equivalent to adding "auto=webp" to all image optimizer requests.
+ Webp *bool `json:"webp,omitempty"`
+ // The default quality to use with WebP output. This can be overridden with the second option in the "quality" URL parameter on specific image optimizer requests.
+ WebpQuality *int `json:"webp_quality,omitempty"`
+ // The default type of JPEG output to use. This can be overridden with "format=bjpeg" and "format=pjpeg" on specific image optimizer requests.
+ JpegType *ImageOptimizerJpegType `json:"jpeg_type,omitempty"`
+ // The default quality to use with JPEG output. This can be overridden with the "quality" parameter on specific image optimizer requests.
+ JpegQuality *int `json:"jpeg_quality,omitempty"`
+ // Whether or not we should allow output images to render at sizes larger than input.
+ Upscale *bool `json:"upscale,omitempty"`
+ // Enables GIF to MP4 transformations on this service.
+ AllowVideo *bool `json:"allow_video,omitempty"`
+}
+
+// GetImageOptimizerDefaultSettings retrives the current Image Optimizer default settings on a given service version.
+//
+// Returns (nil, nil) if no default settings are set.
+func (c *Client) GetImageOptimizerDefaultSettings(i *GetImageOptimizerDefaultSettingsInput) (*ImageOptimizerDefaultSettings, error) {
+ if i.ServiceID == "" {
+ return nil, ErrMissingServiceID
+ }
+ if i.ServiceVersion == 0 {
+ return nil, ErrMissingServiceVersion
+ }
+
+ path := fmt.Sprintf("/service/%s/version/%d/image_optimizer_default_settings", i.ServiceID, i.ServiceVersion)
+
+ resp, err := c.Get(path, nil)
+ if err != nil {
+ if herr, ok := err.(*HTTPError); ok {
+ if herr.StatusCode == 404 {
+ // API endpoint returns 404 for services without Image Optimizer settings set.
+ return nil, nil
+ }
+ }
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var iods *ImageOptimizerDefaultSettings
+ if err := json.NewDecoder(resp.Body).Decode(&iods); err != nil {
+ return nil, err
+ }
+
+ return iods, nil
+}
+
+// UpdateImageOptimizerDefaultSettings Update one or more default settings.
+//
+// A minimum of one non-nil property is required.
+//
+// Returns the new Image Optimizer default settings.
+func (c *Client) UpdateImageOptimizerDefaultSettings(i *UpdateImageOptimizerDefaultSettingsInput) (*ImageOptimizerDefaultSettings, error) {
+ if i.ServiceID == "" {
+ return nil, ErrMissingServiceID
+ }
+ if i.ServiceVersion == 0 {
+ return nil, ErrMissingServiceVersion
+ }
+ if i.ResizeFilter == nil && i.Webp == nil && i.WebpQuality == nil && i.JpegType == nil && i.JpegQuality == nil && i.Upscale == nil && i.AllowVideo == nil {
+ return nil, ErrMissingImageOptimizerDefaultSetting
+ }
+
+ path := fmt.Sprintf("/service/%s/version/%d/image_optimizer_default_settings", i.ServiceID, i.ServiceVersion)
+
+ resp, err := c.PatchJSON(path, i, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var iods *ImageOptimizerDefaultSettings
+ if err := json.NewDecoder(resp.Body).Decode(&iods); err != nil {
+ return nil, err
+ }
+
+ return iods, nil
+}
diff --git a/vendor/github.com/fastly/go-fastly/v9/fastly/tls_subscription.go b/vendor/github.com/fastly/go-fastly/v9/fastly/tls_subscription.go
index 7cbb28f39..deecb2c42 100644
--- a/vendor/github.com/fastly/go-fastly/v9/fastly/tls_subscription.go
+++ b/vendor/github.com/fastly/go-fastly/v9/fastly/tls_subscription.go
@@ -25,7 +25,17 @@ type TLSSubscription struct {
// TLSSubscriptionCertificate represents a subscription certificate.
type TLSSubscriptionCertificate struct {
- ID string `jsonapi:"primary,tls_certificate"`
+ ID string `jsonapi:"primary,tls_certificate"`
+ CreatedAt *time.Time `jsonapi:"attr,created_at,iso8601"`
+ IssuedTo string `jsonapi:"attr,issued_to"`
+ Issuer string `jsonapi:"attr,issuer"`
+ Name string `jsonapi:"attr,name"`
+ NotAfter *time.Time `jsonapi:"attr,not_after,iso8601"`
+ NotBefore *time.Time `jsonapi:"attr,not_before,iso8601"`
+ Replace bool `jsonapi:"attr,replace"`
+ SerialNumber string `jsonapi:"attr,serial_number"`
+ SignatureAlgorithm string `jsonapi:"attr,signature_algorithm"`
+ UpdatedAt *time.Time `jsonapi:"attr,updated_at,iso8601"`
}
// TLSAuthorizations gives information needed to verify domain ownership in
@@ -66,7 +76,7 @@ type ListTLSSubscriptionsInput struct {
FilterState string
// Limit the returned subscriptions to those that include the specific domain.
FilterTLSDomainsID string
- // Include related objects. Optional, comma-separated values. Permitted values: tls_authorizations.
+ // Include related objects. Optional, comma-separated values. Permitted values: tls_authorizations, tls_authorizations.globalsign_email_challenge, tls_authorizations.self_managed_http_challenge, and tls_certificates.
Include string
// Current page.
PageNumber int
@@ -201,7 +211,7 @@ func domainInSlice(haystack []*TLSDomain, needle *TLSDomain) bool {
type GetTLSSubscriptionInput struct {
// ID of the TLS subscription to fetch.
ID string
- // Include related objects. Optional, comma-separated values. Permitted values: tls_authorizations.
+ // Include related objects. Optional, comma-separated values. Permitted values: tls_authorizations, tls_authorizations.globalsign_email_challenge, tls_authorizations.self_managed_http_challenge, and tls_certificates.
Include *string
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index c06e35eaa..927647393 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -225,7 +225,7 @@ github.com/cloudflare/circl/sign/ed448
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
-# github.com/fastly/go-fastly/v9 v9.3.1
+# github.com/fastly/go-fastly/v9 v9.4.0
## explicit; go 1.20
github.com/fastly/go-fastly/v9/fastly
# github.com/fatih/color v1.16.0