Skip to content

Cloudfront update behavior issue #1608

@niqo01

Description

@niqo01

Describe the bug

I am trying to add a cloudfront behavior using kotlin sdk kotlin v1.4.92.
However when trying to send the updated config I get the error:

You must specify IncludeCookies when Enabled is missing or set to true within a Logging object

Regression Issue

  • Select this option if this issue appears to be a regression.

Expected behavior

New cloud front behavior updated correctly.

Current behavior

IllegalUpdate(message=You must specify IncludeCookies when Enabled is missing or set to true within a Logging object)
--
  | 2025-06-05T08:21:40.398Z | at aws.sdk.kotlin.services.cloudfront.model.IllegalUpdate$Builder.build(IllegalUpdate.kt:57)
  | 2025-06-05T08:21:40.398Z | at aws.sdk.kotlin.services.cloudfront.serde.IllegalUpdateDeserializer.deserialize(IllegalUpdateDeserializer.kt:29)
  | 2025-06-05T08:21:40.398Z | at aws.sdk.kotlin.services.cloudfront.serde.UpdateDistributionOperationDeserializerKt.throwUpdateDistributionError(UpdateDistributionOperationDeserializer.kt:96)
  | 2025-06-05T08:21:40.398Z | at aws.sdk.kotlin.services.cloudfront.serde.UpdateDistributionOperationDeserializerKt.access$throwUpdateDistributionError(UpdateDistributionOperationDeserializer.kt:1)
  | 2025-06-05T08:21:40.398Z | at aws.sdk.kotlin.services.cloudfront.serde.UpdateDistributionOperationDeserializer.deserialize(UpdateDistributionOperationDeserializer.kt:22)
  | 2025-06-05T08:21:40.398Z | at aws.sdk.kotlin.services.cloudfront.serde.UpdateDistributionOperationDeserializer.deserialize(UpdateDistributionOperationDeserializer.kt:17)
  | 2025-06-05T08:21:40.398Z | at aws.smithy.kotlin.runtime.http.operation.DeserializeHandler.call(SdkOperationExecution.kt:347)
  | 2025-06-05T08:21:40.398Z | at aws.smithy.kotlin.runtime.http.operation.DeserializeHandler$call$1.invokeSuspend(SdkOperationExecution.kt)
  | 2025-06-05T08:21:40.398Z | at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
  | 2025-06-05T08:21:40.398Z | at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
  | 2025-06-05T08:21:40.398Z | at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:263)
  | 2025-06-05T08:21:40.398Z | at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:94)
  | 2025-06-05T08:21:40.398Z | at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:70)
  | 2025-06-05T08:21:40.398Z | at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
  | 2025-06-05T08:21:40.398Z | at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:48)
  | 2025-06-05T08:21:40.399Z | at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
  | 2025-06-05T08:21:40.399Z | at enkihangouts.customresource.functions.CustomResourceHandler.update(CustomResourceHandler.kt:53)
  | 2025-06-05T08:21:40.399Z | at software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler.getResponse(AbstractCustomResourceHandler.java:103)
  | 2025-06-05T08:21:40.399Z | at software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler.handleRequest(AbstractCustomResourceHandler.java:72)
  | 2025-06-05T08:21:40.399Z | at software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler.handleRequest(AbstractCustomResourceHandler.java:1)
  | 2025-06-05T08:21:40.399Z | at com.amazonaws.services.lambda.runtime.api.client.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:655)
  | 2025-06-05T08:21:40.399Z | at com.amazonaws.services.lambda.runtime.api.client.EventHandlerLoader$2.call(EventHandlerLoader.java:601)
  | 2025-06-05T08:21:40.399Z | at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:240)
  | 2025-06-05T08:21:40.399Z | at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:190)
  | 2025-06-05T08:21:40.399Z | at com.amazonaws.services.lambda.runtime.api.client.AWSLambda.main(AWSLambda.java:180)

Steps to Reproduce

  1. Create an Aws Distribution.
    my current cdk code is:
Distribution.Builder.create(
        this,
        "CdnDistribution",
      )
        .defaultBehavior(
          BehaviorOptions.Builder()
            .origin(S3BucketOrigin.withOriginAccessControl(props.staticAssetsBucket))
            .viewerProtocolPolicy(ViewerProtocolPolicy.HTTPS_ONLY)
            .responseHeadersPolicy(noIndexHeaderPolicy())
            .build(),
        )
        .priceClass(if (props.appEnv.isProd)PriceClass.PRICE_CLASS_ALL else PriceClass.PRICE_CLASS_100)
        .certificate(props.certificate)
        .sslSupportMethod(SSLMethod.SNI)
        .minimumProtocolVersion(SecurityPolicyProtocol.TLS_V1_2_2021)
        .domainNames(aliases) 
        .enableLogging(true)
        .logBucket(logBucket)
        .logFilePrefix("cdn-cloudfront/")
        .logIncludesCookies(false)
        .webAclId(props.webAclId)
        .errorResponses(
          listOf(
            ErrorResponse.builder().httpStatus(500).ttl(Duration.minutes(10)).build(),
            ErrorResponse.builder().httpStatus(501).ttl(Duration.minutes(10)).build(),
            ErrorResponse.builder().httpStatus(502).ttl(Duration.minutes(10)).build(),
            ErrorResponse.builder().httpStatus(503).ttl(Duration.minutes(10)).build(),
            ErrorResponse.builder().httpStatus(504).ttl(Duration.minutes(10)).build(),
          ),
        )
        .build()
  1. Later try to alter the distribution with the goal to add a new behavior. Currently my code running in a java lambda is:
suspend fun addOrUpdateBehavior(
    distributionId: String,
    functionArn: String,
    originDomainName: String,
    originPath: String,
    pathPattern: String,
    cachePolicyId: String,
    originRequestPolicyId: String,
  ) {
    val distConfigResp = clClient.getDistributionConfig {
      id = distributionId
    }

    val config = distConfigResp.distributionConfig!!
    val etag = distConfigResp.eTag!!

    val originId = "custom-${originDomainName.lowercase().replace(Regex("[^a-z0-9-]"), "-")}"

    val originExists = config.origins?.items?.any { it.id == originId } ?: false

    val newOrigins = if (!originExists) {
      logger.info { "Adding new origin $originId with domain $originDomainName and path $originPath" }
      val updatedItems = (config.origins?.items ?: emptyList()).toMutableList()
      updatedItems.add(
        Origin {
          id = originId
          domainName = originDomainName
          this.originPath = originPath
          customOriginConfig = CustomOriginConfig {
            httpPort = 80
            httpsPort = 443
            originProtocolPolicy = OriginProtocolPolicy.HttpsOnly
            originSslProtocols = OriginSslProtocols {
              quantity = 1
              items = listOf(SslProtocol.TlsV1_2)
            }
          }
        },
      )
      Origins {
        quantity = updatedItems.size
        items = updatedItems
      }
    } else {
      config.origins!!
    }

    val newBehavior = CacheBehavior {
      this.pathPattern = pathPattern
      this.targetOriginId = originId
      this.viewerProtocolPolicy = ViewerProtocolPolicy.RedirectToHttps
      this.allowedMethods = AllowedMethods {
        quantity = 2
        items = listOf(Method.Get, Method.Head)
        cachedMethods = CachedMethods {
          quantity = 2
          items = listOf(Method.Get, Method.Head)
        }
      }
      this.compress = true
      this.cachePolicyId = cachePolicyId
      this.originRequestPolicyId = originRequestPolicyId
      this.functionAssociations = FunctionAssociations {
        quantity = 1
        items = listOf(
          FunctionAssociation {
            eventType = EventType.ViewerRequest
            this.functionArn = functionArn
          },
        )
      }
    }

    val updatedBehaviors = config.cacheBehaviors?.items?.map {
      if (it.pathPattern == pathPattern) {
        logger.info { "Updating existing behavior for $pathPattern" }
        newBehavior
      } else {
        it
      }
    }?.toMutableList() ?: mutableListOf(newBehavior)

    // If not updated, add it
    if (updatedBehaviors.none { it.pathPattern == pathPattern }) {
      logger.info { "Adding new behavior for $pathPattern" }
      updatedBehaviors.add(newBehavior)
    } else {
      logger.info { "Behavior for $pathPattern already updated" }
    }

    val newConfig = config.copy {
      origins = newOrigins
      cacheBehaviors {
        quantity = updatedBehaviors.size
        items = updatedBehaviors
      }
    }

    clClient.updateDistribution {
      id = distributionId
      ifMatch = etag
      distributionConfig = newConfig
    }
  }
  1. Run the code and notice: You must specify IncludeCookies when Enabled is missing or set to true within a Logging object

Possible Solution

Unknown Yet. I tried setting the following config but it still produced the same issue:

      logging = config.logging?.copy {
        enabled = config.logging?.enabled ?: false
        includeCookies = config.logging?.includeCookies ?: false
        bucket = config.logging?.bucket ?: ""
        prefix = config.logging?.prefix ?: ""
      }

Context

No response

AWS SDK for Kotlin version

v1.4.92

Platform (JVM/JS/Native)

jvm

Operating system and version

Aws lambda 7 runtime

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions