I have to create a fair handful of programmatic users for tool access into my AWS environment and I thought it would be useful to create a function to distribute to each account.
The purpose is to trigger a one-time creation of a IAM user with programmatic access, and store the keys in the Secrets Manager all while keeping the keys as hidden as possible (i.e. none in the function, none in logging, and none in transit) until landing in Secrets Manager.
I have set the lambda permissions for this specific function to deny the generation of CloudWatch or CloudTrail logs (which seems to be functioning properly through testing). However, the creation of the secret is giving me issues that I can't seem to solve. Below is my script:
import boto3, json
from botocore.exceptions import ClientError
def lambda_handler(event, context):
iam_client = boto3.client('iam')
user_name = 'tenable-cc-test'
policy_arn = 'arn:aws:iam::aws:policy/ReadOnlyAccess'
account_id = boto3.client("sts").get_caller_identity()["Account"]
try:
user = iam_client.create_user(
UserName=user_name,
Tags=[
{
'Key': 'donotdelete',
'Value': 'yes'
}
]
)
except ClientError as error:
if error.response['Error']['Code'] == 'EntityAlreadyExists':
return 'User {0} is already available'.format(user_name)
else:
return 'Unexpected error occurred... User {0} could not be created'.format(user_name)
try:
iam_client.attach_user_policy(
UserName=user_name,
PolicyArn=policy_arn
)
except ClientError as error:
print('Unexpected error occurred while attaching policy... cleaning up', error)
iam_client.delete_user(UserName=user_name)
return 'User could not be created', error
try:
access_secrete_key = iam_client.create_access_key(
UserName=user_name
)
except ClientError as error:
print('Unexpected error occurred while creating access key... cleaning up')
iam_client.detach_user_policy(
UserName= user_name,
PolicyArn= policy_arn
)
iam_client.delete_user(UserName=user_name)
return 'User could not be created', error
print('User {0} has been created successfully'.format(user_name))
access_key = access_secrete_key['AccessKey']['AccessKeyId']
secret_key = access_secrete_key['AccessKey']['SecretAccessKey']
sm = boto3.client('secretsmanager')
sm.create_secret(
Name='tenable-cc-keys',
#ClientRequestToken='string',
Description='Keys for tenable-cc user.',
#KmsKeyId='string',
#SecretBinary=b'bytes',
SecretString={
'username': 'access_key',
'password': 'secret_key'
},
Tags=[
{
'Key': 'donotdelete',
'Value': 'yes'
},
]
)
print('Account:', account_id)
print('Access Key:', access_key)
print('Secret Key:', secret_key)
The error that I am receiving is:
{
"errorMessage": "Parameter validation failed:\nInvalid type for parameter SecretString, value: {'username': 'access_key', 'password': 'secret_key'}, type: <class 'dict'>, valid types: <class 'str'>",
"errorType": "ParamValidationError",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 72, in lambda_handler\n 'Value': 'yes'\n",
" File \"/var/runtime/botocore/client.py\", line 357, in _api_call\n return self._make_api_call(operation_name, kwargs)\n",
" File \"/var/runtime/botocore/client.py\", line 649, in _make_api_call\n api_params, operation_model, context=request_context)\n",
" File \"/var/runtime/botocore/client.py\", line 697, in _convert_to_request_dict\n api_params, operation_model)\n",
" File \"/var/runtime/botocore/validate.py\", line 297, in serialize_to_request\n raise ParamValidationError(report=report.generate_report())\n"
]
}
I have tried all manner of variations that I could think of for the SecretString, removed unused descriptors, commented them out, left them available. All lead me to similar errors. I have poured over the following documentation and I believe I am missing something very simple. Also, no matter what I have tried, it continues to tell me that my Parameter class is 'dict' when it needs to be 'string', I am simply following the format in the aws documentation.
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html
Can anyone offer some council? Any help is greatly appreciated!
SecretString should be string, not a dictionary. One way to do this, is to covert your dict to json string, using json.dumps
:
SecretString=json.dumps({
'username': 'access_key',
'password': 'secret_key'
}),