AccessDenied

Lifecycle Policy Not Deleting Objects? Check IAM Permissions

Server & Cloud Intermediate 👁 5 views 📅 Jun 30, 2026

Your lifecycle policy looks right but objects aren't being deleted. The real issue is often missing IAM permissions on the bucket.

Quick answer

Check the bucket's IAM permissions — the service account running the lifecycle policy needs storage.objects.delete and storage.buckets.update. If those are missing, the policy won't delete a thing.

What's actually happening here

You set up a lifecycle rule in Google Cloud Storage, AWS S3, or Azure Blob Storage. You specified: delete objects older than 30 days. The rule looks correct in the console. But days pass and the objects are still there. You don't get errors in the lifecycle logs — just nothing happens.

The reason is that lifecycle policies are executed by a system service account — not by your user account. In Google Cloud, that's the Cloud Storage service agent. In AWS, it's the bucket owner's IAM role. If that service account doesn't have delete permission on the bucket, the lifecycle action silently fails. The objects stay.

Fix steps

  1. Check the lifecycle logs — In Google Cloud, go to Cloud Logging and filter for protoPayload.methodName="storage.buckets.update" and resource.type="gcs_bucket". In AWS, check S3 access logs if enabled. Look for AccessDenied errors near the time the lifecycle should have run.
  2. Identify the service account — For Google Cloud, run gcloud storage service-agent to get the email. For AWS, it's the bucket owner's AWS account root user or a role you assigned. For Azure, it's the Storage Blob Data Owner principal.
  3. Grant delete permission — Add storage.objects.delete to the bucket's IAM policy for that service account. Don't add it at the project level — add it directly on the bucket. In Google Cloud, use: gsutil iam ch serviceAccount:[SERVICE_AGENT_EMAIL]:objectCreator,objectViewer,objectAdmin gs://[BUCKET_NAME]. In AWS, attach a bucket policy with s3:DeleteObject for the bucket owner role.
  4. Verify object lock or retention — If your bucket has object lock enabled (Google Cloud retention policy, AWS S3 Object Lock, Azure immutability), lifecycle deletion is blocked. You can't delete objects under legal hold. Remove the retention policy first, or set a lifecycle rule that expires the retention before deletion.
  5. Test with a single object — Upload a test object with a short lifecycle rule (e.g., delete after 1 day). Wait 24 hours and check if it's gone. If it stays, the permission issue is still there.

Alternative fixes

If the main fix doesn't work, try these:

  • Check bucket policies — Sometimes a bucket policy explicitly denies the service account. Look for Deny statements that block storage.objects.delete. In Google Cloud, run gsutil iam get gs://[BUCKET_NAME] and look for deny rules.
  • Use a custom service account — In Google Cloud, you can create a service account, give it roles/storage.objectAdmin and roles/storage.legacyBucketOwner, then configure the lifecycle to run under that account. This bypasses the default service agent which might have limited permissions.
  • Check event-based holds — In Google Cloud, if you set a event-based hold on the bucket, lifecycle won't delete objects until the hold is removed. Run gsutil list gs://[BUCKET_NAME] -a and look for event-based-hold: true on any object.
  • Verify time zone — Lifecycle uses UTC. If your rule says "delete after 30 days" but your test object was created at 11 PM UTC, the deletion might happen 31 days later if you check at 10 PM. Check the object's creation timestamp in UTC.

Prevention tip

Before you set any lifecycle rule, grant storage.objects.delete and storage.buckets.update to the service agent on the bucket. Do this first, then create the rule. I also recommend turning on S3 access logs or Cloud Logging for your bucket from day one — you'll see the AccessDenied quickly instead of waiting days for objects to pile up. Don't assume the console rule is enough; permissions are the hidden gatekeeper.

Was this solution helpful?