Search code examples
amazon-web-servicesterraformterraform-provider-aws

The local or global variables in policies file in SNS are not able to resolve the values


I am using terraform to create an SNS Queue. I am also creating my own SNS policy file.

variables.tf

variable "awsAccId" {
   default = "111222433"
}

variable "region" {
  default = "ca-central-1"
}

locals.tf

sns_topic_name      = "fail-notification"
fail_noti_topic_arn = "arn:aws:sns:${var.region}:${var.awsAccId}:${local.sns_topic_name}"
resource "aws_sns_topic" "fail-noti-topic" {
  name = "${local.sns_topic_name }"
}
resource "aws_sns_topic_policy" "fail-noti-topic-policy" {
  arn    = aws_sns_topic.fail-noti-topic.arn
  policy = file("policy/sns-policy.json")
}
resource "aws_sns_topic_subscription" "fail-noti-topic" {
 ...
 ...
 
}

Inside policy(folder) -> sns-policy.json (file)

{
  "Version": "2012-10-17",
  "Id": "${local.sns_topic_name}_policy_ID",
  "Statement": [
    {
      "Sid": "${local.sns_topic_name}_statement_ID",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "SNS:Publish",
        "SNS:RemovePermission",
        "SNS:SetTopicAttributes",
        "SNS:DeleteTopic",
        "SNS:ListSubscriptionsByTopic",
        "SNS:GetTopicAttributes",
        "SNS:AddPermission",
        "SNS:Subscribe"
      ],
      "Resource": "${local.fail_noti_topic_arn}",
      "Condition": {
        "StringEquals": {
          "AWS:SourceOwner": "{var.awsAccId}"
        }
      }
    }
  ]
}

I was expecting that the variables in the json would resolve and I would get the exact values as described in the locals.tf and variables.tf file.

Instead, the access policy, as shown in the AWS console is

part-1-sns-policy

part-2-sns-policy

I tried using policy = jsonencode(file("policy/sns-policy.json")) , but I am getting another error , while terraform apply that (something like this)

policyerror Attribute is not valid.

This error is not there during the planning phase.

I also tried something like

resource "aws_sns_topic_policy" "fail-noti-topic-policy" {
  arn    = aws_sns_topic.fail-noti-topic.arn
  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "${local.sns_topic_name }_policy_ID",
  "Statement": [
    {
      "Sid": "${local.sns_topic_name}_statement_ID",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
......

POLICY

But, this too does not resolve the variables with their corresponding values.

What should I do, to resolve the variables in the policy JSON files, so that I get their actual values, instead of the variables themselves?


Solution

  • What I usually do is use the data source available for creating the policies, because it is the easiest to get the interpolation right with. In this case, that would look something like the following:

    data "aws_iam_policy_document" "sns_topic_policy" {
      statement {
        sid    = "${local.sns_topic_name }_policy_ID"
        effect = "Allow"
        actions = [
          "SNS:Publish",
          "SNS:RemovePermission",
          "SNS:SetTopicAttributes",
          "SNS:DeleteTopic",
          "SNS:ListSubscriptionsByTopic",
          "SNS:GetTopicAttributes",
          "SNS:AddPermission",
          "SNS:Subscribe"
        ]
        condition {
          test     = "StringEquals"
          variable = "aws:SourceOwner"
          values = [
            var.awsAccId
          ]
        }
        resources = [
          local.fail_noti_topic_arn
        ]
        principals {
          type = "AWS"
          identifiers = [
            "*"
          ]
        }
      }
    }
    

    You would then reference it like the following:

    resource "aws_sns_topic_policy" "fail-noti-topic-policy" {
      arn    = aws_sns_topic.fail-noti-topic.arn
      policy = data.aws_iam_policy_document.sns_topic_policy.json
    }
    

    Alternatively, you could use the built-in templatefile function (instead of file) which allows providing the input values. In that case, you would do something like the following:

    resource "aws_sns_topic_policy" "fail-noti-topic-policy" {
      arn = aws_sns_topic.fail-noti-topic.arn
    
      policy = templatefile("${path.root}/policy/sns-policy.json", {
         id         = "${local.sns_topic_name }_policy_ID"
         sid        = "${local.sns_topic_name}_statement_ID"
         resource   = local.fail_noti_topic_arn
         account_id = var.awsAccId
      })
    }
    

    For this to work, you would have to make the changes in the templated JSON file as well:

    {
      "Version": "2012-10-17",
      "Id": "${id}",
      "Statement": [
        {
          "Sid": "${sid}",
          "Effect": "Allow",
          "Principal": {
            "AWS": "*"
          },
          "Action": [
            "SNS:Publish",
            "SNS:RemovePermission",
            "SNS:SetTopicAttributes",
            "SNS:DeleteTopic",
            "SNS:ListSubscriptionsByTopic",
            "SNS:GetTopicAttributes",
            "SNS:AddPermission",
            "SNS:Subscribe"
          ],
          "Resource": "${resource}",
          "Condition": {
            "StringEquals": {
              "AWS:SourceOwner": "${account_id}"
            }
          }
        }
      ]
    }