Skip to content

Commit

Permalink
Improve setup and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
bkrodgers committed Feb 23, 2016
1 parent 9493968 commit 30f3cab
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 42 deletions.
45 changes: 31 additions & 14 deletions assets/custom-types/remote-route53/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,53 @@

In order to manage a domain across accounts, you will need to set up the Lambda function and the appropriate roles. These changes are only needed when you add a new domain, or when you add a new account and/or region that you want to create CloudFormation stacks in which use the domain.

### Note:
The Lambda function needs to be uploaded once for every account that will use this function. It should be uploaded to us-east-1, as that's where the Route 53 endpoint lives. When you upload it this first time, it will also create an SNS topic that can be used for calls from CloudFormation stacks in that same region. To call it from other regions, you'll need to create additional SNS topics, but those topics can all call that same function within a given account.

There are multiple ways you can set up roles and policies to accomplish the same basic thing. For example, if you have multiple domains, you can use a single policy for all of them. Or you can choose to use inline policies instead of managed. Or if you prefer to create separate roles for each domain as well, that works. If you prefer a different approach, you can do so as long as they end up with the same result. The approach we are taking here is:

1. For each domain we want to manage via this CloudFormation type, create a policy for that domain.
2. Create a single role for all domains we want to manage.
3. Create a Lambda execution role in each account where we want to use this type.
4. Grant permission to each of those Lambda exeuction roles to assume the domain manager role.
There is no harm in using this function in templates that are being run from the same account that owns the domain. While you don't need the cross-account functionality in this case and could use the native Route 53 types, if you want to author a template that will be used in multiple accounts, it may be more straightforward to use this function universally rather than try to create conditionals or multiple template versions.

## Pre-reqs:

1. If you haven't already, install the AWS CLI. You'll either want to setup profiles for both accounts (http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-multiple-profiles), or just have both credentials ready and reconfigure.
2. When we refer to authenticating to an account below, you do this either by setting the `AWS_DEFAULT_PROFILE=<profile>` environment variable if you are using profiles, or by running `aws configure` and enter in the credentials for that account. If you are using profiles, and you want to execute against a different region than is in the profile, you can override it with the environment variable `AWS_DEFAULT_REGION=<region>`.
- References to the "Route 53 account" below refer to the AWS account that owns the hosted zone for the domain.
- References to the "CF account" refer to another AWS account where you want to be able to create route 53 entries in the "Route 53 account."
3. You also need `npm` and `jq` installed.


## Per domain setup:

These steps need to be performed for each domain that you want to allow management from in other accounts.
These steps need to be performed for each domain that you want to allow management from in other accounts. This only needs to be done once in the account that owns the domain, not in each AWS account.

1. You will need access to change IAM policies and roles in the Route53 account that is managing the domain ("Hosted Zone" in AWS terminology). You will also want to look up the hosted zone ID for the domain.
2. Authenticate to the Route 53 account.
3. From the `assets/custom-types/remote-route53` directory, run `./create-zone-admin.sh remote-route53-cf-admin <zone-id-to-manage>`. You can use another name for the role (`remote-route53-cf-admin`) if you prefer. If you've already setup another domain in this account, you can reuse the same role name and it will add this domain to the role.
2. Authenticate to the **Route 53 account.**
3. From the `assets/custom-types/remote-route53` directory, run `./create-zone-admin.sh <zone-id-to-manage>`. If you've already setup another domain in this account, it will add this domain to the role.
4. The ARN of the role will be used as the "DestinationRole" parameter to the Cloud Formation resource, regardless of what account you are running the template from.
5. If you are adding an additional domain but have already setup all the accounts and regions per the instructions below, you do not need to repeat these steps. They will pick up the new domain on the existing role.


## Per AWS account setup:

These steps need to be performed for each account that you want to run CloudFormation templates from that use this function (the "CF Account").
These steps need to be performed for each account where you want to run CloudFormation templates from that use this function (the "CF Account").

1. You will need access to change IAM policies and roles in both the Route53 account and the "CF Account". You will also need permissions to create Lambda functions and SNS topics in the CF account.
2. Authenticate to the **CF account.** Choose a region that has Lambda support. us-east-1 is recommended, since that is where the AWS Route 53 endpoints live, but you can use any supported region.
3. Run `./deploy.sh` to create the Lambda function and the appropriate execution role.
4. Look at the output from the script above and note the command it asks you to run (`./add-zone-admin-trust <role-name>`).
5. Authenticate to the **Route 53 account.**
6. Run the command from step 4. This will grant the function permissions to assume the Route 53 role created earlier.

## Per AWS region/account combination setup:

These steps need to be performed for **each** region in **each** account where you want to run CloudFormation templates from that use this function (the "CF Account"). This does not to be run for the region where you uploaded the Lambda function above, as it created an SNS topic for that region. For example, if you uploaded the Lambda function to us-east-1, you would not need to run this for us-east-1. But if you will create stacks in us-west-1 under this account, you would need to create an SNS topic in us-west-1.

1. You will need access to change Lambda function permissions and create SNS topics in the CF account.
2. Authenticate to the **CF account.**
3. Look up the ARN for the Lambda function in this account. If you just performed the steps above, the deploy script will outputted this.
4. Run `./create-sns-topic.sh <lambda-function-arn> <sns-topic-target-region>`.

## Now what?

Now that you have setup your roles, Lambda functions, and SNS topics, you will need two pieces of information to use this in your templates.

1. You will need access to change IAM policies and roles in both the Route53 account and the "CF Account". You will also need permissions to create Lambda functions in the CF account.
3. Authenticate with the CF account.
4. Run `./deploy.sh` to create the Lambda function and the appropriate execution role.
1. When you created the zone admin role, it output the ARN of the role. This value needs to be specified as the `DestinationRole` parameter to the Route 53 type in your template. It will take the form of `arn:aws:iam::<route-53-account-number>:role/remote-route53-cf-admin`. The value will be the same regardless of what account you are running the Cloud Formation template from. Therefore, this value can be hard coded in your template or be set as a default value if you want to parameterize it.
2. The SNS topic ARN for the account and region that the Cloud Formation template is running in must be specified as the `ServiceToken` parameter to the Route 53 type in your template. It will take the form of `arn:aws:sns:<cf-region>:<cf-account-num>:cf-remote-route53`. Because the account and region should always be the values for where you are running the template, you can assemble this dynamically, using CF psuedo-parameters. This would look like `` `Fn::Join`(":", Seq("arn:aws:sns", `AWS::Region`, `AWS::AccountId`, "cf-remote-route53"))``. This can then be hard coded into your template and will fill in with the proper region and account ID automatically.
5 changes: 3 additions & 2 deletions assets/custom-types/remote-route53/add-zone-admin-trust.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
route53RoleName=$1
lambdaRoleARN=$2
lambdaRoleARN=$1

route53RoleName=remote-route53-cf-admin

currentPolicy=$(aws iam get-role --role-name $route53RoleName | jq .AssumeRolePolicyDocument)

Expand Down
10 changes: 6 additions & 4 deletions assets/custom-types/remote-route53/create-sns-topic.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
#!/usr/bin/env bash

lambdaArn=$1
lambdaRegion=$2
snsRegion=$3
snsRegion=$2

lambdaRegion=$(echo $lambdaArn | perl -pe 's/arn:aws:lambda:(.+?):.*/$1/')

topicArn=$(aws sns create-topic --region $snsRegion --name cf-remote-route53 --query TopicArn --output text)
aws sns subscribe --region $snsRegion --topic-arn $topicArn --protocol lambda --notification-endpoint $lambdaArn

sid=$(cat /dev/urandom | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 16; echo)
aws lambda add-permission --region $lambdaRegion --function-name $lambdaArn --statement-id $sid --action lambda:invokeFunction \
aws lambda add-permission --region $lambdaRegion --function-name $lambdaArn \
--statement-id $sid --action lambda:invokeFunction \
--principal sns.amazonaws.com --source-arn $topicArn

echo ""
echo "SNS topic ARN: $topicArn"
echo "Use this ARN in the as the 'ServiceToken' parameter to the Cloud Formation resource."
echo "Use this ARN in the as the 'ServiceToken' parameter to the Cloud Formation resource for templates run in $snsRegion."
echo ""

5 changes: 3 additions & 2 deletions assets/custom-types/remote-route53/create-zone-admin.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
roleName=$1
hostedZoneId=$2
hostedZoneId=$1

roleName=remote-route53-cf-admin

if ! aws iam get-role --role-name $roleName >/dev/null 2>&1 ; then
echo "Creating $roleName"
Expand Down
14 changes: 12 additions & 2 deletions assets/custom-types/remote-route53/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,20 @@ else
sleep 1
done
fi
functionArn=$(aws lambda get-function --function-name $function_name --query Configuration.FunctionArn)
functionArn=$(aws lambda get-function --function-name $function_name --output text --query Configuration.FunctionArn)
functionRole=$(aws lambda get-function --function-name $function_name --output text --query Configuration.Role)

lambdaRegion=$(echo $functionArn | perl -pe 's/arn:aws:lambda:(.+?):.*/$1/')

# Create SNS topic for this region.
./create-sns-topic.sh $functionArn $lambdaRegion

echo ""
echo "Lambda ARN: $functionArn"
#echo "Use this ARN in the as the 'ServiceToken' parameter to the Cloud Formation resource."
echo "Use this ARN to set up SNS topics in additional regions where you want to use it."
echo ""

echo ""
echo "To grant this function permission to manage Route 53, please re-authenticate to the Route 53 AWS account and run:"
echo "./add-zone-admin-trust.sh $functionRole"
echo ""
18 changes: 0 additions & 18 deletions assets/custom-types/remote-route53/sns-lambda-stub.json

This file was deleted.

0 comments on commit 30f3cab

Please sign in to comment.