s3-credentials delete-user command, closes #10

pull/16/head
Simon Willison 2021-11-02 22:16:52 -07:00
rodzic 217f790089
commit eec24e0866
3 zmienionych plików z 93 dodań i 0 usunięć

Wyświetl plik

@ -132,6 +132,23 @@ You can pass any number of usernames here. If you don't specify a username the t
s3-credentials list-user-policies
### delete-user
In trying out this tool it's possible you will create several different user accounts that you later decide to clean up.
Deleting AWS users is a little fiddly: you first need to delete their access keys, then their inline policies and finally the user themselves.
The `s3-credentials delete-user` handles this for you:
```
% s3-credentials delete-user s3.read-write.simonw-test-bucket-10
User: s3.read-write.simonw-test-bucket-10
Deleted policy: s3.read-write.simonw-test-bucket-10
Deleted access key: AKIAWXFXAIOZK3GPEIWR
Deleted user
```
You can pass it multiple usernames to delete multiple users at a time.
## Development
To contribute to this tool, first checkout the code. Then create a new virtual environment:

Wyświetl plik

@ -233,3 +233,38 @@ def list_buckets(array, nl):
click.echo(json.dumps(bucket, indent=4, default=str))
if gathered:
click.echo(json.dumps(gathered, indent=4, default=str))
@cli.command()
@click.argument("usernames", nargs=-1, required=True)
def delete_user(usernames):
"Delete specified users, their access keys and their inline policies"
iam = boto3.client("iam")
policy_paginator = iam.get_paginator("list_user_policies")
access_key_paginator = iam.get_paginator("list_access_keys")
for username in usernames:
click.echo("User: {}".format(username))
# Fetch and delete their policies
policy_names = []
for response in policy_paginator.paginate(UserName=username):
for policy_name in response["PolicyNames"]:
policy_names.append(policy_name)
for policy_name in policy_names:
iam.delete_user_policy(
UserName=username,
PolicyName=policy_name,
)
click.echo(" Deleted policy: {}".format(policy_name))
# Fetch and delete their access keys
access_key_ids = []
for response in access_key_paginator.paginate(UserName=username):
for access_key in response["AccessKeyMetadata"]:
access_key_ids.append(access_key["AccessKeyId"])
for access_key_id in access_key_ids:
iam.delete_access_key(
UserName=username,
AccessKeyId=access_key_id,
)
click.echo(" Deleted access key: {}".format(access_key_id))
iam.delete_user(UserName=username)
click.echo(" Deleted user")

Wyświetl plik

@ -141,3 +141,44 @@ def test_list_user_policies(mocker):
call().get_user_policy(UserName="two", PolicyName="policy-one"),
call().get_user_policy(UserName="two", PolicyName="policy-two"),
]
def test_delete_user(mocker):
boto3 = mocker.patch("boto3.client")
boto3.return_value = Mock()
boto3.return_value.get_user_policy.return_value = {
"PolicyDocument": {"policy": "here"}
}
def get_paginator(type):
m = Mock()
if type == "list_access_keys":
m.paginate.return_value = [
{"AccessKeyMetadata": [{"AccessKeyId": "one"}, {"AccessKeyId": "two"}]}
]
elif type == "list_user_policies":
m.paginate.return_value = [{"PolicyNames": ["policy-one"]}]
return m
boto3().get_paginator.side_effect = get_paginator
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(cli, ["delete-user", "user-123"], catch_exceptions=False)
assert result.exit_code == 0
assert result.output == (
"User: user-123\n"
" Deleted policy: policy-one\n"
" Deleted access key: one\n"
" Deleted access key: two\n"
" Deleted user\n"
)
assert boto3.mock_calls == [
call(),
call("iam"),
call().get_paginator("list_user_policies"),
call().get_paginator("list_access_keys"),
call().delete_user_policy(UserName="user-123", PolicyName="policy-one"),
call().delete_access_key(UserName="user-123", AccessKeyId="one"),
call().delete_access_key(UserName="user-123", AccessKeyId="two"),
call().delete_user(UserName="user-123"),
]