Setting up Lambda to Create AWS CloudFormation without IAM Policy - amazon-web-services

What are the best practices for CloudFormation stacks that create IAM users? I'm creating an API that will automatically add clients to one of our services, i.e., it uses a CloudFormation template to create buckets, dynamo db entries, IAM user, etc.
You send a POST call to a route with specific parameters in the call, and it will create the client and everything that's needed for them; however, due to security concerns, I'm hesitant to allow a lambda role to have IAM permissions. We take IAM user creation extremely seriously, as this could always have a negative connotation.
Is there a way that I could create a CloudFormation stack, but require an Admin's manual approval to create it? I noticed there isn't a way to "delay" a stack for approval from another entity who has the correct permissions, since, for stacks to be created in the first place, proper policies must be in place for the entity creating it.
So, in summary, send a POST request to a URL that creates a stack needing Admin approval with proper permissions to activate the creation.
I'm starting to believe this isn't possible, so what are some recommendations?
We have thought about some other methods:
API call uploads the generated CloudFormation template to an s3, then admins manually create the stack with object url
Modify the CloudFormation template to remove the IAM section, and have Admins create that portion manually
Either way, it kinda takes away from the "automation" aspect.
Any thoughts?

I would suggest to use AWS Step Functions to create a state machine (a codified process) around the approval mechanism. The POST request would then trigger a new Step Function execution with the specific CFN template. I imagine you would need to build a simple frontend to list all the pending Step Function executions (i.e. pending approvals).
An alternative solution could be built on top of CodePipeline. A pipeline execution can have a manual approval action and it can be configured in a way that it would creating the stack by itself.

If you are open to additional tools, you can trigger the cloudformation stack via a Jenkins job and allow only the admins to trigger that job.

Related

Get CloudFormation stack output from client environment

We're working on creating a CloudFormation stack which will be published to clients to install an "on-prem" agent of our product on a client's AWS environment.
We want to initiate the connection from our end and add some configurations based on the outputs of the CloudFormation.
Is there a way to automatically transmit the output of CloudFormation back to the publisher of the template? I've been looking for a solution online but with no avail. Not sure this is possible - but I'm hoping someone here has maybe done something similar.
Couldn't find any solution. From what I'm seeing the "only" way to do so is to ask the client to manually send back the output, but I'm glad to be proven wrong :)
So I happen to be working on something similar, and this is what I've found that works. There are caveats which I will enumerate.
TLDR;
To have a CloudFormation stack "phone home" you can specify a SNS topic that the stack will post changes to when it is created, updates, and/or deleted. The CloudFormation docs (see 'Notification options') and CLI reference (see '--notification-arns') mention this. You can then have a Lambda respond to the SNS messages to enable your desired behavior. Unfortunately you cannot specify this topic in the stack template and can only provide is when making the call to create the stack. This can be worked around by using a nested CloudFormation stack.
My (current) approach:
Use a SNS topic that has a resource policy which allows the installing user's AWS account to publish messages to your topic. AWS documents how to do this, but be warned that using a AWS Account Principal in the SNS topic policy (which so far is the only thing that has worked for me) allows anything in the customer account to publish to the SNS topic. This security issue is one part I have yet to work around and ideally a combination of a Service Principal and restrictive IAM policy conditions could be used instead.
Create a template that you provide to your user that acts as the top-level stack, and contains a nested stack which you will use to create resources in your user's AWS account. This also gives you an opportunity to do things like use a mapping that specifies per-region SNS topics for your CloudFormation notifications.
When specifying your nested stack in your top-level stack template you use the NotificationARNs property to provide the SNS topic ARN so that the nested stack will report back to you.
The Lambda (or other mechanism) that subscribes to the SNS topic will need to parse and filter the messages that CloudFormation posts to react appropriately. When needing to acquire the ARN of something like an IAM role in the user's AWS account my recommendation would be to give the role a static name such that you can know the role's ARN by using the user's AWS account ID and the role name. Ideally the role will have permission to retrieve information about the CloudFormation stack so that you can get ARNs and other data about any other resources your stack created.

How do I create a deployment user on AWS that is truly least privilege?

We want to have deployment users to use in our pipelines, purely for programmatic access. These users will be created per project, rather than using one deployment user for all stacks.
I'm trying to lock down the resources that these deployment users have permission to change, but I'm struggling due to the fact that the ARN is not yet known until the stack is created, meaning that creating the IAM policy to restrict it to only certain resources is proving difficult.
For example, say I want to create an application load balancer (with listeners, rules etc) - I want the deployment user to have permission to create an ALB (easy enough) but I want the deployment user to only have permission to delete or modify the newly created ALB, not any other ALBs.
Any tips / smart ways to do this? The ARNs are generated and "random" as I dislike naming my resources and having to modify the names if I change a setting that requires replacement.
You can use IAM policy conditions to restrict access to resources based on tags.
For example, you can add two policy statements with a condition element to allow specific actions on a resource:
User1 can create a resource only if the request contains owner=user1 tag.
User1 can update or delete a resource only if owner=${aws:username} tag is attached to the resource.
You can find policy example in this guide:
https://docs.aws.amazon.com/IAM/latest/UserGuide/access_tags.html

Automate create same policy to all AWS Accounts

Looking for suggestions on an AWS use case.
I have a lot of accounts in my AWS.
I am looking to create the same policy in all of my accounts. Now I want to automate it.
1) Can I create any lambda function or cloud formation template or any way to automate the creation of the same policy in all of my accounts even if any new account is created and it needs to add to all existing accounts if it's not there?
2)If possible then how I can get access to lamda function to create policies.
Thanks
If you have multiple AWS accounts, you may want to consider using AWS Organizations and Service Control Policies (SCP). The policies are applied at a root level and affect all accounts under that root.
By using organizations, you can get events when account creation is completed and apply any additional changes to the account that you would like by using a Lambda function that receives a CloudWatch event. The event would contain information about the newly created account.
The two options that provided seem to require a lot of manual administrative overhead, but would be happy to answer your concerns. Creating Lambda function which creates policies is done using the SDK. There is an IAM method called "CreatePolicy" which provides this functionality.
If I'm understanding your needs here (and assuming they're still the same after almost 3 years), you might consider using CloudFormation StackSets instead. They allow you to define common resources, including policies, as a CloudFormation template and have that template deployed to every one of the accounts in your organization.
You'll need to enable trusted access for CloudFormation StackSets in your organization AWS account, which will allow it to deploy the stacks to the rest of your organization's accounts.
To create the stack you'll need to:
Define a CloudFormation template using either CloudFormation directly, or capturing the synthesized stack from an AWS CDK application.
Upload the produced template to a location in S3 that's accessible by your top-level organization account.
Create the StackSet in the CloudFormation console (or the CLI, CDK, etc). You can either deploy the stack to all accounts or filter by specific accounts or organizational units (OUs).
Once created, the stacks will be automatically deployed to the desired accounts, and kept up to date when the stack is updated. This will require you to define significantly less infrastructure.

Can I AutoCreate an IAM role for a Cloudformation stack from the template?

I've been told to restrict my Cloudformation to only the commands it needs. With a role. To create the role I can spend months going through my template to decide that launching an EC2 instance actually involves 10 different IAM items (like creating tags, network interfaces, volumes, etc..) and figure out all the ARNs in question and so on for all my resources. (Because these resources aren't created yet, I'm going to need a lot of * for this role to be useful next time.)
OR, is there a tool to do that for me? I imagine supplying my template and the tool going away and making the bulk of the role. Maybe a few bits to change where the template does things based on parameters maybe.
OR, if I create the stack with Cloudtrail turned on, is there a tool to convert from cloudtrail logs to a policy document?
OR any other way to avoid months of work?
Your requirements to restrict your CloudFormation's execution role, to only the commands it needs is definitely best practice, and in the CloudFormation console, when you're launching a template there is an option for "Permissions" that says:
Choose an IAM role to explicitly define how CloudFormation can create, modify, or delete resources in the stack. If you don't choose a role, CloudFormation uses permissions based on your user credentials.
What this is saying is that when deploying your template CloudFormation can either use your users permissions to deploy the services defined in your template or, in accordance with best practice and the principal of least privilege, CloudFormation can assume a role that has been designed and defined with permissions that are specific to this specific template. This is best practice because we want to be certain of the services and resources that are being used and executed.
There are a few ways you can achieve your goal of determining what permissions CloudFormation needs to deploy your template. In your case I imagine you have many resources, and because of this it seems like a mammoth task that will take months to figure out, but the reality is that it will take just a few hours. I'll suggest two ways you could accomplish this... Lets begin:
CLI
Create a new role from my CLI that has no permissions. Be sure to create the trust relationship to allow CloudFormation to assume the role.
Create a new blank policy document and attach that policy to the role you just created.
Add an ALLOW iam:PutRolePolicy for that new policy I just created.
Assume the role new from the CLI, so that you're using that role going forward
Create a bash script that is going to repeatedly attempt to deploy the CloudFormation template until it successfully deploys it. Assuming the template is formatted correctly, each time the script try's to deploy the template it's going to receive a permissions error because the role that was assumed in step 2 doesn't have the correct permissions. The error is going to look something like:
ClientError: An error occurred (AccessDeniedException) when calling the xxxxxxxxxxxx operation: User: arn:aws:sts::[ACCOUNT-NUMBER]:assumed-role/[my-Role-Name]/[Logged In Username] is not authorized to perform: iam:PassRole on resource: xxxxxxxxxxxxx
In the example above, our template needed to perform iam:PassRole but the role assumed doesn't have permission to do that, hence the error. To solve this have the script parse the iam:PassRole from the error (ie the permission it needs), and add then add an iam:PassRole inline policy to the role that was created in step 2 above via the aws iam put-role-policy command (syntax and full requirements here).
Once the policy is attached to the IAM role, the script should then loop back and try to deploy the template again. It should to keep doing this until the template successfully deploys, or until it receives some other error which is unrelated and it stops executing completely. When it deploys successfully, you'll now have a role that has the exact permissions needed, via inline policies, to deploy the template.
NOTE 1: depending on your account settings and/or the number of resources in your template, you could easily find yourself hitting permissions boundaries or exceeding policy size limits, these are outside the scope of this question but you may find yourself needing to address those issues.
NOTE 2: to be sure, this is a potentially dangerous technique and this sort of script should only be run in a dev environment to prevent unwanted outcomes. If you unwittingly had a destructive operation in your CloudFormation template and ran it in a production account, this sort of script could cause a disaster incident; it would be prudent to only run a script like this in a dev so you'll be able to see all the permissions that are created, identify if you have any unexpected or unwanted operations/permissions, and then you can craft an appropriate role and policy for deployments in your production account with the necessary permissions.
CloudTrail with Console based deployment
A less advanced method would be to use CloudTail in a non-production account to audit the permissions being used by CloudFormation and craft a policy from that (effectively the second option in your question). I'm not aware of any off-the-shelf scripts to parse CloudTrail logs and create policy documents, but doing it manually is not difficult and scripting it can also be done. Nonethless, if you do go with this option, at this stage, I'd suggest sticking with using the console as you'll quickly be able to see what you need.
Login to a non-production account with a user that has admin permissions
Head over to CloudFormation and deploy the template using the logged in users permissions (ie admin)
Once deployment is complete, head over to CloudTrail to find all the permissions that were used by that user during the deployment; filter the list by User name, and then from the events you'll be able to see what permissions were used.
Craft a policy that has the permissions that were used by CloudFormation
Take that policy and add it to the deployment role that CloudFormation will use in the production account
Depending on your bash scripting skills, both of these options should take about the same amount of time; a few hours, maybe even a day, definitely not months.
Good luck!

Is it possible to create an AWS IAM policy for automatic resource tagging?

I would like to set up different AWS Identity and Access Management (IAM) users so that if an AWS resource is created by that IAM user, the resource is automatically assigned a specific tag.
For example: if IAM user F creates a new EC2 instance, the instance is automatically tagged as User:MrF. Is it possible to build a custom policy that does this?
My company GorillaStack have an open source lambda function that does exactly that.
The function 'listens' for CloudTrail logs to be delivered and tag the created resource with the ARN of the user that created it. It also support cross account tagging, for cases where a central account collects CloudTrail logs for other accounts.
Github: https://github.com/GorillaStack/auto-tag
Blog Post: http://blog.gorillastack.com/gorillastack-presents-auto-tag
It got a shout out at the 2015 re:Invent conference which is pretty cool :)
Hope that helps!
This is not available when using the AWS APIs directly (i.e. there's no way to command all AWS API's to tag new resources automatically on your behalf), however, depending on the specifics of your use case you could work around that limitation by correlating the creating user with the resource via post hoc tagging:
Workaround
You could activate AWS CloudTrail, which records AWS API calls for your account and delivers log files to you and provides exactly the information you are after:
The recorded information includes the identity of the API caller, the
time of the API call, the source IP address of the API caller, the
request parameters, and the response elements returned by the AWS
service.
Based on that information, a dedicated service of yours could analyze the logs and apply post hoc tags to all resources based on the logged user and created resource via the resp. API actions. Please see my answer to Which user launched EC2 instance? for some caveats/constraints to consider when going down this route.
An even better solution (faster plus I believe cheaper than parsing through CloudTrail logs) is to use CloudTrail but in combination with CloudWatch Events.
This is the basic concept described in a diagram
The implementation is detailed in this article:
https://blogs.aws.amazon.com/security/post/Tx150Z810KS4ZEC/How-to-Automatically-Tag-Amazon-EC2-Resources-in-Response-to-API-Events
The article also describes how to setup an IAM policy that only allows the creator of a resource to perform certain actions (like start / stop, describe, edit, terminate) against it.
I would chose AWS Config. Create a rule that automatically tags resources on creation. No cost, works across multiple accounts. Great for enforcing compliance. https://aws.amazon.com/about-aws/whats-new/2019/03/aws-config-now-supports-tagging-of-aws-config-resources/
Currently there is no such feature on IAM. If what you need is allow/deny based on user names, what you could do is use variables on your policy to allow or deny access based on naming conventions, e.g.:
...
"Resource":"arn:aws:dynamodb:us-east-!:123456789:table/ItemsCatalog_${aws:username}"
...