├── providers.tf ├── vpc_elasticips.tf ├── directory_service.tf ├── vpc_secgroups.tf ├── README ├── ec2_instances.tf ├── variables.tf ├── terraform.tfvars ├── vpc.tf ├── LICENSE ├── ssm.tf ├── vpc_subnets.tf ├── iam_roles.tf └── vpc_routetables.tf /providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | access_key = "${var.provider_aws_access_key}" 3 | secret_key = "${var.provider_aws_secret_key}" 4 | region = "${var.provider_aws_zone}" 5 | } 6 | -------------------------------------------------------------------------------- /vpc_elasticips.tf: -------------------------------------------------------------------------------- 1 | # 2 | # ElasticIP for NAT gateway in AZ1 3 | # 4 | 5 | resource "aws_eip" "eip_natgw_az1" { 6 | vpc = true 7 | } 8 | 9 | # 10 | # ElasticIP for NAT gateway in AZ2 11 | # 12 | 13 | resource "aws_eip" "eip_natgw_az2" { 14 | vpc = true 15 | } 16 | -------------------------------------------------------------------------------- /directory_service.tf: -------------------------------------------------------------------------------- 1 | resource "aws_directory_service_directory" "myapp_ad" { 2 | name = "${var.dir_domain_name}" 3 | password = "${var.dir_admin_password}" 4 | size = "Large" 5 | vpc_settings { 6 | vpc_id = "${aws_vpc.vpc_myapp.id}" 7 | subnet_ids = [ 8 | "${aws_subnet.subnet_priv_az1.id}", 9 | "${aws_subnet.subnet_priv_az2.id}" 10 | ] 11 | } 12 | type = "${var.dir_type}" 13 | } 14 | -------------------------------------------------------------------------------- /vpc_secgroups.tf: -------------------------------------------------------------------------------- 1 | # 2 | # Security group for AD writer. All traffic from my IP. 3 | # 4 | 5 | resource "aws_security_group" "secgroup_adwriter" { 6 | 7 | name = "SECGROUP-ADWRITER" 8 | 9 | vpc_id = "${aws_vpc.vpc_myapp.id}" 10 | 11 | ingress { 12 | from_port = 1 13 | to_port = 65535 14 | protocol = "tcp" 15 | cidr_blocks = [ 16 | "${var.trusted_ip_address}" 17 | ] 18 | } 19 | 20 | egress { 21 | from_port = 0 22 | to_port = 0 23 | protocol = "-1" 24 | cidr_blocks = ["0.0.0.0/0"] 25 | } 26 | 27 | tags { 28 | Name = "SECGROUP-ADWRITER" 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # aws-msad-terraform 2 | 3 | A proof of concept of deploying a highly available, scalable Microsoft Active Directory infrastructure in AWS using AWS Directory Services. 4 | 5 | AWS Directory Services provides the Active Directory in multiple subnets in different availability zones to achieve high availability. After the AD is deployed, we can only interact with it through a Windows machine (I call it "AD Writer") that has joined the domain. 6 | 7 | In this proof of concept, Terraform is used to orchestrate the deployment of the Active Directory and the AD Writer machine. 8 | 9 | More information at my Medium blog. -------------------------------------------------------------------------------- /ec2_instances.tf: -------------------------------------------------------------------------------- 1 | # 2 | # Active Directory writer 3 | # 4 | 5 | resource "aws_instance" "vm_adwriter" { 6 | disable_api_termination = "${var.vm_adwriter_disable_api_termination}" 7 | instance_type = "${var.vm_adwriter_instance_type}" 8 | ami = "${var.vm_adwriter_image}" 9 | iam_instance_profile = "${aws_iam_instance_profile.instance_profile_adwriter.name}" 10 | subnet_id = "${aws_subnet.subnet_dmz_az1.id}" 11 | vpc_security_group_ids = [ 12 | "${aws_security_group.secgroup_adwriter.id}" 13 | ] 14 | root_block_device { 15 | volume_type = "gp2" 16 | volume_size = "${var.vm_adwriter_disk_size}" 17 | delete_on_termination = true 18 | } 19 | monitoring = true 20 | tags { 21 | Name = "ADWRITER" 22 | } 23 | } -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # AWS credentials 2 | variable "provider_aws_access_key" { } 3 | variable "provider_aws_secret_key" { } 4 | variable "provider_aws_zone" { } 5 | 6 | # Availability zones 7 | variable "az1" {} 8 | variable "az2" {} 9 | 10 | # VPC 11 | variable "vpc_cidr" {} 12 | 13 | variable "subnet_dmz_cidr_az1" {} 14 | variable "subnet_dmz_cidr_az2" {} 15 | 16 | variable "subnet_priv_cidr_az1" {} 17 | variable "subnet_priv_cidr_az2" {} 18 | 19 | # Access 20 | variable "trusted_ip_address" {} 21 | 22 | 23 | # Directory Service 24 | variable "dir_domain_name" {} 25 | variable "dir_admin_password" {} 26 | variable "dir_type" {} 27 | variable "dir_computer_ou" {} 28 | 29 | # AD Writer machine 30 | variable "vm_adwriter_disable_api_termination" {} 31 | variable "vm_adwriter_instance_type" {} 32 | variable "vm_adwriter_image" {} 33 | variable "vm_adwriter_disk_size" {} 34 | -------------------------------------------------------------------------------- /terraform.tfvars: -------------------------------------------------------------------------------- 1 | # AWS credentials 2 | provider_aws_access_key = "access_key_here" 3 | provider_aws_secret_key = "secret_key_here" 4 | provider_aws_zone = "ap-southeast-1" 5 | 6 | # Availability zones 7 | az1 = "ap-southeast-1a" 8 | az2 = "ap-southeast-1b" 9 | 10 | # VPC 11 | vpc_cidr = "10.1.0.0/16" 12 | 13 | subnet_priv_cidr_az1 = "10.1.1.0/24" 14 | subnet_priv_cidr_az2 = "10.1.2.0/24" 15 | 16 | subnet_dmz_cidr_az1 = "10.1.3.0/24" 17 | subnet_dmz_cidr_az2 = "10.1.4.0/24" 18 | 19 | trusted_ip_address = "1.2.3.4/32" 20 | 21 | # Directory Service 22 | dir_domain_name = "myapp.com" 23 | dir_admin_password = "Sup3rS3cret" 24 | dir_type = "MicrosoftAD" 25 | dir_computer_ou = "OU=myapp,DC=myapp,DC=com" 26 | 27 | # AD Writer machine 28 | vm_adwriter_disable_api_termination = false 29 | vm_adwriter_instance_type = "t2.small" 30 | vm_adwriter_image = "ami-9e3bbefd" # Windows Server 2012 R2 Base, ap-southeast-1 31 | vm_adwriter_disk_size = 30 32 | -------------------------------------------------------------------------------- /vpc.tf: -------------------------------------------------------------------------------- 1 | # 2 | # VPC 3 | # 4 | 5 | resource "aws_vpc" "vpc_myapp" { 6 | 7 | cidr_block = "${var.vpc_cidr}" 8 | instance_tenancy = "default" 9 | enable_dns_support = true 10 | enable_dns_hostnames = true 11 | 12 | tags { 13 | Name = "VPC-MYAPP" 14 | } 15 | 16 | } 17 | 18 | # 19 | # Internet Gateway - VPC wide 20 | # 21 | 22 | resource "aws_internet_gateway" "igw_main" { 23 | 24 | vpc_id = "${aws_vpc.vpc_myapp.id}" 25 | 26 | tags { 27 | Name = "IGW-MYAPP" 28 | } 29 | 30 | depends_on = ["aws_vpc.vpc_myapp"] 31 | 32 | } 33 | 34 | # 35 | # NAT Gateway in AZ1 36 | # 37 | 38 | resource "aws_nat_gateway" "natgw_az1" { 39 | 40 | allocation_id = "${aws_eip.eip_natgw_az1.id}" 41 | subnet_id = "${aws_subnet.subnet_dmz_az1.id}" 42 | 43 | depends_on = ["aws_internet_gateway.igw_main"] 44 | 45 | } 46 | 47 | # 48 | # NAT Gateway in AZ2 49 | # 50 | 51 | resource "aws_nat_gateway" "natgw_az2" { 52 | 53 | allocation_id = "${aws_eip.eip_natgw_az2.id}" 54 | subnet_id = "${aws_subnet.subnet_dmz_az2.id}" 55 | 56 | depends_on = ["aws_internet_gateway.igw_main"] 57 | 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright Tony P. Hadimulyono (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ssm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_document" "myapp_dir_default_doc" { 2 | name = "myapp_dir_default_doc" 3 | document_type = "Command" 4 | 5 | content = <