A common use case is to manage a small group of users and their
access to AWS resources. This can be done using the
sixtyfour
package, which provides a set of high-level
functions to make your life easier.
User groups
First, create two user groups - admin
and
users
- with the six_admin_setup()
function.
six_admin_setup(users_group = "myusers", admin_group = "myadmin")
#> ℹ whoami: *****
#> ℹ
#> ℹ myusers group created - add users to this group that do not require admin permissions
#> ℹ Added policies to the myusers group: AmazonRDSReadOnlyAccess, AmazonRedshiftReadOnlyAccess, AmazonS3ReadOnlyAccess,
#> AWSBillingReadOnlyAccess, and IAMReadOnlyAccess
#> ℹ
#> ℹ myadmin group created - add users to this group that require admin permissions
#> ℹ Added policies to the myadmin group: AdministratorAccess, Billing, CostOptimizationHubAdminAccess, AWSBillingReadOnlyAccess, and
#> AWSCostAndUsageReportAutomationPolicy
#> ℹ
#> ℹ Done!
Users
Next, create five users - one in the admin
group, three
in the users
group, and one not in either group.
The admin user:
name_admin <- random_user()
user_admin <- six_user_create(name_admin, copy_to_cb = FALSE)
#> ℹ Added policy UserInfo to SleepySubsidy
#> ✔ Key pair created for SleepySubsidy
#> ℹ AccessKeyId: *****
#> ℹ SecretAccessKey: *****
aws_user_add_to_group(name_admin, "myadmin")
The non-admin users:
users_non_admin <- map(1:3, \(x) {
six_user_create(random_user(), copy_to_cb = FALSE)
})
#> ℹ Added policy UserInfo to NoncommercialPub
#> ✔ Key pair created for NoncommercialPub
#> ℹ AccessKeyId: *****
#> ℹ SecretAccessKey: *****
#> ℹ Added policy UserInfo to BridgedSomewhere
#> ✔ Key pair created for BridgedSomewhere
#> ℹ AccessKeyId: *****
#> ℹ SecretAccessKey: *****
#> ℹ Added policy UserInfo to DoctrinalSeating
#> ✔ Key pair created for DoctrinalSeating
#> ℹ AccessKeyId: *****
#> ℹ SecretAccessKey: *****
invisible(map(users_non_admin, \(x) {
aws_user_add_to_group(x$UserName, "myusers")
}))
names_users <- map_chr(users_non_admin, \(x) x$UserName)
And last, create a user that is not part of either of the above groups.
name_user_wo <- random_user()
user_wo_access <- six_user_create(name_user_wo, copy_to_cb = FALSE)
#> ℹ Added policy UserInfo to BrimmingAviation
#> ✔ Key pair created for BrimmingAviation
#> ℹ AccessKeyId: *****
#> ℹ SecretAccessKey: *****
Buckets and files
Create a bucket, and put a file in it.
bucket <- random_bucket()
demo_rds_file <- file.path(system.file(), "Meta/demo.rds")
six_bucket_upload(path = demo_rds_file, remote = bucket, force = TRUE)
#> [1] "s3://bucket-cuwgyafqtleomkhd/demo.rds"
Check access
Then check access for the user that should not have access
withr::with_envvar(
c("AWS_REGION" = user_wo_access$AwsRegion,
"AWS_ACCESS_KEY_ID" = user_wo_access$AccessKeyId,
"AWS_SECRET_ACCESS_KEY" = user_wo_access$SecretAccessKey
),
aws_bucket_list_objects(bucket)
)
#> Error: AccessDenied (HTTP 403). User: arn:aws:iam::*****:user/BrimmingAviation is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::bucket-cuwgyafqtleomkhd" because no identity-based policy allows the s3:ListBucket action
User BrimmingAviation does not have access (read or write), as intended.
Then check access for a user that should have read access and NOT write access:
# Read
withr::with_envvar(
c("AWS_REGION" = users_non_admin[[1]]$AwsRegion,
"AWS_ACCESS_KEY_ID" = users_non_admin[[1]]$AccessKeyId,
"AWS_SECRET_ACCESS_KEY" = users_non_admin[[1]]$SecretAccessKey
),
{
aws_bucket_list_objects(bucket)
}
)
#> # A tibble: 1 × 8
#> bucket key uri size type etag lastmodified storageclass
#> <glue> <chr> <glu> <fs:> <chr> <chr> <dttm> <chr>
#> 1 bucket-cuwgyaf… demo… s3:/… 256 file "\"7… 2025-03-12 22:51:04 STANDARD
# Write
withr::with_envvar(
c("AWS_REGION" = users_non_admin[[1]]$AwsRegion,
"AWS_ACCESS_KEY_ID" = users_non_admin[[1]]$AccessKeyId,
"AWS_SECRET_ACCESS_KEY" = users_non_admin[[1]]$SecretAccessKey
),
{
links_rds_file <- file.path(system.file(), "Meta/links.rds")
six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE)
}
)
#> Error in `map()`:
#> ℹ In index: 1.
#> Caused by error:
#> ! AccessDenied (HTTP 403). User: arn:aws:iam::*****:user/NoncommercialPub is not authorized to perform: s3:PutObject on resource: "arn:aws:s3:::bucket-cuwgyafqtleomkhd/links.rds" because no identity-based policy allows the s3:PutObject action
User NoncommercialPub does have access for read and NOT for write, as intended.
And make sure the admin has read and write access
# Read
withr::with_envvar(
c("AWS_REGION" = user_admin$AwsRegion,
"AWS_ACCESS_KEY_ID" = user_admin$AccessKeyId,
"AWS_SECRET_ACCESS_KEY" = user_admin$SecretAccessKey
), {
aws_bucket_list_objects(bucket)
}
)
#> # A tibble: 1 × 8
#> bucket key uri size type etag lastmodified storageclass
#> <glue> <chr> <glu> <fs:> <chr> <chr> <dttm> <chr>
#> 1 bucket-cuwgyaf… demo… s3:/… 256 file "\"7… 2025-03-12 22:51:04 STANDARD
# Write
withr::with_envvar(
c("AWS_REGION" = user_admin$AwsRegion,
"AWS_ACCESS_KEY_ID" = user_admin$AccessKeyId,
"AWS_SECRET_ACCESS_KEY" = user_admin$SecretAccessKey
), {
links_rds_file <- file.path(system.file(), "Meta/links.rds")
six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE)
}
)
#> [1] "s3://bucket-cuwgyafqtleomkhd/links.rds"
User SleepySubsidy DOES have access for read and write, as intended.
Cleanup
Then cleanup the users, groups and buckets:
# Users
map(c(name_admin, names_users, name_user_wo), six_user_delete)
# Groups
map(c("myadmin", "myusers"), six_group_delete)
# Bucket
six_bucket_delete(bucket, force = TRUE)