DEV Community

Cover image for 16.Send Notifications from IAM Events to SNS Using Terraform
Thu Kha Kyawe
Thu Kha Kyawe

Posted on

16.Send Notifications from IAM Events to SNS Using Terraform

Lab Information

To enable secure inter-service communication, the DevOps team needs to configure access to an SNS topic using IAM roles and policies. The objective is to allow EC2 instances to publish messages to the topic using proper permissions and role assumptions. Please complete the following tasks:

Create an SNS topic named datacenter-sns-topic.

Create an IAM role named datacenter-sns-role with EC2 as the trusted entity.

Attach an IAM policy named datacenter-sns-policy that grants permission to publish messages to the SNS topic.

Use the main.tf file (do not create a separate .tf file) to provision the sns-topic, role and policy.

Create the locals.tfwith the following names:
    KKE_SNS_TOPIC_NAME:name of the sns topic created.
    KKE_ROLE_NAME: name of the role created.
    KKE_POLICY_NAME: name of the policy created.

Create the outputs.tf file to the output the following:

    The name of the SNS topic using the output variable kke_sns_topic_name.

    The name of the role using the output variable kke_role_name.

    The name of the policy using the output variable kke_policy_name.
Enter fullscreen mode Exit fullscreen mode

Lab Solutions

1️⃣ locals.tf

locals {
  KKE_SNS_TOPIC_NAME = "datacenter-sns-topic"
  KKE_ROLE_NAME      = "datacenter-sns-role"
  KKE_POLICY_NAME    = "datacenter-sns-policy"
}
Enter fullscreen mode Exit fullscreen mode

2️⃣ main.tf

# SNS Topic
resource "aws_sns_topic" "datacenter_topic" {
  name = local.KKE_SNS_TOPIC_NAME
}

# IAM Role (trusted by EC2)
resource "aws_iam_role" "datacenter_role" {
  name = local.KKE_ROLE_NAME

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

# IAM Policy (allow publish to SNS topic)
resource "aws_iam_policy" "datacenter_policy" {
  name = local.KKE_POLICY_NAME

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["sns:Publish"]
      Resource = aws_sns_topic.datacenter_topic.arn
    }]
  })
}

# Attach policy to role
resource "aws_iam_role_policy_attachment" "attach_policy" {
  role       = aws_iam_role.datacenter_role.name
  policy_arn = aws_iam_policy.datacenter_policy.arn
}
Enter fullscreen mode Exit fullscreen mode

3️⃣ outputs.tf

output "kke_sns_topic_name" {
  value = aws_sns_topic.datacenter_topic.name
}

output "kke_role_name" {
  value = aws_iam_role.datacenter_role.name
}

output "kke_policy_name" {
  value = aws_iam_policy.datacenter_policy.name
}
Enter fullscreen mode Exit fullscreen mode

4️⃣ Terraform Commands (Run in Order)
terraform init
terraform validate
terraform apply

Type:

yes

✅ Expected Output

bob@iac-server ~/terraform via 💠 default ➜  terraform apply

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_iam_policy.datacenter_policy will be created
  + resource "aws_iam_policy" "datacenter_policy" {
      + arn              = (known after apply)
      + attachment_count = (known after apply)
      + id               = (known after apply)
      + name             = "datacenter-sns-policy"
      + name_prefix      = (known after apply)
      + path             = "/"
      + policy           = (known after apply)
      + policy_id        = (known after apply)
      + tags_all         = (known after apply)
    }

  # aws_iam_role.datacenter_role will be created
  + resource "aws_iam_role" "datacenter_role" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "ec2.amazonaws.com"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "datacenter-sns-role"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = (known after apply)
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # aws_iam_role_policy_attachment.attach_policy will be created
  + resource "aws_iam_role_policy_attachment" "attach_policy" {
      + id         = (known after apply)
      + policy_arn = (known after apply)
      + role       = "datacenter-sns-role"
    }

  # aws_sns_topic.datacenter_topic will be created
  + resource "aws_sns_topic" "datacenter_topic" {
      + arn                         = (known after apply)
      + beginning_archive_time      = (known after apply)
      + content_based_deduplication = false
      + fifo_topic                  = false
      + id                          = (known after apply)
      + name                        = "datacenter-sns-topic"
      + name_prefix                 = (known after apply)
      + owner                       = (known after apply)
      + policy                      = (known after apply)
      + signature_version           = (known after apply)
      + tags_all                    = (known after apply)
      + tracing_config              = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + kke_policy_name    = "datacenter-sns-policy"
  + kke_role_name      = "datacenter-sns-role"
  + kke_sns_topic_name = "datacenter-sns-topic"

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_role.datacenter_role: Creating...
aws_sns_topic.datacenter_topic: Creating...
aws_iam_role.datacenter_role: Creation complete after 1s [id=datacenter-sns-role]
aws_sns_topic.datacenter_topic: Creation complete after 1s [id=arn:aws:sns:us-east-1:000000000000:datacenter-sns-topic]
aws_iam_policy.datacenter_policy: Creating...
aws_iam_policy.datacenter_policy: Creation complete after 0s [id=arn:aws:iam::000000000000:policy/datacenter-sns-policy]
aws_iam_role_policy_attachment.attach_policy: Creating...
aws_iam_role_policy_attachment.attach_policy: Creation complete after 0s [id=datacenter-sns-role-20260130141903393400000001]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

kke_policy_name = "datacenter-sns-policy"
kke_role_name = "datacenter-sns-role"
kke_sns_topic_name = "datacenter-sns-topic"
Enter fullscreen mode Exit fullscreen mode

🧠 Step-by-Step Explanation (Simple & Clear)

Let’s break this down in plain English.

🔹 What problem is this lab solving?

EC2 instances need to:

Publish messages to SNS

Do it securely

Without access keys

IAM roles + policies are the correct solution.

🔹 Why use locals.tf instead of variables?

locals are:

Fixed values

Defined once

Reusable across files

This lab wants static names, not user input.

Think:

variables → user-supplied

locals → internally defined constants

🔹 SNS Topic
aws_sns_topic

This is the message publisher endpoint.

EC2 will send messages here using:

sns:Publish

🔹 IAM Role (EC2 trusted)
aws_iam_role

The trust policy says:

“EC2 instances are allowed to assume this role.”

Without this, EC2 cannot use the role.

🔹 IAM Policy
aws_iam_policy

Policy allows:

"sns:Publish"

And only to:

datacenter-sns-topic

That’s least privilege 🔐
No subscribe, no delete, no list — publish only.

🔹 Attach policy to role
aws_iam_role_policy_attachment

This connects:

Role → Permissions

Now the role actually works.

🔹 What happens during terraform apply?

1️⃣ Terraform creates SNS topic
2️⃣ Terraform creates IAM role
3️⃣ Terraform creates IAM policy
4️⃣ Terraform attaches policy to role
5️⃣ AWS enforces permissions
6️⃣ Terraform outputs resource names

🧠 Easy Memory Model

SNS topic = 📣 message destination

IAM role = 👤 identity for EC2

IAM policy = 🔑 permission rules

Attachment = 🔗 glue

locals = 📌 fixed constants

🚨 Common Mistakes

❌ Using variables instead of locals
❌ Allowing sns:* instead of sns:Publish
❌ Using * for resource ARN
❌ Forgetting trust policy
❌ Output name mismatch


Resources & Next Steps
📦 Full Code Repository: KodeKloud Learning Labs
📖 More Deep Dives: Whispering Cloud Insights - Read other technical articles
💬 Join Discussion: DEV Community - Share your thoughts and questions
💼 Let's Connect: LinkedIn - I'd love to connect with you

Credits
• All labs are from: KodeKloud
• I sincerely appreciate your provision of these valuable resources.

Top comments (0)