diff --git a/Makefile b/Makefile
index 1024dbd0b7..0bd6842713 100644
--- a/Makefile
+++ b/Makefile
@@ -591,3 +591,8 @@ update-concourse-gpg-keys: .download-gpg-keys ## Update vars file for concourse
.PHONY: enable_vpc_peer_db_access
enable_vpc_peer_db_access: ## Update vars file for concourse with the latest GPG keys
@ruby scripts/enable_vpc_peer_db_access.rb
+
+POLICY_NAME := S3BrokerUserPermissionsBoundary
+.PHONY: add_permissions_boundary_to_existing_s3_broker_users
+add_permissions_boundary_to_existing_s3_broker_users:
+ @ruby ./scripts/add_permissions_boundary_to_existing_s3_broker_users.rb --env=$(DEPLOY_ENV) --policy_name=${POLICY_NAME} $(ARGS)
diff --git a/README.md b/README.md
index 02980a4616..5434198c64 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@ It does not include the AWS IAM roles which are assumed by different system comp
1. [Cloud Foundry deployment configuration options](#cloud-foundry-deployment-configuration-options)
1. [Accessing Concourse](#accessing-concourse)
1. [Finding configuration](#finding-configuration)
+1. [Utility Scripts](#utility-scripts)
## What does `paas-cf` contain?
`paas-cf` separates the responsibility for configuring, deploying, running, and monitoring Cloud Foundry, from those responsibilities held by [`paas-bootstrap`](https://github.com/alphagov/paas-bootstrap).
@@ -133,3 +134,29 @@ The following table outlines some important directories in the repository, their
| `terraform/vpc-peering` | Terraform configuration for VPC peering between the Cloud Foundry VPC and others | I want to change a property of our existing VPC peers, and future ones |
| `tools/buildpacks` | Golang implementation of our regular buildpack update emails | I want to make a change to the email we send to tenants about buildpack updates |
| `tools/metrics` | A Prometheus exporter which exposes a variety of platform-level metrics collected from different sources |
- I want to add a new metrics
- I want to change the frequency of the measurement of an existing metric
|
+
+## Utility Scripts
+
+### Add a permissions boundary policy to paas-s3-broker users
+
+Configure the POLICY_NAME variable within the Makefile with the name of the Permissions Boundary policy that you wish to add to the paas-s3-broker users.
+
+Run this command to add a permissions boundary to paas-s3-broker users:
+
+```
+gds aws paas- -- make add_permissions_boundary_to_existing_s3_broker_users ARGS="<--dry-run>"
+```
+
+Replace:
+
+* `` with the environment and role that you want to use e.g. prod-admin.
+* `` with the environment that you want to update e.g. prod-lon.
+* Only use the --dry-run flag if you would like the script to run but not change anything.
+
+If the command is successful, the output will look similar to this:
+
+```
+Dry run? false
+Policy attached to user: paas-s3-broker-dev05-0a094c73-7ae7-42cc-b028-6c78b93985d7
+Policy attached to user: paas-s3-broker-dev05-dad332ff-3557-4f13-a768-5dc0e8421cd4
+```
diff --git a/scripts/add_permissions_boundary_to_existing_s3_broker_users.rb b/scripts/add_permissions_boundary_to_existing_s3_broker_users.rb
new file mode 100755
index 0000000000..a6e13d86f1
--- /dev/null
+++ b/scripts/add_permissions_boundary_to_existing_s3_broker_users.rb
@@ -0,0 +1,77 @@
+#!/usr/bin/env ruby
+
+require "aws-sdk-iam"
+require "optparse"
+
+def get_permissions_boundary_arn(iam_client, permissions_boundary_name)
+ policies = iam_client.list_policies(scope: "Local", only_attached: false).policies
+
+ permissions_boundary = policies.find { |policy| policy.policy_name == permissions_boundary_name }
+
+ permissions_boundary&.arn
+end
+
+def add_permissions_boundary_to_user(iam_client, username, permissions_boundary_arn, dry_run)
+ user = iam_client.get_user(user_name: username).user
+ current_permissions_boundary = user.permissions_boundary&.permissions_boundary_arn
+
+ if current_permissions_boundary.nil? || current_permissions_boundary != permissions_boundary_arn
+ unless dry_run
+ iam_client.put_user_permissions_boundary(
+ user_name: username,
+ permissions_boundary: permissions_boundary_arn,
+ )
+ end
+ puts "Permissions_boundary added successfully to user #{username}."
+ else
+ puts "User #{username} already has the permissions_boundary."
+ end
+end
+
+def main(env, policy_name, dry_run)
+ iam_client = Aws::IAM::Client.new
+
+ permissions_boundary_arn = get_permissions_boundary_arn(iam_client, policy_name)
+
+ if permissions_boundary_arn.nil?
+ puts "Permissions boundary policy with name #{policy_name} not found"
+ else
+ paas_s3_broker_users = iam_client.list_users.users.select { |user| user.user_name.start_with?("paas-s3-broker-#{env}") }
+
+ paas_s3_broker_users.each do |user|
+ add_permissions_boundary_to_user(iam_client, user.user_name, permissions_boundary_arn, dry_run)
+ end
+ end
+end
+
+ARGV << "-h" if ARGV.empty?
+
+options = {}
+parser = OptionParser.new { |opts|
+ opts.banner = "Usage: ./add_permissions_boundary_to_existing_s3_broker_users.rb [options]"
+
+ opts.on("--env DEPLOY_ENV", String, "Specify the env this script should operate on") do |env|
+ options[:env] = env
+ end
+
+ opts.on("--policy_name POLICY_NAME", String, "Specify the policy that will be added to the s3 broker users") do |policy_name|
+ options[:policy_name] = policy_name
+ end
+
+ opts.on("--dry-run", TrueClass, "Specify --dry-run if you want to run the script without changing anything") do |dry_run|
+ puts "Dry run? #{dry_run}"
+ options[:dry_run] = true
+ end
+
+ opts.on_tail("-h", "--help", "Show help") do
+ puts opts
+ exit
+ end
+}.parse!
+parser.parse!
+
+if options[:env].nil? || options[:policy_name].nil?
+ raise "--env and --policy_name are mandatory"
+end
+
+main(options[:env], options[:policy_name], options[:dry_run]) if $PROGRAM_NAME == __FILE__