Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions docs/resources/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ The `scaleway_object` resource allows you to create and manage objects for [Scal

Refer to the [dedicated documentation](https://www.scaleway.com/en/docs/object-storage/how-to/upload-files-into-a-bucket/) for more information on Object Storage objects.


## Example Usage

```terraform
### Basic object creation

resource "scaleway_object_bucket" "some_bucket" {
name = "some-unique-name"
}

resource scaleway_object "some_file" {
resource "scaleway_object" "some_file" {
bucket = scaleway_object_bucket.some_bucket.id
key = "object_path"

Expand All @@ -25,6 +28,40 @@ resource scaleway_object "some_file" {
}
```

```terraform
### Using Write-Only SSE Customer Key

resource "scaleway_object_bucket" "encrypted_bucket" {
name = "encrypted-bucket"
}

# Generate an ephemeral encryption key (not stored in the state)
ephemeral "random_password" "encryption_key" {
length = 32
special = false
upper = true
lower = true
numeric = true
min_upper = 1
min_lower = 1
min_numeric = 1
# Only hex characters for SSE-C keys
override_special = ""
}

resource "scaleway_object" "encrypted_file" {
bucket = scaleway_object_bucket.encrypted_bucket.id
key = "secret-file"
content = "This is a secret content"

# Use write-only encryption key
sse_customer_key_wo = ephemeral.random_password.encryption_key.result
sse_customer_key_wo_version = 1
}
```



## Argument Reference

The following arguments are supported:
Expand Down Expand Up @@ -53,7 +90,11 @@ The following arguments are supported:

* `tags` - (Optional) Map of tags.

* `sse_customer_key` - (Optional) Customer's encryption keys to encrypt data (SSE-C)
* `sse_customer_key` - (Optional) Customer's encryption keys to encrypt data (SSE-C). Only one of `sse_customer_key` or `sse_customer_key_wo` should be specified.

* `sse_customer_key_wo` - (Optional) Customer's encryption keys to encrypt data (SSE-C) in [write-only](https://developer.hashicorp.com/terraform/language/manage-sensitive-data/write-only) mode. Only one of `sse_customer_key` or `sse_customer_key_wo` should be specified. `sse_customer_key_wo` will not be set in the Terraform state. To update the `sse_customer_key_wo`, you must also update the `sse_customer_key_wo_version`. **Important:** Objects encrypted with `sse_customer_key_wo` cannot be read back by Terraform since the encryption key is not stored in state. This means attributes like `content_type`, `metadata`, and `tags` will not be populated from the actual object. Additionally, data sources cannot use `sse_customer_key_wo` because data sources cannot have write-only attributes - the key would be exposed in the state, defeating the purpose of write-only mode.

* `sse_customer_key_wo_version` - (Optional) The version of the [write-only](https://developer.hashicorp.com/terraform/language/manage-sensitive-data/write-only) SSE customer key. To update the `sse_customer_key_wo`, you must also update the `sse_customer_key_wo_version`.

* `project_id` - (Defaults to [provider](../index.md#arguments-reference) `project_id`) The ID of the project the bucket is associated with.

Expand Down
13 changes: 13 additions & 0 deletions examples/resources/scaleway_object/resource-basic.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### Basic object creation

resource "scaleway_object_bucket" "some_bucket" {
name = "some-unique-name"
}

resource "scaleway_object" "some_file" {
bucket = scaleway_object_bucket.some_bucket.id
key = "object_path"

file = "myfile"
hash = filemd5("myfile")
}
29 changes: 29 additions & 0 deletions examples/resources/scaleway_object/resource-encryption-wo.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
### Using Write-Only SSE Customer Key

resource "scaleway_object_bucket" "encrypted_bucket" {
name = "encrypted-bucket"
}

# Generate an ephemeral encryption key (not stored in the state)
ephemeral "random_password" "encryption_key" {
length = 32
special = false
upper = true
lower = true
numeric = true
min_upper = 1
min_lower = 1
min_numeric = 1
# Only hex characters for SSE-C keys
override_special = ""
}

resource "scaleway_object" "encrypted_file" {
bucket = scaleway_object_bucket.encrypted_bucket.id
key = "secret-file"
content = "This is a secret content"

# Use write-only encryption key
sse_customer_key_wo = ephemeral.random_password.encryption_key.result
sse_customer_key_wo_version = 1
}
27 changes: 24 additions & 3 deletions internal/services/object/data_source_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func DataSourceObject() *schema.Resource {

datasource.FixDatasourceSchemaFlags(dsSchema, true, "bucket", "key")

datasource.AddOptionalFieldsToSchema(dsSchema, "region", "project_id")
datasource.AddOptionalFieldsToSchema(dsSchema, "region", "project_id", "sse_customer_key")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Currently we could not get an sse_customer_key encrypted datasource (the key is required to build the proper s3 HeadObject call headers)


return &schema.Resource{
ReadContext: DataSourceObjectRead,
Expand Down Expand Up @@ -51,10 +51,31 @@ func DataSourceObjectRead(ctx context.Context, d *schema.ResourceData, m any) di

tflog.Debug(ctx, fmt.Sprintf("SCW object read for bucket=%s key=%s", bucket, key))

_, err = s3Client.HeadObject(ctx, &s3.HeadObjectInput{
req := &s3.HeadObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
}

// Add encryption headers if present (similar to resourceObjectRead)
// Only the regular (non Write Only) sse_customer_key can be set.
// Data sources cannot read objects encrypted with write-only keys
// since the actual key is not available in the data source configuration.
// Data sources cannot have WriteOnly attributes. Making it available would
// set the key in the state.
Comment on lines +59 to +64
Copy link
Copy Markdown
Contributor Author

@estellesoulard estellesoulard Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This limitation is why I am considering not merging this PR at all. Is this restriction (not being able to query the datasource specifically for sse_customer_key_wo encrypted objects) something we are OK with ? We might as well have a dedicated Ephemeral Resource at that point.

if encryptionKey, ok := d.GetOk("sse_customer_key"); ok {
encryptionKeyStr := encryptionKey.(string)

digestMD5, encryption, err := EncryptCustomerKey(encryptionKeyStr)
if err != nil {
return diag.FromErr(err)
}

req.SSECustomerAlgorithm = aws.String("AES256")
req.SSECustomerKeyMD5 = aws.String(digestMD5)
req.SSECustomerKey = encryption
}

_, err = s3Client.HeadObject(ctx, req)
if err != nil {
return diag.FromErr(fmt.Errorf("couldn't read object %s/%s: %w", bucket, key, err))
}
Expand Down
141 changes: 141 additions & 0 deletions internal/services/object/data_source_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,144 @@ func TestAccDataSourceObject_Basic(t *testing.T) {
},
})
}

func TestAccDataSourceObject_Encrypted(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()

bucketName := sdkacctest.RandomWithPrefix("test-acc-scaleway-ds-obj-encrypted")
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
objectchecks.IsObjectDestroyed(tt),
objectchecks.IsBucketDestroyed(tt),
),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "scaleway_object_bucket" "base-01" {
name = "%s"
region= "%s"
tags = {
foo = "bar"
}
}

resource scaleway_object "file" {
bucket = scaleway_object_bucket.base-01.id
key = "myfile"
content = "Hello World"
sse_customer_key = "%s"
}

`, bucketName, objectTestsMainRegion, encryptionStr),
Check: resource.ComposeTestCheckFunc(
objectchecks.CheckBucketExists(tt, "scaleway_object_bucket.base-01", true),
objectchecks.IsObjectExists(tt, "scaleway_object.file"),
),
},
{
Config: fmt.Sprintf(`
resource "scaleway_object_bucket" "base-01" {
name = "%s"
region= "%s"
tags = {
foo = "bar"
}
}

resource scaleway_object "file" {
bucket = scaleway_object_bucket.base-01.id
key = "myfile"
content = "Hello World"
sse_customer_key = "%s"
}

data scaleway_object "by-key" {
key = "myfile"
bucket = scaleway_object_bucket.base-01.id
sse_customer_key = "%s"
}
`, bucketName, objectTestsMainRegion, encryptionStr, encryptionStr),
Check: resource.ComposeTestCheckFunc(
objectchecks.CheckBucketExists(tt, "scaleway_object_bucket.base-01", true),
resource.TestCheckResourceAttr("data.scaleway_object.by-key", "key", "myfile"),
resource.TestCheckResourceAttrSet("data.scaleway_object.by-key", "id"),
),
},
},
})
}

func TestAccDataSourceObject_EncryptedWO(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()

bucketName := sdkacctest.RandomWithPrefix("test-acc-scaleway-ds-obj-encryptedwo")
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
objectchecks.IsObjectDestroyed(tt),
objectchecks.IsBucketDestroyed(tt),
),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "scaleway_object_bucket" "base-01" {
name = "%s"
region= "%s"
tags = {
foo = "bar"
}
}

resource scaleway_object "file" {
bucket = scaleway_object_bucket.base-01.id
key = "myfile"
content = "Hello World"
sse_customer_key_wo = "%s"
sse_customer_key_wo_version = 1
}

`, bucketName, objectTestsMainRegion, encryptionStr),
Check: resource.ComposeTestCheckFunc(
objectchecks.CheckBucketExists(tt, "scaleway_object_bucket.base-01", true),
objectchecks.IsObjectExists(tt, "scaleway_object.file"),
),
},
// The only way to get an encrypted object is to provide the sse_customer_key. For a sse_customer_key_wo,
// datasources cannot have Write Only attributes, so we have to pass it as a regular sse_customer_key.
// This is not ideal, as the key is then set in the state, making the Write Only useless...
// Querying objects encrypted with a sse_customer_key_wo is discouraged.
Comment on lines +182 to +185
Copy link
Copy Markdown
Contributor Author

@estellesoulard estellesoulard Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This limitation is why I am considering not merging this PR at all. Is this restriction (not being able to query the datasource specifically for sse_customer_key_wo encrypted objects) something we are OK with ? We might as well have a dedicated Ephemeral Resource at that point.

{
Config: fmt.Sprintf(`
resource "scaleway_object_bucket" "base-01" {
name = "%s"
region= "%s"
tags = {
foo = "bar"
}
}

resource scaleway_object "file" {
bucket = scaleway_object_bucket.base-01.id
key = "myfile"
content = "Hello World"
sse_customer_key_wo = "%s"
sse_customer_key_wo_version = 1
}

data scaleway_object "by-key" {
key = "myfile"
bucket = scaleway_object_bucket.base-01.id
sse_customer_key = "%s"
}
`, bucketName, objectTestsMainRegion, encryptionStr, encryptionStr),
Check: resource.ComposeTestCheckFunc(
objectchecks.CheckBucketExists(tt, "scaleway_object_bucket.base-01", true),
objectchecks.IsObjectExists(tt, "scaleway_object.file"),
),
},
},
})
}
3 changes: 3 additions & 0 deletions internal/services/object/descriptions/object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The `scaleway_object` resource allows you to create and manage objects for [Scaleway Object storage](https://www.scaleway.com/en/docs/object-storage/).

Refer to the [dedicated documentation](https://www.scaleway.com/en/docs/object-storage/how-to/upload-files-into-a-bucket/) for more information on Object Storage objects.
Loading
Loading