Terraform vs Cloudformation

New employer uses Terraform, so I’ve finally had a reason to grok Terraform and what it can do. I’m not convinced it is better than CloudFormation. Here are my thoughts on it.


1) Terraform can manage more than just AWS Resources. Useful if you need to orchestrate across multiple clouds, but I’d fear the dependency issues there. At my ex-job I’d have been very interested in how Terraform could control both AWS and Chef.
2) terraform plan is way easier to read that CF ChangeSets
3) Back in the day it probably supported more AWS Products. CF has caught up in the last year.
4) Before YAML templates, Terraform was much easier to read and could even be commented.
5) Modules are good for code re-use, and can be versioned with git commit or version tags.
6) You can actually add existing resources into terraform management with terraform import
7) Terraform can manage files locally (useful for uploading a lambda.zip)

1) Remote state is awful. I hear it’s better in the latest version 0.9.
2) CloudFormation can tell you what stack a resource is owned by. No such ability in Terraform. Hope you thought of and enforce a good tagging strategy before you deployed your first resources.
3) CloudFormation has much better options for generating and managing Instance UserData. Heaven help you if accidentally change your UserData in terraform.
4) Terraform ignores the aws:: tags when showing a plan. You need to manually check for that if doing an import/plan/apply.

I’m not about to go and re-write all my automation CloudFormation Templates as terraform. I like deploying these via templates in an a-la-carte fashion depending on my needs. My latest automation did get a terraform module to wrap the CFT since Terraform does support CloudFormation as a resource.

User Password & Key Expiration in AWS

AWS provides the ability to set a password policy on an account that will require a user to change their password after a certain period of time. However there is no method by which you can notify a user it is about to expire, nor is there anything that would expire an access key that hasn’t been rotated.

I wanted something that would implement policy that would deny any usage if the password was past-due (even if they hadn’t logged in for awhile) and would de-activate a key if it was older than the date set in the password policy. This would bring my AWS stuff into corporate compliance and prevent me from having to bother my users myself.

Since all our IAM usernames match the user’s email address, this is pretty easy to implement via automation. I’ve created a lambda that uses both the AWS Credential Report and the IAM APIs to iterate across all users and send them an SES notification when there password or key is about to expire.

The cloudformation template creates an IAM Group called UsersWithExpiredCredentials that limits the user to just the IAM actions necessary to change their password. There is the Lambda that does the work (and the appropriate Role and Invocation permissions), an Invocation Failure Alarm that will send to my account’s Alert Topic and a scheduled CloudWatch Event to trigger it to run every day.

The template takes as parameters:
* GracePeriodInDays (how far out from expiration do we warn the user)
* Boolean to indicate if we just warn or actually disable the user or key
* Boolean to indicate if we should use SES to email the user
* The SES authorized From Address
* Header and footer language for the email
* and an SNS topic where a summary of all actions taken gets published
(plus all the usual stuff needed to deploy a lambda that’s too big to fit in-line)

The Lambda itself uses the GetCredentialReport to see how long till the password will expire and it queries ListAccessKeys to see when the key was created. The Lambda only does this for IAM Users with LoginProfiles (ie they have a password), so it would not expire a IAM User that only had an access key (that’s a bit more dangerous and requires different handling)1. It uses GetAccountPasswordPolicy to determine the expiration age for both passwords and keys.

If an Access Key is past the expiration age it is not deleted and the user is not added to the BlackHole group. The key is simply flipped to deactivated.

In order to email users, you must either have had AWS support remove the sandbox limitation on your account, or you must verify all the users you intend to send to. Since the users are all IAM Users of your account, either are a viable option2.

1) Yes, a user can be dumb and embed their personal key into a production process somewhere. Said users need a flogging.
2) And adding all users to SES so you can send them system-wide notices might not be a bad thing to add to my add_user script and my RequireMFA stack.

Deploying a CloudFormation Template simply

If you’re deploying a cloudformation stack via the CLI, there is a lot that goes into it. Here are the options in the AWS CLI:
[--template-body ]
[--template-url ]
[--parameters ]
[--disable-rollback | --no-disable-rollback]
[--timeout-in-minutes ]
[--notification-arns ]
[--capabilities ]
[--resource-types ]
[--role-arn ]
[--on-failure ]
[--stack-policy-body ]
[--stack-policy-url ]
[--tags ]

The Parameters are passed in via a string that looks like this:
ParameterKey=key1,ParameterValue=value1,UsePreviousValue=boolean ParameterKey=key2,ParameterValue=value2,UsePreviousValue=boolean ParameterKey=key3,ParameterValue=value3,UsePreviousValue=boolean

There are Stack Policies that you probably want to apply to protect important stateful resources from mistaken Replacement or Deletions.

You might want to reference resources across stacks (this is better now that stacks can export values, but you might not want to do that in every case).

So I created deploy_stack.rb. It takes as it’s input a Manifest file that contains:
o StackName
o Template Location (file or S3)
o Region to deploy to
o TimeOut
o Parameters
o Stack Tags
o Stack Policy
o Pre and Post install scripts that can be used to do things like zip up a lambda or execute an AWS CLI command that’s not available via Cloudformation

Continue reading “Deploying a CloudFormation Template simply”

Creating a set of generic SNS Topics

When I’m creating a new AWS account, I find it helpful to have a generic set of SNS topics that ping me and my team if something goes wrong.

The following CloudFormation template can be used for that purpose. It requires a few parameters and includes an optional Lambda that will send the alerts to a Slack Channel.

Three Topics are created for critical, error and info-level alerts. Critical alerts will send me a text and email, while error only sends an email. Info alerts typically only go to slack (or a filtered email folder).

A generic Spending Alarm is also created by the Template.

The SlackNotification Lambda is small enough to be included in-line. The Webhook URL, AccountDescription, Slack Channel and IconEmoji are template parameters that are passed to the function via Lambda Environment Variables.

The stack will output the ARN for the three Topics so they can be referenced in other places, and I’m using Stack Exports so you don’t need to use deploy_stack trickery to find them.

Requiring AWS IAM Users to Enable MFA

When AWS announced Lambda at the 2014 re:Invent, my immediate thought was “Cool, you can now program the cloud itself”. Since then, everyone has jumped on the “serverless” bandwagon for building apps. After this year’s re:Invent I’m inspired to get back to using Lambda to program the cloud.

One of the sessions I attended was on Security Automation. I’ll have more to say on that later. However, it gave me the idea for a setup that would require users to have MFA enabled, or otherwise be blocked from doing anything with their IAM User in the AWS account.

Thus over the holidays I published RequireMFA which you can get here: https://github.com/jchrisfarris/aws-account-automation/blob/master/cloudformation/requireMFA-Template.yaml
Continue reading “Requiring AWS IAM Users to Enable MFA”

Various things to run in Terminal on a new Mac (Updated)

# Get rid of the annoying network stores:
  defaults write com.apple.desktopservices DSDontWriteNetworkStores true

# Stop telling me shit I already know:
  defaults write com.apple.LaunchServices LSQuarantine -bool NO

# Put Screenshots in their own Directory on the Desktop
mkdir ~/Desktop/Screenshots
defaults write com.apple.screencapture location ~/Desktop/Screenshots

# Set a Login Message:
sudo defaults write /Library/Preferences/com.apple.loginwindow LoginwindowText "Room17: Unauthorized Access Prohibited"

# Disable saving to iCloud
defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool FALSE

# Disable Dashboard
defaults write com.apple.dashboard mcx-disabled -boolean TRUE

# Disable MobileBackups in TimeMachine
sudo tmutil disablelocal