├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Images ├── architecture.jpg ├── dynamoDBTableCreation.png └── kubectl_get_nodes.png ├── LICENSE ├── README.md ├── THIRD-PARTY-LICENSES ├── eks └── cluster │ ├── main-var.tf │ ├── main.tf │ └── output.tf ├── example-workloads └── windows │ ├── eks-sample-deployment.yaml │ └── eks-sample-service.yaml ├── main-input.tfvars ├── main-var.tf ├── main.tf ├── network ├── bastion_host_policy.json ├── main-input.tfvars ├── main-var.tf ├── main.tf └── output.tf ├── output.tf └── yaml-templates ├── additional_roles_aws_auth.yaml ├── aws-auth-cm.yaml └── vpc-resource-controller-configmap.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must end with two \r 7 | Icon 8 | 9 | # Thumbnails 10 | ._* 11 | 12 | # Files that might appear in the root of a volume 13 | .DocumentRevisions-V100 14 | .fseventsd 15 | .Spotlight-V100 16 | .TemporaryItems 17 | .Trashes 18 | .VolumeIcon.icns 19 | .com.apple.timemachine.donotpresent 20 | 21 | # Directories potentially created on remote AFP share 22 | .AppleDB 23 | .AppleDesktop 24 | Network Trash Folder 25 | Temporary Items 26 | .apdisk 27 | 28 | # Local .terraform directories 29 | **/.terraform/* 30 | 31 | # .tfstate files 32 | *.tfstate 33 | *.tfstate.* 34 | **/*.tfstate.* 35 | **/*.tfstate 36 | # Crash log files 37 | crash.log 38 | crash.*.log 39 | 40 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 41 | # password, private keys, and other secrets. These should not be part of version 42 | # control as they are data points which are potentially sensitive and subject 43 | # to change depending on the environment. 44 | # 45 | #*.tfvars 46 | 47 | *.terraform.lock.hcl 48 | # Ignore override files as they are usually used to override resources locally and so 49 | # are not checked in 50 | override.tf 51 | override.tf.json 52 | *_override.tf 53 | *_override.tf.json 54 | 55 | # Include override files you do wish to add to version control using negated pattern 56 | # 57 | # !example_override.tf 58 | 59 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 60 | # example: *tfplan* 61 | 62 | # Ignore CLI configuration files 63 | .terraformrc 64 | terraform.rc 65 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /Images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/private-eks-for-windows-workloads-with-terraform/8e22a4a6c8505256cb7f28bf143bfa429c908d18/Images/architecture.jpg -------------------------------------------------------------------------------- /Images/dynamoDBTableCreation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/private-eks-for-windows-workloads-with-terraform/8e22a4a6c8505256cb7f28bf143bfa429c908d18/Images/dynamoDBTableCreation.png -------------------------------------------------------------------------------- /Images/kubectl_get_nodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/private-eks-for-windows-workloads-with-terraform/8e22a4a6c8505256cb7f28bf143bfa429c908d18/Images/kubectl_get_nodes.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # private-eks-for-windows-workloads-with-terraform 2 | 3 | This is a sample repository for the accompanying AWS Container Blog Post [Running Windows workloads on a private EKS cluster](https://aws.amazon.com/blogs/containers/running-windows-workloads-on-a-private-eks-cluster/). 4 | 5 | This repository provides a Terraform implementation that deploys an Amazon EKS cluster in a private VPC and deploys Windows and Linux worker nodes into the cluster. The private VPC and EKS cluster are deployed via a bastion host in a public VPC that can access the private VPC via VPC peering. The public VPC and bastion host setup is part of this repository as well. 6 | 7 | Solution Architecture: 8 | 9 | ![architecture](./Images/architecture.jpg) 10 | 11 | ### Prerequisites 12 | 13 | - AWS Account with command line access, https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html. 14 | - Set up Terraform. For steps, see Terraform downloads (https://www.terraform.io/downloads.html). 15 | - S3 bucket to save the state. 16 | - DynamoDB table for the statelock with partition key "LockID" of type String. 17 | 18 | ### How to use 19 | 20 | #### VPC and bastion host setup 21 | 22 | This performs the deployment of the VPC including the setup of the bastion host in a separate VPC. 23 | 24 | 1. Clone this repository 25 | 26 | 2. Create an EC2 SSH key in your AWS account if there is none existing by following this documentation: [Create a key pair using Amazon EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair) 27 | 28 | 3. Replace the backed configuration in the [network/main.tf](./network/main.tf) with an S3 bucket and Amazon DynamoDB table with partition key *LockID* of type String in your AWS account. Refer to [S3 Backend](https://www.terraform.io/language/settings/backends/s3) for details. 29 | 30 | 1. Follow the [Userguide](https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html) to create a S3 bucket. 31 | 2. Follow the [Developerguide](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/getting-started-step-1.html) to create a DynamoDB table with the correct Partition key: 32 | 33 | dynamoDBTableCreation 34 | 35 | 3. Insert correct values in [network/main.tf](./network/main.tf) : 36 | 37 | ```` 38 | //Modify the bucket and dynamoDB table that are used by Terraform 39 | terraform { 40 | backend "s3" { 41 | bucket = "DOC-EXAMPLE-BUCKET" 42 | key = "network.tfstate" 43 | region = "eu-central-1" 44 | dynamodb_table = "private-windows-eks-tf-lock" 45 | } 46 | } 47 | ```` 48 | 49 | 4. Make sure that you are logged into your AWS account using the AWS CLI. Refer to [CLI Quickstart](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) for details. 50 | 51 | 5. Open a command line and move into the folder *network* and execute the deployement with Terraform: 52 | 53 | ````bash 54 | |-- private-eks-for-windows-workloads-with-terraform 55 | | |-- network 56 | | | |-- main.tf 57 | | | |-- main-input.tfvars 58 | ```` 59 | 60 | ``` 61 | $ cd network 62 | $ terraform init 63 | $ terraform apply -var-file main-input.tfvars 64 | ``` 65 | 66 | 6. Note down the output of *out_bastion_public_ip*. 67 | 68 | #### EKS Cluster setup 69 | 70 | This performs the deployment of the EKS cluster and the nodegroups for Windows and Linux. 71 | 72 | Make sure to execute the Terraform script from inside the bastion host as otherwise Terraform will not be able to connect to the EKS cluster as the private endpoint will only be accessible from within the private VPC itself or a peered VPC. 73 | 74 | 1. SSH into the Linux node. 75 | 76 | ``` 77 | $ ssh ec2-user@ -i 78 | ``` 79 | 80 | 2. Clone the git repo to the bastion host. 81 | 82 | 3. Replace the backed configuration in the [main.tf](./main.tf) with the same S3 bucket used for the network setup and DynamoDB table in your AWS account. 83 | Add the correct backend configuration for *terraform_remote_state.network* as well. 84 | 85 | ```` 86 | //Modify the bucket and dynamoDB table that are used by Terraform 87 | terraform { 88 | backend "s3" { 89 | bucket = "DOC-EXAMPLE-BUCKET" 90 | key = "private-windows-eks.tfstate" 91 | region = "eu-central-1" 92 | dynamodb_table = "private-windows-eks-tf-lock" 93 | } 94 | } 95 | 96 | data terraform_remote_state "network" { 97 | backend = "s3" 98 | config = { 99 | bucket = "DOC-EXAMPLE-BUCKET" 100 | key = "network.tfstate" 101 | region = "eu-central-1" 102 | } 103 | } 104 | ```` 105 | 106 | 4. If you are using a federated role to access the AWS console, then replace the role ARN in [additional_roles_aws_auth.yaml](./yaml-templates/additional_roles_aws_auth.yaml) with the role that gets federated to allow access to the EKS cluster from the AWS console for you. 107 | 108 | 5. Deploy the EKS cluster with the following commands from the root folder of the solution: 109 | 110 | ````bash 111 | |-- private-eks-for-windows-workloads-with-terraform 112 | | |-- main.tf 113 | | |-- main-input.tfvars 114 | ```` 115 | 116 | ```bash 117 | $ terraform init 118 | $ terraform apply -var-file main-input.tfvars 119 | ``` 120 | 121 | 5. The Windows nodes can take a few minutes until they are successfully bootstrapped and connected to the cluster. 122 | 123 | #### Validate deployment 124 | 125 | 1. After the deployment is done, you can configure the local kubectl on the bastion host to connect to the EKS cluster. 126 | 127 | ```bash 128 | $ aws eks update-kubeconfig --name sample-cluster-01 --region eu-central-1 129 | $ kubectl get nodes 130 | ``` 131 | 132 | ![kubectl_get_nodes](./Images/kubectl_get_nodes.png) 133 | 134 | ## Cleanup 135 | 136 | ### EKS Cluster 137 | 138 | Execute the following inside of the root path of the repository inside the bastion host to clean-up the EKS cluster as well as the worker nodes: 139 | 140 | ````bash 141 | |-- private-eks-for-windows-workloads-with-terraform 142 | | |-- main.tf 143 | | |-- main-input.tfvars 144 | ```` 145 | 146 | ```bash 147 | $ terraform destroy -var-file main-input.tfvars 148 | ``` 149 | 150 | ### Bastion Host & VPCs 151 | 152 | Execute the same Terraform command again from your local workstation inside the network directory to clean-up the bastion host and both VPCs: 153 | 154 | ````bash 155 | |-- private-eks-for-windows-workloads-with-terraform 156 | | |-- network 157 | | | |-- main.tf 158 | | | |-- main-input.tfvars 159 | ```` 160 | 161 | ```bash 162 | $ terraform destroy -var-file main-input.tfvars 163 | ``` 164 | 165 | 166 | 167 | ## Parameters in main-input.tfvars 168 | 169 | The repository provides the following defaults for the setup: 170 | 171 | - region = "eu-central-1" 172 | - VPC 173 | - azs_private = ["eu-central-1a", "eu-central-1b", "eu-central-1c"] 174 | - private_subnets = ["10.10.1.0/24", "10.10.2.0/24", "10.10.3.0/24"] 175 | - vpc_private_cidr = "10.10.0.0/16" 176 | - vpc_public_cidr = "10.20.0.0/16" 177 | - azs_public = ["eu-central-1a"] 178 | - public_subnets = ["10.20.1.0/24"] 179 | 180 | - EKS cluster 181 | - eks_cluster_name = "sample-cluster-01" 182 | - eks_cluster_version = "1.21" 183 | - Linux nodegroup 184 | - lin_desired_size = "2" 185 | - lin_max_size = "2" 186 | - lin_min_size = "2" 187 | - lin_instance_type = "t3.medium" 188 | 189 | - Windows nodegroup 190 | - win_desired_size = "2" 191 | - win_max_size = "2" 192 | - win_min_size = "2" 193 | - win_instance_type = "t3.xlarge" 194 | 195 | ## Security 196 | 197 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 198 | 199 | ## License 200 | 201 | This library is licensed under the MIT-0 License. See the LICENSE file. 202 | 203 | -------------------------------------------------------------------------------- /THIRD-PARTY-LICENSES: -------------------------------------------------------------------------------- 1 | The Amazon private-eks-for-windows-workloads-with-terraform Product includes the following third-party software/licensing: 2 | 3 | ** SDN Sample Scripts v.1.0 - https://github.com/microsoft/SDN 4 | Copyright (c) Microsoft Corporation 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /eks/cluster/main-var.tf: -------------------------------------------------------------------------------- 1 | variable "private_subnet_ids" { 2 | description = "Please enter a list of private subnet ids to be used" 3 | type = any 4 | } 5 | variable "bastion_host_SG_id" { 6 | description = "Please enter the ID of the security group of the bastion host" 7 | type = string 8 | } 9 | 10 | variable "vpc_id" { 11 | description = "Please enter the ID of the VPC" 12 | type = string 13 | } 14 | variable "region" { 15 | description = "Please enter the region used to deploy this infrastructure" 16 | type = string 17 | } 18 | variable "eks_cluster_version" { 19 | description = "Please enter the EKS cluster version" 20 | type = string 21 | } 22 | variable "eks_cluster_name" { 23 | description = "Please enter an EKS cluster name" 24 | type = string 25 | } 26 | variable "lin_instance_type" { 27 | description = "Please enter the instance type to be used for the Linux worker nodes" 28 | type = string 29 | } 30 | variable "lin_min_size" { 31 | description = "Please enter the minimal size for the Linux ASG" 32 | type = string 33 | } 34 | variable "lin_max_size" { 35 | description = "Please enter the maximal size for the Linux ASG" 36 | type = string 37 | } 38 | variable "lin_desired_size" { 39 | description = "Please enter the desired size for the Linux ASG" 40 | type = string 41 | } 42 | variable "win_min_size" { 43 | description = "Please enter the minimal size for the Windows ASG" 44 | type = string 45 | } 46 | variable "win_max_size" { 47 | description = "Please enter the maximal size for the Windows ASG" 48 | type = string 49 | } 50 | variable "win_desired_size" { 51 | description = "Please enter the desired size for the Windows ASG" 52 | type = string 53 | } 54 | variable "win_instance_type" { 55 | description = "Please enter the instance type to be used for the Windows worker nodes" 56 | type = string 57 | } 58 | variable "node_host_key_name" { 59 | description = "Please enter the name of the SSH key pair that should be assigned to the worker nodes of the cluster" 60 | type = string 61 | } 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /eks/cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.72.0" 6 | } 7 | } 8 | } 9 | provider "aws" { 10 | region = var.region 11 | } 12 | data "aws_caller_identity" "current" {} 13 | locals { 14 | account_id = data.aws_caller_identity.current.account_id 15 | } 16 | #### Nodegroups - Images 17 | 18 | data "aws_ami" "lin_ami" { 19 | most_recent = true 20 | owners = ["amazon"] 21 | filter { 22 | name = "name" 23 | values = ["amazon-eks-node-${var.eks_cluster_version}-*"] 24 | } 25 | } 26 | data "aws_ami" "win_ami" { 27 | most_recent = true 28 | owners = ["amazon"] 29 | filter { 30 | name = "name" 31 | values = ["Windows_Server-2019-English-Core-EKS_Optimized-${var.eks_cluster_version}-*"] 32 | } 33 | } 34 | resource "aws_kms_key" "eks" { 35 | description = "EKS Encryption Key" 36 | } 37 | 38 | module "eks" { 39 | source = "terraform-aws-modules/eks/aws" 40 | version = "18.6.0" 41 | vpc_id = var.vpc_id 42 | cluster_name = var.eks_cluster_name 43 | subnet_ids = var.private_subnet_ids 44 | cluster_enabled_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"] 45 | cluster_endpoint_private_access = true 46 | cluster_endpoint_public_access = false 47 | cluster_version = var.eks_cluster_version 48 | cluster_encryption_config = [ 49 | { 50 | provider_key_arn = aws_kms_key.eks.arn 51 | resources = ["secrets"] 52 | } 53 | ] 54 | ### Allow SSM access for Nodes 55 | self_managed_node_group_defaults = { 56 | iam_role_additional_policies = ["arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"] 57 | } 58 | tags = { 59 | Name = "${var.eks_cluster_name}" 60 | } 61 | # Extend node-to-node security group rules 62 | node_security_group_additional_rules = { 63 | ingress_self_all = { 64 | description = "Node to node all ports/protocols" 65 | protocol = "-1" 66 | from_port = 0 67 | to_port = 0 68 | type = "ingress" 69 | self = true 70 | } 71 | egress_all = { 72 | description = "Node all egress" 73 | protocol = "-1" 74 | from_port = 0 75 | to_port = 0 76 | type = "egress" 77 | cidr_blocks = ["0.0.0.0/0"] 78 | ipv6_cidr_blocks = ["::/0"] 79 | } 80 | ## Enable access from bastion host to Nodes 81 | ingress_bastion = { 82 | description = "Allow access from Bastion Host" 83 | type = "ingress" 84 | from_port = 443 85 | to_port = 443 86 | protocol = "tcp" 87 | source_security_group_id = var.bastion_host_SG_id 88 | } 89 | ## Enable RDP access from bastion host to Nodes 90 | ingress_bastion_win = { 91 | description = "Allow access from Bastion Host via RDP" 92 | type = "ingress" 93 | from_port = 3389 94 | to_port = 3389 95 | protocol = "tcp" 96 | source_security_group_id = var.bastion_host_SG_id 97 | } 98 | } 99 | ## Enable access from bastion host to EKS endpoint 100 | cluster_security_group_additional_rules = { 101 | ingress_bastion = { 102 | description = "Allow access from Bastion Host" 103 | type = "ingress" 104 | from_port = 443 105 | to_port = 443 106 | protocol = "tcp" 107 | source_security_group_id = var.bastion_host_SG_id 108 | } 109 | } 110 | self_managed_node_groups = { 111 | linux = { 112 | platform = "linux" 113 | name = "linux" 114 | public_ip = false 115 | instance_type = var.lin_instance_type 116 | key_name = var.node_host_key_name 117 | desired_size = var.lin_desired_size 118 | max_size = var.lin_max_size 119 | min_size = var.lin_min_size 120 | ami_id = data.aws_ami.lin_ami.id 121 | } 122 | windows = { 123 | platform = "windows" 124 | name = "windows" 125 | public_ip = false 126 | instance_type = var.win_instance_type 127 | key_name = var.node_host_key_name 128 | desired_size = var.win_desired_size 129 | max_size = var.win_max_size 130 | min_size = var.win_min_size 131 | ami_id = data.aws_ami.win_ami.id 132 | } 133 | } 134 | } 135 | ### Prerequisites for Windows Node enablement 136 | data "aws_eks_cluster_auth" "this" { 137 | name = module.eks.cluster_id 138 | } 139 | 140 | locals { 141 | kubeconfig = yamlencode({ 142 | apiVersion = "v1" 143 | kind = "Config" 144 | current-context = "terraform" 145 | clusters = [{ 146 | name = module.eks.cluster_id 147 | cluster = { 148 | certificate-authority-data = module.eks.cluster_certificate_authority_data 149 | server = module.eks.cluster_endpoint 150 | } 151 | }] 152 | contexts = [{ 153 | name = "terraform" 154 | context = { 155 | cluster = module.eks.cluster_id 156 | user = "terraform" 157 | } 158 | }] 159 | users = [{ 160 | name = "terraform" 161 | user = { 162 | token = data.aws_eks_cluster_auth.this.token 163 | } 164 | }] 165 | }) 166 | } 167 | ### Apply changes to aws_auth 168 | ### Windows node Cluster enablement: https://docs.aws.amazon.com/eks/latest/userguide/windows-support.html 169 | resource "null_resource" "apply" { 170 | triggers = { 171 | kubeconfig = base64encode(local.kubeconfig) 172 | cmd_patch = <<-EOT 173 | kubectl create configmap aws-auth -n kube-system --kubeconfig <(echo $KUBECONFIG | base64 --decode) 174 | kubectl patch configmap/aws-auth --patch "${module.eks.aws_auth_configmap_yaml}" -n kube-system --kubeconfig <(echo $KUBECONFIG | base64 --decode) 175 | kubectl get cm aws-auth -n kube-system -o json --kubeconfig <(echo $KUBECONFIG | base64 --decode) | jq --arg add "`cat yaml-templates/additional_roles_aws_auth.yaml`" '.data.mapRoles += $add' | kubectl apply --kubeconfig <(echo $KUBECONFIG | base64 --decode) -f - 176 | kubectl apply --kubeconfig <(echo $KUBECONFIG | base64 --decode) -f yaml-templates/vpc-resource-controller-configmap.yaml 177 | EOT 178 | } 179 | provisioner "local-exec" { 180 | interpreter = ["/bin/bash", "-c"] 181 | environment = { 182 | KUBECONFIG = self.triggers.kubeconfig 183 | } 184 | command = self.triggers.cmd_patch 185 | } 186 | } 187 | 188 | # VPC Endpoints for private EKS cluster 189 | # https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html#vpc-endpoints-private-clusters 190 | 191 | #### Route Tables for S3 Gateway 192 | data "aws_route_table" "private-a" { 193 | subnet_id = var.private_subnet_ids[0] 194 | } 195 | data "aws_route_table" "private-b" { 196 | subnet_id = var.private_subnet_ids[1] 197 | } 198 | data "aws_route_table" "private-c" { 199 | subnet_id = var.private_subnet_ids[2] 200 | } 201 | 202 | resource "aws_vpc_endpoint" "vpce_s3_gw" { 203 | policy = jsonencode( 204 | { 205 | Statement = [ 206 | { 207 | Action = "*" 208 | Effect = "Allow" 209 | Principal = "*" 210 | Resource = "*" 211 | }, 212 | ] 213 | Version = "2008-10-17" 214 | } 215 | ) 216 | route_table_ids = [ 217 | "${data.aws_route_table.private-a.id}", 218 | "${data.aws_route_table.private-b.id}", 219 | "${data.aws_route_table.private-c.id}" 220 | ] 221 | service_name = format("com.amazonaws.${var.region}.s3") 222 | vpc_endpoint_type = "Gateway" 223 | vpc_id = var.vpc_id 224 | } 225 | resource "aws_vpc_endpoint" "vpce_ec2" { 226 | policy = jsonencode( 227 | { 228 | Statement = [ 229 | { 230 | Action = "*" 231 | Effect = "Allow" 232 | Principal = "*" 233 | Resource = "*" 234 | }, 235 | ] 236 | } 237 | ) 238 | private_dns_enabled = true 239 | 240 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 241 | service_name = format("com.amazonaws.${var.region}.ec2") 242 | subnet_ids = var.private_subnet_ids 243 | vpc_endpoint_type = "Interface" 244 | vpc_id = var.vpc_id 245 | } 246 | resource "aws_vpc_endpoint" "vpce_logs" { 247 | policy = jsonencode( 248 | { 249 | Statement = [ 250 | { 251 | Action = "*" 252 | Effect = "Allow" 253 | Principal = "*" 254 | Resource = "*" 255 | }, 256 | ] 257 | } 258 | ) 259 | private_dns_enabled = true 260 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 261 | service_name = format("com.amazonaws.${var.region}.logs") 262 | subnet_ids = var.private_subnet_ids 263 | vpc_endpoint_type = "Interface" 264 | vpc_id = var.vpc_id 265 | } 266 | resource "aws_vpc_endpoint" "vpce_ecrapi" { 267 | policy = jsonencode( 268 | { 269 | Statement = [ 270 | { 271 | Action = "*" 272 | Effect = "Allow" 273 | Principal = "*" 274 | Resource = "*" 275 | }, 276 | ] 277 | } 278 | ) 279 | private_dns_enabled = true 280 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 281 | service_name = format("com.amazonaws.${var.region}.ecr.api") 282 | subnet_ids = var.private_subnet_ids 283 | vpc_endpoint_type = "Interface" 284 | vpc_id = var.vpc_id 285 | } 286 | resource "aws_vpc_endpoint" "vpce_autoscaling" { 287 | policy = jsonencode( 288 | { 289 | Statement = [ 290 | { 291 | Action = "*" 292 | Effect = "Allow" 293 | Principal = "*" 294 | Resource = "*" 295 | }, 296 | ] 297 | } 298 | ) 299 | private_dns_enabled = true 300 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 301 | service_name = format("com.amazonaws.${var.region}.autoscaling") 302 | subnet_ids = var.private_subnet_ids 303 | vpc_endpoint_type = "Interface" 304 | vpc_id = var.vpc_id 305 | 306 | } 307 | 308 | resource "aws_vpc_endpoint" "vpce_sts" { 309 | policy = jsonencode( 310 | { 311 | Statement = [ 312 | { 313 | Action = "*" 314 | Effect = "Allow" 315 | Principal = "*" 316 | Resource = "*" 317 | }, 318 | ] 319 | } 320 | ) 321 | private_dns_enabled = true 322 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 323 | service_name = format("com.amazonaws.${var.region}.sts") 324 | subnet_ids = var.private_subnet_ids 325 | vpc_endpoint_type = "Interface" 326 | vpc_id = var.vpc_id 327 | 328 | } 329 | resource "aws_vpc_endpoint" "vpce_elb" { 330 | policy = jsonencode( 331 | { 332 | Statement = [ 333 | { 334 | Action = "*" 335 | Effect = "Allow" 336 | Principal = "*" 337 | Resource = "*" 338 | }, 339 | ] 340 | } 341 | ) 342 | private_dns_enabled = true 343 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 344 | service_name = format("com.amazonaws.${var.region}.elasticloadbalancing") 345 | subnet_ids = var.private_subnet_ids 346 | vpc_endpoint_type = "Interface" 347 | vpc_id = var.vpc_id 348 | } 349 | resource "aws_vpc_endpoint" "vpce_ecrdkr" { 350 | policy = jsonencode( 351 | { 352 | Statement = [ 353 | { 354 | Action = "*" 355 | Effect = "Allow" 356 | Principal = "*" 357 | Resource = "*" 358 | }, 359 | ] 360 | } 361 | ) 362 | private_dns_enabled = true 363 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 364 | service_name = format("com.amazonaws.${var.region}.ecr.dkr") 365 | subnet_ids = var.private_subnet_ids 366 | vpc_endpoint_type = "Interface" 367 | vpc_id = var.vpc_id 368 | } 369 | ### SSM Access 370 | resource "aws_vpc_endpoint" "vpce_ec2messages" { 371 | policy = jsonencode( 372 | { 373 | Statement = [ 374 | { 375 | Action = "*" 376 | Effect = "Allow" 377 | Principal = "*" 378 | Resource = "*" 379 | }, 380 | ] 381 | } 382 | ) 383 | private_dns_enabled = true 384 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 385 | service_name = format("com.amazonaws.${var.region}.ec2messages") 386 | subnet_ids = var.private_subnet_ids 387 | vpc_endpoint_type = "Interface" 388 | vpc_id = var.vpc_id 389 | } 390 | 391 | resource "aws_vpc_endpoint" "vpce_ssm" { 392 | policy = jsonencode( 393 | { 394 | Statement = [ 395 | { 396 | Action = "*" 397 | Effect = "Allow" 398 | Principal = "*" 399 | Resource = "*" 400 | }, 401 | ] 402 | } 403 | ) 404 | private_dns_enabled = true 405 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 406 | service_name = format("com.amazonaws.${var.region}.ssm") 407 | subnet_ids = var.private_subnet_ids 408 | vpc_endpoint_type = "Interface" 409 | vpc_id = var.vpc_id 410 | } 411 | 412 | resource "aws_vpc_endpoint" "vpce_ssmmessages" { 413 | policy = jsonencode( 414 | { 415 | Statement = [ 416 | { 417 | Action = "*" 418 | Effect = "Allow" 419 | Principal = "*" 420 | Resource = "*" 421 | }, 422 | ] 423 | } 424 | ) 425 | private_dns_enabled = true 426 | security_group_ids = [module.eks.node_security_group_id,module.eks.cluster_security_group_id] 427 | service_name = format("com.amazonaws.${var.region}.ssmmessages") 428 | subnet_ids = var.private_subnet_ids 429 | vpc_endpoint_type = "Interface" 430 | vpc_id = var.vpc_id 431 | 432 | } 433 | 434 | -------------------------------------------------------------------------------- /eks/cluster/output.tf: -------------------------------------------------------------------------------- 1 | output "out_eks_cluster" { 2 | value = module.eks 3 | } 4 | output "out_eks_cluster_ca" { 5 | value = module.eks.cluster_certificate_authority_data 6 | } 7 | -------------------------------------------------------------------------------- /example-workloads/windows/eks-sample-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: eks-sample-windows-deployment 5 | namespace: eks-sample-app 6 | labels: 7 | app: eks-sample-windows-app 8 | spec: 9 | replicas: 3 10 | selector: 11 | matchLabels: 12 | app: eks-sample-windows-app 13 | template: 14 | metadata: 15 | labels: 16 | app: eks-sample-windows-app 17 | spec: 18 | affinity: 19 | nodeAffinity: 20 | requiredDuringSchedulingIgnoredDuringExecution: 21 | nodeSelectorTerms: 22 | - matchExpressions: 23 | - key: beta.kubernetes.io/arch 24 | operator: In 25 | values: 26 | - amd64 27 | containers: 28 | - name: windows-server-iis 29 | image: mcr.microsoft.com/windows/servercore:ltsc2019 30 | ports: 31 | - name: http 32 | containerPort: 80 33 | imagePullPolicy: IfNotPresent 34 | command: 35 | - powershell.exe 36 | - -command 37 | - "<#code used from https://gist.github.com/wagnerandrade/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ; ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$ip=(Get-NetAdapter | Get-NetIpAddress); $$header='

Windows Container Web Server

' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='

IP {0} callerCount {1} ' -f $$ip[1].IPAddress,$$callerCounts.Item($$_) } ;$$footer='' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus) } ; " 38 | nodeSelector: 39 | kubernetes.io/os: windows 40 | -------------------------------------------------------------------------------- /example-workloads/windows/eks-sample-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: eks-sample-windows-service 5 | namespace: eks-sample-app 6 | labels: 7 | app: eks-sample-windows-app 8 | spec: 9 | selector: 10 | app: eks-sample-windows-app 11 | ports: 12 | - protocol: TCP 13 | port: 80 14 | targetPort: 80 15 | -------------------------------------------------------------------------------- /main-input.tfvars: -------------------------------------------------------------------------------- 1 | region = "eu-central-1" 2 | ### Cluster 3 | eks_cluster_name = "sample-cluster-01" 4 | eks_cluster_version = "1.21" 5 | 6 | ### Linux Nodegroup 7 | lin_desired_size = "2" 8 | lin_max_size = "2" 9 | lin_min_size = "2" 10 | lin_instance_type = "t3.medium" 11 | 12 | ### Windows Nodegroup 13 | win_desired_size = "2" 14 | win_max_size = "2" 15 | win_min_size = "2" 16 | win_instance_type = "t3.xlarge" 17 | 18 | -------------------------------------------------------------------------------- /main-var.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "Please enter the region used to deploy this infrastructure" 3 | type = string 4 | } 5 | variable "eks_cluster_version" { 6 | description = "Please enter the EKS cluster version" 7 | type = string 8 | } 9 | variable "eks_cluster_name" { 10 | description = "Please enter an EKS cluster name" 11 | type = string 12 | } 13 | variable "lin_instance_type" { 14 | description = "Please enter the instance type to be used for the Linux worker nodes" 15 | type = string 16 | } 17 | variable "lin_min_size" { 18 | description = "Please enter the minimal size for the Linux ASG" 19 | type = string 20 | } 21 | variable "lin_max_size" { 22 | description = "Please enter the maximal size for the Linux ASG" 23 | type = string 24 | } 25 | variable "lin_desired_size" { 26 | description = "Please enter the desired size for the Linux ASG" 27 | type = string 28 | } 29 | variable "win_min_size" { 30 | description = "Please enter the minimal size for the Windows ASG" 31 | type = string 32 | } 33 | variable "win_max_size" { 34 | description = "Please enter the maximal size for the Windows ASG" 35 | type = string 36 | } 37 | variable "win_desired_size" { 38 | description = "Please enter the desired size for the Windows ASG" 39 | type = string 40 | } 41 | variable "win_instance_type" { 42 | description = "Please enter the instance type to be used for the Windows worker nodes" 43 | type = string 44 | } 45 | variable "node_host_key_name" { 46 | description = "Please enter the name of the SSH key pair that should be assigned to the worker nodes of the cluster" 47 | type = string 48 | } 49 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.72.0" 6 | } 7 | } 8 | } 9 | provider "aws" { 10 | region = var.region 11 | } 12 | 13 | //Modify the bucket and dynamoDB table that are used by Terraform 14 | terraform { 15 | backend "s3" { 16 | bucket = "DOC-EXAMPLE-BUCKET" 17 | key = "private-windows-eks.tfstate" 18 | region = "eu-central-1" 19 | dynamodb_table = "private-windows-eks-tf-lock" 20 | } 21 | } 22 | 23 | data terraform_remote_state "network" { 24 | backend = "s3" 25 | config = { 26 | bucket = "DOC-EXAMPLE-BUCKET" 27 | key = "network.tfstate" 28 | region = "eu-central-1" 29 | } 30 | } 31 | 32 | module "cluster" { 33 | source = "./eks/cluster" 34 | region = var.region 35 | eks_cluster_name = var.eks_cluster_name 36 | eks_cluster_version = var.eks_cluster_version 37 | private_subnet_ids = data.terraform_remote_state.network.outputs.out_private_vpc.private_subnets 38 | vpc_id = data.terraform_remote_state.network.outputs.out_private_vpc.vpc_id 39 | bastion_host_SG_id = data.terraform_remote_state.network.outputs.out_bastion_host_security_group_id 40 | lin_desired_size = var.lin_desired_size 41 | lin_max_size = var.lin_max_size 42 | lin_min_size = var.lin_min_size 43 | lin_instance_type = var.lin_instance_type 44 | win_desired_size = var.win_desired_size 45 | win_max_size = var.win_max_size 46 | win_min_size = var.win_min_size 47 | win_instance_type = var.win_instance_type 48 | node_host_key_name = var.node_host_key_name 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /network/bastion_host_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "ec2:*", 8 | "autoscaling:*", 9 | "kms:CreateKey", 10 | "ssm:ListInstanceAssociations", 11 | "ssm:UpdateInstanceInformation", 12 | "sts:GetCallerIdentity" 13 | ], 14 | "Resource": "*" 15 | }, 16 | { 17 | "Effect": "Allow", 18 | "Action": [ 19 | "ec2:CreateVpcEndpoint", 20 | "route53:AssociateVPCWithHostedZone" 21 | ], 22 | "Resource": "*" 23 | }, 24 | { 25 | "Effect": "Allow", 26 | "Action": "ec2:RunInstances", 27 | "Resource": "*" 28 | }, 29 | { 30 | "Effect": "Allow", 31 | "Action": [ 32 | "iam:AddRoleToInstanceProfile", 33 | "iam:CreateInstanceProfile", 34 | "iam:DeleteInstanceProfile", 35 | "iam:GetInstanceProfile", 36 | "iam:RemoveRoleFromInstanceProfile", 37 | "iam:TagInstanceProfile", 38 | "iam:TagOpenIDConnectProvider" 39 | ], 40 | "Resource": "*" 41 | }, 42 | { 43 | "Effect": "Allow", 44 | "Action": [ 45 | "iam:CreateOpenIDConnectProvider", 46 | "iam:DeleteOpenIDConnectProvider", 47 | "iam:GetOpenIDConnectProvider" 48 | ], 49 | "Resource": "*" 50 | }, 51 | { 52 | "Effect": "Allow", 53 | "Action": [ 54 | "iam:AttachRolePolicy", 55 | "iam:CreateRole", 56 | "iam:CreateServiceLinkedRole", 57 | "iam:DeleteRole", 58 | "iam:DetachRolePolicy", 59 | "iam:GetRole", 60 | "iam:ListAttachedRolePolicies", 61 | "iam:ListInstanceProfilesForRole", 62 | "iam:ListRolePolicies", 63 | "iam:PassRole", 64 | "iam:TagRole" 65 | ], 66 | "Resource": "*" 67 | }, 68 | { 69 | "Effect": "Allow", 70 | "Action": [ 71 | "kms:CreateGrant", 72 | "kms:DescribeKey", 73 | "kms:GetKeyPolicy", 74 | "kms:GetKeyRotationStatus", 75 | "kms:ListResourceTags", 76 | "kms:PutKeyPolicy", 77 | "kms:ScheduleKeyDeletion", 78 | "kms:TagResource" 79 | ], 80 | "Resource": "*" 81 | }, 82 | { 83 | "Effect": "Allow", 84 | "Action": [ 85 | "logs:CreateLogGroup", 86 | "logs:DeleteLogGroup", 87 | "logs:DescribeLogGroups", 88 | "logs:ListTagsLogGroup", 89 | "logs:PutRetentionPolicy" 90 | ], 91 | "Resource": "*" 92 | }, 93 | { 94 | "Effect": "Allow", 95 | "Action": [ 96 | "eks:*" 97 | ], 98 | "Resource": "*" 99 | }, 100 | { 101 | "Effect": "Allow", 102 | "Action": [ 103 | "cloudwatch:*" 104 | ], 105 | "Resource": "*" 106 | }, 107 | { 108 | "Effect": "Allow", 109 | "Action": [ 110 | "s3:*" 111 | ], 112 | "Resource": "*" 113 | }, 114 | { 115 | "Effect": "Allow", 116 | "Action": [ 117 | "dynamodb:*" 118 | ], 119 | "Resource": "*" 120 | } 121 | ] 122 | } -------------------------------------------------------------------------------- /network/main-input.tfvars: -------------------------------------------------------------------------------- 1 | region = "eu-central-1" 2 | azs_private = ["eu-central-1a", "eu-central-1b", "eu-central-1c"] 3 | private_subnets = ["10.10.1.0/24", "10.10.2.0/24", "10.10.3.0/24"] 4 | vpc_private_cidr = "10.10.0.0/16" 5 | vpc_public_cidr = "10.20.0.0/16" 6 | azs_public = ["eu-central-1a"] 7 | public_subnets = ["10.20.1.0/24"] 8 | 9 | -------------------------------------------------------------------------------- /network/main-var.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "Please enter the region used to deploy this infrastructure" 3 | type = string 4 | } 5 | variable "vpc_private_cidr" { 6 | description = "Please enter the CIDR for the private VPC" 7 | type = string 8 | } 9 | variable "azs_private" { 10 | description = "Please enter a list of Availability zones for the private subnets" 11 | type = any 12 | } 13 | variable "private_subnets" { 14 | description = "Please enter a list of CIDR ranges for the private subnets in the availability zones" 15 | type = any 16 | } 17 | variable "vpc_public_cidr" { 18 | description = "Please enter a CIDR for the public VPC" 19 | type = string 20 | } 21 | variable "azs_public" { 22 | description = "Please enter a list of Availability zones for the public subnets" 23 | type = any 24 | } 25 | variable "public_subnets" { 26 | description = "Please enter a list of CIDR ranges for the public subnets in the availability zones" 27 | type = any 28 | } 29 | variable "bastion_host_key_name" { 30 | description = "Please enter the name of the SSH key pair that should be assigned to the bastion host" 31 | type = string 32 | } 33 | variable "ssh_bastion_cidr" { 34 | description = "Please enter a list of CIDR range(s) that are allowed to access the Bastion Host - Usually these are your corporate CIDR ranges - You can also restrict access to only your IP address by using /32 as prefix e.g. [\"192.168.10.10/32\"] - [\"0.0.0.0/0\"] allows access from all IPv4 adresses but is not recommended" 35 | type = list(string) 36 | } 37 | -------------------------------------------------------------------------------- /network/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.72.0" 6 | } 7 | } 8 | } 9 | provider "aws" { 10 | region = var.region 11 | } 12 | //Modify the bucket and dynamoDB table that are used by Terraform 13 | terraform { 14 | backend "s3" { 15 | bucket = "DOC-EXAMPLE-BUCKET" 16 | key = "network.tfstate" 17 | region = "eu-central-1" 18 | dynamodb_table = "private-windows-eks-tf-lock" 19 | } 20 | } 21 | module "private_vpc" { 22 | source = "terraform-aws-modules/vpc/aws" 23 | name = "sample-repo-vpc-private" 24 | cidr = var.vpc_private_cidr 25 | azs = var.azs_private 26 | private_subnets = var.private_subnets 27 | enable_dns_hostnames = true 28 | create_igw = false 29 | enable_nat_gateway = false 30 | enable_vpn_gateway = false 31 | } 32 | module "public_vpc" { 33 | source = "terraform-aws-modules/vpc/aws" 34 | name = "sample-repo-vpc-public" 35 | cidr = var.vpc_public_cidr 36 | create_egress_only_igw = false 37 | create_igw = true 38 | azs = var.azs_public 39 | public_subnets = var.public_subnets 40 | enable_dns_hostnames = true 41 | enable_nat_gateway = false 42 | enable_vpn_gateway = false 43 | } 44 | resource "aws_iam_instance_profile" "ec2_eks_terraform" { 45 | name = "ec2_eks_terraform" 46 | role = aws_iam_role.ec2_eks_role.name 47 | } 48 | 49 | ### Loads a pre-defined policy 50 | resource "aws_iam_policy" "ec2_eks_terraform_policy" { 51 | name = "ec2_eks_terraform_policy" 52 | path = "/" 53 | description = "Policy to create EKS cluster with Windows and Linux Nodes" 54 | policy = "${file("bastion_host_policy.json")}" 55 | } 56 | resource "aws_iam_role" "ec2_eks_role" { 57 | name = "ec2_eks_role_terraform" 58 | managed_policy_arns = [resource.aws_iam_policy.ec2_eks_terraform_policy.arn] 59 | assume_role_policy = jsonencode({ 60 | Version = "2012-10-17" 61 | Statement = [ 62 | { 63 | Action = "sts:AssumeRole" 64 | Effect = "Allow" 65 | Sid = "" 66 | Principal = { 67 | Service = "ec2.amazonaws.com" 68 | } 69 | }, 70 | ] 71 | }) 72 | } 73 | 74 | module "ec2_instance" { 75 | source = "terraform-aws-modules/ec2-instance/aws" 76 | version = "~> 3.0" 77 | name = "bastion-host" 78 | ami = data.aws_ami.amazon-linux-2.id 79 | instance_type = "t2.micro" 80 | key_name = var.bastion_host_key_name 81 | monitoring = false 82 | vpc_security_group_ids = [aws_security_group.allow_ssh.id] 83 | subnet_id = module.public_vpc.public_subnets[0] 84 | iam_instance_profile = aws_iam_instance_profile.ec2_eks_terraform.name 85 | user_data = <> ~/.bashrc 96 | kubectl version --short --client 97 | EOF 98 | } 99 | resource "aws_security_group" "allow_ssh" { 100 | name = "allow_ssh" 101 | description = "Allow SSH inbound traffic" 102 | vpc_id = module.public_vpc.vpc_id 103 | 104 | ingress { 105 | description = "SSH from VPC" 106 | from_port = 22 107 | to_port = 22 108 | protocol = "tcp" 109 | cidr_blocks = var.ssh_bastion_cidr 110 | } 111 | 112 | egress { 113 | description = "Allow egress" 114 | from_port = 0 115 | to_port = 0 116 | protocol = "-1" 117 | cidr_blocks = ["0.0.0.0/0"] 118 | } 119 | } 120 | data "aws_ami" "amazon-linux-2" { 121 | owners = ["amazon"] 122 | most_recent = true 123 | 124 | filter { 125 | name = "name" 126 | values = ["amzn2-ami-hvm-*-x86_64-ebs"] 127 | } 128 | } 129 | resource "aws_vpc_peering_connection" "bastion-private-EKS" { 130 | peer_vpc_id = module.public_vpc.vpc_id 131 | vpc_id = module.private_vpc.vpc_id 132 | auto_accept = true 133 | tags = { 134 | Name = "VPC Peering between Bastion Host and private EKS cluster" 135 | } 136 | accepter { 137 | allow_remote_vpc_dns_resolution = true 138 | } 139 | 140 | requester { 141 | allow_remote_vpc_dns_resolution = true 142 | } 143 | } 144 | 145 | resource "aws_route" "peeringConnection-private-a" { 146 | route_table_id = module.private_vpc.private_route_table_ids[0] 147 | destination_cidr_block = module.public_vpc.vpc_cidr_block 148 | vpc_peering_connection_id = aws_vpc_peering_connection.bastion-private-EKS.id 149 | } 150 | resource "aws_route" "peeringConnection-private-b" { 151 | route_table_id = module.private_vpc.private_route_table_ids[1] 152 | destination_cidr_block = module.public_vpc.vpc_cidr_block 153 | vpc_peering_connection_id = aws_vpc_peering_connection.bastion-private-EKS.id 154 | } 155 | resource "aws_route" "peeringConnection-private-c" { 156 | route_table_id = module.private_vpc.private_route_table_ids[2] 157 | destination_cidr_block = module.public_vpc.vpc_cidr_block 158 | vpc_peering_connection_id = aws_vpc_peering_connection.bastion-private-EKS.id 159 | } 160 | resource "aws_route" "peeringConnection-public-a" { 161 | route_table_id = module.public_vpc.public_route_table_ids[0] 162 | destination_cidr_block = module.private_vpc.vpc_cidr_block 163 | vpc_peering_connection_id = aws_vpc_peering_connection.bastion-private-EKS.id 164 | } 165 | 166 | -------------------------------------------------------------------------------- /network/output.tf: -------------------------------------------------------------------------------- 1 | output "out_private_vpc" { 2 | value = module.private_vpc 3 | } 4 | output "out_public_vpc" { 5 | value = module.public_vpc 6 | } 7 | output "out_bastion_host_security_group_id" { 8 | value = aws_security_group.allow_ssh.id 9 | } 10 | output "out_private_subnets" { 11 | value = module.private_vpc.private_subnets 12 | } 13 | output "out_vpc_id" { 14 | value = module.private_vpc.vpc_id 15 | } 16 | output "out_bastion_public_ip" { 17 | value = module.ec2_instance.public_ip 18 | } 19 | -------------------------------------------------------------------------------- /output.tf: -------------------------------------------------------------------------------- 1 | output "out_bastion_host_security_group_id" { 2 | value = data.terraform_remote_state.network.outputs.out_bastion_host_security_group_id 3 | } 4 | output "out_bastion_host_public_ip" { 5 | value = data.terraform_remote_state.network.outputs.out_bastion_public_ip 6 | } 7 | output "out_eks_cluster" { 8 | value = module.cluster.out_eks_cluster 9 | } 10 | -------------------------------------------------------------------------------- /yaml-templates/additional_roles_aws_auth.yaml: -------------------------------------------------------------------------------- 1 | - rolearn: arn:aws:iam::111122223333:role/Admin 2 | username: admin 3 | groups: 4 | - system:masters 5 | -------------------------------------------------------------------------------- /yaml-templates/aws-auth-cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: aws-auth 5 | namespace: kube-system 6 | data: 7 | mapRoles: | 8 | - rolearn: arn:aws:iam::111122223333:role/node-role 9 | username: system:node:{{EC2PrivateDNSName}} 10 | groups: 11 | - system:bootstrappers 12 | - system:nodes 13 | - rolearn: arn:aws:iam::111122223333:role/node-role 14 | username: system:node:{{EC2PrivateDNSName}} 15 | groups: 16 | - system:bootstrappers 17 | - system:nodes 18 | - eks:kube-proxy-windows 19 | -------------------------------------------------------------------------------- /yaml-templates/vpc-resource-controller-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: amazon-vpc-cni 5 | namespace: kube-system 6 | data: 7 | enable-windows-ipam: "true" 8 | --------------------------------------------------------------------------------