From c5804f14dab795574c9b2c71274e3bcf113d8b55 Mon Sep 17 00:00:00 2001 From: Magicloud <1886157+Magicloud@users.noreply.github.com> Date: Sun, 21 Jul 2019 20:42:15 +0800 Subject: [PATCH 1/3] New: module iam-instance-profile Abstract a usage pattern for IAM instance profile. The instance level should setup this module and pass the role name to modules that attach the policy. Refer to single-node-asg and persistent-ebs for usage. Simply export profile id for attaching to instance, and role name for ataching policies. --- CHANGELOG.md | 3 +- examples/nexus-asg/nexus.tf | 5 ++-- modules/iam-instance-profile/README.md | 21 +++++++++++++ modules/iam-instance-profile/main.tf | 38 ++++++++++++++++++++++++ modules/iam-instance-profile/versions.tf | 4 +++ modules/persistent-ebs/data.tf | 21 ++++--------- modules/persistent-ebs/iam.tf | 19 ------------ modules/persistent-ebs/main.tf | 26 ++-------------- modules/persistent-ebs/variables.tf | 4 +++ modules/single-node-asg/main.tf | 29 ++++++++++-------- modules/single-node-asg/outputs.tf | 16 ---------- 11 files changed, 98 insertions(+), 88 deletions(-) create mode 100644 modules/iam-instance-profile/README.md create mode 100644 modules/iam-instance-profile/main.tf create mode 100644 modules/iam-instance-profile/versions.tf delete mode 100644 modules/persistent-ebs/iam.tf diff --git a/CHANGELOG.md b/CHANGELOG.md index 092bd274..bdcf72a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Modules +* `iam-instance-profile`: Abstract the usage pattern of IAM instance profile. + ### Examples @@ -40,7 +42,6 @@ * `load-asg`: updated to use new `autoscaling-policy-metric-alarm-pair` module - # v0.9.0 ### Summary diff --git a/examples/nexus-asg/nexus.tf b/examples/nexus-asg/nexus.tf index c6fdaef5..2254b70c 100644 --- a/examples/nexus-asg/nexus.tf +++ b/examples/nexus-asg/nexus.tf @@ -16,16 +16,17 @@ variable "region" { description = "The region to put resources in" - default = "us-east-1" + default = "us-east-2" } variable "az" { description = "The availability zone to put resources in" - default = "us-east-1a" + default = "us-east-2b" } variable "key_name" { description = "The keypair used to ssh into the asg intances" + default = "shida-east-2" } module "vpc" { diff --git a/modules/iam-instance-profile/README.md b/modules/iam-instance-profile/README.md new file mode 100644 index 00000000..e1d2db4d --- /dev/null +++ b/modules/iam-instance-profile/README.md @@ -0,0 +1,21 @@ +# IAM Instance Profile + +This module abstracts the useage pattern of IAM instance profile. The caller provides role/policy, and gets profile id to assign to instance. + +Sample usgae: + +``` +module "iam_instance_profile" { + source = "../iam-instance-profile" + assume_role_policy = "${data.aws_iam_policy_document.attach_ebs.json}" + policy = "${data.aws_iam_policy_document.attach_ebs_policy.json}" + name_prefix = "persistent-ebs" +} + +module "server" { + source = "../asg" + iam_profile = "${module.iam_instance_profile.iam_profile_id}" + + # other things here is ignored +} +``` diff --git a/modules/iam-instance-profile/main.tf b/modules/iam-instance-profile/main.tf new file mode 100644 index 00000000..b8178e70 --- /dev/null +++ b/modules/iam-instance-profile/main.tf @@ -0,0 +1,38 @@ +variable "name_prefix" { + description = "Creates a unique name beginning with the specified prefix." +} + +resource "aws_iam_instance_profile" "profile" { + name_prefix = var.name_prefix + role = aws_iam_role.role.name +} + +resource "aws_iam_role" "role" { + name = var.name_prefix + path = "/" + assume_role_policy = <<-EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] + } +EOF + +} + +output "iam_role_name" { + value = aws_iam_role.role.name +} + +output "iam_profile_id" { + value = aws_iam_instance_profile.profile.id +} + diff --git a/modules/iam-instance-profile/versions.tf b/modules/iam-instance-profile/versions.tf new file mode 100644 index 00000000..ac97c6ac --- /dev/null +++ b/modules/iam-instance-profile/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/modules/persistent-ebs/data.tf b/modules/persistent-ebs/data.tf index d82c7d89..51c1b231 100644 --- a/modules/persistent-ebs/data.tf +++ b/modules/persistent-ebs/data.tf @@ -4,21 +4,7 @@ data "aws_caller_identity" "current" { data "aws_partition" "current" { } -data "aws_iam_policy_document" "attach_ebs" { - statement { - sid = "" - effect = "Allow" - - principals { - type = "Service" - identifiers = ["ec2.amazonaws.com"] - } - - actions = ["sts:AssumeRole"] - } -} - -data "aws_iam_policy_document" "attach_ebs_policy" { +data "aws_iam_policy_document" "attach_ebs_policy_doc" { statement { sid = "" effect = "Allow" @@ -35,3 +21,8 @@ data "aws_iam_policy_document" "attach_ebs_policy" { } } +resource "aws_iam_policy" "attach_ebs_policy" { + name = "attach_ebs" + + policy = data.aws_iam_policy_document.attach_ebs_policy_doc.json +} diff --git a/modules/persistent-ebs/iam.tf b/modules/persistent-ebs/iam.tf deleted file mode 100644 index 916432b1..00000000 --- a/modules/persistent-ebs/iam.tf +++ /dev/null @@ -1,19 +0,0 @@ -resource "aws_iam_instance_profile" "attach_ebs" { - name = "${var.name_prefix}-${var.az}-attach-ebs" - role = aws_iam_role.attach_ebs.name -} - -# -resource "aws_iam_role" "attach_ebs" { - name = "${var.name_prefix}-${var.az}-attach-ebs" - path = "/" - assume_role_policy = data.aws_iam_policy_document.attach_ebs.json -} - -# -resource "aws_iam_role_policy" "attach_ebs" { - name = "${var.name_prefix}-${var.az}-attach-ebs-${aws_ebs_volume.main.id}" - role = aws_iam_role.attach_ebs.id - policy = data.aws_iam_policy_document.attach_ebs_policy.json -} - diff --git a/modules/persistent-ebs/main.tf b/modules/persistent-ebs/main.tf index 3ae00a09..9020f723 100644 --- a/modules/persistent-ebs/main.tf +++ b/modules/persistent-ebs/main.tf @@ -25,29 +25,9 @@ resource "aws_ebs_volume" "main" { ) } -output "iam_profile_id" { - value = aws_iam_instance_profile.attach_ebs.id - description = "`id` exported from the `aws_iam_instance_profile`" -} - -output "iam_profile_arn" { - value = aws_iam_instance_profile.attach_ebs.arn - description = "`arn` exported from the `aws_iam_instance_profile`" -} - -output "iam_profile_policy_document" { - value = aws_iam_role_policy.attach_ebs.policy - description = "`policy` exported from the `aws_iam_role_policy`" -} - -output "iam_role_arn" { - value = aws_iam_role.attach_ebs.arn - description = "`arn` exported from the `aws_iam_role`" -} - -output "iam_role_name" { - value = aws_iam_role.attach_ebs.name - description = "`name` exported from the `aws_iam_role`" +resource "aws_iam_role_policy_attachment" "attach_ebs" { + role = var.iam_instance_profile_role_name + policy_arn = aws_iam_policy.attach_ebs_policy.arn } output "volume_id" { diff --git a/modules/persistent-ebs/variables.tf b/modules/persistent-ebs/variables.tf index 1e1e9be7..1f70f89a 100644 --- a/modules/persistent-ebs/variables.tf +++ b/modules/persistent-ebs/variables.tf @@ -56,3 +56,7 @@ variable "extra_tags" { type = map(string) } +variable "iam_instance_profile_role_name" { + description = "The role to attach policy needed by this module." + type = string +} diff --git a/modules/single-node-asg/main.tf b/modules/single-node-asg/main.tf index 9e70c6d6..01b9b95b 100644 --- a/modules/single-node-asg/main.tf +++ b/modules/single-node-asg/main.tf @@ -12,16 +12,22 @@ */ module "service-data" { - source = "../persistent-ebs" - name_prefix = "${var.name_prefix}-${var.name_suffix}-data" - region = var.region - az = data.aws_subnet.server-subnet.availability_zone - size = var.data_volume_size - iops = var.data_volume_iops - volume_type = var.data_volume_type - encrypted = var.data_volume_encrypted - kms_key_id = var.data_volume_kms_key_id - snapshot_id = var.data_volume_snapshot_id + source = "../persistent-ebs" + name_prefix = "${var.name_prefix}-${var.name_suffix}-data" + region = var.region + az = data.aws_subnet.server-subnet.availability_zone + size = var.data_volume_size + iops = var.data_volume_iops + volume_type = var.data_volume_type + encrypted = var.data_volume_encrypted + kms_key_id = var.data_volume_kms_key_id + snapshot_id = var.data_volume_snapshot_id + iam_instance_profile_role_name = module.instance_profile.iam_role_name +} + +module "instance_profile" { + source = "../iam-instance-profile" + name_prefix = "${var.name_prefix}-${var.name_suffix}" } module "server" { @@ -44,8 +50,7 @@ module "server" { root_volume_type = var.root_volume_type root_volume_size = var.root_volume_size - # - iam_profile = module.service-data.iam_profile_id + iam_profile = module.instance_profile.iam_profile_id user_data = < Date: Wed, 24 Jul 2019 15:05:53 +0800 Subject: [PATCH 2/3] New function: single-node-asg module supports binding EIP by itself. Since it is single node, binding an EIP to the instance is possible. And it eases other things since the public interface is constant. Add assign_eip variable to single-node-asg. If turns it on, an EIP will be allocated, and assocated with the instance. --- examples/single-node-asg-test/tester.tf | 65 +++++++++++++++++++++++++ modules/persistent-ebs/data.tf | 3 +- modules/single-node-asg/main.tf | 31 ++++++++++-- modules/single-node-asg/variables.tf | 7 ++- modules/vpc-scenario-1/main.tf | 1 + modules/vpc-scenario-1/variables.tf | 9 +++- 6 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 examples/single-node-asg-test/tester.tf diff --git a/examples/single-node-asg-test/tester.tf b/examples/single-node-asg-test/tester.tf new file mode 100644 index 00000000..59885b35 --- /dev/null +++ b/examples/single-node-asg-test/tester.tf @@ -0,0 +1,65 @@ +variable "region" { + description = "The region to put resources in" + default = "us-east-1" +} + +variable "az" { + description = "The availability zone to put resources in" + default = "us-east-1c" +} + +variable "key_name" { + description = "The keypair used to ssh into the asg intances" + default = "shida-east-1" +} + +provider "aws" { + region = var.region +} + +module "vpc" { + source = "../../modules/vpc-scenario-1" + azs = [var.az] + name_prefix = "eiptest" + cidr = "192.168.0.0/16" + public_subnet_cidrs = ["192.168.0.0/16"] + region = var.region + map_on_launch = false +} + +module "snasg" { + source = "../../modules/single-node-asg" + name_prefix = "unit" + name_suffix = "eiptest" + ami = module.ubuntu-ami.id + instance_type = "t2.micro" + region = var.region + key_name = var.key_name + subnet_id = module.vpc.public_subnet_ids[0] + security_group_ids = [aws_security_group.eiptest.id] + assign_eip = true +} + +module "ubuntu-ami" { + source = "../../modules/ami-ubuntu" + release = "16.04" +} + +resource "aws_security_group" "eiptest" { + name = "eiptest" + vpc_id = module.vpc.vpc_id + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/modules/persistent-ebs/data.tf b/modules/persistent-ebs/data.tf index 51c1b231..c10dc27c 100644 --- a/modules/persistent-ebs/data.tf +++ b/modules/persistent-ebs/data.tf @@ -22,7 +22,6 @@ data "aws_iam_policy_document" "attach_ebs_policy_doc" { } resource "aws_iam_policy" "attach_ebs_policy" { - name = "attach_ebs" - + name = "attach_ebs" policy = data.aws_iam_policy_document.attach_ebs_policy_doc.json } diff --git a/modules/single-node-asg/main.tf b/modules/single-node-asg/main.tf index 01b9b95b..e53f9c48 100644 --- a/modules/single-node-asg/main.tf +++ b/modules/single-node-asg/main.tf @@ -11,6 +11,10 @@ * */ +resource "aws_eip" "eip" { + count = var.assign_eip ? 1 : 0 +} + module "service-data" { source = "../persistent-ebs" name_prefix = "${var.name_prefix}-${var.name_suffix}-data" @@ -42,20 +46,21 @@ module "server" { ami = var.ami subnet_ids = [var.subnet_id] azs = [data.aws_subnet.server-subnet.availability_zone] - public_ip = var.public_ip key_name = var.key_name elb_names = var.load_balancers max_nodes = 1 min_nodes = 1 root_volume_type = var.root_volume_type root_volume_size = var.root_volume_size - - iam_profile = module.instance_profile.iam_profile_id + iam_profile = module.instance_profile.iam_profile_id user_data = < Date: Fri, 2 Aug 2019 19:39:55 +0800 Subject: [PATCH 3/3] New: module bastion Setup a ssh bastion for specified VPC. This is useful when managing a VPC. Simply a single node ASG (with EIP) with SSH-in allowed. --- examples/bastion-test/tester.tf | 37 +++++++++++++ examples/single-node-asg-test/tester.tf | 2 +- modules/bastion/README.md | 3 + modules/bastion/main.tf | 73 +++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 examples/bastion-test/tester.tf create mode 100644 modules/bastion/README.md create mode 100644 modules/bastion/main.tf diff --git a/examples/bastion-test/tester.tf b/examples/bastion-test/tester.tf new file mode 100644 index 00000000..785f6521 --- /dev/null +++ b/examples/bastion-test/tester.tf @@ -0,0 +1,37 @@ +variable "region" { + description = "The region to put resources in" + default = "us-east-1" +} + +variable "az" { + description = "The availability zone to put resources in" + default = "us-east-1c" +} + +variable "key_name" { + description = "The keypair used to ssh into the asg intances" + default = "shida-east-1" +} + +provider "aws" { + region = var.region +} + +module "vpc" { + source = "../../modules/vpc-scenario-1" + azs = [var.az] + name_prefix = "bastion-test" + cidr = "192.168.0.0/16" + public_subnet_cidrs = ["192.168.0.0/16"] + region = var.region + map_on_launch = false +} + +module "bastion" { + source = "../../modules/bastion" + region = var.region + key_name = var.key_name + public_subnet_id = module.vpc.public_subnet_ids[0] + identifier = "test" + vpc_id = module.vpc.vpc_id +} diff --git a/examples/single-node-asg-test/tester.tf b/examples/single-node-asg-test/tester.tf index 59885b35..e17bb05a 100644 --- a/examples/single-node-asg-test/tester.tf +++ b/examples/single-node-asg-test/tester.tf @@ -37,7 +37,7 @@ module "snasg" { key_name = var.key_name subnet_id = module.vpc.public_subnet_ids[0] security_group_ids = [aws_security_group.eiptest.id] - assign_eip = true + assign_eip = false # true case is tested in bastion-test example } module "ubuntu-ami" { diff --git a/modules/bastion/README.md b/modules/bastion/README.md new file mode 100644 index 00000000..db23197a --- /dev/null +++ b/modules/bastion/README.md @@ -0,0 +1,3 @@ +# SSH Bastion + +This is a module to provide a bastion to access the inside of a VPC from Internet. diff --git a/modules/bastion/main.tf b/modules/bastion/main.tf new file mode 100644 index 00000000..8f75333d --- /dev/null +++ b/modules/bastion/main.tf @@ -0,0 +1,73 @@ +variable "vpc_id" { + type = string + description = "ID of the VPC." +} + +variable "identifier" { + type = string + description = "Identifier of related resources." +} + +variable "region" { + type = string + description = "AWS region for this bastion to be in." +} + +variable "key_name" { + type = string + description = "SSH key pair name for the bastion." +} + +variable "public_subnet_id" { + type = string + description = "The subnet for the bastion. The subnet must be able to access Internet." +} + +variable "instance_type" { + type = string + default = "t2.nano" + description = "Bastion instance type." +} + +variable "egress_cidrs" { + type = list(string) + default = ["0.0.0.0/0"] + description = "Egress subnets that bastion can access." +} + +module "instance" { + source = "../single-node-asg" + name_prefix = var.identifier + name_suffix = "bastion" + ami = module.ubuntu-ami.id + instance_type = var.instance_type + region = var.region + key_name = var.key_name + subnet_id = var.public_subnet_id + security_group_ids = [aws_security_group.bastion.id] + assign_eip = true +} + +resource "aws_security_group" "bastion" { + name = "${var.identifier}-bastion" + vpc_id = var.vpc_id + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = var.egress_cidrs + } +} + +module "ubuntu-ami" { + source = "../../modules/ami-ubuntu" + release = "18.04" +}