Tutorial · Cloud Storage

Migrate from simplified to multi-user S3

Your team has grown past the point where one shared IAM user makes sense. This guide upgrades you to per-user credentials with RBAC, without re-uploading a single byte.

Estimated time: 20–30 min · Last reviewed: 2026-05 · Difficulty: Intermediate

Before you start
  • You currently have a working Cloud Gallery on Amazon S3 using the simplified single-user setup
  • You can sign in to the AWS Management Console as root or as an IAM user with full IAM and S3 permissions
  • You're a team admin (super-admin) inside webQsee, only admins can change team-wide Cloud settings
  • Active webQsee Professional or Team plan, see pricing
The good news

Nothing in the bucket needs to move. The objects already live under /shared/, /protected/ and /private/, which are the exact prefixes the multi-user policies expect. The only thing that changes is who can do what, not where data is stored. No data migration, no downtime for already-uploaded items.

Migration plan

Six phases:

  1. Add the webqsee-user policy you didn't create the first time.
  2. Add the webqsee-user group.
  3. Create one IAM user per real team member, each with the webqsee-user-id tag.
  4. Distribute the new credentials.
  5. Flip the team-wide setting in webQsee from "single shared" to "per-user credentials".
  6. Retire (or keep) the old shared IAM user.

1. Add the webqsee-user IAM policy

This is the per-user policy that uses the aws:PrincipalTag/webqsee-user-id condition to give each IAM user write access only to their own /private/<user-id>/ prefix.

  1. Open IAM → Policies → Create policy

    Sign in to AWS, navigate to IAM → Policies → Create policy → JSON.

    Sanity check: The empty JSON editor is open.
  2. Paste the user policy JSON

    If your bucket is named something other than webqsee, replace it in the ARNs below.

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "UserListBucketScoped",
          "Effect": "Allow",
          "Action": [
            "s3:ListBucket",
            "s3:GetBucketLocation",
            "s3:GetBucketCors"
          ],
          "Resource": "arn:aws:s3:::webqsee",
          "Condition": {
            "StringLike": {
              "s3:prefix": [
                "shared/*",
                "shared/",
                "protected/*",
                "protected/",
                "private/${aws:PrincipalTag/webqsee-user-id}/*",
                "private/${aws:PrincipalTag/webqsee-user-id}/",
                ""
              ]
            }
          }
        },
        {
          "Sid": "UserSharedReadWrite",
          "Effect": "Allow",
          "Action": [
            "s3:GetObject", "s3:GetObjectTagging",
            "s3:PutObject", "s3:PutObjectTagging",
            "s3:DeleteObject",
            "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts"
          ],
          "Resource": "arn:aws:s3:::webqsee/shared/*"
        },
        {
          "Sid": "UserPrivateReadWrite",
          "Effect": "Allow",
          "Action": [
            "s3:GetObject", "s3:GetObjectTagging",
            "s3:PutObject", "s3:PutObjectTagging",
            "s3:DeleteObject",
            "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts"
          ],
          "Resource": "arn:aws:s3:::webqsee/private/${aws:PrincipalTag/webqsee-user-id}/*"
        },
        {
          "Sid": "UserProtectedReadOnly",
          "Effect": "Allow",
          "Action": ["s3:GetObject", "s3:GetObjectTagging"],
          "Resource": "arn:aws:s3:::webqsee/protected/*"
        }
      ]
    }
    Sanity check: JSON validates without errors. Click Next and confirm the review screen lists four statements.
  3. Save as webqsee-user

    Name: webqsee-user. Description: "webQsee Cloud Gallery, per-user, tag-scoped /private". Click Create policy.

    Sanity check: The Customer managed policies list now contains both webqsee-admin (from the original setup) and the new webqsee-user.

2. Create the webqsee-user group

  1. Create the group

    IAM → User groupsCreate group. Name: webqsee-user. Attach the webqsee-user policy. Click Create group.

    Sanity check: Two groups now exist: webqsee-admin (from before) and webqsee-user (new), each with 1 attached policy.

3. Create one IAM user per team member

  1. Collect webQsee user IDs

    Open webQsee → Settings → Current User and Team. The team roster shows the User ID for every member. Write them down (name → ID).

    Sanity check: You have a complete (name, webQsee user ID) mapping for every member who needs cloud access.
  2. Create one IAM user per member

    IAM → UsersCreate user. For each:

    • Username: <first-name>-webqsee (e.g. alice-webqsee)
    • Do not grant console access
    • Group: webqsee-user (or webqsee-admin for power users who should be able to write to /protected/)
    • Tag: key webqsee-user-id, value = that person's webQsee user ID (exact case)
    Sanity check: IAM → Users shows one IAM user per real team member, each Active, each in the right group, each with the matching webqsee-user-id tag visible on their Tags tab.
  3. Generate access keys

    For every new user: Security credentialsCreate access keyApplication running outside AWS. Save the Access Key ID + Secret immediately, secrets are shown once.

    Sanity check: You have one (Access Key ID, Secret) pair stored per team member in your password manager.

4. Distribute credentials securely

  1. Send each member their own pair

    Use an encrypted channel: 1Password sharing, Bitwarden Send, signed PGP email, Keybase, etc. Never paste secrets in Slack/Teams/Email in clear text. Each member only needs their own Access Key ID and Secret, the team-wide bucket name and region come down automatically from webQsee.

    Sanity check: Each member confirms receipt of their own credentials before you continue.

5. Flip webQsee from single-shared to multi-user mode

  1. Open team-wide Cloud settings

    webQsee → main window → GalleryCloud GalleryEdit SettingsTeam-wide section.

    Sanity check: You see the current settings: Storage Type = "S3 compatible storage, KEY-Auth", Authentication mode = "Single shared credentials".
  2. Switch the authentication mode

    Change Authentication mode from Single shared credentials (simplified) to Per-user credentials (multi-user). Bucket name, region and CORS stay the same.

    The shared Access Key / Secret fields will be replaced by a personal credentials section.

    Sanity check: A confirmation dialog warns "Switching to multi-user will require every team member to enter their own credentials". Click Confirm.
  3. Save the team-wide change

    Click Save team settings. The shared key is no longer used.

    Sanity check: Toast "Team cloud settings updated". The panel header reads "Cloud storage requires personal credentials".
  4. Enter your own personal credentials

    In the Personal section paste your Access Key ID and Secret (the ones generated for your IAM user in step 3). Click Save personal credentials.

    Sanity check: "Cloud Storage connected ✓" lights up green. Open the Cloud Gallery's Shared tab, the items you uploaded under the old shared user are still there.
  5. Ask team members to do the same

    Each member opens their own webQsee, sees a prompt "Team has switched to per-user credentials, please enter your Access Key", and pastes their own pair.

    Sanity check: Every team member reports "Cloud Storage connected ✓" and can see existing Shared and Protected items unchanged.
  6. Verify per-user isolation

    Have one user upload a screenshot to Private. Confirm it appears under private/<their-webqsee-id>/ in the S3 console. Have a second user try (via webQsee) to view it, they shouldn't see it in their own Private tab.

    Sanity check: S3 console shows the new object under private/u_8f3c9a1e/… (the uploader's ID). The other user's Private tab does not show it.

6. (Optional) Retire the old shared IAM user

  1. Confirm nothing depends on it anymore

    Wait a day. Confirm with the team that uploads, downloads and deletes all work with the new per-user setup. Keep a screenshot or note of which user holds which Access Key ID, in case you need a forensic trail.

    Sanity check: No team member reports CORS, 403 or "connection lost" errors. Everyone is on per-user credentials.
  2. Deactivate the old shared user's access key

    IAM → Users → webqsee-sharedSecurity credentials. Find the existing access key and click Make inactive. Wait 24 hours; if nothing breaks, click Delete.

    Sanity check: The access key shows Status: Inactive. webQsee Cloud Gallery still works for everyone because they now use their own keys.
  3. (Optional) Delete the IAM user itself

    Once the access key is gone for 24h with no issues, IAM → Users → webqsee-sharedDelete.

    Sanity check: webqsee-shared no longer appears in the Users list. Only the per-person IAM users remain.
Final sanity check

Open webQsee → Gallery → Cloud Gallery on at least two different members' machines. Both should see "Cloud Storage connected ✓". Each should see the same Shared items. Each should see only their own Private items.

In the AWS S3 console, the bucket layout should be unchanged structurally, shared/, protected/, private/<id>/…, just now with per-user authorisation.

Troubleshooting

  • "Cannot save team settings, mode not allowed": the bucket has objects under /private/ that don't match any current member's webQsee ID. Either delete those stragglers in the S3 console or temporarily widen the policy. (This usually only happens if you had ad-hoc paths from prior experiments.)
  • 403 on existing items after switching: a member can no longer download something they previously uploaded under the simplified scheme. Cause: the object lives under /private/ but at a path that doesn't match their webqsee-user-id. Move the object to the correct user's prefix using the S3 console (Copy → Move) or to /shared/.
  • "Access denied to /private/" for the uploader of a brand-new object: the IAM user is missing the webqsee-user-id tag or its value differs from the webQsee user ID. Re-check step 3 step 2.
  • One user still works with the old shared key: webQsee caches credentials. Ask them to Edit Settings → Personal → Clear → Save and then re-paste their own pair.
  • CORS error after the switch: should not happen, CORS is bucket-level and doesn't change. Re-apply the CORS JSON from the multi-user tutorial step 7 if needed.

Related tutorials

Multi-user S3 with RBAC

The full reference for the multi-user setup you're migrating to.

Open tutorial

Simplified S3 integration

The original setup you're moving away from, useful as a reference.

Open tutorial

Stop guessing. Start webQseeing.

Add webQsee to Chrome or Edge in one click. Most features are free, forever, no signup needed. Upgrade only if your team needs cloud sharing, S3 storage and Pro-grade tooling.

Works in Chrome 103+, Edge 103+ and most Chromium-based browsers. Install instructions.