├── .gitignore ├── LICENSE ├── README.md ├── examples └── complete │ └── main.tf ├── outputs.tf ├── providers.tf ├── variables.tf └── vpc.tf /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 - Lauro Fialho Müller 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-networking-tf-course 2 | Networking module created during Lauro Müller's Terraform course: https://www.lauromueller.com/courses/mastering-terraform 3 | 4 | This module manages the creation of VPCs and Subnets, allowing for the creation of both private and public subnets. 5 | 6 | Example usage: 7 | ``` 8 | module "vpc" { 9 | source = "./modules/networking" 10 | 11 | vpc_config = { 12 | cidr_block = "10.0.0.0/16" 13 | name = "your_vpc" 14 | } 15 | 16 | subnet_config = { 17 | subnet_1 = { 18 | cidr_block = "10.0.0.0/24" 19 | az = "eu-west-1a" 20 | } 21 | subnet_2 = { 22 | cidr_block = "10.0.1.0/24" 23 | public = true 24 | az = "eu-west-1b" 25 | } 26 | } 27 | } 28 | ``` 29 | 30 | **Make sure to check my other courses:** 31 | 32 | - 👉 The Complete Docker and Kubernetes Course: From Zero to Hero - https://www.lauromueller.com/courses/docker-kubernetes 33 | - 👉 The Definitive Helm Course: From Beginner to Master - https://www.lauromueller.com/courses/definitive-helm-course 34 | - 👉 Mastering GitHub Actions: From Beginner to Expert - https://www.lauromueller.com/courses/mastering-github-actions 35 | - 👉 Write better code: 20 code smells and how to get rid of them - https://www.lauromueller.com/courses/writing-clean-code 36 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "./modules/networking" 3 | 4 | vpc_config = { 5 | cidr_block = "10.0.0.0/16" 6 | name = "13-local-modules" 7 | } 8 | 9 | subnet_config = { 10 | subnet_1 = { 11 | cidr_block = "10.0.0.0/24" 12 | az = "eu-west-1a" 13 | } 14 | subnet_2 = { 15 | cidr_block = "10.0.1.0/24" 16 | # Public subnets are indicated by setting the "public" option to true. 17 | public = true 18 | az = "eu-west-1b" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | # 1. VPC ID 2 | # 2. Public subnets - subnet_key => { subnet_id, availability_zone } 3 | # 3. Private subnets - subnet_key => { subnet_id, availability_zone } 4 | 5 | locals { 6 | output_public_subnets = { 7 | for key in keys(local.public_subnets) : key => { 8 | subnet_id = aws_subnet.this[key].id 9 | availability_zone = aws_subnet.this[key].availability_zone 10 | } 11 | } 12 | 13 | output_private_subnets = { 14 | for key in keys(local.private_subnets) : key => { 15 | subnet_id = aws_subnet.this[key].id 16 | availability_zone = aws_subnet.this[key].availability_zone 17 | } 18 | } 19 | } 20 | 21 | output "vpc_id" { 22 | description = "The AWS ID from the created VPC" 23 | value = aws_vpc.this.id 24 | } 25 | 26 | output "public_subnets" { 27 | description = "The ID and the availability zone of public subnets." 28 | value = local.output_public_subnets 29 | } 30 | 31 | output "private_subnets" { 32 | description = "The ID and the availability zone of private subnets." 33 | value = local.output_private_subnets 34 | } -------------------------------------------------------------------------------- /providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 5.0" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_config" { 2 | description = "Contains the VPC configuration. More specifically, the required cidr_block and the VPC name." 3 | 4 | type = object({ 5 | cidr_block = string 6 | name = string 7 | }) 8 | 9 | validation { 10 | condition = can(cidrnetmask(var.vpc_config.cidr_block)) 11 | error_message = "The cidr_block config option must contain a valid CIDR block." 12 | } 13 | } 14 | 15 | variable "subnet_config" { 16 | description = < config if config.public 4 | } 5 | 6 | private_subnets = { 7 | for key, config in var.subnet_config : key => config if !config.public 8 | } 9 | } 10 | 11 | data "aws_availability_zones" "available" { 12 | state = "available" 13 | } 14 | 15 | resource "aws_vpc" "this" { 16 | cidr_block = var.vpc_config.cidr_block 17 | 18 | tags = { 19 | Name = var.vpc_config.name 20 | } 21 | } 22 | 23 | resource "aws_subnet" "this" { 24 | for_each = var.subnet_config 25 | vpc_id = aws_vpc.this.id 26 | availability_zone = each.value.az 27 | cidr_block = each.value.cidr_block 28 | 29 | tags = { 30 | Name = each.key 31 | Access = each.value.public ? "Public" : "Private" 32 | } 33 | 34 | lifecycle { 35 | precondition { 36 | condition = contains(data.aws_availability_zones.available.names, each.value.az) 37 | error_message = <<-EOT 38 | The AZ "${each.value.az}" provided for the subnet "${each.key}" is invalid. 39 | 40 | The applied AWS region "${data.aws_availability_zones.available.id}" supports the following AZs: 41 | [${join(", ", data.aws_availability_zones.available.names)}] 42 | EOT 43 | } 44 | } 45 | } 46 | 47 | resource "aws_internet_gateway" "this" { 48 | count = length(local.public_subnets) > 0 ? 1 : 0 49 | vpc_id = aws_vpc.this.id 50 | } 51 | 52 | resource "aws_route_table" "public_rtb" { 53 | count = length(local.public_subnets) > 0 ? 1 : 0 54 | vpc_id = aws_vpc.this.id 55 | 56 | route { 57 | cidr_block = "0.0.0.0/0" 58 | gateway_id = aws_internet_gateway.this[0].id 59 | } 60 | } 61 | 62 | resource "aws_route_table_association" "public" { 63 | for_each = local.public_subnets 64 | 65 | subnet_id = aws_subnet.this[each.key].id 66 | route_table_id = aws_route_table.public_rtb[0].id 67 | } --------------------------------------------------------------------------------