Selaa lähdekoodia

Merge pull request #246 from mdr-engineering/feature/ftd_MSOCI-1787_AMI_Backups

Enables Creation of a DLM Policy for Cross-Region AMI Backups
Frederick Damstra 4 vuotta sitten
vanhempi
sitoutus
aee63272ce

+ 31 - 0
base/account_standards/ami_backups.tf

@@ -0,0 +1,31 @@
+# At this time, terraform does not support DLM AMI policies, only snapshots.
+# So we do it ourselves
+# 
+# NOTE: This will not update an existing policy, but will create one if it's missing.
+
+
+# Grab the current policy name. This turned out to be unnecessary for my purposes, but
+# will be useful if in the future we decide to implement a 'modify' resource.
+#
+# WARNING: External data sources are run before the apply, and even before any decision
+#          is made whether or not to apply, so do not make changes in such a script.
+data "external" "get_dlm_policies" {
+  program = ["bin/get_current_dlm_policies"]
+}
+
+# In rare cases, you may need/want to manually recreate this. To do so, run
+#    terragrunt taint null_resource.create_dlm_policy
+resource "null_resource" "create_dlm_policy" {
+  #count = data.external.get_dlm_policies.result["PolicyId"] == "null" ? 1 : 0
+  #count = data.external.get_dlm_policies.result["PolicyId"] == "policy-02af49210b5b375d5" ? 1 : 0
+
+  # Could maybe find some sort of trigger here, in case the DLM is deleted?
+
+  provisioner "local-exec" {
+    command = "bin/create_dlm_policy ${var.aws_partition} ${var.aws_region} ${var.aws_account_id} ${var.account_name}"
+  }
+
+  depends_on = [
+    aws_iam_role.dlm_lifecycle_role
+  ]
+}

+ 111 - 0
base/account_standards/bin/create_dlm_policy

@@ -0,0 +1,111 @@
+#! /bin/bash
+#
+# Creates the XDR DLM Policy to backup AMIs daily and copy them cross-region.
+#
+# NOTE: If you create a new policy, the old policy will remain. Use the modify
+# script instead. And even if you delete the old policy, the images created by
+# it will remain and continue to incur charges.
+PARTITION=$1
+REGION=$2
+ACCOUNT=$3
+ACCOUNT_NAME=$4
+
+# Fix for some accounts having -gov already appended and some not.
+# Accounts in gov will get it appended.
+ACCOUNT_NAME=${ACCOUNT_NAME%%-gov}
+
+if [[ ${REGION} == "us-gov-east-1" ]]; then
+  PROFILE=${ACCOUNT_NAME}-gov
+  TARGET_REGION="us-gov-west-1"
+elif [[ ${REGION} == "us-gov-west-1" ]]; then
+  PROFILE=${ACCOUNT_NAME}-gov
+  TARGET_REGION="us-gov-east-1"
+elif [[ ${REGION} == "us-east-1" ]]; then
+  PROFILE=${ACCOUNT_NAME}
+  TARGET_REGION="us-west-1"
+elif [[ ${REGION} == "us-west-1" ]]; then
+  PROFILE=${ACCOUNT_NAME}
+  TARGET_REGION="us-east-1"
+else
+  >&2 echo ERROR: Could not determine target region from source region \"${REGION}\"
+  exit -1
+fi
+
+# Fix the accounts that we foolish prepended 'afs-' to.
+PROFILE=${PROFILE##afs-}
+
+# Find the target region key ARN, since we can't use aliases here
+KMS_KEY_ID=$(aws --profile ${PROFILE} --region ${TARGET_REGION} kms list-aliases | jq -r '.Aliases[] | select(.AliasName=="alias/aws/ebs") | .TargetKeyId')
+KMS_ARN=$(aws --profile ${PROFILE} --region ${TARGET_REGION} kms describe-key --key-id ${KMS_KEY_ID} | jq -r '.KeyMetadata.Arn')
+
+tmpfile=$(mktemp /tmp/create_dlm_policy.XXXXXXX)
+cat > ${tmpfile} <<EOF
+{
+    "PolicyType": "IMAGE_MANAGEMENT",
+    "ResourceTypes": [
+        "INSTANCE"
+    ],
+    "TargetTags": [
+        {
+            "Key": "Snapshot",
+            "Value": "Daily"
+        }
+    ],
+    "Schedules": [
+        {
+            "Name": "XDR AMI Backups with Cross Region Replication",
+            "CopyTags": true,
+            "TagsToAdd": [
+                {
+                    "Key": "SnapshotPolicy",
+                    "Value": "Daily"
+                },
+                {
+                    "Key": "SnapshotCreator",
+                    "Value": "XDR AMI Backups with Cross Region Replication"
+                }
+            ],
+            "VariableTags": [
+                {
+                    "Key": "instance-id",
+                    "Value": "\$(instance-id)"
+                }
+            ],
+            "CreateRule": {
+                "Interval": 24,
+                "IntervalUnit": "HOURS",
+                "Times": [
+                    "03:30"
+                ]
+            },
+            "RetainRule": {
+                "Count": 2
+            },
+            "CrossRegionCopyRules": [
+                {
+                    "TargetRegion": "${TARGET_REGION}",
+                    "Encrypted": true,
+                    "CmkArn": "${KMS_ARN}",
+                    "CopyTags": true,
+                    "RetainRule": {
+                        "Interval": 2,
+                        "IntervalUnit": "DAYS"
+                    }
+                }
+            ]
+        }
+    ],
+    "Parameters": {
+        "NoReboot": true
+    }
+}
+EOF
+
+aws --profile ${PROFILE} --region ${REGION} dlm create-lifecycle-policy \
+  --execution-role-arn arn:${PARTITION}:iam::${ACCOUNT}:role/dlm-lifecycle-role \
+  --description "XDR AMI Backups with Cross Region Replication" \
+  --state ENABLED \
+  --tags '{ "Name": "XDR-AMI-XRegion", "SnapshotPolicy": "Daily" }' \
+  --policy-details file://${tmpfile}
+
+rm $tmpfile

+ 18 - 0
base/account_standards/bin/get_current_dlm_policies

@@ -0,0 +1,18 @@
+#! /bin/bash
+# Gets the current dlm policies, if any.
+#
+# WARNING: THIS IS RUN DURRING A 'PLAN' STEP. Do not make changes. Read-only in this script.
+
+# TODO: Pass these in
+PROFILE=mdr-test-c2-gov
+REGION=us-gov-east-1
+ACCOUNT=738800754746
+
+POLICIES=$(aws --profile ${PROFILE} --region ${REGION} dlm get-lifecycle-policies)
+
+# Extracts the policy IDs of IMAGE_MANAGEMENT policye
+POLICY_IDS=$(echo $POLICIES | jq -r '[.Policies[] | select(.PolicyType=="IMAGE_MANAGEMENT") | select(.Tags.SnapshotPolicy=="Daily")] | first | .PolicyId')
+
+# jq ensures a safe json output
+# Will return 'null' if no matching policy
+jq -n --arg policy $POLICY_IDS '{ "PolicyId": $policy }'

+ 35 - 1
base/account_standards/iam.tf

@@ -175,7 +175,10 @@ resource "aws_iam_role_policy" "dlm_lifecycle" {
             "ec2:CreateSnapshot",
             "ec2:DeleteSnapshot",
             "ec2:DescribeVolumes",
-            "ec2:DescribeSnapshots"
+            "ec2:DescribeSnapshots",
+            "ec2:DescribeImages",
+            "ec2:DescribeInstances",
+            "ec2:DescribeImageAttribute"
          ],
          "Resource": "*"
       },
@@ -184,7 +187,38 @@ resource "aws_iam_role_policy" "dlm_lifecycle" {
          "Action": [
             "ec2:CreateTags"
          ],
+         "Resource": [
+           "arn:${var.aws_partition}:ec2:*::snapshot/*",
+           "arn:${var.aws_partition}:ec2:*::image/*"
+         ]
+      },
+      {
+         "Effect": "Allow",
+         "Action": "ec2:DeleteSnapshot",
          "Resource": "arn:${var.aws_partition}:ec2:*::snapshot/*"
+      },
+      {
+         "Effect": "Allow",
+         "Action": [
+            "ec2:ResetImageAttribute",
+            "ec2:DeregisterImage",
+            "ec2:CreateImage",
+            "ec2:CopyImage",
+            "ec2:ModifyImageAttribute"
+         ],
+         "Resource": "*"
+      },
+      {
+         "Effect": "Allow",
+         "Action": [
+            "kms:ReEncrypt*",
+            "kms:GenerateDataKey*",
+            "kms:Encrypt",
+            "kms:DescribeKey",
+            "kms:Decrypt",
+            "kms:Create*"
+         ],
+         "Resource": "*"
       }
    ]
 }

+ 1 - 0
base/account_standards/vars.tf

@@ -36,6 +36,7 @@ variable "log_group_name" {
 # ----------------------------------
 # Below this line are variables inherited from higher levels, so they
 # do not need to be explicitly passed to this module.
+variable "account_name" { type = string }
 variable "binaries_bucket" { type = string}
 variable "binaries_key" { type = string}
 variable "is_legacy" { type = bool }

+ 4 - 2
submodules/kms/ebs-key/main.tf

@@ -77,7 +77,8 @@ data "aws_iam_policy_document" "kms_policy" {
         identifiers = concat(
           var.key_user_arns, 
           [ "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/user/mdr_terraformer",
-            "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling"
+            "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling",
+            "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/dlm-lifecycle-role"
           ] 
         )
       }
@@ -100,7 +101,8 @@ data "aws_iam_policy_document" "kms_policy" {
           var.key_attacher_arns, 
           [  
             "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/user/mdr_terraformer",
-            "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling"
+            "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling",
+            "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/dlm-lifecycle-role"
           ]
         )
       }