Search code examples
amazon-web-servicesamazon-ec2terraformaws-ssm

How to pass AssumeRole and associate SSM Document with EC2 using Terraform


I am trying to associate an SSM Document (which joins a linux server with AD Domain) with an EC2 instance.

I get the following error during association -

aws_ssm_association.rhel: Creating...
╷
│ Error: Error creating SSM association: ValidationException: The assume role is invalid.
│       status code: 400, request id: 3e2e23f0-da9e-4d0d-947f-2f121aa653e9
│ 
│   with aws_ssm_association.rhel,
│   on ssm.tf line 10, in resource "aws_ssm_association" "rhel":
│   10: resource "aws_ssm_association" "rhel" {

Here's my Terraform code -

main.tf

provider "aws" {
  region              = "us-west-2"
  allowed_account_ids = ["1234"]

  assume_role {
    role_arn = "arn:aws:iam::1234:role/my-role"
  }
}

terraform {
  required_version = "= 1.0.9"
}

ec2.tf

resource "aws_key_pair" "rhel" {
  key_name_prefix = "rhel_domain_join_test"
  public_key      = "ssh-rsa AMAMAMMMxxxx"
}

resource "aws_instance" "rhel" {
  ami                    = "ami-0b28dfc7adc3xxx" # us-west-2
  instance_type          = "t3.medium"
  subnet_id              = "subnet-023db3ebxxx"
  iam_instance_profile   = aws_iam_instance_profile.rhel_instance_profile.id
  vpc_security_group_ids = ["sg-077f9f9aceexxxx"]
  key_name               = aws_key_pair.rhel.id

  tags = {
    Name = "w2domainjointestpoc"
  }
}

iam.tf

resource "aws_iam_instance_profile" "rhel_instance_profile" {
  name_prefix = "rhel_instance_profile"
  role        = aws_iam_role.rhel_instance_role.name
}

resource "aws_iam_role" "rhel_instance_role" {
  name_prefix        = "rhel_instance_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.ssm_role_policy.json
}

resource "aws_iam_role_policy_attachment" "rhel_instance" {
  role       = aws_iam_role.rhel_instance_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole"
}

resource "aws_iam_role_policy_attachment" "rhel_instance_2" {
  role       = aws_iam_role.rhel_instance_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
}

resource "aws_iam_role_policy_attachment" "ec2-attach" {
  role       = aws_iam_role.rhel_instance_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
}


resource "aws_iam_policy" "ssm_role_passrole" {
  name_prefix = "ssm_automation"
  description = "My test policy"
  policy      = data.aws_iam_policy_document.ssm_role_passrole.json
}

resource "aws_iam_role_policy_attachment" "ssm_role_passrole" {
  role       = aws_iam_role.rhel_instance_role.name
  policy_arn = aws_iam_policy.ssm_role_passrole.arn
}

data.tf

data "aws_iam_policy_document" "ssm_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com", "ssm.amazonaws.com"]
    }
  }
}


data "aws_iam_policy_document" "ssm_role_passrole" {
  statement {
    actions   = ["iam:GetRole", "iam:PassRole"]
    resources = [aws_iam_role.rhel_instance_role.arn]
  }
}

ssm.tf

resource "aws_ssm_document" "rhel_domain_join_document" {
  name            = "rhel_domain_join_document"
  document_format = "JSON"
  document_type   = "Automation"


  content = file("${path.module}/redhat_linux_launch_automation_document.json")
}

resource "aws_ssm_association" "rhel" {
  name = aws_ssm_document.rhel_domain_join_document.name

  targets {
    key    = "InstanceIds"
    values = [aws_instance.rhel.id]
  }
}

Could you help me understand what I'm missing here?

Thank you


Solution

  • I think you are confusing SSM Document types. For SSM State Manager you can use three types of documents:

    1. Policy
    2. Command
    3. Automation

    Your redhat_linux_launch_automation_document.json is an automation. Because of this, targets block in your aws_ssm_association.rhel does not fully apply. targets block is only for the first two document types or rate controlled Automation.

    For simple execution of automation type you just provide parameters in aws_ssm_association.rhel, assuming that you don't want any rate or scheduled execution controls. Also your redhat_linux_launch_automation_document.json does not assume any role.

    So it should be:

    redhat_linux_launch_automation_document.json (partial view)

    Add role AutomationAssumeRole:

    {
        "description": "Launch Automation for RedHat Linux instance",
        "schemaVersion": "0.3",
        "assumeRole": "{{AutomationAssumeRole}}",    
        "parameters": {
            "instanceIds": {
                "type": "StringList",
                "description": "InstanceIds to run launch setup"
            },
            "AutomationAssumeRole": {
              "default": "",
              "type": "String",
              "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
            }        
        },
    

    main.tf:

    Create SSM role:

    resource "aws_iam_role" "ssm" {
      path               = "/"
      assume_role_policy = <<EOL
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": ["ec2.amazonaws.com", "ssm.amazonaws.com"]
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    EOL
    
      managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole"]
    
      inline_policy {
        name = "my_inline_policy"
    
        policy = jsonencode({
          Version = "2012-10-17"
          Statement = [
            {
              Action   = ["iam:PassRole"]
              Effect   = "Allow"
              Resource = "*"
            },
          ]
        })
      }
    
    }
    

    and finally fix aws_ssm_association.rhel:

    resource "aws_ssm_association" "rhel" {
    
      name = aws_ssm_document.rhel_domain_join_document.name
      
      parameters = {
          AutomationAssumeRole = aws_iam_role.ssm.arn
          instanceIds = aws_instance.rhel.id
      }  
    }
    

    NOTE: My answer addresses your error msg only. I did not check whether your automation document actually works or not. For that you may need to create new question.