September 13, 2018·4 min read·engineeringterraformawsencryptionproviders

AWS provider 1.36 manages CloudFront public keys

The new CloudFront resource manages uploaded public keys for uses such as field-level encryption. Its schema makes key replacement and configuration updates distinct operations.

An uploaded public key has a lifecycle beyond the file containing its bytes. CloudFront assigns it an identifier and a configuration version, and later changes must refer to that remote object. AWS provider 1.36.0 adds aws_cloudfront_public_key, bringing those operations into Terraform. The September 13 release notes identify it as the release's new resource.

The resource documentation describes uploaded keys for CloudFront features such as field-level encryption. That is the relevant scope of this addition. Creating a public-key object is one step in configuring a feature that uses it, so the resource should not be read as a complete encryption deployment.

I find this sort of addition useful because Terraform can manage an identifiable object that previously needed another procedure. The value comes from making its configuration and lifecycle explicit. That requires understanding which changes preserve the object and which changes replace it, especially when other configuration refers to its identifier.

Configuration supplies the public bytes

The required argument is encoded_key. A name and comment are optional. The documentation's example reads a public-key file through Terraform's file interpolation function, which keeps the key content separate from the resource declaration without making the file itself a remote object.

resource "aws_cloudfront_public_key" "example" {
  name        = "example-field-encryption"
  comment     = "Public key for field-level encryption"
  encoded_key = "${file("public_key.pem")}"
}

The file in this example contains the public key to upload. This resource does not generate a key pair or declare storage for the private key. Its required argument and API request concern the encoded public material. Keeping that scope clear avoids assigning key-generation or private-key handling responsibilities to a resource that does not implement them.

The name can instead be generated. The 1.36.0 implementation accepts an explicit name, generates a unique name using a supplied name_prefix, or generates one using tf- when neither is present. Explicit name and prefix conflict, so a configuration chooses one naming mode.

A generated name and the CloudFront identifier are different values. Terraform uses the identifier returned by CreatePublicKey as the resource ID. The read operation subsequently loads configuration through that ID. A meaningful name helps people recognize the object, while its remote identifier connects subsequent API operations to the object that was created.

Replacing a key differs from editing its comment

The schema marks encoded_key, name, and name_prefix as ForceNew. Changing any of these configured properties requests replacement in this provider version. The optional comment has no such marker and can pass through the update handler.

That distinction affects how a rotation appears in a plan. Replacing public bytes is not represented as an ordinary in-place edit by this resource. An operator reviewing the proposed change needs to consider a new object's identity and the references associated with the previous one. The declaration does not, on its own, describe every consumer that might need to change.

The update handler submits the public-key configuration along with the current ETag. It then performs another read. This makes an editable property such as the comment part of the remote configuration lifecycle, while preserving the schema's replacement rules for key material and naming.

I prefer an explicit replacement contract to assuming that all arguments have equivalent update behavior. A provider schema is part of an infrastructure design's operational meaning. Two fields can sit next to each other in a configuration block and still imply substantially different remote operations when changed.

ETags identify the configuration version

The documentation exports etag, describing it as the current version of the public key. The implementation stores the ETag returned by GetPublicKey, then supplies it through IfMatch in both update and delete requests. It therefore carries a version obtained from a read into a later mutation request.

This is separate from the Terraform resource identifier. An ID identifies which object the operation addresses. The ETag accompanies the request with the version of that object's configuration known to the provider. Describing both as identifiers without distinguishing their roles would conceal why the mutation request needs both values.

The resource also exports caller_reference. Creation supplies a timestamp-based value if no existing reference is present, and later reads preserve the value returned by CloudFront. The request-building function reuses that recorded reference on subsequent configuration operations. These fields let the provider retain remote metadata that does not need to appear as user-supplied configuration.

Reading a missing public key clears its Terraform identifier, including the specific no-such-key response and empty-object cases. Deletion treats an already absent public key as successful. Those behaviors describe how Terraform reconciles the remote object's absence with its own state, rather than requiring the caller to reproduce the original deletion action.

The addition is consequently useful at a precise level. It brings public-key creation, observation, editable configuration, and deletion into the provider, while replacement semantics govern changes to key bytes. Features that consume the key still need their own configuration, and private-key handling remains outside this resource's declared responsibilities.