Skip to content

Lesson 1: Security Group, Instance and Role

Dr. Rob Lambert, PhD edited this page Jan 19, 2018 · 12 revisions

Why do we use Security groups, instances, and roles?

  • Security groups are the main line of IT security defenses within AWS. An instance is placed within a security group and inherits a firewall from the rules of the group.
  • Instances are the building blocks of an AWS infrastructure, they are virtual machines running in the aws cloud.
  • Roles allow a machine by itself to obtain certain permissions, such as getting data from a specific S3 bucket. It's a great way to give tasks to machines in the absence of users, or to distribute processing nicely.

Why do you need to know about it?

  • To teach the complexities and intricacies of Terraform, now we move to a much more real-world example and demonstrate how much Terraform can contain and streamline our infrastructure allocation.

Skip over if:

  • You are already a Terraform expert or this is not a relevent part of Terraform for you.

Lesson 1: Outside Terraform

We can create security groups, roles and instances with the aws console. It's pretty easy to do that.

security groups

And then we can add an instance to that security group:

instances

And we won't stop there, now we can add a role to the instance so that people or applications on that instance have access to S3. Amazon describes this themselves using their own tutorial videos:

roles

roles

Now, you could repeat this process for each new machine, and juggle around the configuration of security groups in your head, on pieces of paper, on diagrams, and in internal communications. However this just isn't scalable to large infrastructures and does not permit roll-backs in case of mistakes. It commits you to certain static infrastructure. To escape this, we need IaC.

Lesson 1: Inside Terraform

In Terraform, creating a security group, a Role, and an instance, for an existing VPC would be done with a file like this:

#provider
provider "aws" {
  region = "eu-west-1"
  access_key = "${var.aws_access_key}"
  secret_key = "${var.aws_secret_key}"
}

# variables

variable "myamiid" {
  type = "string"
  default = "ami-332422"
}

variable "myvpcid" {
  type = "string"
  default = "vpc-1701"
}

#find info, I want to place into the first and only default subnet

data "aws_subnet_ids" "mysubnetid" {
  vpc_id = "${var.myvpcid}"
}

# make security group

resource "aws_security_group" "allow_all" {
  name        = "allow_all"
  description = "Allow all traffic"
  vpc_id      = "${var.myvpcid}"

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }
}

# make policy inside a role directly

resource "aws_iam_role" "test_role" {
  name = "test_role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    },
    {
      "Action": "s3:*",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_instance_profile" "s3_profile" {
  name  = "${aws_iam_role.test_role.name}"
  role =  "${aws_iam_role.test_role.name}"
}


# make instance attached to this policy, in this group

resource "aws_instance" "talktos3" {
  ami           = "${var.myamiid}"
  instance_type = "t2.micro"
  subnet_id     = "${element(data.aws_subnet_ids.mysubnetid.ids, 0)}"
  vpc_security_group_ids     = ["${aws_security_group.allow_all.id}"]
  iam_instance_profile = "${aws_iam_instance_profile.s3_profile.name}"
  associate_public_ip_address = true
}

# outputs

output "ip" {
  value = "${aws_instance.talktos3.public_ip}"
}

To summarize: We started by creating a security group with a ingress and egress rules that are open to the world, in production, you should refrain from doing that for security reason. In production only allow ingress and egress to and from places you trust.

We later created a role with an inline policy and an instance profile. The instance profile allows aws ec2 service to find that role and we made sure the role can be assumed by an ec2 instance with the inline policy. Once we had the IAM resources we need we used the subnet data resource in order to get a list of available subnets. Since this tutorial assumes we are using the default subnet with the default vpc we can told Terraform to use the first (and only) subnet in the resulting list.

We also used the data resource for finding the latest Ubuntu AMI and finally we used all of the resources we created to define our ec2 instance.

Exercise a (5 minutes):

Create a .tf file based upon the code above, validate it and 'plan' it if possible with Terraform. If you are on a sandbox environment, try applying/destroying this infrastructure with Terraform. Note that you will have to find your VPC-id.

Exercise b (10 minutes):

We use an inline policy above, which helps us directly create a role and a dedicated policy together. There are several other ways to construct this such as:

  • reading the policy from a separate json file
  • creating the policy in the console and referencing it here
  • creating the policy as a separate resource declaration

Choose one of these three options to implement yourself, and get through to the 'validate' stage.

What did we learn?

The nature of a security group, instance and role, within aws. How to create and destroy these with Terraform.

NEXT: Lesson 2: an S3 bucket, ACL