diff --git a/.gitignore b/.gitignore index 072e772..ba8cdac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,40 @@ -# Local .terraform directories -*.terraform -**/.terraform/* - -# .tfstate files -*.tfstate -*.tfstate.* - -# Local .hcl configurations -*.hcl - -# Crash log files -crash.log - -# Crash log files -crash.log - -# Ignore any .tfvars files that are generated automatically for each Terraform run. Most -# .tfvars files are managed as part of configuration and so should be included in -# version control. -# -# example.tfvars - -# Ignore override files as they are usually used to override resources locally and so -# are not checked in -override.tf -override.tf.json -*_override.tf -*_override.tf.json - -# Include override files you do wish to add to version control using negated pattern -# -# !example_override.tf - -# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan -# example: *tfplan* +# Local .terraform directories +*.terraform +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Local .hcl configurations +*.hcl + +# Crash log files +crash.log + +# Crash log files +crash.log + +# Ignore any .tfvars files that are generated automatically for each Terraform run. Most +# .tfvars files are managed as part of configuration and so should be included in +# version control. +# +# example.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +kubeconfig/* +provider.tf +backend.tf \ No newline at end of file diff --git a/README.md b/README.md index e813b8f..7ad3ea8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # terraform-aws-eks Hashicorp Terraform AWS EKS Module +Testing diff --git a/cloudwatch.tf b/cloudwatch.tf index 075166f..0136e81 100644 --- a/cloudwatch.tf +++ b/cloudwatch.tf @@ -1,10 +1,10 @@ -resource "aws_cloudwatch_log_group" "eks_cluster_cloudwatch_log_group" { - count = length(var.eks_cluster_enabled_log_types) > 0 ? 1 : 0 - name = "/aws/eks/${var.eks_cluster_prefix}-${var.eks_cluster_environment}/cluster" - retention_in_days = var.eks_cluster_log_retention_in_days +# resource "aws_cloudwatch_log_group" "eks_cluster_cloudwatch_log_group" { +# count = length(var.eks_cluster_enabled_log_types) > 0 ? 1 : 0 +# name = "/aws/eks/${var.eks_cluster_prefix}-${var.eks_cluster_environment}/cluster" +# retention_in_days = var.eks_cluster_log_retention_in_days - tags = { - Name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}" - Environment = var.eks_cluster_environment - } -} \ No newline at end of file +# tags = { +# Name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}" +# Environment = var.eks_cluster_environment +# } +# } \ No newline at end of file diff --git a/iam.tf b/iam.tf index 982e7fb..9bb1678 100644 --- a/iam.tf +++ b/iam.tf @@ -1,54 +1,54 @@ -data "aws_iam_policy_document" "eks_cluster_assume_role_policy" { - statement { - effect = "Allow" - principals { - type = "Service" - identifiers = ["eks.amazonaws.com"] - } - actions = [ - "sts:AssumeRole", - ] - } -} +# data "aws_iam_policy_document" "eks_cluster_assume_role_policy" { +# statement { +# effect = "Allow" +# principals { +# type = "Service" +# identifiers = ["eks.amazonaws.com"] +# } +# actions = [ +# "sts:AssumeRole", +# ] +# } +# } -data "aws_iam_policy_document" "eks_cluster_elb_service_link_role_policy" { - statement { - effect = "Allow" - actions = [ - "ec2:DescribeAccountAttributes", - "ec2:DescribeInternetGateways", - "ec2:DescribeAddresses" - ] - resources = ["*"] - } -} +# data "aws_iam_policy_document" "eks_cluster_elb_service_link_role_policy" { +# statement { +# effect = "Allow" +# actions = [ +# "ec2:DescribeAccountAttributes", +# "ec2:DescribeInternetGateways", +# "ec2:DescribeAddresses" +# ] +# resources = ["*"] +# } +# } -resource "aws_iam_role" "eks_cluster_role" { - name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}-eks-cluster-role" - assume_role_policy = data.aws_iam_policy_document.eks_cluster_assume_role_policy.json -} +# resource "aws_iam_role" "eks_cluster_role" { +# name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}-eks-cluster-role" +# assume_role_policy = data.aws_iam_policy_document.eks_cluster_assume_role_policy.json +# } -resource "aws_iam_policy" "eks_cluster_elb_service_link_policy" { - name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}-eks-cluster-elb-service-link-policy" - policy = data.aws_iam_policy_document.eks_cluster_elb_service_link_role_policy.json -} +# resource "aws_iam_policy" "eks_cluster_elb_service_link_policy" { +# name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}-eks-cluster-elb-service-link-policy" +# policy = data.aws_iam_policy_document.eks_cluster_elb_service_link_role_policy.json +# } -resource "aws_iam_role_policy_attachment" "eks_cluster_policy" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" - role = aws_iam_role.eks_cluster_role.name -} +# resource "aws_iam_role_policy_attachment" "eks_cluster_policy" { +# policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" +# role = aws_iam_role.eks_cluster_role.name +# } -resource "aws_iam_role_policy_attachment" "eks_service_policy" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy" - role = aws_iam_role.eks_cluster_role.name -} +# resource "aws_iam_role_policy_attachment" "eks_service_policy" { +# policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy" +# role = aws_iam_role.eks_cluster_role.name +# } -resource "aws_iam_role_policy_attachment" "eks_vpc_resource_controller_policy" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController" - role = aws_iam_role.eks_cluster_role.name -} +# resource "aws_iam_role_policy_attachment" "eks_vpc_resource_controller_policy" { +# policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController" +# role = aws_iam_role.eks_cluster_role.name +# } -resource "aws_iam_role_policy_attachment" "eks_cluster_elb_service_link_policy_attachment" { - policy_arn = aws_iam_policy.eks_cluster_elb_service_link_policy.arn - role = aws_iam_role.eks_cluster_role.name -} \ No newline at end of file +# resource "aws_iam_role_policy_attachment" "eks_cluster_elb_service_link_policy_attachment" { +# policy_arn = aws_iam_policy.eks_cluster_elb_service_link_policy.arn +# role = aws_iam_role.eks_cluster_role.name +# } \ No newline at end of file diff --git a/main.tf b/main.tf index a2ede96..48a2854 100644 --- a/main.tf +++ b/main.tf @@ -1,64 +1,88 @@ -provider "aws" { - region = "ap-south-1" -} -terraform { - required_version = ">= 0.12.0" -} - -resource "aws_eks_cluster" "eks_cluster" { - name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}" - role_arn = aws_iam_role.eks_cluster_role.arn - version = var.kubernetes_version - - vpc_config { - subnet_ids = var.subnet_ids - } - kubernetes_network_config { - service_ipv4_cidr = var.eks_cluster_service_ipv4_cidr - } - - timeouts { - create = var.eks_cluster_create_timeout - delete = var.eks_cluster_delete_timeout - update = var.eks_cluster_update_timeout - } - - depends_on = [ - aws_iam_role_policy_attachment.eks_cluster_policy, - aws_iam_role_policy_attachment.eks_vpc_resource_controller_policy, - aws_cloudwatch_log_group.eks_cluster_cloudwatch_log_group - ] - - tags = { - Name = "${var.eks_cluster_prefix}-${var.eks_cluster_environment}" - Environment = var.eks_cluster_environment - } -} - -# data "tls_certificate" "eks_cluster_tls_certificate" { -# url = aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer -# } - -# resource "aws_iam_openid_connect_provider" "eks_cluster_openid_connect_provider" { -# client_id_list = ["sts.amazonaws.com"] -# thumbprint_list = [data.tls_certificate.eks_cluster_tls_certificate.certificates[0].sha1_fingerprint] -# url = aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer -# } - -# data "aws_iam_policy_document" "eks_cluster_assume_role_policy" { -# statement { -# actions = ["sts:AssumeRoleWithWebIdentity"] -# effect = "Allow" - -# condition { -# test = "StringEquals" -# variable = "${replace(aws_iam_openid_connect_provider.eks_cluster_openid_connect_provider.url, "https://", "")}:sub" -# values = ["system:serviceaccount:kube-system:aws-node"] -# } - -# principals { -# identifiers = [aws_iam_openid_connect_provider.eks_cluster_openid_connect_provider.arn] -# type = "Federated" -# } -# } -# } \ No newline at end of file +####################################################### +# Terraform Configuration +####################################################### + +# Specify the required providers and backend for Terraform state +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + backend "s3" { + # Backend configuration for S3 will be added here + } +} + +# AWS provider configuration +provider "aws" { + region = var.region +} + +####################################################### +# VPC Module +####################################################### + +# Create the VPC and subnets using a module +module "vpc" { + source = "./modules/vpc" + cluster_prefix = var.cluster_prefix + cidr = var.cidr + subnet_bits = var.subnet_bits +} + +####################################################### +# EKS Cluster Module +####################################################### + +# Create the EKS cluster using a module +module "eks" { + source = "./modules/eks" + cluster_prefix = var.cluster_prefix + kubernetes_version = var.kubernetes_version + private_subnet_ids = module.vpc.private_subnet_ids + eks_cluster_enabled_log_types = var.eks_cluster_enabled_log_types +} + +####################################################### +# EKS Node Groups Module +####################################################### + +# Create EKS node groups using a module +module "nodes" { + depends_on = [module.eks] + for_each = var.nodes + source = "./modules/eks/nodes" + cluster_prefix = var.cluster_prefix + node_environment = each.key + subnet_ids = module.vpc.private_subnet_ids + cluster_name = module.eks.cluster_name + node_type = each.value.node_type + instance_type = try(each.value.instance_type, null) + desired_size = try(each.value.desired_size, null) + max_size = try(each.value.max_size, null) + min_size = try(each.value.min_size, null) + selector = each.value.node_type == "fargate" ? each.value.selector : null +} + +####################################################### +# RDS Database Module +####################################################### + +# Create RDS instances using a module +module "database" { + for_each = var.databases + source = "./modules/rds" + cluster_prefix = var.cluster_prefix + db_environment = each.key + db_engine = each.value.db_engine + db_instance_class = each.value.db_instance_class + db_version = each.value.db_version + db_storage = each.value.db_storage + db_name = each.value.db_name + db_username = each.value.db_username + db_password = each.value.db_password + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.storage_subnet_ids +} diff --git a/modules/ecr/main.tf b/modules/ecr/main.tf new file mode 100644 index 0000000..f20192d --- /dev/null +++ b/modules/ecr/main.tf @@ -0,0 +1,19 @@ +################################################################################### +# Ecr Configuration +################################################################################### + +resource "aws_ecr_repository" "ecr" { + for_each = { for repo in var.repository_name : repo => repo } + + name = "${var.cluster_prefix}-${each.key}" + image_tag_mutability = var.image_tag_mutability + image_scanning_configuration { + scan_on_push = var.scan_on_push + } +} + + +# resource "aws_ecr_lifecycle_policy" "this" { +# repository = aws_ecr_repository.ecr.name +# policy = var.lifecycle_policy +# } diff --git a/modules/ecr/outputs.tf b/modules/ecr/outputs.tf new file mode 100644 index 0000000..77bd0bd --- /dev/null +++ b/modules/ecr/outputs.tf @@ -0,0 +1,13 @@ +################################################################################### +# Output Configuration +################################################################################### + +output "repository_urls" { + description = "The URLs of the created repositories" + value = { for key, ecr_repo in aws_ecr_repository.ecr : key => ecr_repo.repository_url } +} + +output "repository_names" { + description = "The names of the repositories" + value = { for key, _ in aws_ecr_repository.ecr : key => aws_ecr_repository.ecr[key].name } +} \ No newline at end of file diff --git a/modules/ecr/variables.tf b/modules/ecr/variables.tf new file mode 100644 index 0000000..d3ac32e --- /dev/null +++ b/modules/ecr/variables.tf @@ -0,0 +1,27 @@ +################################################################################### +# Variables +################################################################################### + + +variable "cluster_prefix" { + description = "Prefix for the ECR repositories" + type = string +} + +variable "repository_name" { + description = "The name of the ECR repository" + type = list(string) +} + +variable "image_tag_mutability" { + description = "The tag mutability setting for the repository. Valid values are MUTABLE and IMMUTABLE." + type = string + default = "MUTABLE" +} + + +variable "scan_on_push" { + description = "Indicates whether images are scanned after being pushed to the repository (true/false)." + type = bool + default = false +} \ No newline at end of file diff --git a/modules/eks/iam_policy.json b/modules/eks/iam_policy.json new file mode 100644 index 0000000..ebfef10 --- /dev/null +++ b/modules/eks/iam_policy.json @@ -0,0 +1,242 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "iam:CreateServiceLinkedRole" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeAccountAttributes", + "ec2:DescribeAddresses", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInternetGateways", + "ec2:DescribeVpcs", + "ec2:DescribeVpcPeeringConnections", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTags", + "ec2:GetCoipPoolUsage", + "ec2:DescribeCoipPools", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DescribeListenerCertificates", + "elasticloadbalancing:DescribeSSLPolicies", + "elasticloadbalancing:DescribeRules", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "elasticloadbalancing:DescribeTargetHealth", + "elasticloadbalancing:DescribeTags", + "elasticloadbalancing:DescribeTrustStores" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "cognito-idp:DescribeUserPoolClient", + "acm:ListCertificates", + "acm:DescribeCertificate", + "iam:ListServerCertificates", + "iam:GetServerCertificate", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "shield:GetSubscriptionState", + "shield:DescribeProtection", + "shield:CreateProtection", + "shield:DeleteProtection" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateSecurityGroup" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "StringEquals": { + "ec2:CreateAction": "CreateSecurityGroup" + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags", + "ec2:DeleteTags" + ], + "Resource": "arn:aws:ec2:*:*:security-group/*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress", + "ec2:DeleteSecurityGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:DeleteListener", + "elasticloadbalancing:CreateRule", + "elasticloadbalancing:DeleteRule" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*", + "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:SetIpAddressType", + "elasticloadbalancing:SetSecurityGroups", + "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:DeleteTargetGroup" + ], + "Resource": "*", + "Condition": { + "Null": { + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "StringEquals": { + "elasticloadbalancing:CreateAction": [ + "CreateTargetGroup", + "CreateLoadBalancer" + ] + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets" + ], + "Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:SetWebAcl", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:ModifyRule" + ], + "Resource": "*" + } + ] +} \ No newline at end of file diff --git a/modules/eks/main.tf b/modules/eks/main.tf new file mode 100644 index 0000000..776987e --- /dev/null +++ b/modules/eks/main.tf @@ -0,0 +1,141 @@ +###################################################################################### +# EKS Cluster +###################################################################################### + +// AWS EKS Cluster resource definition +resource "aws_eks_cluster" "eks_cluster" { + name = "${var.cluster_prefix}-cluster" // Name of the EKS cluster + role_arn = aws_iam_role.eks_cluster_role.arn // IAM role ARN for the EKS cluster + version = var.kubernetes_version // Kubernetes version for the cluster + + // VPC configuration for the cluster + vpc_config { + subnet_ids = var.private_subnet_ids // Subnet IDs where EKS resources will be deployed + } + + // Kubernetes network configuration + kubernetes_network_config { + service_ipv4_cidr = var.eks_cluster_service_ipv4_cidr // CIDR block for Kubernetes service IPs + } + + // Timeouts configuration for create, delete, and update operations + timeouts { + create = var.eks_cluster_create_timeout + delete = var.eks_cluster_delete_timeout + update = var.eks_cluster_update_timeout + } + + // Dependencies on IAM resources and policies + depends_on = [ + aws_iam_role_policy_attachment.eks_cluster_policy, + aws_iam_role_policy_attachment.eks_vpc_resource_controller_policy, + aws_iam_role_policy_attachment.eks_cluster_elb_service_link_policy_attachment, + + aws_iam_role.eks_cluster_role, + aws_iam_policy.alb_policy, + aws_cloudwatch_log_group.eks_cluster_cloudwatch_log_group, + ] + + // Tags for the EKS cluster resource + tags = { + Name = "${var.cluster_prefix}-cluster" + Environment = "all-environment" + } +} + +################################################################################# +# Creating Role and Policies for the cluster +################################################################################# + +// IAM policy document for EKS cluster assume role policy +data "aws_iam_policy_document" "eks_cluster_assume_role_policy" { + statement { + effect = "Allow" + principals { + type = "Service" + identifiers = ["eks.amazonaws.com"] + } + actions = [ + "sts:AssumeRole", + ] + } +} + +// IAM policy document for EKS cluster ELB service link role policy +data "aws_iam_policy_document" "eks_cluster_elb_service_link_role_policy" { + statement { + effect = "Allow" + actions = [ + "ec2:DescribeAccountAttributes", + "ec2:DescribeInternetGateways", + "ec2:DescribeAddresses", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:*" + ] + resources = ["*"] + } +} + +// IAM role for EKS cluster +resource "aws_iam_role" "eks_cluster_role" { + name = "${var.cluster_prefix}-eks-cluster-role" + assume_role_policy = data.aws_iam_policy_document.eks_cluster_assume_role_policy.json +} + +// IAM policy for EKS cluster ELB service link +resource "aws_iam_policy" "eks_cluster_elb_service_link_policy" { + name = "${var.cluster_prefix}-eks-cluster-elb-service-link-policy" + policy = data.aws_iam_policy_document.eks_cluster_elb_service_link_role_policy.json +} + +// IAM policy for AWS Load Balancer Controller +resource "aws_iam_policy" "alb_policy" { + name = "AWSLoadBalancerControllerIAMPolicy" + policy = file("${path.module}/iam_policy.json") +} + +// Attachment of AWS Load Balancer Controller IAM policy to EKS cluster role +resource "aws_iam_role_policy_attachment" "alb_policy" { + policy_arn = aws_iam_policy.alb_policy.arn + role = aws_iam_role.eks_cluster_role.name +} + +// Attachment of AmazonEKS policies to EKS cluster role +resource "aws_iam_role_policy_attachment" "eks_cluster_policy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" + role = aws_iam_role.eks_cluster_role.name +} + +resource "aws_iam_role_policy_attachment" "eks_service_policy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy" + role = aws_iam_role.eks_cluster_role.name +} + +// Attachment of AmazonEKSVPCResourceController policy to EKS cluster role +resource "aws_iam_role_policy_attachment" "eks_vpc_resource_controller_policy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController" + role = aws_iam_role.eks_cluster_role.name +} + +// Attachment of EKS cluster ELB service link policy to EKS cluster role +resource "aws_iam_role_policy_attachment" "eks_cluster_elb_service_link_policy_attachment" { + policy_arn = aws_iam_policy.eks_cluster_elb_service_link_policy.arn + role = aws_iam_role.eks_cluster_role.name +} + +################################################################################# +# Cloudwatch for the cluster +################################################################################# + +// CloudWatch log group for EKS cluster +resource "aws_cloudwatch_log_group" "eks_cluster_cloudwatch_log_group" { + count = length(var.eks_cluster_enabled_log_types) > 0 ? 1 : 0 + name = "/aws/eks/${var.cluster_prefix}-cluster" + retention_in_days = var.eks_cluster_log_retention_in_days + + // Tags for the CloudWatch log group + tags = { + Name = "${var.cluster_prefix}-cloudwatch" + Environment = "all-environments" + } +} diff --git a/modules/eks/nodes/main.tf b/modules/eks/nodes/main.tf new file mode 100644 index 0000000..f85e7e8 --- /dev/null +++ b/modules/eks/nodes/main.tf @@ -0,0 +1,124 @@ +################################################################################# +# VM Node Group +################################################################################# + +// AWS EKS Node Group resource +resource "aws_eks_node_group" "node_group" { + count = var.node_type == "vm" ? 1 : 0 // Conditional creation based on node_type + cluster_name = var.cluster_name // Name of the EKS cluster + node_group_name = "${var.cluster_prefix}-${var.node_environment}-node-group" // Name of the node group + node_role_arn = aws_iam_role.vm_node_group[0].arn // IAM role ARN for the node group + subnet_ids = var.subnet_ids // Subnet IDs for the node group instances + instance_types = var.instance_type // Instance types for the node group + disk_size = var.disk_size // Disk size for the node group instances + + // Scaling configuration for the node group + scaling_config { + desired_size = var.desired_size // Desired number of instances + max_size = var.max_size // Maximum number of instances + min_size = var.min_size // Minimum number of instances + } + + // Lifecycle configuration to ignore changes in desired_size + # lifecycle { + # ignore_changes = [scaling_config[0].desired_size] + # } + + // Update configuration for the node group + update_config { + max_unavailable = 1 // Maximum number of unavailable instances during updates + } + + // Dependencies on IAM role policies before creating the node group + depends_on = [ + aws_iam_role.vm_node_group, + aws_iam_role_policy_attachment.AmazonEKSWorkerNodePolicy[0], + aws_iam_role_policy_attachment.AmazonEKS_CNI_Policy[0], + aws_iam_role_policy_attachment.AmazonEC2ContainerRegistryReadOnly[0], + ] +} + +// IAM role for VM Node Group +resource "aws_iam_role" "vm_node_group" { + count = var.node_type == "vm" ? 1 : 0 // Conditional creation based on node_type + name = "${var.cluster_prefix}-${var.node_environment}-vm-role" // Name of the IAM role + + // Assume role policy allowing EC2 instances to assume this role + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "ec2.amazonaws.com" + } + }] + Version = "2012-10-17" + }) +} + +// IAM role policy attachment for AmazonEKSWorkerNodePolicy +resource "aws_iam_role_policy_attachment" "AmazonEKSWorkerNodePolicy" { + count = var.node_type == "vm" ? 1 : 0 // Conditional creation based on node_type + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy" // ARN of the policy + role = aws_iam_role.vm_node_group[0].name // Name of the IAM role to attach the policy to +} + +// IAM role policy attachment for AmazonEKS_CNI_Policy +resource "aws_iam_role_policy_attachment" "AmazonEKS_CNI_Policy" { + count = var.node_type == "vm" ? 1 : 0 // Conditional creation based on node_type + policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" // ARN of the policy + role = aws_iam_role.vm_node_group[0].name // Name of the IAM role to attach the policy to +} + +// IAM role policy attachment for AmazonEC2ContainerRegistryReadOnly +resource "aws_iam_role_policy_attachment" "AmazonEC2ContainerRegistryReadOnly" { + count = var.node_type == "vm" ? 1 : 0 // Conditional creation based on node_type + policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" // ARN of the policy + role = aws_iam_role.vm_node_group[0].name // Name of the IAM role to attach the policy to +} + +################################################################################# +# Fargate Profile +################################################################################# + +// AWS EKS Fargate Profile resource +resource "aws_eks_fargate_profile" "fargate" { + count = var.node_type == "fargate" ? 1 : 0 // Conditional creation based on node_type + cluster_name = var.cluster_name // Name of the EKS cluster + fargate_profile_name = "${var.cluster_prefix}-${var.node_environment}-fargate-profile" // Name of the Fargate profile + pod_execution_role_arn = aws_iam_role.fargate_iam[0].arn // IAM role ARN for Fargate pods + subnet_ids = var.subnet_ids // Subnet IDs for Fargate + + // Dynamic selector for Fargate profile + dynamic "selector" { + for_each = var.selector != null && length(var.selector) > 0 ? toset(var.selector) : [] // Selectors for namespaces + content { + namespace = selector.value // Namespace selector + } + } +} + +// IAM role for Fargate pods +resource "aws_iam_role" "fargate_iam" { + count = var.node_type == "fargate" ? 1 : 0 // Conditional creation based on node_type + name = "${var.cluster_prefix}-${var.node_environment}-fargate-role" // Name of the IAM role + + // Assume role policy allowing EKS Fargate pods to assume this role + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "eks-fargate-pods.amazonaws.com" + } + }] + Version = "2012-10-17" + }) +} + +// IAM role policy attachment for AmazonEKSFargatePodExecutionRolePolicy +resource "aws_iam_role_policy_attachment" "AmazonEKSFargatePodExecutionRolePolicy" { + count = var.node_type == "fargate" ? 1 : 0 // Conditional creation based on node_type + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy" // ARN of the policy + role = aws_iam_role.fargate_iam[0].name // Name of the IAM role to attach the policy to +} diff --git a/modules/eks/nodes/outputs.tf b/modules/eks/nodes/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/eks/nodes/variables.tf b/modules/eks/nodes/variables.tf new file mode 100644 index 0000000..57cdcc8 --- /dev/null +++ b/modules/eks/nodes/variables.tf @@ -0,0 +1,67 @@ +################################################################################# +# Variables +################################################################################# + + +// Prefix used in naming resources within the EKS cluster +variable "cluster_prefix" { + type = string +} + +// Name of the EKS cluster +variable "cluster_name" { + type = string +} + +// List of subnet IDs where EKS resources (nodes or Fargate profiles) will be deployed +variable "subnet_ids" { + type = list(string) +} + +// Desired number of instances in the node group (default: 1) +variable "desired_size" { + type = number + default = 1 +} + +// Maximum number of instances in the node group (default: 1) +variable "max_size" { + type = number + default = 1 +} + +// Minimum number of instances in the node group (default: 1) +variable "min_size" { + type = number + default = 1 +} + +// List of instance types for nodes in the node group (default: ["t3.medium"]) +variable "instance_type" { + type = list(string) + default = ["t3.medium"] +} + +// Environment identifier used in naming resources within the cluster +variable "node_environment" { + type = string +} + +// Size of the disk attached to each instance in the node group (default: "30" GB) +variable "disk_size" { + type = string + default = "30" +} + +// Type of the node group to create. Possible values are 'fargate' or 'vm'. +variable "node_type" { + description = "Type of the node group to create. Possible values are 'fargate' or 'vm'." + type = string + default = "vm" +} + +// List of namespaces for Fargate profile selectors (optional) +variable "selector" { + description = "List of namespaces for Fargate profile selectors." + type = list(string) +} diff --git a/modules/eks/output.tf b/modules/eks/output.tf new file mode 100644 index 0000000..fc5791f --- /dev/null +++ b/modules/eks/output.tf @@ -0,0 +1,16 @@ +###################################################################################### +# Outputs for the KS Modules +###################################################################################### + + +// Output: Cluster Endpoint +output "cluster_endpoint" { + description = "Endpoint for EKS control plane" + value = aws_eks_cluster.eks_cluster.endpoint +} + +// Output: Cluster Name +output "cluster_name" { + description = "Kubernetes Cluster Name" + value = aws_eks_cluster.eks_cluster.name +} diff --git a/modules/eks/variables.tf b/modules/eks/variables.tf new file mode 100644 index 0000000..5f85ada --- /dev/null +++ b/modules/eks/variables.tf @@ -0,0 +1,63 @@ +###################################################################################### +# Variable for the Eks Modules +###################################################################################### + +// Prefix used to apply generic naming to the EKS Cluster +variable "cluster_prefix" { + description = "To apply generic naming to EKS Cluster" + type = string +} + +// Kubernetes version for the EKS Cluster +variable "kubernetes_version" { + description = "Kubernetes version for EKS Cluster" + type = string + default = "1.29" +} + +// List of Control Plane Logging options to enable +variable "eks_cluster_enabled_log_types" { + description = "List of Control Plane Logging options to enable" + type = list(any) + default = ["api", "audit"] +} + +// CloudWatch log events retention period in days for EKS Cluster +variable "eks_cluster_log_retention_in_days" { + description = "Cloudwatch log events retention period in days for EKS Cluster" + type = number + default = 7 +} + +// EKS Cluster - Kubernetes Service IP address range +variable "eks_cluster_service_ipv4_cidr" { + description = "EKS Cluster - Kubernetes Service IP address range" + type = string + default = "10.43.0.0/16" +} + +// Average wait time in minutes for the EKS Cluster to be created +variable "eks_cluster_create_timeout" { + description = "Average wait time in minutes for the EKS Cluster to be created" + type = string + default = "30m" +} + +// Average wait time in minutes for the EKS Cluster to be deleted +variable "eks_cluster_delete_timeout" { + description = "Average wait time in minutes for the EKS Cluster to be deleted" + type = string + default = "15m" +} + +// Average wait time in minutes for the EKS Cluster to be updated +variable "eks_cluster_update_timeout" { + description = "Average wait time in minutes for the EKS Cluster to be updated" + type = string + default = "60m" +} + +// List of private subnet IDs where EKS resources will be deployed +variable "private_subnet_ids" { + type = list(string) +} diff --git a/modules/rds/main.tf b/modules/rds/main.tf new file mode 100644 index 0000000..61556d9 --- /dev/null +++ b/modules/rds/main.tf @@ -0,0 +1,75 @@ +# KMS Key for RDS +resource "aws_kms_key" "kms" { + description = "KMS key for RDS" +} + +data "aws_rds_engine_version" "current" { + engine = var.db_engine + version = var.db_version +} + + +# Security group for RDS instance +resource "aws_security_group" "rds_sg" { + name_prefix = "${var.cluster_prefix}-${var.db_environment}-rds-sg" + description = "Security group for RDS instance" + vpc_id = var.vpc_id + + ingress { + from_port = var.db_port + to_port = var.db_port + protocol = "tcp" + cidr_blocks = var.allowed_ip # Consider restricting this to your IP range or VPC + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.cluster_prefix}-${var.db_environment}-rds-sg" + } +} + +# Subnet group for RDS instance +resource "aws_db_subnet_group" "subnet_group" { + name = "${var.cluster_prefix}-${var.db_environment}-rds-sng" + subnet_ids = var.subnet_ids + description = "Subnet group for RDS instance" + + tags = { + Name = "${var.cluster_prefix}-${var.db_environment}-rds-sng" + } + depends_on = [ + aws_security_group.rds_sg + ] +} + +# RDS instance +resource "aws_db_instance" "db_instance" { + allocated_storage = var.db_storage + # identifier = "${lower(var.cluster_prefix)}-${lower(var.db_environment)}-db" + identifier = "${lower(var.cluster_prefix)}-${lower(var.db_environment)}-db" + db_name = var.db_name + engine = var.db_engine + engine_version = var.db_version + instance_class = var.db_instance_class + + vpc_security_group_ids = [aws_security_group.rds_sg.id] + db_subnet_group_name = aws_db_subnet_group.subnet_group.name + + # manage_master_user_password = true + # master_user_secret_kms_key_id = aws_kms_key.kms.key_id + username = var.db_username + password = var.db_password + skip_final_snapshot = true + delete_automated_backups = true + + tags = { + Name = "${var.cluster_prefix}-${var.db_environment}-db" + } + depends_on = [ aws_db_subnet_group.subnet_group, aws_security_group.rds_sg ] +} diff --git a/modules/rds/outputs.tf b/modules/rds/outputs.tf new file mode 100644 index 0000000..aee4a99 --- /dev/null +++ b/modules/rds/outputs.tf @@ -0,0 +1,24 @@ +############################################################################## +# Outputs +############################################################################# + +# Output: Database Instance Endpoint +# The endpoint address of the RDS instance +output "db_instance_endpoint" { + description = "The endpoint address of the RDS instance" + value = aws_db_instance.db_instance.endpoint +} + +# Output: Database Instance ID +# The unique identifier of the RDS instance +output "db_instance_id" { + description = "The unique identifier of the RDS instance" + value = aws_db_instance.db_instance.id +} + +# Output: RDS Security Group ID +# The ID of the security group associated with the RDS instance +output "rds_security_group_id" { + description = "The ID of the security group associated with the RDS instance" + value = aws_security_group.rds_sg.id +} diff --git a/modules/rds/variables.tf b/modules/rds/variables.tf new file mode 100644 index 0000000..07391a9 --- /dev/null +++ b/modules/rds/variables.tf @@ -0,0 +1,85 @@ +############################################################################## +# Outputs +############################################################################# + +# Variable Definitions + +# Prefix for naming resources +variable "cluster_prefix" { + description = "Prefix for naming resources" + type = string +} + +# Storage size for the database +variable "db_storage" { + description = "Allocated storage size for the RDS instance in GB" + type = number +} + +# Environment for the database (e.g., dev, prod) +variable "db_environment" { + description = "Environment for the database (e.g., dev, prod)" + type = string +} + +# Database engine type (e.g., postgres, mysql) +variable "db_engine" { + description = "Type of the database engine (e.g., postgres, mysql)" + type = string +} + +# Instance class for the database +variable "db_instance_class" { + description = "Instance class for the RDS database instance" + type = string +} + +# Port for the database +variable "db_port" { + description = "Port number on which the database accepts connections. Defaults to 5432 for PostgreSQL" + type = string + default = "5432" +} + +# Allowed IP addresses for database access +variable "allowed_ip" { + description = "List of allowed IP addresses for database access" + type = list(string) + default = ["0.0.0.0/0"] +} + +# Version of the database engine +variable "db_version" { + description = "Version of the database engine" + type = string +} + +# Name of the database +variable "db_name" { + description = "Name of the database" + type = string +} + +# Username for the database +variable "db_username" { + description = "Username for the database" + type = string +} + +# Password for the database +variable "db_password" { + description = "Password for the database" + type = string +} + +# List of subnet IDs for the database +variable "subnet_ids" { + description = "List of subnet IDs for the RDS instance" + type = list(string) +} + +# ID of the VPC +variable "vpc_id" { + description = "ID of the VPC" + type = string +} diff --git a/modules/vpc/gateway/main.tf b/modules/vpc/gateway/main.tf new file mode 100644 index 0000000..82f20e5 --- /dev/null +++ b/modules/vpc/gateway/main.tf @@ -0,0 +1,46 @@ +####################################################### +# Internet Gateway +####################################################### + +# Resource: aws_internet_gateway.this +# Description: Creates an Internet Gateway if gateway_type is "internet" to provide internet access to public subnets. +resource "aws_internet_gateway" "this" { + count = var.gateway_type == "internet" ? 1 : 0 + + vpc_id = var.vpc_id + + tags = { + Name = "${var.cluster_prefix}-${var.gateway_type}-gateway" + } +} + +####################################################### +# Elastic IP (EIP) +####################################################### + +# Resource: aws_eip.nat +# Description: Creates an Elastic IP (EIP) if gateway_type is "nat" to associate with a NAT Gateway. +resource "aws_eip" "nat" { + count = var.gateway_type == "nat" ? 1 : 0 + + tags = { + Name = "${var.cluster_prefix}-eip" + } +} + +####################################################### +# NAT Gateway +####################################################### + +# Resource: aws_nat_gateway.this +# Description: Creates a NAT Gateway if gateway_type is "nat" to allow private subnet instances to access the internet. +resource "aws_nat_gateway" "this" { + count = var.gateway_type == "nat" ? 1 : 0 + + allocation_id = aws_eip.nat[0].id + subnet_id = var.subnet_id + + tags = { + Name = "${var.cluster_prefix}-nat-gateway" + } +} diff --git a/modules/vpc/gateway/outputs.tf b/modules/vpc/gateway/outputs.tf new file mode 100644 index 0000000..9fb1e8a --- /dev/null +++ b/modules/vpc/gateway/outputs.tf @@ -0,0 +1,24 @@ +####################################################### +# Outputs +####################################################### + +# Output: internet_gateway_id +# Description: The ID of the Internet Gateway if gateway_type is "internet", otherwise null. +output "internet_gateway_id" { + description = "The ID of the Internet Gateway" + value = var.gateway_type == "internet" ? aws_internet_gateway.this[0].id : null +} + +# Output: nat_gateway_id +# Description: The ID of the NAT Gateway if gateway_type is "nat", otherwise null. +output "nat_gateway_id" { + description = "The ID of the NAT Gateway" + value = var.gateway_type == "nat" ? aws_nat_gateway.this[0].id : null +} + +# Output: nat_gateway_eip +# Description: The Elastic IP of the NAT Gateway if gateway_type is "nat", otherwise null. +output "nat_gateway_eip" { + description = "The Elastic IP of the NAT Gateway" + value = var.gateway_type == "nat" ? aws_eip.nat[0].public_ip : null +} diff --git a/modules/vpc/gateway/variables.tf b/modules/vpc/gateway/variables.tf new file mode 100644 index 0000000..4875ae6 --- /dev/null +++ b/modules/vpc/gateway/variables.tf @@ -0,0 +1,43 @@ +####################################################### +# Variables +####################################################### + +# Variable: cluster_prefix +# Description: Prefix to apply generic naming to resources like VPC and subnets +variable "cluster_prefix" { + type = string +} + +# Variable: vpc_id +# Description: The ID of the VPC where the gateway will be attached +variable "vpc_id" { + description = "The ID of the VPC" + type = string +} + +# Variable: gateway_type +# Description: Type of gateway to create: 'nat' for NAT Gateway or 'internet' for Internet Gateway +variable "gateway_type" { + description = "Type of gateway: nat or internet" + type = string + validation { + condition = contains(["nat", "internet"], var.gateway_type) + error_message = "gateway_type must be either 'nat' or 'internet'." + } +} + +# Variable: subnet_id +# Description: The ID of the subnet in which to place the NAT Gateway (required if gateway_type is 'nat') +variable "subnet_id" { + description = "The ID of the subnet in which to place the NAT Gateway (required if NAT Gateway)" + type = string + default = null +} + +# Variable: tags +# Description: Tags to assign to the gateway +variable "tags" { + description = "Tags to assign to the gateway" + type = map(string) + default = {} +} diff --git a/modules/vpc/main.tf b/modules/vpc/main.tf new file mode 100644 index 0000000..2b1d018 --- /dev/null +++ b/modules/vpc/main.tf @@ -0,0 +1,104 @@ +####################################################### +# VPC and Subnets +####################################################### + +# Create a VPC with DNS support and hostnames enabled +resource "aws_vpc" "vpc" { + cidr_block = var.cidr + enable_dns_support = true + enable_dns_hostnames = true + + tags = { + Name = "${var.cluster_prefix}-vpc" + } +} + +# Create public subnets +module "public_subnets" { + + source = "./subnet" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + subnet_type = "public" + cidr = var.cidr + subnet_bits = var.subnet_bits + offset = 0 +} + +# Create private subnets +module "private_subnets" { + + source = "./subnet" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + subnet_type = "private" + cidr = var.cidr + subnet_bits = var.subnet_bits + offset = 1 +} + +# Create storage subnets +module "storage_subnets" { + source = "./subnet" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + subnet_type = "storage" + cidr = var.cidr + subnet_bits = var.subnet_bits + offset = 2 +} + +####################################################### +# Gateways +####################################################### + +# Create an Internet Gateway for the public subnets +module "internet_gateway" { + source = "./gateway" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + gateway_type = "internet" +} + +# Create a NAT Gateway for the private and storage subnets +module "nat_gateway" { + source = "./gateway" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + gateway_type = "nat" + subnet_id = module.public_subnets.subnet_ids[0] +} + +####################################################### +# Route Tables +####################################################### + +# Create a public route table and associate it with public subnets +module "public_route_table" { + source = "./route_table" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + subnet_ids = module.public_subnets.subnet_ids + route_table_type = "public" + internet_gateway_id = module.internet_gateway.internet_gateway_id +} + +# Create a private route table and associate it with private subnets +module "private_route_table" { + source = "./route_table" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + subnet_ids = module.private_subnets.subnet_ids + route_table_type = "private" + nat_gateway_id = module.nat_gateway.nat_gateway_id +} + +# Create a storage route table and associate it with storage subnets +module "storage_route_table" { + source = "./route_table" + cluster_prefix = var.cluster_prefix + vpc_id = aws_vpc.vpc.id + subnet_ids = module.storage_subnets.subnet_ids + route_table_type = "private" + nat_gateway_id = module.nat_gateway.nat_gateway_id +} diff --git a/modules/vpc/output.tf b/modules/vpc/output.tf new file mode 100644 index 0000000..cb7390d --- /dev/null +++ b/modules/vpc/output.tf @@ -0,0 +1,38 @@ +####################################################### +# Outputs +####################################################### + +# Output: VPC ID +# The ID of the created VPC +output "vpc_id" { + description = "The ID of the created VPC" + value = aws_vpc.vpc.id +} + +# Output: Public Subnet IDs +# List of IDs of the created public subnets +output "public_subnets_ids" { + description = "List of IDs of the created public subnets" + value = module.public_subnets.subnet_ids +} + +# Output: Private Subnet IDs +# List of IDs of the created private subnets +output "private_subnet_ids" { + description = "List of IDs of the created private subnets" + value = module.private_subnets.subnet_ids +} + +# Output: Storage Subnet IDs +# List of IDs of the created storage subnets +output "storage_subnet_ids" { + description = "List of IDs of the created storage subnets" + value = module.storage_subnets.subnet_ids +} + +# Output: Internet Gateway ID +# The ID of the created Internet Gateway +output "internet_gateway_id" { + description = "The ID of the created Internet Gateway" + value = module.internet_gateway.internet_gateway_id +} diff --git a/modules/vpc/route_table/main.tf b/modules/vpc/route_table/main.tf new file mode 100644 index 0000000..13cbda0 --- /dev/null +++ b/modules/vpc/route_table/main.tf @@ -0,0 +1,43 @@ +####################################################### +# Route Tables +####################################################### + +# Resource: aws_route_table +# Description: Manages a route table for a VPC. +resource "aws_route_table" "this" { + vpc_id = var.vpc_id + + # Dynamic Block: route (Public Route) + # Description: Defines a route for the public subnet(s) to route traffic via an Internet Gateway. + dynamic "route" { + for_each = var.route_table_type == "public" ? [1] : [] + content { + cidr_block = "0.0.0.0/0" + gateway_id = var.internet_gateway_id + } + } + + # Dynamic Block: route (Private Route) + # Description: Defines a route for the private subnet(s) to route traffic via a NAT Gateway. + dynamic "route" { + for_each = var.route_table_type == "private" ? [1] : [] + content { + cidr_block = "0.0.0.0/0" + nat_gateway_id = var.nat_gateway_id + } + } + + # Tags: Name + # Description: Tags the route table with a name based on the cluster prefix and route table type. + tags = { + Name = "${var.cluster_prefix}-${var.route_table_type}-rt" + } +} + +# Resource: aws_route_table_association +# Description: Associates subnet(s) with the specified route table. +resource "aws_route_table_association" "this" { + count = length(var.subnet_ids) + subnet_id = element(var.subnet_ids, count.index) + route_table_id = aws_route_table.this.id +} diff --git a/modules/vpc/route_table/outputs.tf b/modules/vpc/route_table/outputs.tf new file mode 100644 index 0000000..00c2950 --- /dev/null +++ b/modules/vpc/route_table/outputs.tf @@ -0,0 +1,10 @@ +####################################################### +# Outputs +####################################################### + +# Output: route_table_id +# Description: Provides the ID of the created route table. +output "route_table_id" { + description = "The ID of the route table" + value = aws_route_table.this.id +} diff --git a/modules/vpc/route_table/variables.tf b/modules/vpc/route_table/variables.tf new file mode 100644 index 0000000..1f985cd --- /dev/null +++ b/modules/vpc/route_table/variables.tf @@ -0,0 +1,47 @@ +####################################################### +# Variablesw +####################################################### + +# Variable Definitions + +# The ID of the VPC +variable "vpc_id" { + description = "The ID of the VPC" + type = string +} + +# The ID of the Internet Gateway (required if route table type is public) +variable "internet_gateway_id" { + description = "The ID of the Internet Gateway (required if public)" + type = string + default = null +} + +# The ID of the NAT Gateway (required if route table type is private) +variable "nat_gateway_id" { + description = "The ID of the NAT Gateway (required if private)" + type = string + default = null +} + +# Prefix for naming resources +variable "cluster_prefix" { + description = "Prefix for naming resources" + type = string +} + +# Type of route table: public or private +variable "route_table_type" { + description = "Type of route table: public or private" + type = string + validation { + condition = contains(["public", "private"], var.route_table_type) + error_message = "route_table_type must be either 'public' or 'private'." + } +} + +# List of subnet IDs to associate with the route table +variable "subnet_ids" { + description = "List of subnet IDs to associate with the route table" + type = list(string) +} diff --git a/modules/vpc/security_group/main.tf b/modules/vpc/security_group/main.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/vpc/security_group/outputs.tf b/modules/vpc/security_group/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/vpc/security_group/variables.tf b/modules/vpc/security_group/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/vpc/subnet/main.tf b/modules/vpc/subnet/main.tf new file mode 100644 index 0000000..7d0e742 --- /dev/null +++ b/modules/vpc/subnet/main.tf @@ -0,0 +1,33 @@ +####################################################### +# Subnets +####################################################### + +#Data Source: AWS Availability Zones +# Fetches a list of all available availability zones in the current region +data "aws_availability_zones" "available_zones" {} + +# Local Variables +locals { + # Offset calculation based on the number of available availability zones + offset = length(data.aws_availability_zones.available_zones.names) +} + +# Resource: AWS Subnet +# Creates subnets across all available availability zones +resource "aws_subnet" "subnet" { + count = length(data.aws_availability_zones.available_zones.names) + + # CIDR block calculation for the subnet + cidr_block = cidrsubnet(var.cidr, var.subnet_bits, local.offset * var.offset + count.index) + + # VPC ID in which the subnet will be created + vpc_id = var.vpc_id + + # Assigns the subnet to a specific availability zone + availability_zone = data.aws_availability_zones.available_zones.names[count.index] + + # Tags assigned to the subnet for identification + tags = { + Name = "${var.cluster_prefix}-${lower(var.subnet_type)}-subnet-${count.index + 1}" + } +} diff --git a/modules/vpc/subnet/outputs.tf b/modules/vpc/subnet/outputs.tf new file mode 100644 index 0000000..626857b --- /dev/null +++ b/modules/vpc/subnet/outputs.tf @@ -0,0 +1,10 @@ +####################################################### +# Subnets +####################################################### + +# Output: Subnet IDs +# Provides the IDs of the subnets created +output "subnet_ids" { + description = "The IDs of the created subnets" + value = aws_subnet.subnet[*].id +} diff --git a/modules/vpc/subnet/variables.tf b/modules/vpc/subnet/variables.tf new file mode 100644 index 0000000..2b9b516 --- /dev/null +++ b/modules/vpc/subnet/variables.tf @@ -0,0 +1,45 @@ +####################################################### +# Variables +####################################################### + +# Variable: cidr +# Description: The CIDR block of the VPC. +variable "cidr" { + description = "The CIDR block of the VPC" + type = string +} + +# Variable: subnet_bits +# Description: The number of bits for the subnet. +variable "subnet_bits" { + description = "The number of bits for subnet" + type = number +} + +# Variable: vpc_id +# Description: The ID of the VPC. +variable "vpc_id" { + description = "The ID of the VPC" + type = string +} + +# Variable: cluster_prefix +# Description: Prefix for the cluster, used for naming resources. +variable "cluster_prefix" { + description = "Prefix for the cluster" + type = string +} + +# Variable: subnet_type +# Description: Type of subnet (e.g., public, private, storage). +variable "subnet_type" { + description = "Type of subnet" + type = string +} + +# Variable: offset +# Description: Offset value used for calculating subnet CIDR blocks. +variable "offset" { + description = "Offset value for subnet calculation" + type = number +} diff --git a/modules/vpc/variables.tf b/modules/vpc/variables.tf new file mode 100644 index 0000000..361f88b --- /dev/null +++ b/modules/vpc/variables.tf @@ -0,0 +1,24 @@ +####################################################### +# Variables +####################################################### + +# Variable: cluster_prefix +# Description: Prefix to apply generic naming to resources like VPC and subnets +variable "cluster_prefix" { + type = string + description = "Prefix to apply generic naming to resources like VPC and subnets" +} + +# Variable: cidr +# Description: CIDR block for the VPC +variable "cidr" { + type = string + description = "CIDR block for the VPC" +} + +# Variable: subnet_bits +# Description: Number of bits to use for subnet calculations +variable "subnet_bits" { + type = string + description = "Number of bits to use for subnet calculations" +} diff --git a/output.tf b/output.tf index b3469c9..fdf83bd 100644 --- a/output.tf +++ b/output.tf @@ -1,15 +1,39 @@ -output "endpoint" { - value = aws_eks_cluster.eks_cluster.endpoint -} - -output "kubeconfig_certificate_authority_data" { - value = aws_eks_cluster.eks_cluster.certificate_authority[0].data -} - -output "eks_cluster_policy" { - value = aws_iam_role_policy_attachment.eks_cluster_policy -} - -output "eks_vpc_resource_controller_policy" { - value = aws_iam_role_policy_attachment.eks_vpc_resource_controller_policy -} +####################################################### +# Outputs +####################################################### + +# EKS cluster endpoint +output "cluster_endpoint" { + description = "Endpoint for EKS control plane" + value = module.eks.cluster_endpoint +} + +# AWS region +output "region" { + description = "AWS region" + value = var.region +} + +# EKS cluster name +output "cluster_name" { + description = "Kubernetes Cluster Name" + value = module.eks.cluster_name +} + +# RDS endpoints +output "rds_endpoints" { + description = "RDS Endpoints" + value = { for key, db in module.database : key => db.db_instance_endpoint } +} + +# Public subnets IDs +output "public_subnets" { + description = "IDs of public subnets" + value = module.vpc.public_subnets_ids +} + +# VPC ID +output "vpc_id" { + description = "ID of the VPC" + value = module.vpc.vpc_id +} diff --git a/terraform.tfvars b/terraform.tfvars new file mode 100644 index 0000000..2b5128b --- /dev/null +++ b/terraform.tfvars @@ -0,0 +1,80 @@ +# Terraform variables for configuring the infrastructure + +# To apply generic naming to EKS Cluster +cluster_prefix = "source4learn" + +# AWS Region for the infrastructure +region = "ap-southeast-2" + +# CIDR block for the VPC +cidr = "10.0.0.0/16" + +# Number of subnet bits +subnet_bits = 4 + +# Kubernetes version for EKS Cluster +kubernetes_version = "1.28" + +# List of Control Plane Logging options to enable +eks_cluster_enabled_log_types = ["api", "audit"] + +# Cloudwatch log events retention period in days for EKS Cluster +eks_cluster_log_retention_in_days = 7 + +# EKS Cluster - Kubernetes Service IP address range +eks_cluster_service_ipv4_cidr = "10.43.0.0/16" + +# Average wait time in minutes for the EKS Cluster to be created +eks_cluster_create_timeout = "30m" + +# Average wait time in minutes for the EKS Cluster to be deleted +eks_cluster_delete_timeout = "15m" + +# Average wait time in minutes for the EKS Cluster to be updated +eks_cluster_update_timeout = "60m" + +# Map of node groups to create with their respective configurations +nodes= { + dev = { + instance_type = ["t3.medium"] + node_type = "vm" + desired_size = 3 + max_size = 4 + min_size = 1 + } + prod = { + node_type = "fargate" + selector = ["production"] + } +} + +# Map of databases to create with their respective configurations +databases = { + dev = { + db_engine = "postgres" + db_instance_class = "db.t3.micro" + db_version = "13.13" + db_storage = 20 + db_name = "devapi2" + db_username = "devapi2admin" + db_password = "" + } +} + + +# ECR configuration + +ecr = { + dev = { + repositories = [ + "deva", + "redis" + ] + } + prod = { + repositories = [ + "prodapi1", + "test2" + ] + } +} \ No newline at end of file diff --git a/variables.tf b/variables.tf index ca9781b..b51110c 100644 --- a/variables.tf +++ b/variables.tf @@ -1,60 +1,111 @@ -# Terraform Variables -variable "eks_cluster_prefix" { - description = "To apply generic naming to EKS Cluster" - type = string - default = "source4learn" -} - -variable "eks_cluster_environment" { - description = "To apply generic environment to EKS Cluster" - type = string - default = "devops" -} - -variable "kubernetes_version" { - description = "Kubernetes version for EKS Cluster" - type = string - default = "1.19" -} - -variable "subnet_ids" { - description = "List of VPC Subnets to be set in EKS Cluster" - type = list(any) - default = ["subnet-d12321b9", "subnet-4a245706", "subnet-38299343"] -} - -variable "eks_cluster_enabled_log_types" { - description = "List of Control Plane Logging options to enable" - type = list(any) - default = ["api", "audit"] -} - -variable "eks_cluster_log_retention_in_days" { - description = "Cloudwatch log events retention period in days for EKS Cluster" - type = number - default = 7 -} - -variable "eks_cluster_service_ipv4_cidr" { - description = "EKS Cluster - Kubernetes Service IP address range" - type = string - default = "10.43.0.0/16" -} - -variable "eks_cluster_create_timeout" { - description = "Average wait time in minutes for the EKS Cluster to be created" - type = string - default = "30m" -} - -variable "eks_cluster_delete_timeout" { - description = "Average wait time in minutes for the EKS Cluster to be deleted" - type = string - default = "15m" -} - -variable "eks_cluster_update_timeout" { - description = "Average wait time in minutes for the EKS Cluster to be updated" - type = string - default = "60m" +####################################################### +# Terraform Variables +####################################################### +# Terraform Variables + +# To apply generic naming to EKS Cluster +variable "cluster_prefix" { + description = "Prefix to apply to EKS Cluster" + type = string + default = "source4learn" +} + +# AWS Region for the infrastructure +variable "region" { + description = "AWS Region where the infrastructure will be deployed" + type = string +} + +# CIDR block for the VPC +variable "cidr" { + description = "CIDR block for the VPC" + type = string +} + +# Number of subnet bits for subnet calculation +variable "subnet_bits" { + description = "Number of bits to use for subnetting" + type = number +} + +# Kubernetes version for EKS Cluster +variable "kubernetes_version" { + description = "Version of Kubernetes for the EKS Cluster" + type = string + default = "1.19" +} + +# List of Control Plane Logging options to enable +variable "eks_cluster_enabled_log_types" { + description = "List of Control Plane Logging options to enable" + type = list(any) + default = ["api", "audit"] +} + +# Cloudwatch log events retention period in days for EKS Cluster +variable "eks_cluster_log_retention_in_days" { + description = "Retention period in days for CloudWatch log events" + type = number + default = 7 +} + +# EKS Cluster - Kubernetes Service IP address range +variable "eks_cluster_service_ipv4_cidr" { + description = "IP address range for Kubernetes services in the EKS Cluster" + type = string + default = "10.43.0.0/16" +} + +# Average wait time in minutes for the EKS Cluster to be created +variable "eks_cluster_create_timeout" { + description = "Average wait time in minutes for EKS Cluster creation" + type = string + default = "30m" +} + +# Average wait time in minutes for the EKS Cluster to be deleted +variable "eks_cluster_delete_timeout" { + description = "Average wait time in minutes for EKS Cluster deletion" + type = string + default = "15m" +} + +# Average wait time in minutes for the EKS Cluster to be updated +variable "eks_cluster_update_timeout" { + description = "Average wait time in minutes for EKS Cluster update" + type = string + default = "60m" +} + +# Map of node groups to create with their respective configurations +variable "nodes" { + description = "Map of node groups to create with their respective configurations" + type = map(object({ + instance_type = optional(list(string)) + node_type = string + desired_size = optional(number) + max_size = optional(number) + min_size = optional(number) + selector = optional(list(string)) + })) +} + +# Map of databases to create with their respective configurations +variable "databases" { + description = "Map of databases to create with their respective configurations" + type = map(object({ + db_engine = string + db_instance_class = string + db_version = string + db_storage = number + })) +} + + +# ECR variable +variable "ecr" { + type = map(object({ + repositories = list(string) + })) + } \ No newline at end of file