├── .gitignore ├── DOC.md ├── LICENSE ├── README.md ├── aks ├── README.md ├── aks.tf ├── common.tf ├── files │ └── demo.yaml ├── variables.tf ├── versions.tf └── vm-jbox.tf ├── blob └── hello.txt ├── common.tf ├── gw-nat.tf ├── images ├── terraform_aks.png ├── terraform_azure.png ├── terraform_azure2.png ├── terraform_azure3.png └── terraform_azure4.png ├── packer ├── README.md └── lx_nginx.json ├── script ├── README.md └── cloud-init.txt ├── variables.tf ├── vm-app.tf ├── vm-blob.tf ├── vm-jbox.tf ├── vm-web.tf └── vmss ├── README.md ├── common.tf ├── variables.tf └── vmss.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | .terraform.* 3 | terraform.* 4 | override.tf 5 | _* -------------------------------------------------------------------------------- /DOC.md: -------------------------------------------------------------------------------- 1 | # Terraform 2 | 3 | > New `azurerm_linux_virtual_machine` is used. 4 | 5 | ## VM login 6 | 7 | By password: 8 | 9 | ``` 10 | # alternative login method 11 | computer_name = "vm${count.index}" 12 | admin_username = "${var.vm.admin_username}" 13 | admin_password = "${var.vm.admin_password}" 14 | 15 | disable_password_authentication = false 16 | ``` 17 | 18 | By ssh public key: 19 | 20 | ``` 21 | computer_name = "vm${count.index}" 22 | admin_username = "${var.vm.admin_username}" 23 | 24 | disable_password_authentication = true 25 | 26 | admin_ssh_keys { 27 | username = "azureuser" 28 | key_data = file("~/.ssh/id_rsa.pub") 29 | } 30 | ``` 31 | 32 | ## OS and data disk 33 | 34 | To attach os disk size with >30GiB. 35 | 36 | ``` 37 | os_disk { 38 | name = "${format("%s-app-%03d-osdisk", var.resource.prefix, count.index + 1)}" 39 | caching = "ReadWrite" 40 | storage_account_type = "Premium_LRS" 41 | 42 | disk_size_gb = "64" # increase default os disk 43 | } 44 | ``` 45 | 46 | > Note: Some OSs do not automatically increase disk size. To increase os disk size, please refer: https://blogs.msdn.microsoft.com/linuxonazure/2017/04/03/how-to-resize-linux-osdisk-partition-on-azure/ 47 | 48 | Method of attaching data disk changed. See [azurerm_virtual_machine_data_disk_attachment](https://www.terraform.io/docs/providers/azurerm/r/virtual_machine_data_disk_attachment.html) for more information. 49 | 50 | ## OS image 51 | 52 | For azure provided images, 53 | 54 | ``` 55 | source_image_reference { 56 | publisher = "Canonical" 57 | offer = "UbuntuServer" 58 | sku = "16.04.0-LTS" 59 | version = "latest" 60 | } 61 | ``` 62 | 63 | Note: getting az image list, `az vm image list --offer UbuntuServer --all --output table ` 64 | 65 | For custom/user image, 66 | 67 | ``` 68 | source_image_id = "${var.vm.osimageuri}" 69 | ``` 70 | 71 | Note: To get `id`, copy `uri id` from image 72 | 73 | ## Create multiple VMs 74 | 75 | Use `count` and `${count.index}` to create repeatable resources 76 | 77 | ``` 78 | resource "azurerm_linux_virtual_machine" "tfwebvm" { 79 | count = var.vm.webcount 80 | name = "${var.resource.prefix}webvm${count.index}" 81 | location = var.resource.location 82 | resource_group_name = azurerm_resource_group.tfrg.name 83 | network_interface_ids = [azurerm_network_interface.tfwebnic[count.index].id] 84 | vm_size = var.vmsize 85 | availability_set_id = azurerm_availability_set.tfwebavset.id 86 | 87 | computer_name = format("tfwebvm%03d", count.index + 1) 88 | admin_username = var.vm.admin_username 89 | admin_password = var.vm.admin_password 90 | disable_password_authentication = false 91 | 92 | ... 93 | } 94 | ``` 95 | 96 | ## Cloud-Init 97 | 98 | __cloud-init__ is used instead of __custom script__ with VM Extension for this demo. Declare `custom_data` in `azurerm_linux_virtual_machine`/ 99 | 100 | ``` 101 | custom_data = base64encode( file("./script/cloud-init.txt") ) 102 | ``` 103 | 104 | ## Load Balancer 105 | 106 | To attach VM to lb, add `load_balancer_backend_address_pools_ids` in nic's `ip_configuration` like below 107 | 108 | ``` 109 | resource "azurerm_network_interface" "tfwebnic" { 110 | count = "${var.vm.webcount}" 111 | name = "${var.resource.prefix}-webnic${count.index}" 112 | location = "${var.resource.location}" 113 | resource_group_name = "${azurerm_resource_group.tfrg.name}" 114 | network_security_group_id = "${azurerm_network_security_group.tfwebnsg.id}" 115 | 116 | ip_configuration { 117 | name = "${var.resource.prefix}-webnic-config${count.index}" 118 | subnet_id = "${azurerm_subnet.tfwebvnet.id}" 119 | private_ip_address_allocation = "dynamic" 120 | 121 | load_balancer_backend_address_pools_ids = ["${azurerm_lb_backend_address_pool.tflbbackendpool.id}"] 122 | load_balancer_inbound_nat_rules_ids = ["${azurerm_lb_nat_rule.lbnatrule.*.id[count.index]}"] 123 | } 124 | } 125 | ``` 126 | 127 | ## NAT instance 128 | 129 | NAT instance is no longer needed and use [NAT Gateway](https://docs.microsoft.com/en-us/azure/virtual-network/nat-overview). 130 | 131 | ## ASG 132 | 133 | ASG is supported from terraform `azure provider 1.2` and you can create ASG resource. 134 | 135 | Define ASG, 136 | 137 | ``` 138 | resource "azurerm_application_security_group" "tfappasg" { 139 | name = "tf-appasg" 140 | location = "${azurerm_resource_group.tfrg.location}" 141 | resource_group_name = "${azurerm_resource_group.tfrg.name}" 142 | } 143 | ``` 144 | 145 | Add ASG to nics 146 | 147 | ``` 148 | resource "azurerm_network_interface" "tfwebnic" { 149 | ... 150 | 151 | ip_configuration { 152 | ... 153 | application_security_group_ids = ["${azurerm_application_security_group.tfwebasg.id}"] 154 | } 155 | } 156 | 157 | ``` 158 | 159 | ASG rule feature added in azure prover 1.3. To allow only traffic between ASG tag you need to add both `DENY_VNET` and `ALLOW_LB` rules first then add ASG rule. `ALLOW_LB` rule is needed because LB won't work without health probing. 160 | 161 | ``` 162 | security_rule { 163 | name = "DENY_VNET" 164 | priority = 4096 165 | direction = "Inbound" 166 | access = "Deny" 167 | protocol = "*" 168 | source_port_range = "*" 169 | destination_port_range = "*" 170 | source_address_prefix = "VirtualNetwork" 171 | destination_address_prefix = "VirtualNetwork" 172 | } 173 | 174 | security_rule { 175 | name = "ALLOW_LB" 176 | priority = 4095 177 | direction = "Inbound" 178 | access = "Allow" 179 | protocol = "*" 180 | source_port_range = "*" 181 | destination_port_range = "*" 182 | source_address_prefix = "AzureLoadBalancer" 183 | destination_address_prefix = "*" 184 | } 185 | 186 | security_rule { 187 | name = "HTTP_VNET" 188 | priority = 1000 189 | direction = "Inbound" 190 | access = "Allow" 191 | protocol = "Tcp" 192 | source_port_range = "*" 193 | destination_port_range = "80" 194 | 195 | source_application_security_group_ids = ["${azurerm_application_security_group.tfwebasg.id}"] 196 | destination_application_security_group_ids = ["${azurerm_application_security_group.tfappasg.id}"] 197 | } 198 | ``` 199 | 200 | ## Some issue 201 | 202 | For availableset, default 3 not working in some regions like Korea, use 2 instead. 203 | 204 | ``` 205 | platform_fault_domain_count = 2 206 | ``` 207 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Il Joong Kim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Filjoong%2Fazure-terraform&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) 3 | 4 | > Revised for _Terraform AzureRM v3.25.0 with Terraform v1.3.1_ 5 | 6 | ## Sample architecture 7 | 8 | ![sample architecture](./images/terraform_azure4.png) 9 | 10 | N-Tier architecture service with a jumphost and a NAT instance. 11 | NAT instance is used for SNATing outbound from VMs in app-subnet. 12 | 13 | Other samples: 14 | 15 | > Following samples are not tested with latest Terraform. 16 | 17 | - [AKS Sample](./aks/README.md) 18 | - [PaaS (App Service + SQL DB) samples](https://github.com/iljoong/azure-tf-paas) 19 | - [IoT samples](https://github.com/iljoong/azure-tf-iot) 20 | - [Jmeter environment sample](https://github.com/iljoong/azure-jmeter) 21 | - [DevOps environment sample](https://github.com/iljoong/azure-devops/tree/master/terraform) 22 | 23 | ## How to run 24 | 25 | ### Preparation 26 | 27 | Download and install terraform: https://www.terraform.io/downloads.html 28 | 29 | > It is recommended to use azure-cli or managed service identity (MSI) for authentication. 30 | 31 | Update variables such as `subscription_id` and `admin_name` in [variables.tf](./variables.tf) 32 | 33 | ### Azure Service principal 34 | 35 | Run following command to get a service principal info. 36 | Note that if you have multiple subscriptions then you should set right default subscription. 37 | 38 | ``` 39 | az account set -s 40 | az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/" 41 | ``` 42 | 43 | > You don't need to create SP if you're using Azure CLI environment, such as _Azure Shell_. See [Authenticating using the Azure CLI](https://www.terraform.io/docs/providers/azurerm/auth/azure_cli.html) for more information 44 | 45 | > You can also enable MSI to run terraform witout SP on your Azure VM. See [Authenticating using managed identities for Azure resources](https://www.terraform.io/docs/providers/azurerm/guides/managed_service_identity.html) and please refer (MSI documentation)[https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/tutorial-linux-vm-access-arm] for how to setup MSI. 46 | 47 | ### Run terraform 48 | 49 | Initialize first, 50 | 51 | ``` 52 | terraform init 53 | ``` 54 | 55 | Then apply terraform 56 | 57 | ``` 58 | terraform apply 59 | ``` 60 | 61 | ## Feature highlight 62 | 63 | 1. VM login - ssh public key or password 64 | 2. Disk - OS disk with >30GiB and datadisk 65 | 3. OS image - default or custom image 66 | for building custom image, refer [packer](./packer) 67 | 4. Create multiple VMs 68 | 5. Setting LB 69 | 6. NAT Gateway - no more NAT instance 70 | 7. ASG - create and apply ASG 71 | 8. Blob - create blob account and upload a file 72 | 73 | For more information, please refer [DOC.md](./DOC.md) 74 | 75 | ## SNAT test 76 | 77 | After provisioned, login to one of `app` vm through jump box and test source ip using following command 78 | 79 | ``` 80 | curl ipinfo.io 81 | ``` 82 | 83 | ## Reference 84 | 85 | ### Azure 86 | 87 | - provider: https://www.terraform.io/docs/providers/azurerm/ 88 | 89 | - example: https://github.com/terraform-providers/terraform-provider-azurerm/tree/master/examples 90 | 91 | ### Terraform 92 | 93 | - terraform syntax: https://www.terraform.io/docs/configuration/syntax.html 94 | 95 | - iterpolation: https://www.terraform.io/docs/configuration/interpolation.html 96 | 97 | ### Tips 98 | 99 | - lb-pool associate vms: https://github.com/hashicorp/terraform/issues/13663 100 | 101 | - loops: https://blog.gruntwork.io/terraform-tips-tricks-loops-if-statements-and-gotchas-f739bbae55f9 102 | -------------------------------------------------------------------------------- /aks/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | > Revised for _Terraform AzureRM v2.1_ 4 | 5 | Sample terraform script for AKS. 6 | 7 | ## Sample architecture 8 | 9 | ![sample AKS architecture](../images/terraform_aks.png) 10 | 11 | AKS deployment architecture with advanced features. 12 | - [Advanced networking (Azure CNI)](https://docs.microsoft.com/en-us/azure/aks/configure-azure-cni) 13 | - [Autoscale](https://docs.microsoft.com/en-us/azure/aks/cluster-autoscaler) (preview) 14 | - [Multi-node pools](https://docs.microsoft.com/en-us/azure/aks/use-multiple-node-pools) (preview) 15 | - [Availability Zones](https://docs.microsoft.com/en-us/azure/aks/availability-zones) (preview) 16 | 17 | You need to set `load_balancer_sku = "standard"` in `network_profile` in order to use __AZ__. 18 | 19 | > Note: At the time of writing this, you need to manually register preview features in order to use __multi-node pools__ and __AZ__. See documentation for more information 20 | 21 | 22 | ## Run Terraform 23 | 24 | Sample AKS terraform script is located in this folder. 25 | 26 | > Please refer [README](./../README.md) for how to run terraform script. 27 | 28 | ## Test 29 | 30 | Once create AKS cluster, run following CLI to get K8S config key 31 | 32 | ```bash 33 | az aks get-credentials --resource-group $rgname --name $aksname 34 | ``` 35 | 36 | ### Run sample images 37 | 38 | Run a sample image for test. Please refer [terraform examples](https://github.com/terraform-providers/terraform-provider-azurerm/tree/master/examples/kubernetes) for more information 39 | 40 | ```bash 41 | kubectl apply -f ./files/demo.yaml 42 | ``` 43 | 44 | ### Browse K8S dashboard 45 | 46 | ```bash 47 | az aks browse --resource-group $rgname --name $aksname 48 | ``` 49 | 50 | > You need to run following CLI to access dashboard. `#kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard` 51 | 52 | ## Run GPU workload 53 | 54 | To run GPU workload, please refer [AKS GPU cluster documentation](https://docs.microsoft.com/en-us/azure/aks/gpu-cluster) for more information 55 | -------------------------------------------------------------------------------- /aks/aks.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_kubernetes_cluster" "tfaks" { 2 | name = "${var.prefix}-aks" 3 | location = var.location 4 | resource_group_name = azurerm_resource_group.tfrg.name 5 | dns_prefix = "${var.prefix}dns" 6 | 7 | #kubernetes_version = "1.13.11" 8 | 9 | default_node_pool { 10 | name = "default" 11 | node_count = 2 12 | min_count = 2 13 | max_count = 3 14 | vm_size = "Standard_DS1_v2" 15 | 16 | #os_type = "Linux" 17 | os_disk_size_gb = 128 18 | 19 | # Autoscale 20 | type = "VirtualMachineScaleSets" 21 | enable_auto_scaling = true 22 | 23 | # AZ 24 | availability_zones = [1, 2] 25 | 26 | vnet_subnet_id = azurerm_subnet.tfaksvnet.id 27 | } 28 | 29 | linux_profile { 30 | admin_username = var.admin_username 31 | ssh_key { 32 | key_data = var.admin_keydata 33 | } 34 | } 35 | 36 | network_profile { 37 | network_plugin = "azure" 38 | load_balancer_sku = "standard" 39 | } 40 | 41 | service_principal { 42 | client_id = var.client_id 43 | client_secret = var.client_secret 44 | } 45 | 46 | tags = { 47 | environment = var.tag 48 | } 49 | } 50 | 51 | resource "azurerm_kubernetes_cluster_node_pool" "tfgpupool" { 52 | name = "gpu" 53 | kubernetes_cluster_id = azurerm_kubernetes_cluster.tfaks.id 54 | node_count = 1 55 | min_count = 1 56 | max_count = 2 57 | vm_size = "Standard_DS1_v2" #"Standard_NC6" 58 | os_type = "Linux" 59 | os_disk_size_gb = 128 60 | 61 | # Autoscale 62 | enable_auto_scaling = true 63 | 64 | vnet_subnet_id = azurerm_subnet.tfaksvnet.id 65 | } 66 | 67 | output "client_certificate" { 68 | value = azurerm_kubernetes_cluster.tfaks.kube_config[0].client_certificate 69 | } 70 | 71 | output "kube_config" { 72 | value = azurerm_kubernetes_cluster.tfaks.kube_config_raw 73 | } 74 | 75 | -------------------------------------------------------------------------------- /aks/common.tf: -------------------------------------------------------------------------------- 1 | # Configure the Microsoft Azure Provider 2 | provider "azurerm" { 3 | subscription_id = var.subscription_id 4 | client_id = var.client_id 5 | client_secret = var.client_secret 6 | tenant_id = var.tenant_id 7 | 8 | features {} 9 | } 10 | 11 | # Create a resource group if it doesn’t exist 12 | resource "azurerm_resource_group" "tfrg" { 13 | name = "${var.prefix}-rg" 14 | location = var.location 15 | 16 | tags = { 17 | environment = var.tag 18 | } 19 | } 20 | 21 | # Create virtual network 22 | resource "azurerm_virtual_network" "tfvnet" { 23 | name = "${var.prefix}-vnet" 24 | address_space = ["10.1.0.0/16"] 25 | location = var.location 26 | resource_group_name = azurerm_resource_group.tfrg.name 27 | 28 | tags = { 29 | environment = var.tag 30 | } 31 | } 32 | 33 | resource "azurerm_subnet" "tfaksvnet" { 34 | name = "aks-net" 35 | virtual_network_name = azurerm_virtual_network.tfvnet.name 36 | resource_group_name = azurerm_resource_group.tfrg.name 37 | 38 | # 10.1.0.1 ~ 10.1.15.254 39 | address_prefix = "10.1.0.0/20" 40 | } 41 | 42 | resource "azurerm_subnet_network_security_group_association" "tfaksvnet" { 43 | subnet_id = azurerm_subnet.tfaksvnet.id 44 | network_security_group_id = azurerm_network_security_group.tfaksnsg.id 45 | } 46 | 47 | resource "azurerm_subnet_route_table_association" "tfaksvnet" { 48 | subnet_id = azurerm_subnet.tfaksvnet.id 49 | route_table_id = azurerm_route_table.nattable.id 50 | } 51 | 52 | resource "azurerm_subnet" "tfjboxvnet" { 53 | name = "jbox-subnet" 54 | virtual_network_name = azurerm_virtual_network.tfvnet.name 55 | resource_group_name = azurerm_resource_group.tfrg.name 56 | address_prefix = "10.1.200.0/24" 57 | } 58 | 59 | # NSG 60 | resource "azurerm_network_security_group" "tfaksnsg" { 61 | name = "${var.prefix}-aksnsg" 62 | location = var.location 63 | resource_group_name = azurerm_resource_group.tfrg.name 64 | 65 | security_rule { 66 | name = "HTTP" 67 | priority = 1000 68 | direction = "Inbound" 69 | access = "Allow" 70 | protocol = "Tcp" 71 | source_port_range = "*" 72 | destination_port_range = "80" 73 | source_address_prefix = "Internet" 74 | destination_address_prefix = "*" 75 | } 76 | 77 | tags = { 78 | environment = var.tag 79 | } 80 | } 81 | 82 | # UDR 83 | resource "azurerm_route_table" "nattable" { 84 | name = "${var.prefix}-natroutetable" 85 | location = var.location 86 | resource_group_name = azurerm_resource_group.tfrg.name 87 | 88 | route { 89 | name = "natrule1" 90 | address_prefix = "10.100.0.0/14" 91 | next_hop_type = "VirtualAppliance" 92 | next_hop_in_ip_address = "10.10.1.1" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /aks/files/demo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: azure-vote-back 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: azure-vote-back 10 | template: 11 | metadata: 12 | labels: 13 | app: azure-vote-back 14 | spec: 15 | nodeSelector: 16 | "beta.kubernetes.io/os": linux 17 | containers: 18 | - name: azure-vote-back 19 | image: redis 20 | resources: 21 | requests: 22 | cpu: 100m 23 | memory: 128Mi 24 | limits: 25 | cpu: 250m 26 | memory: 256Mi 27 | ports: 28 | - containerPort: 6379 29 | name: redis 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: azure-vote-back 35 | spec: 36 | ports: 37 | - port: 6379 38 | selector: 39 | app: azure-vote-back 40 | --- 41 | apiVersion: apps/v1 42 | kind: Deployment 43 | metadata: 44 | name: azure-vote-front 45 | spec: 46 | replicas: 1 47 | selector: 48 | matchLabels: 49 | app: azure-vote-front 50 | template: 51 | metadata: 52 | labels: 53 | app: azure-vote-front 54 | spec: 55 | nodeSelector: 56 | "beta.kubernetes.io/os": linux 57 | containers: 58 | - name: azure-vote-front 59 | image: microsoft/azure-vote-front:v1 60 | resources: 61 | requests: 62 | cpu: 100m 63 | memory: 128Mi 64 | limits: 65 | cpu: 250m 66 | memory: 256Mi 67 | ports: 68 | - containerPort: 80 69 | env: 70 | - name: REDIS 71 | value: "azure-vote-back" 72 | --- 73 | apiVersion: v1 74 | kind: Service 75 | metadata: 76 | name: azure-vote-front 77 | spec: 78 | type: LoadBalancer 79 | ports: 80 | - port: 80 81 | selector: 82 | app: azure-vote-front -------------------------------------------------------------------------------- /aks/variables.tf: -------------------------------------------------------------------------------- 1 | # azure service principal info 2 | variable "subscription_id" { 3 | default = "add_here" 4 | } 5 | 6 | # client_id or app_id 7 | variable "client_id" { 8 | default = "add_here" 9 | } 10 | 11 | variable "client_secret" { 12 | default = "add_here" 13 | } 14 | 15 | # tenant_id or directory_id 16 | variable "tenant_id" { 17 | default = "add_here" 18 | } 19 | 20 | # admin password 21 | variable "admin_username" { 22 | default = "azureuser" 23 | } 24 | 25 | variable "admin_password" { 26 | default = "_add_here_" 27 | } 28 | 29 | variable "admin_keydata" { 30 | default = "_add_here_" 31 | } 32 | 33 | # service variables 34 | variable "prefix" { 35 | default = "tfaksdemo" 36 | } 37 | 38 | variable "location" { 39 | default = "westus2" 40 | } 41 | 42 | variable "tag" { 43 | default = "demo" 44 | } -------------------------------------------------------------------------------- /aks/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /aks/vm-jbox.tf: -------------------------------------------------------------------------------- 1 | # Create Network Security Group and rule 2 | resource "azurerm_network_security_group" "tfjboxnsg" { 3 | name = "${var.prefix}-jboxnsg" 4 | location = var.location 5 | resource_group_name = azurerm_resource_group.tfrg.name 6 | 7 | security_rule { 8 | name = "SSH" 9 | priority = 1001 10 | direction = "Inbound" 11 | access = "Allow" 12 | protocol = "Tcp" 13 | source_port_range = "*" 14 | destination_port_range = "22" 15 | source_address_prefix = "*" # add source addr 16 | destination_address_prefix = "*" 17 | } 18 | 19 | tags = { 20 | environment = var.tag 21 | } 22 | } 23 | 24 | # Create public IPs 25 | resource "azurerm_public_ip" "tfjboxip" { 26 | name = "${var.prefix}-jboxip" 27 | location = var.location 28 | resource_group_name = azurerm_resource_group.tfrg.name 29 | allocation_method = "Static" 30 | 31 | tags = { 32 | environment = var.tag 33 | } 34 | } 35 | 36 | # Create network interface 37 | resource "azurerm_network_interface" "tfjboxnic" { 38 | name = "${var.prefix}-jboxnic" 39 | location = var.location 40 | resource_group_name = azurerm_resource_group.tfrg.name 41 | #-network_security_group_id = azurerm_network_security_group.tfjboxnsg.id 42 | 43 | ip_configuration { 44 | name = "${var.prefix}-jboxnic-conf" 45 | subnet_id = azurerm_subnet.tfjboxvnet.id 46 | private_ip_address_allocation = "dynamic" 47 | public_ip_address_id = azurerm_public_ip.tfjboxip.id 48 | } 49 | 50 | tags = { 51 | environment = var.tag 52 | } 53 | } 54 | 55 | resource "azurerm_network_interface_security_group_association" "tfjboxnic" { 56 | network_interface_id = azurerm_network_interface.tfjboxnic.id 57 | network_security_group_id = azurerm_network_security_group.tfjboxnsg.id 58 | } 59 | 60 | # Create virtual machine 61 | resource "azurerm_virtual_machine" "tfjboxvm" { 62 | name = "${var.prefix}jboxvm" 63 | location = var.location 64 | resource_group_name = azurerm_resource_group.tfrg.name 65 | network_interface_ids = [azurerm_network_interface.tfjboxnic.id] 66 | vm_size = "Standard_DS1_v2" 67 | 68 | storage_os_disk { 69 | name = "${var.prefix}-ftosdisk-jbox" 70 | caching = "ReadWrite" 71 | create_option = "FromImage" 72 | managed_disk_type = "Premium_LRS" 73 | } 74 | 75 | storage_image_reference { 76 | publisher = "Canonical" 77 | offer = "UbuntuServer" 78 | sku = "16.04.0-LTS" 79 | version = "latest" 80 | } 81 | 82 | os_profile { 83 | computer_name = "tfjobxvm" 84 | admin_username = var.admin_username 85 | admin_password = var.admin_password 86 | } 87 | 88 | os_profile_linux_config { 89 | disable_password_authentication = false 90 | } 91 | 92 | tags = { 93 | environment = var.tag 94 | } 95 | } 96 | 97 | # public_ip must be 'static' in order to print output properly 98 | output "jumphost_ip" { 99 | value = azurerm_public_ip.tfjboxip.ip_address 100 | } 101 | 102 | -------------------------------------------------------------------------------- /blob/hello.txt: -------------------------------------------------------------------------------- 1 | Hello Azure and Terraform -------------------------------------------------------------------------------- /common.tf: -------------------------------------------------------------------------------- 1 | # Configure the Microsoft Azure Provider 2 | terraform { 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | # use latest 7 | #version = "=3.25.0" 8 | } 9 | } 10 | } 11 | 12 | provider "azurerm" { 13 | /*subscription_id = var.azure.subscription_id 14 | client_id = var.azure.client_id 15 | client_secret = var.azure.client_secret 16 | tenant_id = var.azure.tenant_id*/ 17 | 18 | features {} 19 | } 20 | 21 | # Create a resource group if it doesn’t exist 22 | resource "azurerm_resource_group" "tfrg" { 23 | name = "${var.resource.prefix}-rg" 24 | location = var.resource.location 25 | 26 | tags = { 27 | environment = var.resource.tag 28 | } 29 | } 30 | 31 | # Create virtual network 32 | resource "azurerm_virtual_network" "tfvnet" { 33 | name = "${var.resource.prefix}-vnet" 34 | address_space = ["10.0.0.0/16"] 35 | location = var.resource.location 36 | resource_group_name = azurerm_resource_group.tfrg.name 37 | 38 | tags = { 39 | environment = var.resource.tag 40 | } 41 | } 42 | 43 | resource "azurerm_subnet" "tfnatvnet" { 44 | name = "app-natnet" 45 | virtual_network_name = azurerm_virtual_network.tfvnet.name 46 | resource_group_name = azurerm_resource_group.tfrg.name 47 | address_prefixes = ["10.0.0.0/24"] 48 | } 49 | 50 | resource "azurerm_subnet" "tfwebvnet" { 51 | name = "web-subnet" 52 | virtual_network_name = azurerm_virtual_network.tfvnet.name 53 | resource_group_name = azurerm_resource_group.tfrg.name 54 | address_prefixes = ["10.0.1.0/24"] 55 | } 56 | 57 | resource "azurerm_subnet" "tfappvnet" { 58 | name = "app-subnet" 59 | virtual_network_name = azurerm_virtual_network.tfvnet.name 60 | resource_group_name = azurerm_resource_group.tfrg.name 61 | address_prefixes = ["10.0.2.0/24"] 62 | } 63 | 64 | resource "azurerm_subnet" "tfjboxvnet" { 65 | name = "jbox-subnet" 66 | virtual_network_name = azurerm_virtual_network.tfvnet.name 67 | resource_group_name = azurerm_resource_group.tfrg.name 68 | address_prefixes = ["10.0.3.0/24"] 69 | } 70 | 71 | /* 72 | # UDR 73 | resource "azurerm_subnet_route_table_association" "tfappvnet" { 74 | subnet_id = azurerm_subnet.tfappvnet.id 75 | route_table_id = azurerm_route_table.nattable.id 76 | } 77 | 78 | resource "azurerm_route_table" "nattable" { 79 | name = "${var.resource.prefix}-natroutetable" 80 | location = var.resource.location 81 | resource_group_name = azurerm_resource_group.tfrg.name 82 | 83 | route { 84 | name = "natrule1" 85 | address_prefix = "0.0.0.0/0" 86 | next_hop_type = "VirtualAppliance" 87 | next_hop_in_ip_address = "10.0.0.10" 88 | } 89 | } 90 | */ 91 | # ASG 92 | resource "azurerm_application_security_group" "tfwebasg" { 93 | name = "tf-webasg" 94 | location = azurerm_resource_group.tfrg.location 95 | resource_group_name = azurerm_resource_group.tfrg.name 96 | } 97 | 98 | resource "azurerm_application_security_group" "tfjboxasg" { 99 | name = "tf-jboxasg" 100 | location = azurerm_resource_group.tfrg.location 101 | resource_group_name = azurerm_resource_group.tfrg.name 102 | } 103 | 104 | resource "azurerm_application_security_group" "tfappasg" { 105 | name = "tf-appasg" 106 | location = azurerm_resource_group.tfrg.location 107 | resource_group_name = azurerm_resource_group.tfrg.name 108 | } 109 | -------------------------------------------------------------------------------- /gw-nat.tf: -------------------------------------------------------------------------------- 1 | # Reference: 2 | # https://docs.microsoft.com/en-us/azure/virtual-network/nat-overview 3 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway 4 | 5 | # Create outbound public IP 6 | resource "azurerm_public_ip" "tfnatip" { 7 | name = "${var.resource.prefix}-natip" 8 | location = var.resource.location 9 | resource_group_name = azurerm_resource_group.tfrg.name 10 | allocation_method = "Static" 11 | sku = "Standard" 12 | #zone = ["1"] 13 | } 14 | 15 | resource "azurerm_nat_gateway" "tfnatgw" { 16 | name = "${var.resource.prefix}-natgw" 17 | location = var.resource.location 18 | resource_group_name = azurerm_resource_group.tfrg.name 19 | ##public_ip_address_ids = [azurerm_public_ip.tfnatip.id] 20 | sku_name = "Standard" 21 | idle_timeout_in_minutes = 10 22 | 23 | depends_on = [azurerm_public_ip.tfnatip] 24 | } 25 | 26 | resource "azurerm_subnet_nat_gateway_association" "tfnatgw" { 27 | subnet_id = azurerm_subnet.tfappvnet.id 28 | nat_gateway_id = azurerm_nat_gateway.tfnatgw.id 29 | } 30 | 31 | resource "azurerm_nat_gateway_public_ip_association" "tfnatgw" { 32 | nat_gateway_id = azurerm_nat_gateway.tfnatgw.id 33 | public_ip_address_id = azurerm_public_ip.tfnatip.id 34 | } 35 | 36 | # natgw_public_ip_prefix 37 | output "natgw_public_ip" { 38 | value = azurerm_public_ip.tfnatip.ip_address 39 | } 40 | 41 | -------------------------------------------------------------------------------- /images/terraform_aks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iljoong/azure-terraform/fb6128cc10ac56b0296f1b2f95b555df94e6c9a2/images/terraform_aks.png -------------------------------------------------------------------------------- /images/terraform_azure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iljoong/azure-terraform/fb6128cc10ac56b0296f1b2f95b555df94e6c9a2/images/terraform_azure.png -------------------------------------------------------------------------------- /images/terraform_azure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iljoong/azure-terraform/fb6128cc10ac56b0296f1b2f95b555df94e6c9a2/images/terraform_azure2.png -------------------------------------------------------------------------------- /images/terraform_azure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iljoong/azure-terraform/fb6128cc10ac56b0296f1b2f95b555df94e6c9a2/images/terraform_azure3.png -------------------------------------------------------------------------------- /images/terraform_azure4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iljoong/azure-terraform/fb6128cc10ac56b0296f1b2f95b555df94e6c9a2/images/terraform_azure4.png -------------------------------------------------------------------------------- /packer/README.md: -------------------------------------------------------------------------------- 1 | # Packer 2 | 3 | > It is recommended to use azure-cli or managed service identity (MSI) for authentication. See packer [document](https://www.packer.io/plugins/builders/azure) for more information. 4 | 5 | Create nginx image 6 | 7 | ``` 8 | packer inspect lx_nginx.json 9 | 10 | packer build -var rgname=demo-rg -var imagename=nximg001 lx_nginx.json 11 | ``` 12 | -------------------------------------------------------------------------------- /packer/lx_nginx.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "rgname": "test-images", 4 | "imagename": "nximg001" 5 | }, 6 | "builders": [{ 7 | "type": "azure-arm", 8 | 9 | "use_azure_cli_auth": true, 10 | 11 | "managed_image_resource_group_name": "{{user `rgname`}}", 12 | "managed_image_name": "{{user `imagename`}}", 13 | 14 | "os_type": "Linux", 15 | "image_publisher": "Canonical", 16 | "image_offer": "UbuntuServer", 17 | "image_sku": "18.04-LTS", 18 | 19 | "azure_tags": { 20 | "image": "demo" 21 | }, 22 | 23 | "location": "koreacentral", 24 | "vm_size": "Standard_D2as_v4" 25 | }], 26 | "provisioners": [{ 27 | "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'", 28 | "inline": [ 29 | "apt-get update", 30 | "apt-get upgrade -y", 31 | "apt-get -y install nginx", 32 | 33 | "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync" 34 | ], 35 | "inline_shebang": "/bin/sh -x", 36 | "type": "shell" 37 | }] 38 | } -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | # Cloud-Init 2 | 3 | __cloud-init__ is used instead of __custom script__ VM Extension for this demo. 4 | 5 | See [document](https://cloudinit.readthedocs.io/en/latest/) for more information. 6 | 7 | -------------------------------------------------------------------------------- /script/cloud-init.txt: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | package_upgrade: true 3 | packages: 4 | - nginx 5 | write_files: 6 | - owner: www-data:www-data 7 | - path: /var/www/html/index.html 8 | content: | 9 | 10 | 11 | 12 | Azure Terraform Demo (NGINX) 13 | 18 | 19 | 20 |

Azure Terraform Demo (NGINX)

21 |

If you see this page, the nginx web server is successfully installed and 22 | working. Further configuration is required.

23 | 24 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # azure service principal info 2 | variable azure { 3 | default = { 4 | subscription_id = "_add_here_" 5 | # client_id or app_id 6 | client_id = "_add_here_" 7 | client_secret = "_add_here_" 8 | # tenant_id or directory_id 9 | tenant_id = "_add_here_" 10 | } 11 | } 12 | 13 | variable vm { 14 | default = { 15 | admin_username = "_add_here_" 16 | admin_password = "_add_here_" 17 | admin_keydata = "_add_here_" 18 | size = "Standard_D2s_v3" 19 | appcount = 1 20 | webcount = 1 21 | osimageuri = "_add_here_" 22 | } 23 | } 24 | 25 | variable resource { 26 | default = { 27 | prefix = "tfdemo" 28 | location = "koreacentral" 29 | tag = "demo" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vm-app.tf: -------------------------------------------------------------------------------- 1 | # Create Network Security Group and rule 2 | resource "azurerm_network_security_group" "tfappnsg" { 3 | name = "${var.resource.prefix}-appnsg" 4 | location = var.resource.location 5 | resource_group_name = azurerm_resource_group.tfrg.name 6 | 7 | security_rule { 8 | name = "DENY_VNET" 9 | priority = 4096 10 | direction = "Inbound" 11 | access = "Deny" 12 | protocol = "*" 13 | source_port_range = "*" 14 | destination_port_range = "*" 15 | source_address_prefix = "VirtualNetwork" 16 | destination_address_prefix = "VirtualNetwork" 17 | } 18 | 19 | security_rule { 20 | name = "ALLOW_LB" 21 | priority = 4095 22 | direction = "Inbound" 23 | access = "Allow" 24 | protocol = "*" 25 | source_port_range = "*" 26 | destination_port_range = "*" 27 | source_address_prefix = "AzureLoadBalancer" 28 | destination_address_prefix = "*" 29 | } 30 | 31 | security_rule { 32 | name = "SSH_VNET" 33 | priority = 4000 34 | direction = "Inbound" 35 | access = "Allow" 36 | protocol = "Tcp" 37 | source_port_range = "*" 38 | destination_port_range = "22" 39 | source_address_prefix = "VirtualNetwork" 40 | destination_address_prefix = "*" 41 | } 42 | 43 | security_rule { 44 | name = "HTTP_VNET" 45 | priority = 1000 46 | direction = "Inbound" 47 | access = "Allow" 48 | protocol = "Tcp" 49 | source_port_range = "*" 50 | destination_port_range = "80" 51 | 52 | source_application_security_group_ids = [azurerm_application_security_group.tfwebasg.id] 53 | destination_application_security_group_ids = [azurerm_application_security_group.tfappasg.id] 54 | } 55 | 56 | tags = { 57 | environment = var.resource.tag 58 | } 59 | } 60 | 61 | # Create network interface 62 | resource "azurerm_network_interface" "tfappnic" { 63 | count = var.vm.appcount 64 | name = "${var.resource.prefix}-appnic${count.index}" 65 | location = var.resource.location 66 | resource_group_name = azurerm_resource_group.tfrg.name 67 | 68 | #-network_security_group_id = azurerm_network_security_group.tfappnsg.id 69 | 70 | ip_configuration { 71 | name = "${var.resource.prefix}-appnic-conf${count.index}" 72 | subnet_id = azurerm_subnet.tfappvnet.id 73 | 74 | #private_ip_address_allocation = "dynamic" 75 | private_ip_address_allocation = "Static" 76 | private_ip_address = format("10.0.2.%d", count.index + 4) 77 | } 78 | 79 | tags = { 80 | environment = var.resource.tag 81 | } 82 | } 83 | 84 | resource "azurerm_network_interface_security_group_association" "tfappnic" { 85 | count = var.vm.appcount 86 | network_interface_id = azurerm_network_interface.tfappnic[count.index].id 87 | network_security_group_id = azurerm_network_security_group.tfappnsg.id 88 | } 89 | 90 | resource "azurerm_network_interface_backend_address_pool_association" "tfapppoolassc" { 91 | count = var.vm.appcount 92 | network_interface_id = element(azurerm_network_interface.tfappnic.*.id, count.index) 93 | ip_configuration_name = "${var.resource.prefix}-appnic-conf${count.index}" 94 | backend_address_pool_id = azurerm_lb_backend_address_pool.tfapplbbackendpool.id 95 | } 96 | 97 | resource "azurerm_network_interface_application_security_group_association" "tfappsecassc" { 98 | count = var.vm.appcount 99 | network_interface_id = element(azurerm_network_interface.tfappnic.*.id, count.index) 100 | #-ip_configuration_name = "${var.resource.prefix}-appnic-conf${count.index}" 101 | application_security_group_id = azurerm_application_security_group.tfappasg.id 102 | } 103 | 104 | resource "azurerm_availability_set" "tfappavset" { 105 | name = "${var.resource.prefix}-appavset" 106 | location = var.resource.location 107 | resource_group_name = azurerm_resource_group.tfrg.name 108 | managed = "true" 109 | platform_fault_domain_count = 2 # default 3 cannot be used 110 | 111 | tags = { 112 | environment = var.resource.tag 113 | } 114 | } 115 | 116 | # Create virtual machine 117 | # https://www.terraform.io/docs/providers/azurerm/r/linux_virtual_machine.html 118 | resource "azurerm_linux_virtual_machine" "tfappvm" { 119 | count = var.vm.appcount 120 | name = "${var.resource.prefix}appvm${count.index}" 121 | location = var.resource.location 122 | resource_group_name = azurerm_resource_group.tfrg.name 123 | network_interface_ids = [azurerm_network_interface.tfappnic[count.index].id] 124 | size = var.vm.size 125 | availability_set_id = azurerm_availability_set.tfappavset.id 126 | 127 | computer_name = format("tfappvm%03d", count.index + 1) 128 | admin_username = var.vm.admin_username 129 | admin_password = var.vm.admin_password 130 | disable_password_authentication = false 131 | 132 | custom_data = base64encode( file("./script/cloud-init.txt") ) 133 | 134 | os_disk { 135 | name = format("%s-app-%03d-osdisk", var.resource.prefix, count.index + 1) 136 | caching = "ReadWrite" 137 | storage_account_type = "Premium_LRS" 138 | 139 | disk_size_gb = "64" # increase default os disk 140 | } 141 | 142 | //source_image_id = "${var.vm.osimageuri}" 143 | source_image_reference { 144 | publisher = "Canonical" 145 | offer = "UbuntuServer" 146 | sku = "18.04-LTS" 147 | version = "latest" 148 | } 149 | 150 | tags = { 151 | environment = var.resource.tag 152 | } 153 | } 154 | 155 | resource "azurerm_lb" "tfapplb" { 156 | name = "${var.resource.prefix}applb" 157 | location = var.resource.location 158 | resource_group_name = azurerm_resource_group.tfrg.name 159 | sku = "Standard" 160 | 161 | frontend_ip_configuration { 162 | name = "ApplbIPAddress" 163 | subnet_id = azurerm_subnet.tfappvnet.id 164 | private_ip_address = "10.0.2.100" 165 | private_ip_address_allocation = "Static" 166 | } 167 | } 168 | 169 | resource "azurerm_lb_backend_address_pool" "tfapplbbackendpool" { 170 | ##resource_group_name = azurerm_resource_group.tfrg.name 171 | loadbalancer_id = azurerm_lb.tfapplb.id 172 | name = "AppLBBackEndAddressPool" 173 | } 174 | 175 | resource "azurerm_lb_rule" "applb_rule" { 176 | loadbalancer_id = azurerm_lb.tfapplb.id 177 | name = "LBRule" 178 | protocol = "Tcp" 179 | frontend_port = 80 180 | backend_port = 80 181 | frontend_ip_configuration_name = "ApplbIPAddress" 182 | enable_floating_ip = false 183 | backend_address_pool_ids = [azurerm_lb_backend_address_pool.tfapplbbackendpool.id] 184 | idle_timeout_in_minutes = 5 185 | probe_id = azurerm_lb_probe.applb_probe.id 186 | depends_on = [azurerm_lb_probe.applb_probe] 187 | } 188 | 189 | resource "azurerm_lb_probe" "applb_probe" { 190 | loadbalancer_id = azurerm_lb.tfapplb.id 191 | name = "tcpProbe" 192 | protocol = "Tcp" 193 | port = 80 194 | interval_in_seconds = 5 195 | number_of_probes = 2 196 | } 197 | -------------------------------------------------------------------------------- /vm-blob.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_storage_account" "tfblob" { 2 | name = "${var.resource.prefix}blobacct" 3 | resource_group_name = azurerm_resource_group.tfrg.name 4 | location = var.resource.location 5 | account_tier = "Standard" 6 | account_replication_type = "LRS" 7 | 8 | tags = { 9 | environment = var.resource.tag 10 | } 11 | } 12 | 13 | resource "azurerm_storage_container" "tfblob" { 14 | name = "docs" 15 | storage_account_name = azurerm_storage_account.tfblob.name 16 | container_access_type = "private" 17 | } 18 | 19 | resource "azurerm_storage_blob" "tfblob" { 20 | name = "hello.txt" 21 | 22 | storage_account_name = azurerm_storage_account.tfblob.name 23 | storage_container_name = azurerm_storage_container.tfblob.name 24 | 25 | type = "Block" 26 | source = "./blob/hello.txt" 27 | } -------------------------------------------------------------------------------- /vm-jbox.tf: -------------------------------------------------------------------------------- 1 | # Create Network Security Group and rule 2 | resource "azurerm_network_security_group" "tfjboxnsg" { 3 | name = "${var.resource.prefix}-jboxnsg" 4 | location = var.resource.location 5 | resource_group_name = azurerm_resource_group.tfrg.name 6 | 7 | security_rule { 8 | name = "SSH" 9 | priority = 1001 10 | direction = "Inbound" 11 | access = "Allow" 12 | protocol = "Tcp" 13 | source_port_range = "*" 14 | destination_port_range = "22" 15 | source_address_prefix = "*" # add source addr 16 | destination_address_prefix = "*" 17 | } 18 | 19 | tags = { 20 | environment = var.resource.tag 21 | } 22 | } 23 | 24 | # Create public IPs 25 | resource "azurerm_public_ip" "tfjboxip" { 26 | name = "${var.resource.prefix}-jboxip" 27 | location = var.resource.location 28 | resource_group_name = azurerm_resource_group.tfrg.name 29 | allocation_method = "Static" 30 | 31 | tags = { 32 | environment = var.resource.tag 33 | } 34 | } 35 | 36 | # Create network interface 37 | resource "azurerm_network_interface" "tfjboxnic" { 38 | name = "${var.resource.prefix}-jboxnic" 39 | location = var.resource.location 40 | resource_group_name = azurerm_resource_group.tfrg.name 41 | #-network_security_group_id = azurerm_network_security_group.tfjboxnsg.id 42 | 43 | ip_configuration { 44 | name = "${var.resource.prefix}-jboxnic-conf" 45 | subnet_id = azurerm_subnet.tfjboxvnet.id 46 | private_ip_address_allocation = "Dynamic" 47 | public_ip_address_id = azurerm_public_ip.tfjboxip.id 48 | } 49 | 50 | tags = { 51 | environment = var.resource.tag 52 | } 53 | } 54 | 55 | resource "azurerm_network_interface_security_group_association" "tfjboxnic" { 56 | network_interface_id = azurerm_network_interface.tfjboxnic.id 57 | network_security_group_id = azurerm_network_security_group.tfjboxnsg.id 58 | } 59 | 60 | # Create virtual machine 61 | resource "azurerm_linux_virtual_machine" "tfjboxvm" { 62 | name = "${var.resource.prefix}jboxvm" 63 | location = var.resource.location 64 | resource_group_name = azurerm_resource_group.tfrg.name 65 | network_interface_ids = [azurerm_network_interface.tfjboxnic.id] 66 | size = "Standard_DS1_v2" 67 | 68 | computer_name = "tfjobxvm" 69 | admin_username = var.vm.admin_username 70 | admin_password = var.vm.admin_password 71 | disable_password_authentication = false 72 | 73 | os_disk { 74 | name = "${var.resource.prefix}-ftosdisk-jbox" 75 | caching = "ReadWrite" 76 | storage_account_type = "Premium_LRS" 77 | } 78 | 79 | source_image_reference { 80 | publisher = "Canonical" 81 | offer = "UbuntuServer" 82 | sku = "18.04-LTS" 83 | version = "latest" 84 | } 85 | 86 | tags = { 87 | environment = var.resource.tag 88 | } 89 | } 90 | 91 | # public_ip must be 'static' in order to print output properly 92 | output "jumphost_ip" { 93 | value = azurerm_public_ip.tfjboxip.ip_address 94 | } 95 | 96 | -------------------------------------------------------------------------------- /vm-web.tf: -------------------------------------------------------------------------------- 1 | # Create Network Security Group and rule 2 | resource "azurerm_network_security_group" "tfwebnsg" { 3 | name = "${var.resource.prefix}-webnsg" 4 | location = var.resource.location 5 | resource_group_name = azurerm_resource_group.tfrg.name 6 | 7 | security_rule { 8 | name = "web" 9 | priority = 1001 10 | direction = "Inbound" 11 | access = "Allow" 12 | protocol = "Tcp" 13 | source_port_range = "*" 14 | destination_port_range = "80" 15 | source_address_prefix = "*" 16 | destination_address_prefix = "*" 17 | } 18 | 19 | tags = { 20 | environment = var.resource.tag 21 | } 22 | } 23 | 24 | # Create network interface 25 | resource "azurerm_network_interface" "tfwebnic" { 26 | count = var.vm.webcount 27 | name = "${var.resource.prefix}-webnic${count.index}" 28 | location = var.resource.location 29 | resource_group_name = azurerm_resource_group.tfrg.name 30 | #-network_security_group_id = azurerm_network_security_group.tfwebnsg.id 31 | 32 | ip_configuration { 33 | name = "${var.resource.prefix}-webnic-config${count.index}" 34 | subnet_id = azurerm_subnet.tfwebvnet.id 35 | 36 | #private_ip_address_allocation = "dynamic" 37 | private_ip_address_allocation = "Static" 38 | private_ip_address = format("10.0.1.%d", count.index + 4) 39 | } 40 | 41 | tags = { 42 | environment = var.resource.tag 43 | } 44 | } 45 | 46 | resource "azurerm_network_interface_security_group_association" "tfwebnic" { 47 | count = var.vm.webcount 48 | network_interface_id = azurerm_network_interface.tfwebnic[count.index].id 49 | network_security_group_id = azurerm_network_security_group.tfwebnsg.id 50 | } 51 | 52 | resource "azurerm_network_interface_backend_address_pool_association" "tfwebpoolassc" { 53 | count = var.vm.webcount 54 | network_interface_id = element(azurerm_network_interface.tfwebnic.*.id, count.index) 55 | ip_configuration_name = "${var.resource.prefix}-webnic-config${count.index}" 56 | backend_address_pool_id = azurerm_lb_backend_address_pool.tflbbackendpool.id 57 | } 58 | 59 | resource "azurerm_network_interface_nat_rule_association" "tfnatruleassc" { 60 | count = var.vm.webcount 61 | network_interface_id = element(azurerm_network_interface.tfwebnic.*.id, count.index) 62 | ip_configuration_name = "${var.resource.prefix}-webnic-config${count.index}" 63 | nat_rule_id = element(azurerm_lb_nat_rule.lbnatrule.*.id, count.index) 64 | } 65 | 66 | resource "azurerm_network_interface_application_security_group_association" "tfwebsecassc" { 67 | count = var.vm.webcount 68 | network_interface_id = element(azurerm_network_interface.tfwebnic.*.id, count.index) 69 | #-ip_configuration_name = "${var.resource.prefix}-webnic-config${count.index}" 70 | application_security_group_id = azurerm_application_security_group.tfwebasg.id 71 | } 72 | 73 | resource "azurerm_availability_set" "tfwebavset" { 74 | name = "${var.resource.prefix}-webavset" 75 | location = var.resource.location 76 | resource_group_name = azurerm_resource_group.tfrg.name 77 | managed = "true" 78 | platform_fault_domain_count = 2 # default 3 not working in some regions like Korea 79 | 80 | tags = { 81 | environment = var.resource.tag 82 | } 83 | } 84 | 85 | # Create virtual machine 86 | # https://www.terraform.io/docs/providers/azurerm/r/linux_virtual_machine.html 87 | resource "azurerm_linux_virtual_machine" "tfwebvm" { 88 | count = var.vm.webcount 89 | name = "${var.resource.prefix}webvm${count.index}" 90 | location = var.resource.location 91 | resource_group_name = azurerm_resource_group.tfrg.name 92 | network_interface_ids = [azurerm_network_interface.tfwebnic[count.index].id] 93 | size = var.vm.size 94 | availability_set_id = azurerm_availability_set.tfwebavset.id 95 | 96 | computer_name = format("tfwebvm%03d", count.index + 1) 97 | admin_username = var.vm.admin_username 98 | admin_password = var.vm.admin_password 99 | disable_password_authentication = false 100 | 101 | custom_data = base64encode( file("./script/cloud-init.txt") ) 102 | 103 | os_disk { 104 | name = format("%s-web-%03d-osdisk", var.resource.prefix, count.index + 1) 105 | caching = "ReadWrite" 106 | storage_account_type = "Premium_LRS" 107 | } 108 | 109 | //source_image_id = "${var.vm.osimageuri}" 110 | source_image_reference { 111 | publisher = "Canonical" 112 | offer = "UbuntuServer" 113 | sku = "18.04-LTS" 114 | version = "latest" 115 | } 116 | 117 | tags = { 118 | environment = var.resource.tag 119 | } 120 | } 121 | 122 | resource "azurerm_public_ip" "tflbpip" { 123 | name = "${var.resource.prefix}-flbpip" 124 | location = var.resource.location 125 | resource_group_name = azurerm_resource_group.tfrg.name 126 | allocation_method = "Static" 127 | sku = "Standard" 128 | #zones = ["1"] 129 | } 130 | 131 | resource "azurerm_lb" "tflb" { 132 | name = "${var.resource.prefix}lb" 133 | location = var.resource.location 134 | resource_group_name = azurerm_resource_group.tfrg.name 135 | sku = "Standard" 136 | 137 | frontend_ip_configuration { 138 | name = "PublicIPAddress" 139 | public_ip_address_id = azurerm_public_ip.tflbpip.id 140 | } 141 | } 142 | 143 | resource "azurerm_lb_backend_address_pool" "tflbbackendpool" { 144 | ##resource_group_name = azurerm_resource_group.tfrg.name 145 | loadbalancer_id = azurerm_lb.tflb.id 146 | name = "BackEndAddressPool" 147 | } 148 | 149 | resource "azurerm_lb_nat_rule" "lbnatrule" { 150 | count = var.vm.webcount 151 | resource_group_name = azurerm_resource_group.tfrg.name 152 | loadbalancer_id = azurerm_lb.tflb.id 153 | name = "ssh-${count.index}" 154 | protocol = "Tcp" 155 | frontend_port = "5000${count.index + 1}" 156 | backend_port = 22 157 | frontend_ip_configuration_name = "PublicIPAddress" # "${azurerm_lb.tflb.frontend_ip_configuration.name}" not working 158 | } 159 | 160 | resource "azurerm_lb_rule" "lb_rule" { 161 | loadbalancer_id = azurerm_lb.tflb.id 162 | name = "LBRule" 163 | protocol = "Tcp" 164 | frontend_port = 80 165 | backend_port = 80 166 | frontend_ip_configuration_name = "PublicIPAddress" 167 | enable_floating_ip = false 168 | backend_address_pool_ids = [azurerm_lb_backend_address_pool.tflbbackendpool.id] 169 | idle_timeout_in_minutes = 5 170 | probe_id = azurerm_lb_probe.lb_probe.id 171 | depends_on = [azurerm_lb_probe.lb_probe] 172 | } 173 | 174 | resource "azurerm_lb_probe" "lb_probe" { 175 | loadbalancer_id = azurerm_lb.tflb.id 176 | name = "tcpProbe" 177 | protocol = "Tcp" 178 | port = 80 179 | interval_in_seconds = 5 180 | number_of_probes = 2 181 | } 182 | 183 | output "weblb_pip" { 184 | value = azurerm_public_ip.tflbpip.*.ip_address 185 | } 186 | -------------------------------------------------------------------------------- /vmss/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | > Revised for _Terraform AzureRM v3.25.0 with Terraform v1.3.1_ 4 | 5 | Sample terraform script for VMM. 6 | 7 | For large application deployment, it is recommend to deploy VMSS instead of creating multiple VMs. -------------------------------------------------------------------------------- /vmss/common.tf: -------------------------------------------------------------------------------- 1 | # Configure the Microsoft Azure Provider 2 | terraform { 3 | required_providers { 4 | azurerm = { 5 | source = "hashicorp/azurerm" 6 | # use latest 7 | #version = "=3.25.0" 8 | } 9 | } 10 | } 11 | 12 | provider "azurerm" { 13 | /*subscription_id = var.azure.subscription_id 14 | client_id = var.azure.client_id 15 | client_secret = var.azure.client_secret 16 | tenant_id = var.azure.tenant_id*/ 17 | 18 | features {} 19 | } 20 | 21 | 22 | # Create a resource group if it doesn’t exist 23 | resource "azurerm_resource_group" "tfrg" { 24 | name = "${var.resource.prefix}-rg" 25 | location = var.resource.location 26 | 27 | tags = { 28 | environment = var.resource.tag 29 | } 30 | } 31 | 32 | # Create virtual network 33 | resource "azurerm_virtual_network" "tfvnet" { 34 | name = "${var.resource.prefix}-vnet" 35 | address_space = ["10.0.0.0/16"] 36 | location = var.resource.location 37 | resource_group_name = azurerm_resource_group.tfrg.name 38 | 39 | tags = { 40 | environment = var.resource.tag 41 | } 42 | } 43 | 44 | resource "azurerm_subnet" "tfwebvnet" { 45 | name = "web-subnet" 46 | virtual_network_name = azurerm_virtual_network.tfvnet.name 47 | resource_group_name = azurerm_resource_group.tfrg.name 48 | address_prefixes = ["10.0.1.0/24"] 49 | } 50 | 51 | 52 | resource "azurerm_network_security_group" "tfwebnsg" { 53 | name = "${var.resource.prefix}-app-nsg" 54 | location = var.resource.location 55 | resource_group_name = azurerm_resource_group.tfrg.name 56 | 57 | security_rule { 58 | name = "SSH" 59 | priority = 1000 60 | direction = "Inbound" 61 | access = "Allow" 62 | protocol = "Tcp" 63 | source_port_range = "*" 64 | destination_port_range = "22" 65 | source_address_prefix = "*" 66 | destination_address_prefix = "*" 67 | } 68 | 69 | security_rule { 70 | name = "HTTP" 71 | priority = 1100 72 | direction = "Inbound" 73 | access = "Allow" 74 | protocol = "Tcp" 75 | source_port_range = "*" 76 | destination_port_range = "80" 77 | source_address_prefix = "*" 78 | destination_address_prefix = "*" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /vmss/variables.tf: -------------------------------------------------------------------------------- 1 | # azure service principal info 2 | variable azure { 3 | default = { 4 | subscription_id = "_add_here_" 5 | # client_id or app_id 6 | client_id = "_add_here_" 7 | client_secret = "_add_here_" 8 | # tenant_id or directory_id 9 | tenant_id = "_add_here_" 10 | } 11 | } 12 | 13 | variable vm { 14 | default = { 15 | admin_username = "_add_here_" 16 | admin_password = "_add_here_" 17 | admin_keydata = "_add_here_" 18 | size = "Standard_D2s_v3" 19 | webcount = 1 20 | osimageuri = "_add_here_" 21 | } 22 | } 23 | 24 | variable resource { 25 | default = { 26 | prefix = "tfdemo" 27 | location = "koreasouth" 28 | tag = "demo" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vmss/vmss.tf: -------------------------------------------------------------------------------- 1 | # VMSS 2 | #https://www.terraform.io/docs/providers/azurerm/r/linux_virtual_machine_scale_set.html 3 | 4 | resource "azurerm_linux_virtual_machine_scale_set" "tfrg" { 5 | 6 | name = "${var.resource.prefix}webvm" 7 | location = var.resource.location 8 | resource_group_name = azurerm_resource_group.tfrg.name 9 | 10 | upgrade_mode = "Automatic" 11 | /*automatic_os_upgrade_policy = { 12 | disable_automatic_rollback = true 13 | enable_automatic_os_upgrade = false 14 | }*/ 15 | 16 | overprovision = false 17 | 18 | sku = var.vm.size 19 | instances = var.vm.webcount 20 | 21 | computer_name_prefix = "${var.resource.prefix}webvm" 22 | admin_username = var.vm.admin_username 23 | admin_password = var.vm.admin_password 24 | disable_password_authentication = false 25 | 26 | custom_data = base64encode( file("../script/cloud-init.txt") ) 27 | 28 | os_disk { 29 | caching = "ReadWrite" 30 | storage_account_type = "Premium_LRS" 31 | //disk_size_gb = 128 32 | } 33 | 34 | //source_image_id = var.vm.osimageuri 35 | source_image_reference { 36 | publisher = "Canonical" 37 | offer = "UbuntuServer" 38 | sku = "18.04-LTS" 39 | version = "latest" 40 | } 41 | 42 | network_interface { 43 | name = "networkinterface" 44 | primary = true 45 | network_security_group_id = azurerm_network_security_group.tfwebnsg.id 46 | 47 | ip_configuration { 48 | name = "ipconfig" 49 | primary = true 50 | subnet_id = azurerm_subnet.tfwebvnet.id 51 | load_balancer_backend_address_pool_ids = [azurerm_lb_backend_address_pool.vmss.id] 52 | load_balancer_inbound_nat_rules_ids = [azurerm_lb_nat_pool.vmss.id] 53 | } 54 | } 55 | } 56 | 57 | # Public LB 58 | resource "azurerm_public_ip" "vmss" { 59 | name = "vmss-pip" 60 | location = var.resource.location 61 | resource_group_name = azurerm_resource_group.tfrg.name 62 | allocation_method = "Static" 63 | 64 | sku = "Standard" 65 | } 66 | 67 | resource "azurerm_lb" "vmss" { 68 | name = "vmss-lb" 69 | location = var.resource.location 70 | resource_group_name = azurerm_resource_group.tfrg.name 71 | 72 | sku = "Standard" 73 | 74 | frontend_ip_configuration { 75 | name = "vmss-ipconfig" 76 | public_ip_address_id = azurerm_public_ip.vmss.id 77 | } 78 | } 79 | 80 | resource "azurerm_lb_rule" "vmss" { 81 | loadbalancer_id = azurerm_lb.vmss.id 82 | name = "vmss-lbrule" 83 | protocol = "Tcp" 84 | frontend_port = 80 85 | backend_port = 80 86 | frontend_ip_configuration_name = "vmss-ipconfig" 87 | backend_address_pool_ids = [azurerm_lb_backend_address_pool.vmss.id] 88 | probe_id = azurerm_lb_probe.vmss.id 89 | } 90 | 91 | resource "azurerm_lb_backend_address_pool" "vmss" { 92 | loadbalancer_id = azurerm_lb.vmss.id 93 | name = "vmss-bepool" 94 | } 95 | 96 | resource "azurerm_lb_nat_pool" "vmss" { 97 | resource_group_name = azurerm_resource_group.tfrg.name 98 | name = "SSH" 99 | loadbalancer_id = azurerm_lb.vmss.id 100 | protocol = "Tcp" 101 | frontend_port_start = 50000 102 | frontend_port_end = 50119 103 | backend_port = 22 104 | frontend_ip_configuration_name = "vmss-ipconfig" 105 | } 106 | 107 | resource "azurerm_lb_probe" "vmss" { 108 | loadbalancer_id = azurerm_lb.vmss.id 109 | name = "healthprobe" 110 | protocol = "Tcp" 111 | port = 80 112 | } 113 | 114 | 115 | output "vmss_ip_address" { 116 | value = azurerm_public_ip.vmss.ip_address 117 | } --------------------------------------------------------------------------------