GOOGLE CLOUD STORAGE → STACKIT OBJECT STORAGE · TUTORIAL

Google Cloud Storage to STACKIT Object Storage

A Cloud Storage bucket becomes a STACKIT Object Storage bucket, and the run writes that bucket with its name and nothing else. Versioning, lifecycle rules, CORS, the website setting, public access and locks are set through the S3 API or the project, and the run lists each one it leaves. This tutorial follows a demo estate of three buckets from input to a to-do list, then shows the calls that carry the settings over and the limits to check first.

Three buckets in, three buckets out

A demo estate written for this page and assessed by StackToIT on 4 November 2026 with the google provider pinned to 8.4.0: an asset bucket with versioning, lifecycle rules and CORS, a bucket that serves a website to everyone, and an audit bucket with a locked retention policy. Left: the asset bucket and the audit bucket from the input. Right: the three bucket blocks the engine wrote, in file order; the review comments above each block are left out here. The input blocks are not adjacent in main.tf, so each has its own pane, verbatim.

Input: google_storage_bucket.mediamain.tf
resource "google_storage_bucket" "media" {
  name                        = "media-prod-assets"
  location                    = "EUROPE-WEST3"
  storage_class               = "STANDARD"
  uniform_bucket_level_access = true
  public_access_prevention    = "enforced"

  versioning {
    enabled = true
  }

  lifecycle_rule {
    condition {
      age                = 30
      with_state         = "ARCHIVED"
      num_newer_versions = 3
    }
    action {
      type = "Delete"
    }
  }

  lifecycle_rule {
    condition {
      age = 365
    }
    action {
      type          = "SetStorageClass"
      storage_class = "COLDLINE"
    }
  }

  cors {
    origin          = ["https://www.example.com"]
    method          = ["GET", "HEAD"]
    response_header = ["Content-Type"]
    max_age_seconds = 3600
  }
}
Input: google_storage_bucket.auditmain.tf
resource "google_storage_bucket" "audit" {
  name                        = "media-prod-audit"
  location                    = "EUROPE-WEST3"
  uniform_bucket_level_access = true

  retention_policy {
    retention_period = 31536000
    is_locked        = true
  }

  labels = {
    purpose = "audit"
  }
}
STACKIT Terraform by StackToITterraform/storage.tf
resource "stackit_objectstorage_bucket" "audit" {
  project_id = var.project_id
  name       = "media-prod-audit"
}

resource "stackit_objectstorage_bucket" "media" {
  project_id = var.project_id
  name       = "media-prod-assets"
}

resource "stackit_objectstorage_bucket" "site" {
  project_id = var.project_id
  name       = "media-prod-site"
}
  • The bucket resource is a name

    The run states that stackit_objectstorage_bucket has a name and an object lock setting only. Everything else on the Google bucket, from storage class to retention, is either set through the S3 API or not carried.

  • One unit per bucket, one class

    Each bucket is its own unit, classified RECONFIGURE at medium confidence. The service account of the demo, which uploads to the asset bucket, is a separate unit and becomes a STACKIT service account.

  • Bucket names are not yours alone

    Bucket names are 3 to 63 characters long. On uniqueness the STACKIT pages differ: the limits page says a name must be unique within the whole system, across projects, while the concepts page says unique within a region, with EU01 and EU02 independent. Either way a name that is free on Google Cloud can be taken here, so reserve it early; the run asks you to confirm the name against the applications that use it.

What the draft does not carry

Verbatim lines of the comments above the asset bucket and the website bucket in terraform/storage.tf, then the two findings for the versioned bucket and the locked bucket as assessment.json holds them, with their evidence lists cut. The last pane shows the console lines of the run.

Comment lines in the draft (asset bucket)terraform/storage.tf
# NOT CARRIED (google_storage_bucket.media): lifecycle_rule — stackit_objectstorage_bucket has a name and object_lock only (pinned provider): lifecycle rules (expiry, storage-class transitions) are not generated.
# NOT CARRIED (google_storage_bucket.media): cors — stackit_objectstorage_bucket has no CORS attribute (pinned provider): the CORS configuration is not generated.
# NOT CARRIED (google_storage_bucket.media): storage_class = STANDARD — stackit_objectstorage_bucket has a name and object_lock only (pinned provider): the bucket's storage class, Autoclass, hierarchical-namespace and requester-pays settings are not generated.
Comment lines in the draft (website bucket)terraform/storage.tf
# NOT CARRIED (google_storage_bucket.site): website — stackit_objectstorage_bucket has no static-website setting (pinned provider): the index and error pages are not generated — serve the site through the STACKIT CDN or a web server.
Findings (excerpt)assessment.json
{"check_id":"gcs-versioning","severity":"warning","message":"Object versioning is enabled. STACKIT Object Storage supports versioning through the S3 API (put-bucket-versioning; once enabled it can only be suspended), but stackit_objectstorage_bucket has no versioning attribute — enable it after the bucket exists."
{"check_id":"gcs-retention-lock","severity":"info","message":"A retention policy (WORM) is set; STACKIT Object Storage supports object lock — check the retention period against its limits."
Console summary (selected lines)terminal output
Source        6 resources · 4 units · visibility: Complete
Migration     4 drafted (of 4 units the draft considered)
  • Lifecycle, CORS and storage class

    Three lines for the asset bucket: lifecycle rules, CORS and the storage class are not generated. They are critical settings that stay open until a person sets them on the bucket or records that production does not need them.

  • The website stays a web server's job

    STACKIT Object Storage lists no website operation, and the run says to serve the content through the STACKIT CDN with the bucket as origin, or a web server. The index and error page names are written down as not carried.

  • Versioning needs a call after creation

    STACKIT supports versioning through the S3 API, but the bucket resource has no versioning attribute, so you enable it once the bucket exists. The retention lock is a different case: STACKIT's lock is a property of the bucket at creation.

Cloud Storage setting by setting

Cloud StorageSTACKIT Object StorageWhat to do
Location EUROPE-WEST3Region of the provider, eu01 or eu02Confirm residency; the draft sets no region on the bucket
Storage class STANDARD, COLDLINE ruleOne class; no transition documentedCompare capacity cost; drop the class rule
VersioningPut-bucket-versioning; suspend only, never disableEnable after creation; up to 1,000 versions per object
Delete rule on archived versionsExpiry of non-current versions in daysRebuild by days; a count of newer versions has no documented counterpart
CORS rulePut-bucket-cors, rules with origins, methods, headersGoogle returns response_header in both Access-Control-Allow-Headers and Access-Control-Expose-Headers, so list it under AllowedHeaders and ExposeHeaders
Website main page and 404Not listed among supported operationsCDN with the bucket as origin, or a web server
IAM member allUsers, objectViewerBucket policy with Principal *Re-create only if still intended
IAM member service account, objectAdminCredentials group and a bucket policy naming itCredentials are scoped to the project
Locked retention policy, one yearObject Lock, set at bucket creation, at most 365 daysNeeds a project-level Compliance Lock first

Classes and open settings as the run decided them; STACKIT limits from its Object Storage reference pages.

Carrying the settings over by hand

Not engine output. The calls follow the STACKIT how-to pages for versioning, CORS and bucket policies, and the Object Lock page for the audit bucket. The values come from the demo input.

S3 API callsshell
# Written by hand from the STACKIT documentation, not engine output.
# Endpoint of region eu01: https://object.storage.eu01.onstackit.cloud
EP=https://object.storage.eu01.onstackit.cloud

# 1. Versioning (media-prod-assets): once enabled it can only be suspended
aws s3api put-bucket-versioning --bucket media-prod-assets \
  --versioning-configuration Status=Enabled --endpoint-url $EP

# 2. CORS (media-prod-assets): cors-rules.json
# response_header in Google is returned as Access-Control-Allow-Headers and
# Access-Control-Expose-Headers (XML API), so list it in both
{
  "CORSRules": [{
    "AllowedOrigins": ["https://www.example.com"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedHeaders": ["Content-Type"],
    "ExposeHeaders": ["Content-Type"],
    "MaxAgeSeconds": 3600
  }]
}
aws s3api put-bucket-cors --bucket media-prod-assets \
  --cors-configuration file://cors-rules.json --endpoint-url $EP

# 3. Public read (media-prod-site): policy.json
{
  "Statement": [{
    "Sid": "public-read",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "urn:sgws:s3:::media-prod-site/*"
  }]
}
aws s3api put-bucket-policy --bucket media-prod-site \
  --policy file://policy.json --endpoint-url $EP
Object Lock for the audit bucketapi
# Written by hand from the STACKIT documentation, not engine output.
# Project level, once: POST .../v1/project/{projectId}/compliance-lock
# Per bucket, at creation only:
POST https://object-storage.api.{region}.stackit.cloud/v1/project/{projectId}/bucket/media-prod-audit?objectLockEnabled=true
# Default retention (applies to objects uploaded after the update):
PUT  .../v1/project/{projectId}/bucket/media-prod-audit/default-retention
{ "mode": "COMPLIANCE", "days": 365 }
  • Lock is decided at creation

    STACKIT states that Object Lock can only be enabled when the bucket is created and cannot be disabled afterwards. The project needs its Compliance Lock first, and a project with that lock cannot be deleted. Decide it before the first apply, not after the data is in.

  • 365 days is the ceiling

    The demo's retention period is 31,536,000 seconds, exactly 365 days, which is the maximum retention STACKIT lists for the Compliance Lock. A Google policy longer than a year exceeds that maximum. The default retention also applies only to objects uploaded after it is set, so plan for copied objects to start their period when they are uploaded, and test this on a scratch bucket.

  • Credentials are project-wide

    STACKIT credentials are scoped to the project, so one credential reaches every bucket in it. To separate teams, use credentials groups in bucket policies, or separate projects. The bucket policy size limit is 20,480 bytes.

The migration in order

  1. Inventory the buckets and their clients

    List buckets, object counts and sizes, and who reads or writes. Find applications that use the Google client libraries or signed URLs: they need a code change to an S3 SDK. The run does not see objects or clients.

  2. Create buckets, decide the lock first

    Apply the generated bucket blocks in a test project. Create any bucket that needs Object Lock with the lock enabled at creation. Check the name and the region.

  3. Set versioning, lifecycle, CORS and policy

    Make the S3 API calls above. Read each configuration back with the matching get call and compare it with the source.

  4. Create credentials

    Create Object Storage credentials in the STACKIT project and replace the HMAC keys or service-account access that applications used on Google Cloud.

  5. Copy the data

    Copy with a tool that speaks both sides, for example rclone with a Google Cloud Storage remote and an S3 remote for the STACKIT endpoint; the run names rclone as the copy method. S3 tools can also read the source through the XML API with HMAC keys. Run a bulk copy, then a delta copy at cutover. This is general practice, not a step the engine runs.

  6. Verify, switch, keep the source

    Compare object counts and checksums, switch the application endpoints and keys, and keep the Google buckets read-only until the new ones have served real traffic.

Questions storage teams ask

Do the applications need code changes?

Clients that already use the S3-compatible XML API with HMAC keys change endpoint and keys. Clients that use the Google Cloud Storage libraries over the JSON API, and signed URLs made by them, switch to an S3 SDK. The run lists this as a review step.

Can lifecycle rules be copied one to one?

No. STACKIT documents expiry of current objects and expiry of non-current versions, by days, with prefix and tag filters, up to 1,000 rules. Storage-class transitions and counts of newer versions are not in that reference, so those rules are rebuilt or dropped.

What happens to the static website?

STACKIT Object Storage lists no website operation. Serve the content through the STACKIT CDN with the bucket as origin or through a web server, and re-create the public read policy only if it is still intended.

Does the locked retention policy move?

Not as a setting. Object Lock is enabled when a bucket is created, needs the project's Compliance Lock, and allows at most 365 days. Compare your retention requirement with that before choosing it.

Sources

Your next move: STACKIT.

Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.