ds011591
ds011591

Reputation: 494

Can I attach a CloudWatch event rule to a 'built-in target' via Terraform?

The goal here is to create scheduled snapshots of EBS volumes. Looking at Terraform's documentation for aws_cloudwatch_event_target it doesn't seem possible, but I could be missing something.

Upvotes: 8

Views: 4180

Answers (4)

Developer
Developer

Reputation: 2321

I came up with this solution which is compatible with AWS CloudWatch Rules and Amazon EventBridge:

resource "aws_cloudwatch_event_rule" "volume_snapshot_rule" {
  name                = "ebs-volume-snapshot"
  description         = "Create an EBS volume snapshot every 6 hours"
  schedule_expression = "rate(6 hours)"
}

resource "aws_cloudwatch_event_target" "volume_snapshot_target" {
  target_id = "ebs-volume-snapshot-target"
  rule      = aws_cloudwatch_event_rule.volume_snapshot_rule.name
  arn       = "arn:aws:events:eu-central-1:${data.aws_caller_identity.current.account_id}:target/create-snapshot"
  role_arn  = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/create-ebs-snapshot"
  input     = "\"${aws_ebs_volume.storage.id}\""
}

data "aws_caller_identity" "current" {}

And for the IAM role

resource "aws_iam_role" "create_ebs_snapshot" {
  name = "create-ebs-snapshot"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "events.amazonaws.com"
        }
      },
    ]
  })

  inline_policy {
    name = "policy"

    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Action   = ["ec2:CreateSnapshot"]
          Effect   = "Allow"
          Resource = "*"
        },
      ]
    })
  }
}

Upvotes: 0

Chabreck
Chabreck

Reputation: 111

I was able to get this working entirely in terraform by tweaking this response provided by D Swartz. I had to modify the aws_cloudwatch_event_target resource in a few ways:

  1. The arn field needs to point to the target/create-snapshot event instead of the action/EBSCreateSnapshot automation action.

  2. The input field needs to be set to the desired volume's id rather than its arn.

  3. The role_arn needs to be set to the arn of the aws_iam_role that will be running the event.

The updated aws_cloudwatch_event_target resource looks like this:

resource "aws_cloudwatch_event_target" "example_event_target" {
  target_id = "example"
  rule = "${aws_cloudwatch_event_rule.snapshot_example.name}"
  arn = "arn:aws:events:${var.aws_region}:${var.account_id}:target/create-snapshot"
  input = "${jsonencode("${aws_ebs_volume.example.id}")}"
  role_arn = "${aws_iam_role.snapshot_permissions.arn}"
}

Full code snippet below:

resource "aws_cloudwatch_event_rule" "snapshot_example" {
  name = "example-snapshot-volumes"
  description = "Snapshot EBS volumes"
  schedule_expression = "rate(24 hours)"
}

resource "aws_cloudwatch_event_target" "example_event_target" {
  target_id = "example"
  rule = "${aws_cloudwatch_event_rule.snapshot_example.name}"
  arn = "arn:aws:events:${var.aws_region}:${var.account_id}:target/create-snapshot"
  input = "${jsonencode("${aws_ebs_volume.example.id}")}"
  role_arn = "${aws_iam_role.snapshot_permissions.arn}"
}

resource "aws_iam_role" "snapshot_permissions" {
  name = "example"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "automation.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "snapshot_policy" {
  name        = "example-snapshot-policy"
  description = "grant ebs snapshot permissions to cloudwatch event rule"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "ec2:RebootInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "ec2:CreateSnapshot"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "snapshot_policy_attach" {
  role       = "${aws_iam_role.snapshot_permissions.name}"
  policy_arn = "${aws_iam_policy.snapshot_policy.arn}"
}

edit: I'm not entirely clear if this is directly supported by AWS or not - based on their documentation it seems like this may not be an intended feature:

Creating rules with built-in targets is supported only in the AWS Management Console.

Upvotes: 4

D Swartz
D Swartz

Reputation: 155

The previous answer was enough to get everything except for the IAM permissions on the event targets (i.e. go into the console, edit the rule, and in "Step 2", for the "AWS Permissions" section, create a new role, etc). To get this working in terraform, I just added a few resources:

resource "aws_cloudwatch_event_rule" "snapshot_example" {
  name = "example-snapshot-volumes"
  description = "Snapshot EBS volumes"
  schedule_expression = "rate(24 hours)"
}

resource "aws_cloudwatch_event_target" "example_event_target" {
  target_id = "example"
  rule = "${aws_cloudwatch_event_rule.snapshot_example.name}"
  arn = "arn:aws:automation:${var.aws_region}:${var.account_id}:action/EBSCreateSnapshot/EBSCreateSnapshot_example-snapshot-volumes"
  input = "${jsonencode("arn:aws:ec2:${var.aws_region}:${var.account_id}:volume/${aws_ebs_volume.example.id}")}"
}

resource "aws_iam_role" "snapshot_permissions" {
  name = "example"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "automation.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "snapshot_policy" {
    name        = "example-snapshot-policy"
    description = "grant ebs snapshot permissions to cloudwatch event rule"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "ec2:RebootInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "ec2:CreateSnapshot"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "snapshot_policy_attach" {
    role       = "${aws_iam_role.snapshot_permissions.name}"
    policy_arn = "${aws_iam_policy.snapshot_policy.arn}"
}

Upvotes: 3

ydaetskcoR
ydaetskcoR

Reputation: 56839

Cloudwatch Events built-in targets just seem to require an input parameter as well as the ARN that is shown for adding a message to an SNS queue in the example for aws_cloudwatch_event_rule or sending to a Kinesis stream in aws_cloudwatch_event_target.

So we should just be able to do something like this:

resource "aws_cloudwatch_event_target" "ebs_vol_a" {
  target_id = "ebs_vol_a"
  rule = "${aws_cloudwatch_event_rule.snap_ebs.name}"
  arn = "arn:aws:automation:${var.region}:${var.account_id}:action/EBSCreateSnapshot/EBSCreateSnapshot_ebs_vol_a"
  input = "\"arn:aws:ec2:${var.region}:${var.account_id}:volume/vol-${var.ebs_vol_a_id}\""
}

resource "aws_cloudwatch_event_rule" "snap_ebs" {
  name = "snap-ebs-volumes"
  description = "Snapshot EBS volumes"
  schedule_expression = "rate(6 hours)"
}

I haven't yet tested this but it should work. Obviously you probably want to get the EBS volume IDs from the resource you created them but that's beyond the scope of the question. I've also guessed at the ARN after creating a rule in the AWS console and then looking at the output of aws events list-targets-by-rule where it seems to add the rule name to the ARN of the target but that may not always be true/necessary.

Upvotes: 5

Related Questions