GCP for DevOps — Storage Exercises

3 minute read

Storage — Practice Exercises

Test your knowledge of Cloud Storage, versioning, lifecycle, Persistent Disks, and Filestore.


Exercise 1 — Create a Versioned Bucket with Lifecycle

Create a Cloud Storage bucket with versioning enabled and a lifecycle rule that deletes non-current versions older than 7 days, keeping only 3 versions.

Show Answer
# Create the bucket
$ gcloud storage buckets create gs://devops-exercise-storage \
    --location=us-central1 \
    --uniform-bucket-level-access

# Enable versioning
$ gcloud storage buckets update gs://devops-exercise-storage \
    --versioning

# Create lifecycle.json
$ cat > lifecycle.json << 'EOF'
{
  "lifecycle": {
    "rule": [
      {
        "action": {"type": "Delete"},
        "condition": {
          "isLive": false,
          "numNewerVersions": 3,
          "age": 7
        }
      }
    ]
  }
}
EOF

# Apply lifecycle rules
$ gcloud storage buckets update gs://devops-exercise-storage \
    --lifecycle-file=lifecycle.json

# Verify
$ gcloud storage buckets describe gs://devops-exercise-storage \
    --format="json(versioning,lifecycle)"

Exercise 2 — Terraform State Backend

Set up a GCS bucket suitable for Terraform state: versioned, single-region, with lifecycle to keep 10 state versions.

Show Answer
# Create state bucket
$ gcloud storage buckets create gs://devops-exercise-tfstate \
    --location=us-central1 \
    --default-storage-class=STANDARD \
    --uniform-bucket-level-access

# Enable versioning
$ gcloud storage buckets update gs://devops-exercise-tfstate \
    --versioning

# Lifecycle — keep only 10 non-current versions
$ cat > tf-lifecycle.json << 'EOF'
{
  "lifecycle": {
    "rule": [
      {
        "action": {"type": "Delete"},
        "condition": {
          "isLive": false,
          "numNewerVersions": 10
        }
      }
    ]
  }
}
EOF

$ gcloud storage buckets update gs://devops-exercise-tfstate \
    --lifecycle-file=tf-lifecycle.json

Terraform backend block:

terraform {
  backend "gcs" {
    bucket = "devops-exercise-tfstate"
    prefix = "exercise/state"
  }
}

Exercise 3 — Persistent Disk with Snapshot Schedule

Create a 50 GB pd-ssd disk, attach it to a VM, and set up a daily snapshot schedule that retains snapshots for 5 days.

Show Answer
# Create the disk
$ gcloud compute disks create exercise-data-disk \
    --size=50GB \
    --type=pd-ssd \
    --zone=us-central1-a

# Create a VM (if not existing)
$ gcloud compute instances create exercise-vm \
    --zone=us-central1-a \
    --machine-type=e2-micro

# Attach the disk
$ gcloud compute instances attach-disk exercise-vm \
    --disk=exercise-data-disk \
    --zone=us-central1-a

# Create a snapshot schedule
$ gcloud compute resource-policies create snapshot-schedule exercise-daily-snap \
    --region=us-central1 \
    --max-retention-days=5 \
    --daily-schedule \
    --start-time=03:00

# Attach the schedule to the disk
$ gcloud compute disks add-resource-policies exercise-data-disk \
    --resource-policies=exercise-daily-snap \
    --zone=us-central1-a

Exercise 4 — Storage Class Tiering

Create a bucket for logs that automatically transitions from Standard → Nearline (30 days) → Coldline (90 days) → Archive (365 days) → Delete (730 days).

Show Answer
# Create the bucket
$ gcloud storage buckets create gs://devops-exercise-logs \
    --location=us-central1

# Create lifecycle rules
$ cat > log-lifecycle.json << 'EOF'
{
  "lifecycle": {
    "rule": [
      {
        "action": {"type": "SetStorageClass", "storageClass": "NEARLINE"},
        "condition": {"age": 30, "matchesStorageClass": ["STANDARD"]}
      },
      {
        "action": {"type": "SetStorageClass", "storageClass": "COLDLINE"},
        "condition": {"age": 90, "matchesStorageClass": ["NEARLINE"]}
      },
      {
        "action": {"type": "SetStorageClass", "storageClass": "ARCHIVE"},
        "condition": {"age": 365, "matchesStorageClass": ["COLDLINE"]}
      },
      {
        "action": {"type": "Delete"},
        "condition": {"age": 730}
      }
    ]
  }
}
EOF

$ gcloud storage buckets update gs://devops-exercise-logs \
    --lifecycle-file=log-lifecycle.json

# Verify
$ gcloud storage buckets describe gs://devops-exercise-logs \
    --format="json(lifecycle)"

Exercise 5 — Upload with Signed URL

Generate a signed URL that allows a CI/CD runner (without GCP credentials) to upload a build artifact to Cloud Storage.

Show Answer
# Create a service account for signing
$ gcloud iam service-accounts create ci-uploader \
    --display-name="CI Artifact Uploader"

# Grant objectCreator role on the bucket
$ gcloud storage buckets add-iam-policy-binding gs://devops-exercise-storage \
    --member="serviceAccount:ci-uploader@my-project.iam.gserviceaccount.com" \
    --role="roles/storage.objectCreator"

# Create a service account key (for signing)
$ gcloud iam service-accounts keys create ci-uploader-key.json \
    --iam-account=ci-uploader@my-project.iam.gserviceaccount.com

# Generate a signed URL for PUT (1-hour expiry)
$ gcloud storage sign-url \
    gs://devops-exercise-storage/builds/artifact.tar.gz \
    --private-key-file=ci-uploader-key.json \
    --http-verb=PUT \
    --duration=1h

# Use the signed URL to upload (no gcloud needed)
$ curl -X PUT -H "Content-Type: application/gzip" \
    --upload-file artifact.tar.gz \
    "SIGNED_URL_HERE"

Best practice: Use Workload Identity Federation instead of service account keys for production CI/CD systems.


Cleanup

$ gcloud storage rm -r gs://devops-exercise-storage
$ gcloud storage rm -r gs://devops-exercise-tfstate
$ gcloud storage rm -r gs://devops-exercise-logs
$ gcloud compute resource-policies delete exercise-daily-snap --region=us-central1 --quiet
$ gcloud compute instances detach-disk exercise-vm --disk=exercise-data-disk --zone=us-central1-a
$ gcloud compute disks delete exercise-data-disk --zone=us-central1-a --quiet
$ gcloud compute instances delete exercise-vm --zone=us-central1-a --quiet
$ gcloud iam service-accounts delete ci-uploader@my-project.iam.gserviceaccount.com --quiet
$ rm -f ci-uploader-key.json lifecycle.json tf-lifecycle.json log-lifecycle.json

Next Chapter → CI/CD

Updated: