add cloud_watch_logs_role_arn to aws_cloudtrail resource terraform - terraform

Hi I am following the guide for https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf. I would like to complete the infrastructure changes for section 3. To start this process I need to create an aws_cloudtrail resource with SSE-KMS encryption enabled. I'm currently reviewing the documentation for this resource. How do I automate the process of specifying the role for the CloudWatch Logs endpoint to assume to write to a user’s log group?
resource aws_cloudtrail "cisbenchmark" {
name = "cis-benchmark"
enable_logging = true
s3_bucket_name = aws_s3_bucket.cisbenchmark.bucket
enable_log_file_validation = true
is_multi_region_trail = true
include_global_service_events = true
cloud_watch_logs_role_arn = ????
cloud_watch_logs_group_arn = aws_cloudwatch_log_group.cisbenchmark.arn
kms_key_id = var.kms_key_arn
is_organization_trail = true
}
I've added ???? to specify where I'm missing the attribute.

If anyone is still looking for the answer:
resource aws_cloudtrail "cisbenchmark" {
name = "cis-benchmark"
enable_logging = true
s3_bucket_name = aws_s3_bucket.cisbenchmark.bucket
enable_log_file_validation = true
is_multi_region_trail = true
include_global_service_events = true
cloud_watch_logs_role_arn = aws_iam_role.cloud_trail.arn
cloud_watch_logs_group_arn = aws_cloudwatch_log_group.cisbenchmark.arn
kms_key_id = var.kms_key_arn
is_organization_trail = true
}
resource "aws_iam_role" "cloud_trail" {
name = "cloudTrail-cloudWatch-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "aws_iam_role_policy_cloudTrail_cloudWatch" {
name = "cloudTrail-cloudWatch-policy"
role = aws_iam_role.cloud_trail.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailCreateLogStream2014110",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream"
],
"Resource": [
"${aws_cloudwatch_log_group.cisbenchmark.arn}:*"
]
},
{
"Sid": "AWSCloudTrailPutLogEvents20141101",
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
"${aws_cloudwatch_log_group.cisbenchmark.arn}:*"
]
}
]
}
EOF
}

Related

Can an instance profile resource in Terraform have multiple inline policies?

I'm trying to convert a Cloud Formation script to Terraform. One of the issues I'm running into in the script
Cloud Formation:
"Resources": {
"*******InstanceProfile": {
"Condition": "IsInstanceProfile",
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "********-*******-instance",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
{
"Fn::Sub": "arn:aws:iam::*:role/${ReadOnlyRole}"
},
{
"Fn::Sub": "arn:aws:iam::${AWSOrganizationsAccountID}:role/${OrganizationsRole}"
}
]
}
]
}
}
],
"RoleName": {
"Ref": "AccessName"
}
}
}
I am ASSUMING I can use an inline_policy within a resource like in aws_iam_role. Here is my Terraform snippet that I've produced but get errors:
resource "aws_iam_role" "*****_instance_profile" {
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ec2.amazonaws.com"
}
},
]
})
policy = jsonencode({
Version = "2012-10-17"
Statement = [
Action = ["sts:AssumeRole"]
Effect = "Allow"
Resource = "arn:aws:iam::*:role/${ReadOnlyRole"
]
})
}
I guess I'm stuck with adding the resource within the policy. How would anyone handle this conversion? Do you folks think I'm going down the correct path with aws_iam_role? And, if so, how would you handle multiple resources in an inline_policy?
I would just use the official documentation when adding multiple resources to a policy.
data "aws_iam_policy_document" "example" {
statement {
actions = [
"s3:ListAllMyBuckets",
"s3:GetBucketLocation",
]
resources = [
"arn:aws:s3:::*",
]
}
statement {
actions = [
"s3:*",
]
resources = [
"arn:aws:s3:::${var.s3_bucket_name}/home/&{aws:username}",
"arn:aws:s3:::${var.s3_bucket_name}/home/&{aws:username}/*",
]
}
}
resource "aws_iam_user" "user" {
name = "test-user"
}
resource "aws_iam_policy" "policy" {
name = "test-policy"
description = "A test policy"
policy = data.aws_iam_policy_document.example.json
}
resource "aws_iam_user_policy_attachment" "test-attach" {
user = aws_iam_user.user.name
policy_arn = aws_iam_policy.policy.arn
}

Count Not Working Correctly in Terraform Resource aws_iam_policy

I have created resources for log_group for list of given job names
resource "aws_cloudwatch_log_group" "logGroups" {
count = length(var.jobnames)
name = format("/aws/lambda/%s", format(local.function_name_format, var.jobnames[count.index]))
retention_in_days = 7
}
and now for the each log resource i am creating an iam policy
resource "aws_iam_policy" "base_iam_policy" {
count = length(var.jobnames)
name = format(local.base_iam_policy_name_format, var.jobnames[count.index])
path = "/"
description = "Base IAM policy for creating a lambda"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"${element(aws_cloudwatch_log_group.logGroups.*.arn, count.index)}*"
]
},
{
"Action": [
"cloudwatch:PutMetricData",
"cloudwatch:GetMetricData",
"cloudwatch:GetMetricStatistics",
"cloudwatch:ListMetrics"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "${var.region}"
}
}
}
]
}
EOF
}
The issue is that for each BASE_IAM_POLICY, the resource for CreateLogStream is same. Looks like
in this "${element(aws_cloudwatch_log_group.logGroups.*.arn, count.index)}*" count is not getting incremented ?
Honestly, this seems like a bug in terraform. In the meantime I'd recommend indexing the elements directly, like the following
"${aws_cloudwatch_log_group.logGroups[count.index].arn}*"

How to define the ' assume_role_policy' in terraform?

Here is my aws_iam_role definition in terraform
resource "aws_iam_role" "server_role" {
name = "server-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeEnvironment",
"sqs:ChangeMessageVisibility",
"sqs:ReceiveMessage",
"sqs:SendMessage",
"s3:GetObject*",
"s3:ListBucket*",
"s3:PutBucket*",
"s3:PutObject*"
],
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
but i got this error when I try to run terraform plan:
Error: Error applying plan:
1 error(s) occurred:
aws_iam_role.server_role: 1 error(s) occurred:
aws_iam_role.server_role: Error creating IAM Role server-role: MalformedPolicyDocument: AssumeRole policy may
only specify STS AssumeRole actions. status code: 400, request id:
55f1bfaf-a121-11e9-acaf-bb57d635757b
I basically want to allow the server to read/write S3 buckets and read/write SQS queues.
Apparently I cannot add all these sqs:* and s3:* in the same place. How can I do it in terraform?
you are confused IAM Policy and IAM assume role Policy.
Try like below. It will create IAM Profile for EC2 and you can attach it to your EC2 instances.
resource "aws_iam_role" "server_role" {
name = "server-role"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_policy" "server_policy" {
name = "server_policy"
path = "/"
description = "TBD"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sqs:ChangeMessageVisibility",
"sqs:ReceiveMessage",
"sqs:SendMessage",
"s3:GetObject*",
"s3:ListBucket*",
"s3:PutBucket*",
"s3:PutObject*"
],
"Resource": [
"*"
]
,
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "server_policy" {
role = "${aws_iam_role.server_role.name}"
policy_arn = "${aws_iam_policy.server_policy.arn}"
}
resource "aws_iam_instance_profile" "server" {
name = "server_profile"
role = "${aws_iam_role.server_role.name}"
}

Terraform: Setting up logging from AWS LoadBalancer to S3 bucket

I have an aws_lb that I want to log to an S3 bucket.
What I have unsuccessfully tried to do:
data "aws_elb_service_account" "main" {}
data "aws_iam_policy_document" "bucket_policy" {
statement {
sid = ""
actions = ["s3:PutObject"]
resources = ["arn:aws:s3:::my-bucket/*"]
principals {
type = "AWS"
identifiers = ["${data.aws_elb_service_account.main.arn}"]
}
}
}
I also tried this:
resource "aws_iam_role" "lb-logs-role" {
name = "lb-logs-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "elasticloadbalancing.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
tags = {
Name = "lb-logs-role"
Environment = terraform.workspace
Management = "Managed by Terraform"
}
}
resource "aws_iam_role_policy" "s3-logs-access" {
name = "s3-logs-access"
role = aws_iam_role.lb-logs-role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
EOF
}
This is the error I am seeing:
Error: Failure configuring LB attributes: InvalidConfigurationRequest: Access Denied for bucket: my-bucket. Please check S3bucket permission
status code: 400, request id: 5b629210-9738-11e9-bcc6-6f3b4f22bf28
on modules/tableau-linux/lb.tf line 1, in resource "aws_lb" "main":
1: resource "aws_lb" "main" {
Any ideas?
It looks like the API will request the ACL of the bucket to see if it has permission, and populate the initial folder structure, therefore the even though the aws_elb_service_account has permissions to putObject in the bucket the api call will fail. This policy is what the AWS web console creates when it creates the S3 bucket for you, and it solved it for me.
data "aws_region" "current" {}
data "aws_caller_identity" "current" {}
data "aws_elb_service_account" "main" {}
resource "aws_s3_bucket_policy" "lb-bucket-policy" {
bucket = aws_s3_bucket.lb-log-storage-s3.id
policy = <<POLICY
{
"Id": "Policy",
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": [
"${data.aws_elb_service_account.main.arn}"
]
},
"Action": [
"s3:PutObject"
],
"Resource": "${aws_s3_bucket.lb-log-storage-s3.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"
},
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"s3:PutObject"
],
"Resource": "${aws_s3_bucket.lb-log-storage-s3.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
},
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": [
"s3:GetBucketAcl"
],
"Resource": "${aws_s3_bucket.lb-log-storage-s3.arn}"
}
]
}
POLICY
}
It seems the issue is with your policy but you can try my code using aws_lb, Here is the complete configuration to launch to LB in default VPC and create bucket named test-bucket-1-unique-name, policy and LB named test-http-lb. Along with SG and Route53 entry that is commented.
# Creating Load Balancer
resource "aws_lb" "httplb" {
name = "test-http-lb"
internal = false
load_balancer_type = "application"
security_groups = ["${aws_security_group.lbsg.id}"]
subnets = ["subnet-99fdf8e0", "subnet-902b0ddb"]
enable_deletion_protection = false
access_logs {
bucket = "${aws_s3_bucket.bucket.bucket}"
prefix = "http-lb"
enabled = true
}
tags = {
Environment = "test-http"
}
}
# Creating Security Groups for Load Balancer
resource "aws_security_group" "lbsg" {
name = "test-loadbalancer-sg"
description = "test-Allow LB traffic"
tags = {
Name = "test-SG-Balancer"
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
#uncomment this if you want to add route53 record
# resource "aws_route53_record" "web" {
# zone_id = "${data.aws_route53_zone.primary.zone_id}"
# name = "${var.env_prefix_name}.ironman.co
# type = "A"
# alias {
# name = "${aws_lb.httplb.dns_name}"
# zone_id = "${aws_lb.httplb.zone_id}"
# evaluate_target_health = true
# }
# }
data "aws_elb_service_account" "main" {}
# Creating policy on S3, for lb to write
resource "aws_s3_bucket_policy" "lb-bucket-policy" {
bucket = "${aws_s3_bucket.bucket.id}"
policy = <<POLICY
{
"Id": "testPolicy1561031527701",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "testStmt1561031516716",
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::test-bucket-1-for-lb-logs/http-lb/*",
"Principal": {
"AWS": [
"${data.aws_elb_service_account.main.arn}"
]
}
}
]
}
POLICY
}
resource "aws_s3_bucket" "bucket" {
bucket = "test-bucket-1-for-lb-logs"
acl = "private"
region = "us-west-2"
versioning {
enabled = false
}
force_destroy = true
}
Then go to your S3 bucket and verify TestFile.
Here are the logs from terraform
This one stumped me also, but I got it working with this
{
Effect : "Allow",
Principal : {
"AWS" : "arn:aws:iam::127311923021:root"
},
Action : [
"s3:PutObject"
],
Resource : "${aws_s3_bucket.logging_bucket.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*",
}
Where does 127311923021 come from, you ask? Believe me, I did too! This AWS document says it is the ID of the AWS account for Elastic Load Balancing for your Region (us-east-1 in this case). It has a large table of these magic numbers!

Terraform doesn't seem to iterate on my list when invoked from a module

I have a simple module that's something like this:
module "EncryptionKeys" {
source = "../../../../Modules/KeyGenerator"
item_list = ["${module.static_variables.item_list}"]
account_id = "${module.static_variables.account_id}"
key_alias_suffix = "a-suffix"
key_administrator_role = "${data.aws_iam_role.admins.name}"
key_user_suffix = "some-other-suffix"
}
Here is the key resource within the module:
resource "aws_kms_key" "key" {
count = "${length(var.item_list)}"
description = "${var.description}"
policy = "${data.aws_iam_policy_document.key_document.json}"
enable_key_rotation = "${var.enable_key_rotation}"
}
The module itself is making an AWS IAM role/policy with the following statement:
statement {
sid = "Allow use of the key for users"
effect = "Allow"
principals {
identifiers =
["arn:aws:iam::${var.account_id}:role/${var.key_administrator_role}", "${element(split(".",var.item_list[count.index]),0)}-${var.key_user_suffix}"]
type = "AWS"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = ["*"]
}
The problem? When I view the successful terraform plan, item_list is parsed correctly according to the element, but it's only ever the same value. i.e. if I have item_list defined as:
item_list = ["a.blah", "b.foo", "c.bar", "d.foobar"]
there will be four instances of the relevant resources, the correct split will occur on the ".", but all will be named for "a".
"{
"Version": "2012-10-17",
"Id": "key=consolepolicy-3",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Action": "kms:*",
"Resource": "*",
"Principal": {
"AWS": "arn:aws:iam::123456789:role/Admins"
}
},
{
"Sid": "Allow attachment of persistent resources for admin",
"Effect": "Allow",
"Action": [
"kms:RevokeGrant",
"kms:ListGrants",
"kms:CreateGrant"
],
"Resource": "*",
"Principal": {
"AWS": "arn:aws:iam::123456789:role/Admins"
},
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
},
{
"Sid": "Allow use of the key for users",
"Effect": "Allow",
"Action": [
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Encrypt",
"kms:DescribeKey",
"kms:Decrypt"
],
"Resource": "*",
"Principal": {
"AWS": [
"a-stg-role",
"arn:aws:iam::123456789:role/Admins"
]
}
},
{
"Sid": "Allow attachment of persistent resources for users",
"Effect": "Allow",
"Action": [
"kms:RevokeGrant",
"kms:ListGrants",
"kms:CreateGrant"
],
"Resource": "*",
"Principal": {
"AWS": [
"a-stg-role",
"arn:aws:iam::123456789:role/Admins"
]
},
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
}
]
}"
Am I doing something fundamentally wrong with count/count.index here? Why won't it loop item_list, and why does it always seem to get the same value?
You specify the count on the resource key, but that does not mean it is available for your aws_iam_policy_document.
Try to include the count in the aws_iam_policy_document, e.g.
data "aws_iam_policy_document" "key_document" {
count = "${length(var.item_list)}"
# rest of template ....
}
Then reference the policy list from the key resource using the count in the key resource: policy = "${element(data.aws_iam_policy_document.key_document.*.json, count.index)}"

Resources