diff --git a/data/data/azure/bootstrap/main.tf b/data/data/azure/bootstrap/main.tf new file mode 100644 index 00000000000..09e65359b3e --- /dev/null +++ b/data/data/azure/bootstrap/main.tf @@ -0,0 +1,160 @@ +locals { + bootstrap_nic_ip_configuration_name = "bootstrap-nic-ip" + ssh_nat_rule_id = var.ssh_nat_rule_id +} + +resource "random_string" "storage_suffix" { + length = 5 + upper = false + special = false + + keepers = { + # Generate a new ID only when a new resource group is defined + resource_group = var.resource_group_name + } +} + +resource "azurerm_storage_account" "ignition" { + name = "ignitiondata${random_string.storage_suffix.result}" + resource_group_name = var.resource_group_name + location = var.region + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_storage_account_sas" "ignition" { + connection_string = azurerm_storage_account.ignition.primary_connection_string + https_only = true + + resource_types { + service = false + container = false + object = true + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = timestamp() + expiry = timeadd(timestamp(), "24h") + + permissions { + read = true + list = true + create = false + add = false + delete = false + process = false + write = false + update = false + } +} + +resource "azurerm_storage_container" "ignition" { + resource_group_name = var.resource_group_name + name = "ignition" + storage_account_name = azurerm_storage_account.ignition.name + container_access_type = "private" +} + +resource "local_file" "ignition_bootstrap" { + content = var.ignition + filename = "${path.module}/ignition_bootstrap.ign" +} + +resource "azurerm_storage_blob" "ignition" { + name = "bootstrap.ign" + source = local_file.ignition_bootstrap.filename + resource_group_name = var.resource_group_name + storage_account_name = azurerm_storage_account.ignition.name + storage_container_name = azurerm_storage_container.ignition.name + type = "block" +} + +data "ignition_config" "redirect" { + replace { + source = "${azurerm_storage_blob.ignition.url}${data.azurerm_storage_account_sas.ignition.sas}" + } +} + +resource "azurerm_network_interface" "bootstrap" { + name = "${var.cluster_id}-bootstrap-nic" + location = var.region + resource_group_name = var.resource_group_name + + ip_configuration { + subnet_id = var.subnet_id + name = local.bootstrap_nic_ip_configuration_name + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_network_interface_nat_rule_association" "bootstrap_ssh" { + network_interface_id = azurerm_network_interface.bootstrap.id + ip_configuration_name = local.bootstrap_nic_ip_configuration_name + nat_rule_id = local.ssh_nat_rule_id +} + +resource "azurerm_network_interface_backend_address_pool_association" "public_lb_bootstrap" { + network_interface_id = azurerm_network_interface.bootstrap.id + backend_address_pool_id = var.elb_backend_pool_id + ip_configuration_name = local.bootstrap_nic_ip_configuration_name +} + +resource "azurerm_network_interface_backend_address_pool_association" "internal_lb_bootstrap" { + network_interface_id = azurerm_network_interface.bootstrap.id + backend_address_pool_id = var.ilb_backend_pool_id + ip_configuration_name = local.bootstrap_nic_ip_configuration_name +} + +data "azurerm_subscription" "current" { +} + +resource "azurerm_virtual_machine" "bootstrap" { + name = "${var.cluster_id}-bootstrap" + location = var.region + resource_group_name = var.resource_group_name + network_interface_ids = [azurerm_network_interface.bootstrap.id] + vm_size = var.vm_size + + delete_os_disk_on_termination = true + delete_data_disks_on_termination = true + + identity { + type = "UserAssigned" + identity_ids = [var.identity] + } + + storage_os_disk { + name = "${var.cluster_id}-bootstrap_OSDisk" # os disk name needs to match cluster-api convention + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Premium_LRS" + disk_size_gb = 100 + } + + storage_image_reference { + id = "${data.azurerm_subscription.current.id}${var.vm_image}" + } + + os_profile { + computer_name = "${var.cluster_id}-bootstrap-vm" + admin_username = "core" + admin_password = "P@ssword1234!" + custom_data = data.ignition_config.redirect.rendered + } + + os_profile_linux_config { + disable_password_authentication = false + } + + boot_diagnostics { + enabled = true + storage_uri = var.boot_diag_blob_endpoint + } +} + diff --git a/data/data/azure/bootstrap/variables.tf b/data/data/azure/bootstrap/variables.tf new file mode 100644 index 00000000000..e66cc45c709 --- /dev/null +++ b/data/data/azure/bootstrap/variables.tf @@ -0,0 +1,66 @@ +variable "vm_size" { + type = string + description = "The SKU ID for the bootstrap node." +} + +variable "vm_image" { + type = string + description = "The resource id of the vm image used for bootstrap." +} + +variable "region" { + type = string + description = "The region for the deployment." +} + +variable "resource_group_name" { + type = string + description = "The resource group name for the deployment." +} + +variable "cluster_id" { + type = string + description = "The identifier for the cluster." +} + +variable "identity" { + type = string + description = "The user assigned identity id for the vm." +} + +variable "ignition" { + type = string + description = "The content of the bootstrap ignition file." +} + +variable "subnet_id" { + type = string + description = "The subnet ID for the bootstrap node." +} + +variable "elb_backend_pool_id" { + type = string + description = "The external load balancer bakend pool id. used to attach the bootstrap NIC" +} + +variable "ilb_backend_pool_id" { + type = string + description = "The internal load balancer bakend pool id. used to attach the bootstrap NIC" +} + +variable "boot_diag_blob_endpoint" { + type = string + description = "the blob endpoint where machines should store their boot diagnostics." +} + +variable "ssh_nat_rule_id" { + type = string + description = "ssh nat rule to make the bootstrap node reachable" +} + +variable "tags" { + type = map(string) + default = {} + description = "tags to be applied to created resources." +} + diff --git a/data/data/azure/bootstrap/versions.tf b/data/data/azure/bootstrap/versions.tf new file mode 100644 index 00000000000..ac97c6ac8e7 --- /dev/null +++ b/data/data/azure/bootstrap/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/data/data/azure/dns/dns.tf b/data/data/azure/dns/dns.tf new file mode 100644 index 00000000000..46bf564084d --- /dev/null +++ b/data/data/azure/dns/dns.tf @@ -0,0 +1,63 @@ +locals { + // extracting "api." from + api_external_name = "api.${replace(var.cluster_domain, ".${var.base_domain}", "")}" +} + +resource "azurerm_dns_zone" "private" { + name = var.cluster_domain + resource_group_name = var.resource_group_name + zone_type = "Private" + resolution_virtual_network_ids = [var.internal_dns_resolution_vnet_id] +} + +resource "azurerm_dns_cname_record" "apiint_internal" { + name = "api-int" + zone_name = azurerm_dns_zone.private.name + resource_group_name = var.resource_group_name + ttl = 300 + record = var.external_lb_fqdn +} + +resource "azurerm_dns_cname_record" "api_internal" { + name = "api" + zone_name = azurerm_dns_zone.private.name + resource_group_name = var.resource_group_name + ttl = 300 + record = var.external_lb_fqdn +} + +resource "azurerm_dns_cname_record" "api_external" { + name = local.api_external_name + zone_name = var.base_domain + resource_group_name = var.base_domain_resource_group_name + ttl = 300 + record = var.external_lb_fqdn +} + +resource "azurerm_dns_a_record" "etcd_a_nodes" { + count = var.etcd_count + name = "etcd-${count.index}" + zone_name = azurerm_dns_zone.private.name + resource_group_name = var.resource_group_name + ttl = 60 + records = [var.etcd_ip_addresses[count.index]] +} + +resource "azurerm_dns_srv_record" "etcd_cluster" { + name = "_etcd-server-ssl._tcp" + zone_name = azurerm_dns_zone.private.name + resource_group_name = var.resource_group_name + ttl = 60 + + dynamic "record" { + for_each = azurerm_dns_a_record.etcd_a_nodes.*.name + iterator = name + content { + target = "${name.value}.${azurerm_dns_zone.private.name}" + priority = 10 + weight = 10 + port = 2380 + } + } +} + diff --git a/data/data/azure/dns/variables.tf b/data/data/azure/dns/variables.tf new file mode 100644 index 00000000000..bc3efdb6f97 --- /dev/null +++ b/data/data/azure/dns/variables.tf @@ -0,0 +1,52 @@ +variable "tags" { + type = map(string) + default = {} + description = "tags to be applied to created resources." +} + +variable "cluster_domain" { + description = "The domain for the cluster that all DNS records must belong" + type = string +} + +variable "base_domain" { + description = "The base domain used for public records" + type = string +} + +variable "base_domain_resource_group_name" { + description = "The resource group where the base domain is" + type = string +} + +variable "external_lb_fqdn" { + description = "External API's LB fqdn" + type = string +} + +variable "internal_lb_ipaddress" { + description = "External API's LB Ip address" + type = string +} + +variable "internal_dns_resolution_vnet_id" { + description = "the vnet id to be attached to the private DNS zone" + type = string +} + +variable "etcd_count" { + description = "The number of etcd members." + type = string +} + +variable "etcd_ip_addresses" { + description = "List of string IPs for machines running etcd members." + type = list(string) + default = [] +} + +variable "resource_group_name" { + type = string + description = "Resource group for the deployment" +} + diff --git a/data/data/azure/dns/versions.tf b/data/data/azure/dns/versions.tf new file mode 100644 index 00000000000..ac97c6ac8e7 --- /dev/null +++ b/data/data/azure/dns/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/data/data/azure/main.tf b/data/data/azure/main.tf new file mode 100644 index 00000000000..a2a31de6048 --- /dev/null +++ b/data/data/azure/main.tf @@ -0,0 +1,107 @@ +locals { + tags = merge( + { + "kubernetes.io_cluster.${var.cluster_id}" = "owned" + }, + var.azure_extra_tags, + ) + + master_subnet_cidr = cidrsubnet(var.machine_cidr, 3, 0) #master subnet is a smaller subnet within the vnet. i.e from /21 to /24 + node_subnet_cidr = cidrsubnet(var.machine_cidr, 3, 1) #node subnet is a smaller subnet within the vnet. i.e from /21 to /24 +} + +module "bootstrap" { + source = "./bootstrap" + resource_group_name = azurerm_resource_group.main.name + region = var.azure_region + vm_size = var.azure_bootstrap_vm_type + vm_image = var.azure_image_id + identity = azurerm_user_assigned_identity.main.id + cluster_id = var.cluster_id + ignition = var.ignition_bootstrap + subnet_id = module.vnet.public_subnet_id + elb_backend_pool_id = module.vnet.public_lb_backend_pool_id + ilb_backend_pool_id = module.vnet.internal_lb_backend_pool_id + tags = local.tags + boot_diag_blob_endpoint = azurerm_storage_account.bootdiag.primary_blob_endpoint + ssh_nat_rule_id = module.vnet.bootstrap_ssh_nat_rule_id +} + +module "vnet" { + source = "./vnet" + resource_group_name = azurerm_resource_group.main.name + vnet_cidr = var.machine_cidr + master_subnet_cidr = local.master_subnet_cidr + node_subnet_cidr = local.node_subnet_cidr + cluster_id = var.cluster_id + region = var.azure_region + dns_label = var.cluster_id + master_count = var.master_count +} + +module "master" { + source = "./master" + resource_group_name = azurerm_resource_group.main.name + cluster_id = var.cluster_id + region = var.azure_region + vm_size = var.azure_master_vm_type + vm_image = var.azure_image_id + identity = azurerm_user_assigned_identity.main.id + ignition = var.ignition_master + external_lb_id = module.vnet.public_lb_id + elb_backend_pool_id = module.vnet.public_lb_backend_pool_id + ilb_backend_pool_id = module.vnet.internal_lb_backend_pool_id + subnet_id = module.vnet.public_subnet_id + master_subnet_cidr = local.master_subnet_cidr + instance_count = var.master_count + boot_diag_blob_endpoint = azurerm_storage_account.bootdiag.primary_blob_endpoint + os_volume_size = var.azure_master_root_volume_size + ssh_nat_rule_ids = module.vnet.mmaster_ssh_nat_rule_ids +} + +module "dns" { + source = "./dns" + cluster_domain = var.cluster_domain + base_domain = var.base_domain + external_lb_fqdn = module.vnet.public_lb_pip_fqdn + internal_lb_ipaddress = module.vnet.internal_lb_ip_address + resource_group_name = azurerm_resource_group.main.name + base_domain_resource_group_name = var.azure_base_domain_resource_group_name + internal_dns_resolution_vnet_id = module.vnet.vnet_id + etcd_count = var.master_count + etcd_ip_addresses = module.master.ip_addresses +} + +resource "random_string" "storage_suffix" { + length = 5 + upper = false + special = false +} + +resource "azurerm_resource_group" "main" { + name = "${var.cluster_id}-rg" + location = var.azure_region + tags = local.tags +} + +resource "azurerm_storage_account" "bootdiag" { + name = "bootdiagmasters${random_string.storage_suffix.result}" + resource_group_name = azurerm_resource_group.main.name + location = var.azure_region + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_user_assigned_identity" "main" { + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + + name = "${var.cluster_id}-identity" +} + +resource "azurerm_role_assignment" "main" { + scope = azurerm_resource_group.main.id + role_definition_name = "Contributor" + principal_id = azurerm_user_assigned_identity.main.principal_id +} + diff --git a/data/data/azure/master/master.tf b/data/data/azure/master/master.tf new file mode 100644 index 00000000000..3bfc7e56339 --- /dev/null +++ b/data/data/azure/master/master.tf @@ -0,0 +1,89 @@ +locals { + // The name of the masters' ipconfiguration is hardcoded to "pipconfig". It needs to match cluster-api + // https://github.com/openshift/cluster-api-provider-azure/blob/master/pkg/cloud/azure/services/networkinterfaces/networkinterfaces.go#L131 + ip_configuration_name = "pipConfig" +} + +resource "azurerm_network_interface" "master" { + count = var.instance_count + name = "${var.cluster_id}-master${count.index}-nic" + location = var.region + resource_group_name = var.resource_group_name + + ip_configuration { + subnet_id = var.subnet_id + name = local.ip_configuration_name + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_network_interface_nat_rule_association" "master_ssh" { + count = var.instance_count + network_interface_id = element(azurerm_network_interface.master.*.id, count.index) + ip_configuration_name = local.ip_configuration_name + nat_rule_id = element(var.ssh_nat_rule_ids, count.index) +} + +resource "azurerm_network_interface_backend_address_pool_association" "master" { + count = var.instance_count + network_interface_id = element(azurerm_network_interface.master.*.id, count.index) + backend_address_pool_id = var.elb_backend_pool_id + ip_configuration_name = local.ip_configuration_name #must be the same as nic's ip configuration name. +} + +resource "azurerm_network_interface_backend_address_pool_association" "master_internal" { + count = var.instance_count + network_interface_id = element(azurerm_network_interface.master.*.id, count.index) + backend_address_pool_id = var.ilb_backend_pool_id + ip_configuration_name = local.ip_configuration_name #must be the same as nic's ip configuration name. +} + +data "azurerm_subscription" "current" { +} + +resource "azurerm_virtual_machine" "master" { + count = var.instance_count + name = "${var.cluster_id}-master${count.index}" + location = var.region + resource_group_name = var.resource_group_name + network_interface_ids = [element(azurerm_network_interface.master.*.id, count.index)] + vm_size = var.vm_size + + delete_os_disk_on_termination = true + + identity { + type = "UserAssigned" + identity_ids = [var.identity] + } + + storage_os_disk { + name = "${var.cluster_id}-master-${count.index}_OSDisk" # os disk name needs to match cluster-api convention + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Premium_LRS" + disk_size_gb = var.os_volume_size + } + + storage_image_reference { + id = "${data.azurerm_subscription.current.id}${var.vm_image}" + } + + //we don't provide a ssh key, because it is set with ignition. + //it is required to provide at least 1 auth method to deploy a linux vm + os_profile { + computer_name = "${var.cluster_id}-master-${count.index}" + admin_username = "core" + admin_password = "P@ssword1234!" + custom_data = var.ignition + } + + os_profile_linux_config { + disable_password_authentication = false + } + + boot_diagnostics { + enabled = true + storage_uri = var.boot_diag_blob_endpoint + } +} + diff --git a/data/data/azure/master/outputs.tf b/data/data/azure/master/outputs.tf new file mode 100644 index 00000000000..414be4c4807 --- /dev/null +++ b/data/data/azure/master/outputs.tf @@ -0,0 +1,4 @@ +output "ip_addresses" { + value = azurerm_network_interface.master.*.private_ip_address +} + diff --git a/data/data/azure/master/variables.tf b/data/data/azure/master/variables.tf new file mode 100644 index 00000000000..e64acf0eb1b --- /dev/null +++ b/data/data/azure/master/variables.tf @@ -0,0 +1,90 @@ +variable "region" { + type = string + description = "The region for the deployment." +} + +variable "resource_group_name" { + type = string + description = "The resource group name for the deployment." +} + +variable "cluster_id" { + type = string +} + +variable "vm_size" { + type = string +} + +variable "vm_image" { + type = string + description = "The resource id of the vm image used for masters." +} + +variable "identity" { + type = string + description = "The user assigned identity id for the vm." +} + +variable "instance_count" { + type = string +} + +variable "external_lb_id" { + type = string +} + +variable "elb_backend_pool_id" { + type = string +} + +variable "ilb_backend_pool_id" { + type = string +} + +variable "ignition_master" { + type = string + default = "" +} + +variable "kubeconfig_content" { + type = string + default = "" +} + +variable "subnet_id" { + type = string + description = "The subnet to attach the masters to." +} + +variable "os_volume_size" { + type = string + description = "The size of the volume in gigabytes for the root block device." + default = "100" +} + +variable "tags" { + type = map(string) + default = {} + description = "tags to be applied to created resources." +} + +variable "boot_diag_blob_endpoint" { + type = string + description = "the blob endpoint where machines should store their boot diagnostics." +} + +variable "ignition" { + type = string +} + +variable "master_subnet_cidr" { + type = string + description = "the master subnet cidr" +} + +variable "ssh_nat_rule_ids" { + type = list(string) + description = "ssh nat rule to make the master nodes reachable" +} + diff --git a/data/data/azure/master/versions.tf b/data/data/azure/master/versions.tf new file mode 100644 index 00000000000..ac97c6ac8e7 --- /dev/null +++ b/data/data/azure/master/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/data/data/azure/variables-azure.tf b/data/data/azure/variables-azure.tf new file mode 100644 index 00000000000..0eca2d36cc5 --- /dev/null +++ b/data/data/azure/variables-azure.tf @@ -0,0 +1,53 @@ +variable "azure_config_version" { + description = <