diff --git a/.gitignore b/.gitignore index 41859c8..9ea47ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,25 @@ +# Variable files +terraform.tfvars + +### https://raw.github.com/github/gitignore/abad92dac5a4306f72242dae3bca6e277bce3615/Terraform.gitignore + # Compiled files *.tfstate *.tfstate.backup # Module directory .terraform/ + + +### https://raw.github.com/github/gitignore/abad92dac5a4306f72242dae3bca6e277bce3615/Global/Vim.gitignore + +# swap +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d1ca00f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4eac07c --- /dev/null +++ b/README.md @@ -0,0 +1,120 @@ +Deploys 1+ Virtual Machines to your provided VNet +================================================= + +This Terraform module deploys Virtual Machines in Azure with the following characteristics: + +- Ability to specify a simple string to get the latest marketplace image using `var.vm_os_simple` +- All VMs use managed disks +- Network Security Group (NSG) created and only if `var.remote_port` specified, then remote access rule created and opens this port to all nics +- VM nics attached to a single virtual network subnet of your choice (new or existing) via `var.vnet_subnet_id`. +- Public IP is created and attached only to the first VM's nic. Once into this VM, connection can be make to the other vms using the private ip on the VNet. + +Module Input Variables +---------------------- + +- `resource_group_name` - The name of the resource group in which the resources will be created. - default `compute` +- `location` - The Azure location where the resources will be created. +- `vnet_subnet_id` - The subnet id of the virtual network where the virtual machines will reside. +- `public_ip_dns` - Optional globally unique per datacenter region domain name label to apply to the public ip address. e.g. thisvar.varlocation.cloudapp.azure.com +- `admin_password` - The password of the administrator account. The password must comply with the complexity requirements for Azure virtual machines. +- `ssh_key` - The path on the local machine of the ssh public key in the case of a Linux deployment. - default `~/.ssh/id_rsa.pub` +- `remote_port` - Tcp port number to enable remote access to the nics on the vms via a NSG rule. Set to blank to disable. +- `admin_username` - The name of the administrator to access the machines part of the virtual machine scale set. - default `azureuser` +- `storage_account_type` - Defines the type of storage account to be created. Valid options are Standard_LRS, Standard_ZRS, Standard_GRS, Standard_RAGRS, Premium_LRS. - default `Premium_LRS` +- `vm_size` - The initial size of the virtual machine that will be deployed. - default `Standard_DS1_V2` +- `nb_instances` - The number of instances that will be initially deployed in the virtual machine scale set. - default `1` +- `vm_hostname` - local name of the VM. - default `myvm` +- `vm_os_simple`- This variable allows to use a simple name to reference Linux or Windows operating systems. When used, you can ommit the `vm_os_publisher`, `vm_os_offer` and `vm_os_sku`. The supported values are: "UbuntuServer", "WindowsServer", "RHEL", "openSUSE-Leap", "CentOS", "Debian", "CoreOS" and "SLES". +- `vm_os_id` - The ID of the image that you want to deploy if you are using a custom image. When used, you can ommit the `vm_os_publisher`, `vm_os_offer` and `vm_os_sku`. +- `vm_os_publisher` - The name of the publisher of the image that you want to deploy, for example "Canonical" if you are not using the `vm_os_simple` or `vm_os_id` variables. +- `vm_os_offer` - The name of the offer of the image that you want to deploy, for example "UbuntuServer" if you are not using the `vm_os_simple` or `vm_os_id` variables. +- `vm_os_sku` - The sku of the image that you want to deploy, for example "14.04.2-LTS" if you are not using the `vm_os_simple` or `vm_os_id` variables. +- `vm_os_version` - The version of the image that you want to deploy. - default `latest` +- `public_ip_address_allocation` - Defines how an IP address is assigned. Options are Static or Dynamic. - default `static` +- `tags` - A map of the tags to use on the resources that are deployed with this module. + +Usage +----- + +Provisions 2 Windows 2016 Datacenter Server VMs using `vm_os_simple` to a new VNet and opens up port 3389 for RDP access: + +```hcl + module "mycompute" { + source = "github.com/Azure/terraform-azurerm-compute" + resource_group_name = "mycompute" + location = "East US 2" + admin_password = ComplxP@ssw0rd! + vm_os_simple = "WindowsServer" + public_ip_dns = "mywindowsservers225" + remote_port = "3389" + nb_instances = 2 + vnet_subnet_id = "${module.network.vnet_subnets[0]}" + } + + module "network" { + source = "github.com/Azure/terraform-azurerm-network" + location = "East US 2" + resource_group_name = "mycompute" + } + + output "vm_public_name"{ + value = "${module.mycompute.public_ip_dns_name}" + } + + output "vm_public_ip" { + value = "${module.mycompute.public_ip_address}" + } + + output "vm_private_ips" { + value = "${module.mycompute.network_interface_private_ip}" + } +} + +``` +Provisions 2 Ubuntu 14.04 Server VMs using `vm_os_publisher`, `vm_os_offer` and `vm_os_sku` to a new VNet and opens up port 22 for SSH access with ~/.ssh/id_rsa.pub : + +```hcl +module "mycompute2" { + source = "github.com/Azure/terraform-azurerm-compute" + resource_group_name = "mycompute2" + location = "westus" + public_ip_dns = "myubuntuservers225" + remote_port = "22" + nb_instances = 2 + vm_os_publisher = "Canonical" + vm_os_offer = "UbuntuServer" + vm_os_sku = "14.04.2-LTS" + vnet_subnet_id = "${module.network.vnet_subnets[0]}" + tags = { + environment = "dev" + costcenter = "it" + } +} + module "network" { + source = "github.com/Azure/terraform-azurerm-network" + location = "westus" + resource_group_name = "mycompute2" + } + +``` + +Outputs +======= + +- `vm_ids`- Virtual machine ids created +- `network_security_group_id` - id of the security group provisioned +- `network_interface_ids` - ids of the vm nics provisoned +- `network_interface_private_ip` - private ip addresses of the vm nics +- `public_ip_id` - id of the public ip address provisoned +- `public_ip_address` - The actual ip address allocated for the resource. +- `public_ip_dns_name` - fqdn to connect to the first vm provisioned. +- `availability_set_id` - id of the availability set where the vms are provisioned. + +Authors +======= +Originally created by [David Tesar](http://github.com/dtzar) + +License +======= + +[MIT](LICENSE) diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..acb842e --- /dev/null +++ b/main.tf @@ -0,0 +1,137 @@ +provider "azurerm" { + version = "~> 0.1" +} + +module "os" { + source = "./os" + vm_os_simple = "${var.vm_os_simple}" +} + +resource "azurerm_resource_group" "vm" { + name = "${var.resource_group_name}" + location = "${var.location}" + tags = "${var.tags}" +} + +resource "azurerm_virtual_machine" "vm-linux" { + count = "${contains(list("${var.vm_os_simple}","${var.vm_os_offer}"), "WindowsServer") ? 0 : var.nb_instances}" + name = "${var.vm_hostname}${count.index}" + location = "${var.location}" + resource_group_name = "${azurerm_resource_group.vm.name}" + availability_set_id = "${azurerm_availability_set.vm.id}" + vm_size = "${var.vm_size}" + network_interface_ids = ["${element(azurerm_network_interface.vm.*.id, count.index)}"] + + storage_image_reference { + id = "${var.vm_os_id}" + publisher = "${coalesce(var.vm_os_publisher, module.os.calculated_value_os_publisher)}" + offer = "${coalesce(var.vm_os_offer, module.os.calculated_value_os_offer)}" + sku = "${coalesce(var.vm_os_sku, module.os.calculated_value_os_sku)}" + version = "${var.vm_os_version}" + } + + storage_os_disk { + name = "osdisk${count.index}" + create_option = "FromImage" + caching = "ReadWrite" + managed_disk_type = "${var.storage_account_type}" + } + + os_profile { + computer_name = "${var.vm_hostname}" + admin_username = "${var.admin_username}" + admin_password = "${var.admin_password}" + } + + os_profile_linux_config { + + disable_password_authentication = true + + ssh_keys { + path = "/home/${var.admin_username}/.ssh/authorized_keys" + key_data = "${file("${var.ssh_key}")}" + } + } +} + +resource "azurerm_virtual_machine" "vm-windows" { + count = "${contains(list("${var.vm_os_simple}","${var.vm_os_offer}"), "WindowsServer") ? var.nb_instances : 0}" + name = "${var.vm_hostname}${count.index}" + location = "${var.location}" + resource_group_name = "${azurerm_resource_group.vm.name}" + availability_set_id = "${azurerm_availability_set.vm.id}" + vm_size = "${var.vm_size}" + network_interface_ids = ["${element(azurerm_network_interface.vm.*.id, count.index)}"] + + storage_image_reference { + id = "${var.vm_os_id}" + publisher = "${coalesce(var.vm_os_publisher, module.os.calculated_value_os_publisher)}" + offer = "${coalesce(var.vm_os_offer, module.os.calculated_value_os_offer)}" + sku = "${coalesce(var.vm_os_sku, module.os.calculated_value_os_sku)}" + version = "${var.vm_os_version}" + } + + storage_os_disk { + name = "osdisk${count.index}" + create_option = "FromImage" + caching = "ReadWrite" + managed_disk_type = "${var.storage_account_type}" + } + + os_profile { + computer_name = "${var.vm_hostname}" + admin_username = "${var.admin_username}" + admin_password = "${var.admin_password}" + } +} + +resource "azurerm_availability_set" "vm" { + name = "${var.vm_hostname}avset" + location = "${azurerm_resource_group.vm.location}" + resource_group_name = "${azurerm_resource_group.vm.name}" + platform_fault_domain_count = 2 + platform_update_domain_count = 2 + managed = true +} + +resource "azurerm_public_ip" "vm" { + name = "${var.vm_hostname}-publicIP" + location = "${var.location}" + resource_group_name = "${azurerm_resource_group.vm.name}" + public_ip_address_allocation = "${var.public_ip_address_allocation}" + domain_name_label = "${var.public_ip_dns}" +} + +resource "azurerm_network_security_group" "vm" { + name = "remote-access-nsg" + location = "${azurerm_resource_group.vm.location}" + resource_group_name = "${azurerm_resource_group.vm.name}" + + security_rule { + name = "allow_remote_in_all" + description = "Allow remote protocol in from all locations" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "${var.remote_port}" + source_address_prefix = "*" + destination_address_prefix = "*" + } +} + +resource "azurerm_network_interface" "vm" { + count = "${var.nb_instances}" + name = "nic${count.index}" + location = "${azurerm_resource_group.vm.location}" + resource_group_name = "${azurerm_resource_group.vm.name}" + network_security_group_id = "${azurerm_network_security_group.vm.id}" + + ip_configuration { + name = "ipconfig${count.index}" + subnet_id = "${var.vnet_subnet_id}" + private_ip_address_allocation = "Dynamic" + public_ip_address_id = "${count.index == 0 ? azurerm_public_ip.vm.id : ""}" + } +} \ No newline at end of file diff --git a/os/outputs.tf b/os/outputs.tf new file mode 100644 index 0000000..f7938af --- /dev/null +++ b/os/outputs.tf @@ -0,0 +1,13 @@ + +output "calculated_value_os_publisher" { + value = "${element(split(",", lookup(var.standard_os, var.vm_os_simple, "")), 0)}" +} + +output "calculated_value_os_offer" { + value = "${element(split(",", lookup(var.standard_os, var.vm_os_simple, "")), 1)}" +} + +output "calculated_value_os_sku" { + value = "${element(split(",", lookup(var.standard_os, var.vm_os_simple, "")), 2)}" +} + diff --git a/os/variables.tf b/os/variables.tf new file mode 100644 index 0000000..4f27c3c --- /dev/null +++ b/os/variables.tf @@ -0,0 +1,19 @@ + +variable "vm_os_simple" { + default = "" +} + + +# Definition of the standard OS with "SimpleName" = "publisher,offer,sku" +variable "standard_os" { + default = { + "UbuntuServer" = "Canonical,UbuntuServer,16.04-LTS" + "WindowsServer" = "MicrosoftWindowsServer,WindowsServer,2016-Datacenter" + "RHEL" = "RedHat,RHEL,7.3" + "openSUSE-Leap" = "SUSE,openSUSE-Leap,42.2" + "CentOS" = "OpenLogic,CentOS,7.3" + "Debian" = "credativ,Debian,8" + "CoreOS" = "CoreOS,CoreOS,Stable" + "SLES" = "SUSE,SLES,12-SP2" + } +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..fc86435 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,39 @@ +output "vm_ids" { + description = "Virtual machine ids created." + value = "${concat(azurerm_virtual_machine.vm-windows.*.id, azurerm_virtual_machine.vm-linux.*.id)}" +} + +output "network_security_group_id" { + description = "id of the security group provisioned" + value = "${azurerm_network_security_group.vm.id}" +} + +output "network_interface_ids" { + description = "ids of the vm nics provisoned." + value = "${azurerm_network_interface.vm.*.id}" +} + +output "network_interface_private_ip"{ + description = "private ip addresses of the vm nics" + value = "${azurerm_network_interface.vm.*.private_ip_address}" +} + +output "public_ip_id" { + description = "id of the public ip address provisoned." + value = "${azurerm_public_ip.vm.id}" +} + +output "public_ip_address" { + description = "The actual ip address allocated for the resource." + value = "${azurerm_public_ip.vm.ip_address}" +} + +output "public_ip_dns_name" { + description = "fqdn to connect to the first vm provisioned." + value = "${azurerm_public_ip.vm.fqdn}" +} + +output "availability_set_id" { + description = "id of the availability set where the vms are provisioned." + value = "${azurerm_availability_set.vm.id}" +} \ No newline at end of file diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..559f761 --- /dev/null +++ b/variables.tf @@ -0,0 +1,99 @@ +variable "resource_group_name" { + description = "The name of the resource group in which the resources will be created" + default = "compute" +} + +variable "location" { + description = "The location/region where the virtual network is created. Changing this forces a new resource to be created." +} + +variable "vnet_subnet_id"{ + description = "The subnet id of the virtual network where the virtual machines will reside." +} + +variable "public_ip_dns" { + description = "Optional globally unique per datacenter region domain name label to apply to the public ip address. e.g. thisvar.varlocation.cloudapp.azure.com" + default = "" +} + +variable "admin_password" { + description = "The admin password to be used on the VMSS that will be deployed. The password must meet the complexity requirements of Azure" + default = "" +} + +variable "ssh_key" { + description = "Path to the public key to be used for ssh access to the VM. Only used with non-Windows vms and can be left as-is even if using Windows vms. If specifying a path to a certification on a Windows machine to provision a linux vm use the / in the path versus backslash. e.g. c:/home/id_rsa.pub" + default = "~/.ssh/id_rsa.pub" +} + +variable "remote_port"{ + description = "Remote tcp port to be used for access to the vms created via the nsg applied to the nics." + default = "" +} + +variable "admin_username" { + description = "The admin username of the VM that will be deployed" + default = "azureuser" +} + +variable "storage_account_type" { + description = "Defines the type of storage account to be created. Valid options are Standard_LRS, Standard_ZRS, Standard_GRS, Standard_RAGRS, Premium_LRS." + default = "Premium_LRS" +} + +variable "vm_size" { + description = "Specifies the size of the virtual machine." + default = "Standard_DS1_V2" +} + +variable "nb_instances" { + description = "Specify the number of vm instances" + default = "1" +} + +variable "vm_hostname" { + description = "local name of the VM" + default = "myvm" +} + +variable "vm_os_simple" { + description = "Specify UbuntuServer, WindowsServer, RHEL, openSUSE-Leap, CentOS, Debian, CoreOS and SLES to get the latest image version of the specified os. Do not provide this value if a custom value is used for vm_os_publisher, vm_os_offer, and vm_os_sku." + default = "" +} + +variable "vm_os_publisher" { + description = "The name of the publisher of the image that you want to deploy. Not necessary if using vm_os_simple." + default = "" +} + +variable "vm_os_offer" { + description = "The name of the offer of the image that you want to deploy. Not necessary if using vm_os_simple." + default = "" +} + +variable "vm_os_sku" { + description = "The sku of the image that you want to deploy. Not necessary if using vm_os_simple." + default = "" +} + +variable "vm_os_version" { + description = "The version of the image that you want to deploy." + default = "latest" +} + +variable "vm_os_id" { + description = "The ID of the image that you want to deploy if you are using a custom image." + default = "" +} + +variable "tags" { + type = "map" + description = "A map of the tags to use on the resources that are deployed with this module." + default = { + source = "terraform" + } +} +variable "public_ip_address_allocation" { + description = "Defines how an IP address is assigned. Options are Static or Dynamic." + default = "static" +}