Archive for the ‘AWS’ Category

Amazon EC2-Changing instance type

Posted: April 24, 2018 in AWS, Linux

If we face hardware limitations of our EC2 instance, we can’t just increase Memory/CPU cores as in VMWare, instead we must change instance type.It’s set of predefined images with different hardware specifications.(More info here)

First,stop EC2 instance:

 

1

 

Then, from Action menu,select Instance Settings-Change Instance Type

 

2

 

Select instance type and click Apply

 

3

Now start instance. (Note that new public IP is assigned.The instance retains its private IPv4 addresses

Advertisements

In previous article we created federation trust between Azure and AWS by creating Amazon user and used it’s credentials to create trust between Azure and AWS (automatic provisioning).This method has 2 main drawbacks: it takes a long time for Azure to retrive all IAM roles,and it’s not possible to provide more than 1 IAM credentials (situation when need to federate same Azure Enterprise application with 2 or more AWS accounts).Most of the steps are same as for manual provisioning but i’ll put it here again for the sake of transparency

Adding Amazon Application to Azure portal

On Azure portal Azure Active Directory-Enterprise Applications-All applications-New Application

1

In search box type Amazone-select Amazon Web Services (AWS)

2

On AWS app properties click on Single sign-on

3

Click Add attribute

4

Add attributes as in picture below

Attribute name Attribute value Namespace
RoleSessionName user.userprincipalname https://aws.amazon.com/SAML/Attributes
Role user.assignedroles https://aws.amazon.com/SAML/Attributes

 

 

5

In the SAML Signing Certificate section, select Metadata XML. Then save the metadata file on your computer.

5-1

5-2

Then click Save

5-3

AWS Console:Creating Provider and IAM role

In AWS console we need to add Provider, IAM role and policy

Select Identity and access management-IAM

6

Identity Providers-Create Provider

7

Choose SAML as Provider Type,set name and browse for metadata file downloaded from Azure portal

9

10

Still in IAM Click Roles-Create Role

11

Select Saml 2.0 Federation-SAML provider-provider we created earlier-Allow programatic and AWS Management Console Access (Attribute and Value fields populate automatically)

12

In Attach permission policies click Next:Review

13

In Create Role create as many roles as you need

14

Besides Azure_Role, i created another one and attached one IAM policy, we’ll map this role to another Azure AD Group

Untitled

Azure portal:Create User and Group-add user to group

In this section we’ll map Azure AD group to AWS role we just created (Azure_Role)

Creating new user:

Azure Active Directory-Users-All Users:

26

Create user

27

Creating AD group

Azure Active Directory-Groups

Untitled

Specify Group Type,name-Membership Type:Assigned-specify user(s) to add to group-Select-Create

Untitled

In the same way i created another Azure AD group (AWS_Second_Test_Group) to map it with another IAM role we created earlier (AWS_Second_Test_Role), i added Don.Hall user to this group too

Editing Azure Active directory manifest file

Manifest file is a JSON file that represents application’s identity configuration.We’ll edit this file to map Azure AD group with AWS IAM Role.Access scopes and roles are exposed through this file

In Azure portal, in search box type App Registrations-Select Amazon Web Services (AWS)

Untitled.png

Click on Manifest

Untitled

Now, we’ll map IAM AWS roles to Azure AD groups:

IAM Role name Azure AD Group Name
 Azure_Role Azure_AD_Group
AWS_Second_Test_Role AWS_Second_Test_Group

Ideally, names of IAM Roles and groups should be the same to avoid confusion

In order to edit manifest file we need to obtanin IAM Role ARN,AWS Identity provider ARN and Azure AD group ID (Azure AD Group ID must be unique-as a rule of thumb i just changed last 2 digits)

Capture

AWS IAM role ARN:

Untitled.png

Untitled.png

AWS Identity provider ARN:

Untitled

Azure Group’s ID:

Click on group-Properties:

Capture

Untitled

Remember, Azure AD group ID’s needs to be unique, so change last digit(s) values

These 2 sections are added to manifest file:

displayName:Name of Azure AD group

id:id of Azure AD group (changes last 2 digits-needs to be unique)

value:AWS IAM role ARN,AWS identity provider ARN

"appRoles": [
    {
      "allowedMemberTypes": [
        "User"
      ],
      "displayName": "AWS_Second_Test_Group",
      "id": "faa9acbc-49db-4a04-9a66-2050998f1c15",
      "isEnabled": true,
      "description": "Azure AD Second group",
      "value": "arn:aws:iam::233135199200:role/AWS_Second_Test_Role,arn:aws:iam::233135199200:saml-provider/WindowsAD"
    },
    {
      "allowedMemberTypes": [
        "User"
      ],
      "displayName": "Azure_AD_Group",
      "id": "b40569c7-ebf0-4c32-959c-b0b3b1cbfc12",
      "isEnabled": true,
      "description": "Azure AD First group",
      "value": "arn:aws:iam::233135199200:role/Azure_Role,arn:aws:iam::233135199200:saml-provider/WindowsAD"
    },

If we need to map more roles to groups we just need to add allowedMemberTypes sections (separate each one with comma)

Here is complete manifest file:

{
  "appId": "1def2fa6-5467-4565-b3f0-e598b3007b42",
  "appRoles": [
    {
      "allowedMemberTypes": [
        "User"
      ],
      "displayName": "AWS_Second_Test_Group",
      "id": "faa9acbc-49db-4a04-9a66-2050998f1c15",
      "isEnabled": true,
      "description": "Azure AD Second group",
      "value": "arn:aws:iam::233135199200:role/AWS_Second_Test_Role,arn:aws:iam::233135199200:saml-provider/WindowsAD"
    },
    {
      "allowedMemberTypes": [
        "User"
      ],
      "displayName": "Azure_AD_Group",
      "id": "b40569c7-ebf0-4c32-959c-b0b3b1cbfc12",
      "isEnabled": true,
      "description": "Azure AD First group",
      "value": "arn:aws:iam::233135199200:role/Azure_Role,arn:aws:iam::233135199200:saml-provider/WindowsAD"
    },
    {
      "allowedMemberTypes": [
        "User"
      ],
      "displayName": "msiam_access",
      "id": "7dfd756e-8c27-4472-b2b7-38c17fc5de5e",
      "isEnabled": true,
      "description": "msiam_access",
      "value": null
    }
  ],
  "availableToOtherTenants": false,
  "displayName": "Amazon Web Services (AWS)",
  "errorUrl": null,
  "groupMembershipClaims": null,
  "optionalClaims": null,
  "acceptMappedClaims": null,
  "homepage": "https://signin.aws.amazon.com/saml?metadata=aws|ISV9.1|primary|z",
  "informationalUrls": {
    "privacy": null,
    "termsOfService": null
  },
  "identifierUris": [
    "http://awsDC46DF5ECB354EEA858E81622348A0BE",
    "http://instanceid_8b1025e4-1dd2-430b-a150-2ef79cd700f5_EAAEA402D2364790A14A5099A13A3B7E",
    "http://aws/d38c1eb9-ca01-420f-a982-210c0583dc49"
  ],
  "keyCredentials": [],
  "knownClientApplications": [],
  "logoutUrl": null,
  "oauth2AllowImplicitFlow": false,
  "oauth2AllowUrlPathMatching": false,
  "oauth2Permissions": [
    {
      "adminConsentDescription": "Allow the application to access Amazon Web Services (AWS) on behalf of the signed-in user.",
      "adminConsentDisplayName": "Access Amazon Web Services (AWS)",
      "id": "e81ccfaa-9095-4cbc-87fe-10538a57f314",
      "isEnabled": true,
      "type": "User",
      "userConsentDescription": "Allow the application to access Amazon Web Services (AWS) on your behalf.",
      "userConsentDisplayName": "Access Amazon Web Services (AWS)",
      "value": "user_impersonation"
    }
  ],
  "oauth2RequirePostResponse": false,
  "objectId": "dd1dc07d-87dc-48bb-9fd3-1c0274c789a5",
  "parentalControlSettings": {
    "countriesBlockedForMinors": [],
    "legalAgeGroupRule": "Allow"
  },
  "passwordCredentials": [],
  "publicClient": false,
  "replyUrls": [
    "https://signin.aws.amazon.com/saml"
  ],
  "requiredResourceAccess": [],
  "samlMetadataUrl": null
}

Azure Active Directory-Enterprise Applications-Amazon Web Services (AWS)-Users and Groups-Add User

Capture.PNG

In Users section Assign user- in Roles section new roles should appear-select role

Capture

And assign it

Capture

Capture

Make sure manual provision method is selected (Amazon Web Services (AWS)-Provisioning

Capture

Testing access to AWS console

Don.Hall should be able now to acces to Amazon Web Console

Go to http://myapps.microsoft.com, log in as Don.Hall

click on Amazon Web Service, you should be able to sign in automatically to AWS console

31

 

Capture

In this article we’ll create Azure AD User and log him in into Amazon Web Console using single sign-on

Adding Amazon Application to Azure portal

Azure Active Directory-Enterprise Applications-All applications-New Application

1

In search box type Amazone-select Amazon Web Services (AWS)

2

On AWS app properties click on Single sign-on

3

Click Add attribute

4

Add attributes as in picture below

Attribute name Attribute value Namespace
RoleSessionName user.userprincipalname https://aws.amazon.com/SAML/Attributes
Role user.assignedroles https://aws.amazon.com/SAML/Attributes

 

5.png

In the SAML Signing Certificate section, select Metadata XML. Then save the metadata file on your computer.

5-1

5-2

Then click Save

5-3

Configuring AWS part

In AWS console we need to add Provider, IAM role and policy

Select Identity and access management-IAM

6

Identity Providers-Create Provider

7.png

Choose SAML as Provider Type,set name and browse for metadata file downloaded from Azure portal

9

10

Still in IAM Click Roles-Create Role

11

Select Saml 2.0 Federation-SAML provider-provider we created earlier-Allow programatic and AWS Management Console Access (Attribute and Value fields populate automatically)

12

In Attach permission policies click Next:Review

13

In Create Role create as many roles as you need

14

Creating Policy

Policies-Create policy (this policy will grab all IAM roles from AWS account)

15

Click JSON tab and paste following code:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:ListRoles"
],
"Resource": "*"
}
]
}

 

 

16

17

Creating new AWS user

We need to create new user,attach policy we just created,get credentials so we can submit it to Azure AWS application so we can get all Amazon AWS roles

18

19

20

Download CSV file (Access and shared access keys are there)

21

In Azure portal,in AWS app properties click Provisioning-for client secret enter AWS user access key,for Sercret token enter AWS user secret and click Test Connection

22

Scroll down, set On for Provision status then click Save

23

Creating Azure AD  user

Azure Active Directory-Users-Al Users

26

Create user

27

Enabling Azure Single sign-on for user

In AWS application properties select Users and Groups

28

Select user and click Select button

29

Click assign

30

Testing access to AWS console

Don.Hall should be able now to acces to Amazon Web Console

Go to http://myapps.microsoft.com, log in as Don.Hall

click on Amazon Web Service, you should be able to sign in automatically to AWS console

 

31

 

32

 

 

 

 

A VPC Peering is a networking connection between two VPCs that enables routing traffic between them.Instances between VPC’s can communicate using private IP’s,instead of public.

In this example one AWS account is presented with Google Chrome (Requester-AWS account which requests VPC peering,and another with Mozilla Firefox-Accepter-AWS account which needs to accept peering request;)

Requester settings

I created VPC (for detailed steps take a look here).  Take especial care not to have same IP ranges in both VPCs, it wont work.

In this example i created a VPC with 10.1.0.0/16 range and 10.1.1.0/24 subnet

 

1

Creating VPC peering

Now we’ll create VPC peering between Custom VPC (10.1.1.0/24-Requester) and default VPC in another AWS account (172.31.0.0/16-Accepter)

From VPC dashboard:-Peering Connection-Create Peering Connection

1

 

Put a name-specify local VPC-Another account (specify AWS ID of remote account-Accepter)-Region and remote VPC ID and click Create Peering Connection

 

1

 

Remote VPC ID

1

 

 

 

1

Accepter settings:

Now, on Accepter AWS account (to which we want create VPC peer to) console Accept VPC peering connection

1.PNG

 

Modifying Route tables

One step remains-we now need to modify routing tables to allow remote networks:

On Requester:

Local network is 10.1.1.0/24 and remote is 172.31.0.0/16, so we need to associate that network with VPC peer we just created.From VPC dashboard-Route Tables-click on Routes tab-Edit-add another route

1

 

On Accepter AWS console,remote network is 10.1.1.0/24 so associate that network to VPC peer

1.PNG

Now create new EC2 instance and assign it to Custom VPC,machine on this VPC should communicate with EC2 on another AWS accounts using private IP addresses.

 

 

 

 

 

 

 

 

vars.tf

In this file region, path to private/public key is specified,AMI,as well as RDS password

variable "AWS_REGION" {
  default = "eu-west-1"
}
variable "PATH_TO_PRIVATE_KEY" {
  default = "mykey"
}
variable "PATH_TO_PUBLIC_KEY" {
  default = "mykey.pub"
}
variable "AMIS" {
  type = "map"
  default = {
    us-east-1 = "ami-13be557e"
    us-west-2 = "ami-06b94666"
    eu-west-1 = "ami-844e0bf7"
  }
}
variable "RDS_PASSWORD" {
default="MyRDSsimplePassword"
}

instance.tf

instance type,VPC subnet, security group and public key for instance-all returned from vars.tf

resource "aws_instance" "example" {
ami = "${lookup(var.AMIS, var.AWS_REGION)}"
instance_type = "t2.micro"

# the VPC subnet
subnet_id = "${aws_subnet.main-public-1.id}"

# the security group
vpc_security_group_ids = ["${aws_security_group.example-instance.id}"]

# the public SSH key
key_name = "${aws_key_pair.mykeypair.key_name}"

}

sg.tf

In this file 2 security groups are specified: one will allow access to port 3306  from security group  (example-instance,specified in same file), and second one the incoming traffic on port 22 from  anywhere (0.0.0.0/0)

resource "aws_security_group" "example-instance" {
vpc_id = "${aws_vpc.main.id}"
name = "allow-ssh"
description = "security group that allows ssh and all egress traffic"
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
tags {
Name = "example-instance"
}
}

resource "aws_security_group" "allow-mariadb" {
vpc_id = "${aws_vpc.main.id}"
name = "allow-mariadb"
description = "allow-mariadb"
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = ["${aws_security_group.example-instance.id}"] # allowing access from our example instance
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
self = true
}
tags {
Name = "allow-mariadb"
}
}

vpc.tf

In this file Virtual Private Network is specified,3 public and 3 private subnets, internet gateway,route table and will associate public subnets to route table (so subnets can be available from the internet)

# Internet VPC
resource "aws_vpc" "main" {
    cidr_block = "10.0.0.0/16"
    instance_tenancy = "default"
    enable_dns_support = "true"
    enable_dns_hostnames = "true"
    enable_classiclink = "false"
    tags {
        Name = "main"
    }
}


# Subnets
resource "aws_subnet" "main-public-1" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.1.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1a"

    tags {
        Name = "main-public-1"
    }
}
resource "aws_subnet" "main-public-2" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.2.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1b"

    tags {
        Name = "main-public-2"
    }
}
resource "aws_subnet" "main-public-3" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.3.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1c"

    tags {
        Name = "main-public-3"
    }
}
resource "aws_subnet" "main-private-1" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.4.0/24"
    map_public_ip_on_launch = "false"
    availability_zone = "eu-west-1a"

    tags {
        Name = "main-private-1"
    }
}
resource "aws_subnet" "main-private-2" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.5.0/24"
    map_public_ip_on_launch = "false"
    availability_zone = "eu-west-1b"

    tags {
        Name = "main-private-2"
    }
}
resource "aws_subnet" "main-private-3" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.6.0/24"
    map_public_ip_on_launch = "false"
    availability_zone = "eu-west-1c"

    tags {
        Name = "main-private-3"
    }
}

# Internet GW
resource "aws_internet_gateway" "main-gw" {
    vpc_id = "${aws_vpc.main.id}"

    tags {
        Name = "main"
    }
}

# route tables
resource "aws_route_table" "main-public" {
    vpc_id = "${aws_vpc.main.id}"
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.main-gw.id}"
    }

    tags {
        Name = "main-public-1"
    }
}

# route associations public
resource "aws_route_table_association" "main-public-1-a" {
    subnet_id = "${aws_subnet.main-public-1.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}
resource "aws_route_table_association" "main-public-2-a" {
    subnet_id = "${aws_subnet.main-public-2.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}
resource "aws_route_table_association" "main-public-3-a" {
    subnet_id = "${aws_subnet.main-public-3.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}

provider.tf

Gets region from vars.tf

provider "aws" {
    region = "${var.AWS_REGION}"
}

rds.tf

This file specifies subnet group (in which subnet database will be in-group will consist of 2 private subnets), parameter group (parameters to change settings in the database),db instance type,credentials, availability zone, subnet and security group

resource "aws_db_subnet_group" "mariadb-subnet" {
name = "mariadb-subnet"
description = "RDS subnet group"
subnet_ids = ["${aws_subnet.main-private-1.id}","${aws_subnet.main-private-2.id}"]
}

resource "aws_db_parameter_group" "mariadb-parameters" {
name = "mariadb-parameters"
family = "mariadb10.1"
description = "MariaDB parameter group"

parameter {
name = "max_allowed_packet"
value = "16777216"
}
}
resource "aws_db_instance" "mariadb" {
allocated_storage = 100 # 100 GB of storage, gives us more IOPS than a lower number
engine = "mariadb"
engine_version = "10.1.14"
instance_class = "db.t2.small" # use micro if you want to use the free tier
identifier = "mariadb"
name = "mariadb"
username = "root" # username
password = "${var.RDS_PASSWORD}" # password
db_subnet_group_name = "${aws_db_subnet_group.mariadb-subnet.name}"
parameter_group_name = "${aws_db_parameter_group.mariadb-parameters.name}"
multi_az = "false" # set to true to have high availability: 2 instances synchronized with each other
vpc_security_group_ids = ["${aws_security_group.allow-mariadb.id}"]
storage_type = "gp2"
backup_retention_period = 30 # how long you’re going to keep your backups
availability_zone = "${aws_subnet.main-private-1.availability_zone}" # prefered AZ

tags {
Name = "mariadb-instance"
}
}

 

Create key pair and spin up the instance:

 

 ssh-keygen -f mykey && echo "yes" | terraform apply

 

1.PNG

1.PNG

As we can see, RDS id really located on private network (as we specified in security group)

host mariadb.c3wxcgbi9ky2.eu-west-1.rds.amazonaws.com
mariadb.c3wxcgbi9ky2.eu-west-1.rds.amazonaws.com has address 10.0.4.159

Amazon Route 53 (Route 53) is a scalable and highly available Domain Name System (DNS). It is part of Amazon.com’s cloud computing platform, Amazon Web Services (AWS). The name is a reference to TCP or UDP port 53, where DNS server requests are addressed

route3.tf file

In this file DNS zone astrahome.xyz is created, added two A records, one for WWW and second for server1, then one MX record with TTL (time-to-live),determines how frequently your DNS records get updated.MX records are in fact google mail servers

104.236.247.8 presents public IP address

Last section just outputs Amazon name servers

resource "aws_route53_zone" "some-zone" {
name = "astrahome.xyz"
}
resource "aws_route53_record" "server1-record" {
zone_id = "${aws_route53_zone.some-zone.zone_id}"
name = "server1.astrahome.xyz"
type = "A"
ttl = "300"
records = ["104.236.247.8"]
}
resource "aws_route53_record" "www-record" {
zone_id = "${aws_route53_zone.some-zone.zone_id}"
name = "www.astrahome.xyz"
type = "A"
ttl = "300"
records = ["104.236.247.8"]
}
resource "aws_route53_record" "mail1-record" {
zone_id = "${aws_route53_zone.some-zone.zone_id}"
name = "aztrahome.xyz"
type = "MX"
ttl = "300"
records = [
"1 aspmx.l.google.com.",
"5 alt1.aspmx.l.google.com.",
"5 alt2.aspmx.l.google.com.",
"10 aspmx2.googlemail.com.",
"10 aspmx3.googlemail.com."
]
}

output "ns-servers" {
value = "${aws_route53_zone.some-zone.name_servers}"
}

provider.tf-specifies AWS region

provider "aws" {
    region = "${var.AWS_REGION}"
}

vars.tf-variable file, in this case we defined only one variable-aws region

variable "AWS_REGION" {
  default = "eu-west-1"
}

Unlike previous examples, in this one we don’t need file with AWS credentials, because we can install AWS CLI tools, but first we need to install python, because i’m used CentOS minimal, i used this approach to install it. Then i installed AWS CLI.

Now keys are located on local machine (ls ~/.aws), so no need for storing it on terraform file.When Route 53 is deployed using terraform

1.PNG

 

We can check AWS console

1.PNG

 

Auto Scaling helps you maintain application availability and allows to dynamically scale Amazon instances capacity up or down automatically according to defined conditions. Auto Scaling detects impaired Amazon EC2 instances and unhealthy applications, and replace the instances without user intervention.

key.tf

Creating key pair

lifecycle {
ignore_changes = [“public_key”]
}

don’t recreate key pair if public key/path to public key changes

resource "aws_key_pair" "mykeypair" { 
key_name = "mykeypair"
public_key = "${file("${var.PATH_TO_PUBLIC_KEY}")}"
lifecycle { 
ignore_changes = ["public_key"]
} 
}

sg.tf

Define security group which will allow ssh traffic from everywhere

resource "aws_security_group" "allow-ssh" {
vpc_id = "${aws_vpc.main.id}"
name = "allow-ssh"
description = "security group that allows ssh and all egress traffic"
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
tags {
Name = "allow-ssh"
}
}

vars.tf

Define variables for Access keys,Private and public keys as well as AWS regions and AMIs

variable "AWS_ACCESS_KEY" {}
variable "AWS_SECRET_KEY" {}
variable "AWS_REGION" {
default = "eu-west-1"
}
variable "PATH_TO_PRIVATE_KEY" {
default = "mykey"
}
variable "PATH_TO_PUBLIC_KEY" {
default = "mykey.pub"
}
variable "AMIS" {
type = "map"
default = {
us-east-1 = "ami-13be557e"
us-west-2 = "ami-06b94666"
eu-west-1 = "ami-844e0bf7"
}
}

terraform.tfvars

specify AWS access keys

AWS_ACCESS_KEY="key"
AWS_SECRET_KEY="key"

provider.tf

point to variable in vars.tf and terraform.tfvars

provider "aws" {
region = "${var.AWS_REGION}"
access_key="${var.AWS_ACCESS_KEY}"
secret_key="${var.AWS_SECRET_KEY}"
}

vpc.tf

Creates Virtual Private Cloud (subnets, internet gateway and modifies default route

# Internet VPC
resource "aws_vpc" "main" {
    cidr_block = "10.0.0.0/16"
    instance_tenancy = "default"
    enable_dns_support = "true"
    enable_dns_hostnames = "true"
    enable_classiclink = "false"
    tags {
        Name = "main"
    }
}


# Subnets
resource "aws_subnet" "main-public-1" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.1.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1a"

    tags {
        Name = "main-public-1"
    }
}
resource "aws_subnet" "main-public-2" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.2.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1b"

    tags {
        Name = "main-public-2"
    }
}
resource "aws_subnet" "main-public-3" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.3.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1c"

    tags {
        Name = "main-public-3"
    }
}
resource "aws_subnet" "main-private-1" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.4.0/24"
    map_public_ip_on_launch = "false"
    availability_zone = "eu-west-1a"

    tags {
        Name = "main-private-1"
    }
}
resource "aws_subnet" "main-private-2" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.5.0/24"
    map_public_ip_on_launch = "false"
    availability_zone = "eu-west-1b"

    tags {
        Name = "main-private-2"
    }
}
resource "aws_subnet" "main-private-3" {
    vpc_id = "${aws_vpc.main.id}"
    cidr_block = "10.0.6.0/24"
    map_public_ip_on_launch = "false"
    availability_zone = "eu-west-1c"

    tags {
        Name = "main-private-3"
    }
}

# Internet GW
resource "aws_internet_gateway" "main-gw" {
    vpc_id = "${aws_vpc.main.id}"

    tags {
        Name = "main"
    }
}

# route tables
resource "aws_route_table" "main-public" {
    vpc_id = "${aws_vpc.main.id}"
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.main-gw.id}"
    }

    tags {
        Name = "main-public-1"
    }
}

# route associations public
resource "aws_route_table_association" "main-public-1-a" {
    subnet_id = "${aws_subnet.main-public-1.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}
resource "aws_route_table_association" "main-public-2-a" {
    subnet_id = "${aws_subnet.main-public-2.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}
resource "aws_route_table_association" "main-public-3-a" {
    subnet_id = "${aws_subnet.main-public-3.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}

 

autoscaling.tf

contains definition for launch configuration and auto scaling group

launch configuration is a template that an Auto Scaling group uses to launch EC2 instances.

An Auto Scaling group contains a collection of EC2 instances that share similar characteristics and are treated as a logical grouping for the purposes of instance scaling and management.

File defines launch configuration first (instance AMI and type) and keypair,

then defines autoscaling group (deployed in 2 subnets), it will start with one instance, if after 300 seconds instance is unhelathy it will spin-up another one (health_check_type = “EC2”)

resource "aws_launch_configuration" "example-launchconfig" {
name_prefix = "example-launchconfig"
image_id = "${lookup(var.AMIS, var.AWS_REGION)}"
instance_type = "t2.micro"
key_name = "${aws_key_pair.mykeypair.key_name}"
security_groups = ["${aws_security_group.allow-ssh.id}"]
}
resource "aws_autoscaling_group" "example-autoscaling" {
name = "example-autoscaling"
vpc_zone_identifier = ["${aws_subnet.main-public-1.id}", "${aws_subnet.main-public-2.id}"]
launch_configuration = "${aws_launch_configuration.example-launchconfig.name}"
min_size = 1
max_size = 2
health_check_grace_period = 300
health_check_type = "EC2"
force_delete = true
tag {
key = "Name"
value = "ec2 instance"
propagate_at_launch = true
}
}

autoscalingpolicy.tf

This file contains autoscaling policy

Here we define how we want to scale in response to changing demand

We specified increasing instance by 1 (scaling_adjustment = “1”), period without scaling (5 minutes-cooldown), policy type,Simple scaling—Increase or decrease the current capacity of the group based on a single scaling adjustment.Then we created cloudwatch alarm wich triggers autoscaling policy which will compare CPU utilization.If average of CPU utilization is higher than 30 % then new instance will be created

# scale up alarm
resource "aws_autoscaling_policy" "example-cpu-policy" {
name = "example-cpu-policy"
autoscaling_group_name = "${aws_autoscaling_group.example-autoscaling.name}"
adjustment_type = "ChangeInCapacity"
scaling_adjustment = "1"
cooldown = "300"
policy_type = "SimpleScaling"
}
resource "aws_cloudwatch_metric_alarm" "example-cpu-alarm" {
alarm_name = "example-cpu-alarm"
alarm_description = "example-cpu-alarm"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "30"
dimensions = {
"AutoScalingGroupName" = "${aws_autoscaling_group.example-autoscaling.name}"
}
actions_enabled = true
alarm_actions = ["${aws_autoscaling_policy.example-cpu-policy.arn}"]
}
# scale down alarm
resource "aws_autoscaling_policy" "example-cpu-policy-scaledown" {
name = "example-cpu-policy-scaledown"
autoscaling_group_name = "${aws_autoscaling_group.example-autoscaling.name}"
adjustment_type = "ChangeInCapacity"
scaling_adjustment = "-1"
cooldown = "300"
policy_type = "SimpleScaling"
}
resource "aws_cloudwatch_metric_alarm" "example-cpu-alarm-scaledown" {
alarm_name = "example-cpu-alarm-scaledown"
alarm_description = "example-cpu-alarm-scaledown"
comparison_operator = "LessThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "5"
dimensions = {
"AutoScalingGroupName" = "${aws_autoscaling_group.example-autoscaling.name}"
}
actions_enabled = true
alarm_actions = ["${aws_autoscaling_policy.example-cpu-policy-scaledown.arn}"]
}

As usual, test it first:

terraform init && terraform plan