diff --git a/README.md b/README.md index eb4f2d80..bed4af4e 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,19 @@ private_dns_rg_name = "myResourceGroup" ``` **If you don't pass these params, we will automatically create the network resources for you.** +## Using pre-created storage account with disabled public access +To use secured storage account with function app, user should create private storage account first, and then upload our function code zip file to the container ``. +Zip file is accessible in our public storage acccount by this url: +https://wekaeastus.blob.core.windows.net/weka-tf-functions-deployment-eastus/dev/144dd946da08cb0cf72a8ce1947c1a71.zip + +```hcl +allow_sa_public_network_access = false +deployment_function_app_code_blob = "function_app_code.zip" +deployment_storage_account_name = "myprivatestorage" +deployment_container_name = "weka-deployment" +``` +User should also create file share in storage account with the name `${var.deployment_container_name}-share`. + ## Usage example ```hcl provider "azurerm" { @@ -313,6 +326,7 @@ proxy_url = VALUE |------|--------|---------| | [clients](#module\_clients) | ./modules/clients | n/a | | [iam](#module\_iam) | ./modules/iam | n/a | +| [logicapp](#module\_logicapp) | ./modules/logic_app | n/a | | [network](#module\_network) | ./modules/network | n/a | | [nfs\_protocol\_gateways](#module\_nfs\_protocol\_gateways) | ./modules/protocol_gateways | n/a | | [peering](#module\_peering) | ./modules/peering_vnets | n/a | @@ -327,7 +341,6 @@ proxy_url = VALUE | [azurerm_key_vault.key_vault](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault) | resource | | [azurerm_key_vault_access_policy.function_app_secret_permissions](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | | [azurerm_key_vault_access_policy.key_vault_access_policy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | -| [azurerm_key_vault_access_policy.standard_logic_app_get_secret_permission](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | | [azurerm_key_vault_secret.function_app_default_key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | | [azurerm_key_vault_secret.get_weka_io_token](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | | [azurerm_key_vault_secret.private_ssh_keys](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | @@ -344,7 +357,6 @@ proxy_url = VALUE | [azurerm_lb_rule.ui_lb_rule](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/lb_rule) | resource | | [azurerm_linux_function_app.function_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_function_app) | resource | | [azurerm_log_analytics_workspace.la_workspace](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/log_analytics_workspace) | resource | -| [azurerm_logic_app_standard.logic_app_standard](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/logic_app_standard) | resource | | [azurerm_monitor_diagnostic_setting.function_diagnostic_setting](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_diagnostic_setting) | resource | | [azurerm_monitor_diagnostic_setting.insights_diagnostic_setting](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_diagnostic_setting) | resource | | [azurerm_private_dns_a_record.dns_a_record_backend_lb](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_a_record) | resource | @@ -353,38 +365,36 @@ proxy_url = VALUE | [azurerm_private_dns_resolver_forwarding_rule.resolver_forwarding_rule](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_forwarding_rule) | resource | | [azurerm_private_dns_resolver_outbound_endpoint.outbound_endpoint](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_outbound_endpoint) | resource | | [azurerm_private_dns_resolver_virtual_network_link.dns_forwarding_virtual_network_link](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_virtual_network_link) | resource | +| [azurerm_private_dns_zone.blob](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone) | resource | +| [azurerm_private_dns_zone.file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone) | resource | +| [azurerm_private_dns_zone_virtual_network_link.blob_privatelink](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone_virtual_network_link) | resource | +| [azurerm_private_dns_zone_virtual_network_link.file_privatelink](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone_virtual_network_link) | resource | +| [azurerm_private_endpoint.blob_endpoint](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.file_endpoint](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.weka_obs_blob_endpoint](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | | [azurerm_proximity_placement_group.ppg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/proximity_placement_group) | resource | | [azurerm_public_ip.backend_ip](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip) | resource | | [azurerm_public_ip.ui_ip](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip) | resource | | [azurerm_service_plan.app_service_plan](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) | resource | -| [azurerm_service_plan.logicapp_service_plan](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) | resource | | [azurerm_storage_account.deployment_sa](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account) | resource | | [azurerm_storage_account.logicapp](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account) | resource | | [azurerm_storage_blob.nfs_state](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_blob) | resource | | [azurerm_storage_blob.state](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_blob) | resource | -| [azurerm_storage_blob.vmss_config](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_blob) | resource | | [azurerm_storage_container.deployment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | | [azurerm_storage_container.nfs_deployment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | -| [azurerm_storage_share_directory.share_directory_scale_down](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_directory) | resource | -| [azurerm_storage_share_directory.share_directory_scale_up](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_directory) | resource | -| [azurerm_storage_share_file.connections_share_file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_file) | resource | -| [azurerm_storage_share_file.scale_down_share_file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_file) | resource | -| [azurerm_storage_share_file.scale_up_share_file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_file) | resource | | [azurerm_subnet.dns_resolver_subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | | [azurerm_subnet.logicapp_subnet_delegation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | | [azurerm_subnet.subnet_delegation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | -| [local_file.connections_workflow_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [local_file.private_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [local_file.public_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | -| [local_file.scale_down_workflow_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | -| [local_file.scale_up_workflow_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [tls_private_key.ssh_key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | | [azurerm_application_insights.application_insights](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/application_insights) | data source | | [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source | | [azurerm_function_app_host_keys.function_keys](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/function_app_host_keys) | data source | | [azurerm_resource_group.rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group) | data source | | [azurerm_storage_account.deployment_blob](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_account) | data source | -| [azurerm_storage_share.storage_share](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_share) | data source | +| [azurerm_storage_account.weka_obs](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_account) | data source | +| [azurerm_storage_account_blob_container_sas.function_app_code_sas](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_account_blob_container_sas) | data source | | [azurerm_subnet.subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subnet) | data source | | [azurerm_virtual_network.vnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/virtual_network) | data source | @@ -393,6 +403,7 @@ proxy_url = VALUE | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [address\_space](#input\_address\_space) | The range of IP addresses the virtual network uses. | `string` | `"10.0.0.0/16"` | no | +| [allow\_sa\_public\_network\_access](#input\_allow\_sa\_public\_network\_access) | Allow public network access to the storage account. | `bool` | `true` | no | | [allow\_ssh\_cidrs](#input\_allow\_ssh\_cidrs) | Allow port 22, if not provided, i.e leaving the default empty list, the rule will not be included in the SG | `list(string)` | `[]` | no | | [allow\_weka\_api\_cidrs](#input\_allow\_weka\_api\_cidrs) | Allow connection to port 14000 on weka backends from specified CIDRs, by default no CIDRs are allowed. All ports (including 14000) are allowed within Vnet | `list(string)` | `[]` | no | | [application\_insights\_name](#input\_application\_insights\_name) | The Application Insights name. | `string` | `""` | no | @@ -414,10 +425,11 @@ proxy_url = VALUE | [containers\_config\_map](#input\_containers\_config\_map) | Maps the number of objects and memory size per machine type. |
map(object({
compute = number
drive = number
frontend = number
nvme = number
nics = number
memory = list(string)
}))
|
{
"Standard_L16as_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"72GB",
"73GB"
],
"nics": 8,
"nvme": 2
},
"Standard_L16s_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"79GB",
"72GB"
],
"nics": 8,
"nvme": 2
},
"Standard_L32as_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"190GB",
"190GB"
],
"nics": 8,
"nvme": 4
},
"Standard_L32s_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"197GB",
"189GB"
],
"nics": 8,
"nvme": 4
},
"Standard_L48as_v3": {
"compute": 3,
"drive": 3,
"frontend": 1,
"memory": [
"308GB",
"308GB"
],
"nics": 8,
"nvme": 6
},
"Standard_L48s_v3": {
"compute": 3,
"drive": 3,
"frontend": 1,
"memory": [
"314GB",
"306GB"
],
"nics": 8,
"nvme": 6
},
"Standard_L64as_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"384GB",
"384GB"
],
"nics": 8,
"nvme": 8
},
"Standard_L64s_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"357GB",
"384GB"
],
"nics": 8,
"nvme": 8
},
"Standard_L80as_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"384GB",
"384GB"
],
"nics": 8,
"nvme": 8
},
"Standard_L80s_v3": {
"compute": 4,
"drive": 2,
"frontend": 1,
"memory": [
"384GB",
"384GB"
],
"nics": 8,
"nvme": 8
},
"Standard_L8as_v3": {
"compute": 1,
"drive": 1,
"frontend": 1,
"memory": [
"29GB",
"29GB"
],
"nics": 4,
"nvme": 1
},
"Standard_L8s_v3": {
"compute": 1,
"drive": 1,
"frontend": 1,
"memory": [
"33GB",
"31GB"
],
"nics": 4,
"nvme": 1
}
}
| no | | [create\_lb](#input\_create\_lb) | Create backend and UI load balancers for weka cluster. | `bool` | `true` | no | | [create\_nat\_gateway](#input\_create\_nat\_gateway) | NAT needs to be created when no public ip is assigned to the backend, to allow internet access | `bool` | `false` | no | +| [create\_storage\_account\_private\_links](#input\_create\_storage\_account\_private\_links) | Create private links for storage accounts (in case if public network access for the storage account is disabled). | `bool` | `true` | no | | [debug\_down\_backends\_removal\_timeout](#input\_debug\_down\_backends\_removal\_timeout) | Don't change this value without consulting weka support team. Timeout for removing down backends. Valid time units are ns, us (or µs), ms, s, m, h. | `string` | `"3h"` | no | | [default\_disk\_size](#input\_default\_disk\_size) | The default disk size. | `number` | `48` | no | | [deployment\_container\_name](#input\_deployment\_container\_name) | Name of exising deployment container | `string` | `""` | no | -| [deployment\_storage\_account\_access\_key](#input\_deployment\_storage\_account\_access\_key) | The access key of the existing Blob object store container. | `string` | `""` | no | +| [deployment\_function\_app\_code\_blob](#input\_deployment\_function\_app\_code\_blob) | The path to the function app code blob file. | `string` | `"function_app_code.zip"` | no | | [deployment\_storage\_account\_name](#input\_deployment\_storage\_account\_name) | Name of exising deployment storage account | `string` | `""` | no | | [enable\_application\_insights](#input\_enable\_application\_insights) | Enable Application Insights. | `bool` | `true` | no | | [function\_access\_restriction\_enabled](#input\_function\_access\_restriction\_enabled) | Allow public access, Access restrictions apply to inbound access to internal vent | `bool` | `false` | no | @@ -428,7 +440,7 @@ proxy_url = VALUE | [function\_app\_storage\_account\_prefix](#input\_function\_app\_storage\_account\_prefix) | Weka storage account name prefix | `string` | `"weka"` | no | | [function\_app\_subnet\_delegation\_cidr](#input\_function\_app\_subnet\_delegation\_cidr) | Subnet delegation enables you to designate a specific subnet for an Azure PaaS service. | `string` | `"10.0.1.0/25"` | no | | [function\_app\_subnet\_delegation\_id](#input\_function\_app\_subnet\_delegation\_id) | Required to specify if subnet\_name were used to specify pre-defined subnets for weka. Function subnet delegation requires an additional subnet, and in the case of pre-defined networking this one also should be pre-defined | `string` | `""` | no | -| [function\_app\_version](#input\_function\_app\_version) | Function app code version (hash) | `string` | `"0e787ecc4b4936228cdb784e52ec408a"` | no | +| [function\_app\_version](#input\_function\_app\_version) | Function app code version (hash) | `string` | `"144dd946da08cb0cf72a8ce1947c1a71"` | no | | [get\_weka\_io\_token](#input\_get\_weka\_io\_token) | The token to download the Weka release from get.weka.io. | `string` | `""` | no | | [hotspare](#input\_hotspare) | Number of hotspares to set on weka cluster. Refer to https://docs.weka.io/overview/ssd-capacity-management#hot-spare | `number` | `1` | no | | [install\_cluster\_dpdk](#input\_install\_cluster\_dpdk) | Install weka cluster with DPDK | `bool` | `true` | no | diff --git a/Taskfile.yml b/Taskfile.yml index 99550d5d..87a1b834 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -27,7 +27,7 @@ tasks: write_function_hash_to_variables: cmds: - - ./zip_function_app_creation/write_function_hash_to_variables.sh {{OS}} ${FUNCTION_CODE_PATH} + - ./zip_function_app_creation/write_function_hash_to_variables.sh ${DIST} {{OS}} ${FUNCTION_CODE_PATH} create_and_upload_zip: preconditions: diff --git a/blob.tf b/blob.tf index 65c124f7..1162c46e 100644 --- a/blob.tf +++ b/blob.tf @@ -1,15 +1,15 @@ locals { - clusterization_target = var.clusterization_target != null ? var.clusterization_target : min(var.cluster_size, max(20, ceil(var.cluster_size * 0.8))) - # fields that depend on LB creation - vmss_health_probe_id = var.create_lb ? azurerm_lb_probe.backend_lb_probe[0].id : null - lb_backend_pool_ids = var.create_lb ? [azurerm_lb_backend_address_pool.lb_backend_pool[0].id] : [] + deployment_storage_account_id = var.deployment_storage_account_name == "" ? azurerm_storage_account.deployment_sa[0].id : data.azurerm_storage_account.deployment_blob[0].id + deployment_storage_account_name = var.deployment_storage_account_name == "" ? azurerm_storage_account.deployment_sa[0].name : var.deployment_storage_account_name + deployment_sa_connection_string = var.deployment_storage_account_name == "" ? azurerm_storage_account.deployment_sa[0].primary_connection_string : data.azurerm_storage_account.deployment_blob[0].primary_connection_string + deployment_container_name = var.deployment_container_name == "" ? "${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}-deployment" : var.deployment_container_name + deployment_sa_access_key = var.deployment_storage_account_name == "" ? azurerm_storage_account.deployment_sa[0].primary_access_key : data.azurerm_storage_account.deployment_blob[0].primary_access_key } - resource "azurerm_storage_account" "deployment_sa" { - count = var.deployment_storage_account_name == "" ? 1 : 0 + count = var.deployment_storage_account_name == "" && var.allow_sa_public_network_access ? 1 : 0 name = substr("${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}deployment", 0, 24) - location = data.azurerm_resource_group.rg.location + location = local.location resource_group_name = var.rg_name account_kind = "StorageV2" account_tier = "Standard" @@ -21,7 +21,7 @@ resource "azurerm_storage_account" "deployment_sa" { } resource "azurerm_storage_container" "deployment" { - count = var.deployment_container_name == "" ? 1 : 0 + count = var.deployment_container_name == "" && var.allow_sa_public_network_access ? 1 : 0 name = "${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}-deployment" storage_account_name = local.deployment_storage_account_name container_access_type = "private" @@ -29,6 +29,7 @@ resource "azurerm_storage_container" "deployment" { } resource "azurerm_storage_blob" "state" { + count = var.allow_sa_public_network_access ? 1 : 0 name = "state" storage_account_name = local.deployment_storage_account_name storage_container_name = local.deployment_container_name @@ -41,97 +42,9 @@ resource "azurerm_storage_blob" "state" { } } -data "azurerm_storage_account" "deployment_blob" { - count = var.deployment_storage_account_name != "" ? 1 : 0 - name = var.deployment_storage_account_name - resource_group_name = var.rg_name -} - -resource "azurerm_storage_blob" "vmss_config" { - name = "vmss-config" - storage_account_name = local.deployment_storage_account_name - storage_container_name = local.deployment_container_name - type = "Block" - - source_content = jsonencode({ - name = "${var.prefix}-${var.cluster_name}-vmss" - location = data.azurerm_resource_group.rg.location - zones = var.zone != null ? [var.zone] : [] - resource_group_name = var.rg_name - sku = var.instance_type - upgrade_mode = "Manual" - health_probe_id = local.vmss_health_probe_id - admin_username = var.vm_username - ssh_public_key = local.public_ssh_key - computer_name_prefix = "${var.prefix}-${var.cluster_name}-backend" - custom_data = base64encode(local.custom_data_script) - disable_password_authentication = true - proximity_placement_group_id = local.placement_group_id - single_placement_group = var.vmss_single_placement_group - source_image_id = var.source_image_id - overprovision = false - orchestration_mode = "Uniform" - tags = merge(var.tags_map, { - "weka_cluster" : var.cluster_name, - "user_id" : data.azurerm_client_config.current.object_id, - }) - - os_disk = { - caching = "ReadWrite" - storage_account_type = "Premium_LRS" - } - - data_disk = { - lun = 0 - caching = "None" - create_option = "Empty" - disk_size_gb = local.disk_size - storage_account_type = "Premium_LRS" - } - - identity = { - type = "UserAssigned" - identity_ids = [local.vmss_identity_id] - } - - primary_nic = { - name = "${var.prefix}-${var.cluster_name}-backend-nic-0" - network_security_group_id = local.sg_id - enable_accelerated_networking = var.install_cluster_dpdk - - ip_configurations = [{ - primary = true - subnet_id = data.azurerm_subnet.subnet.id - load_balancer_backend_address_pool_ids = local.lb_backend_pool_ids - public_ip_address = { - assign = local.assign_public_ip - name = "${var.prefix}-${var.cluster_name}-public-ip" - domain_name_label = "${var.prefix}-${var.cluster_name}-backend" - } - }] - } - - secondary_nics = { - number = local.nics_numbers - 1 - name_prefix = "${var.prefix}-${var.cluster_name}-backend-nic" - network_security_group_id = local.sg_id - enable_accelerated_networking = var.install_cluster_dpdk - ip_configurations = [{ - primary = true - subnet_id = data.azurerm_subnet.subnet.id - load_balancer_backend_address_pool_ids = local.lb_backend_pool_ids - }] - } - }) - depends_on = [ - azurerm_storage_container.deployment, azurerm_lb_backend_address_pool.lb_backend_pool, azurerm_lb_probe.backend_lb_probe, - azurerm_proximity_placement_group.ppg, azurerm_lb_rule.backend_lb_rule, azurerm_lb_rule.ui_lb_rule - ] -} - # state for protocols resource "azurerm_storage_container" "nfs_deployment" { - count = var.nfs_deployment_container_name == "" ? 1 : 0 + count = var.nfs_deployment_container_name == "" && var.allow_sa_public_network_access ? 1 : 0 name = "${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}-protocol-deployment" storage_account_name = local.deployment_storage_account_name container_access_type = "private" @@ -139,7 +52,7 @@ resource "azurerm_storage_container" "nfs_deployment" { } resource "azurerm_storage_blob" "nfs_state" { - count = var.nfs_protocol_gateways_number > 0 ? 1 : 0 + count = var.nfs_protocol_gateways_number > 0 && var.allow_sa_public_network_access ? 1 : 0 name = "nfs_state" storage_account_name = local.deployment_storage_account_name storage_container_name = local.nfs_deployment_container_name @@ -157,3 +70,157 @@ resource "azurerm_storage_blob" "nfs_state" { ignore_changes = all } } + +resource "azurerm_storage_account" "logicapp" { + count = var.allow_sa_public_network_access ? 1 : 0 + name = substr("${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}logicappsa", 0, 24) + resource_group_name = var.rg_name + location = local.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_storage_account" "deployment_blob" { + count = var.deployment_storage_account_name != "" ? 1 : 0 + name = var.deployment_storage_account_name + resource_group_name = local.resource_group_name +} + +resource "azurerm_private_dns_zone" "blob" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links ? 1 : 0 + name = "privatelink.blob.core.windows.net" + resource_group_name = local.resource_group_name + + lifecycle { + precondition { + condition = var.deployment_function_app_code_blob != "" + error_message = "Function app code blob is not provided" + } + precondition { + condition = var.deployment_container_name != "" + error_message = "Function app storage account container name is not provided" + } + precondition { + condition = var.deployment_storage_account_name != "" + error_message = "Function app storage account name is not provided" + } + } +} + +resource "azurerm_private_dns_zone" "file" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links ? 1 : 0 + name = "privatelink.file.core.windows.net" + resource_group_name = local.resource_group_name + + depends_on = [azurerm_private_dns_zone.blob] +} + +resource "azurerm_private_dns_zone_virtual_network_link" "blob_privatelink" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links ? 1 : 0 + name = "${var.prefix}-${var.cluster_name}-blob-privatelink" + resource_group_name = local.resource_group_name + private_dns_zone_name = azurerm_private_dns_zone.blob[0].name + virtual_network_id = data.azurerm_virtual_network.vnet.id +} + +resource "azurerm_private_dns_zone_virtual_network_link" "file_privatelink" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links ? 1 : 0 + name = "${var.prefix}-${var.cluster_name}-file-privatelink" + resource_group_name = local.resource_group_name + private_dns_zone_name = azurerm_private_dns_zone.file[0].name + virtual_network_id = data.azurerm_virtual_network.vnet.id +} + +resource "azurerm_private_endpoint" "file_endpoint" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links ? 1 : 0 + name = "${var.prefix}-${var.cluster_name}-file-endpoint" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + subnet_id = data.azurerm_subnet.subnet.id + tags = merge(var.tags_map, { "weka_cluster" : var.cluster_name }) + + private_dns_zone_group { + name = "${var.prefix}-${var.cluster_name}-dns-zone-group-file" + private_dns_zone_ids = [azurerm_private_dns_zone.file[0].id] + } + + private_service_connection { + name = "${var.prefix}-${var.cluster_name}-privateFileSvcCon" + is_manual_connection = false + private_connection_resource_id = local.deployment_storage_account_id + subresource_names = ["file"] + } +} + +resource "azurerm_private_endpoint" "blob_endpoint" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links ? 1 : 0 + name = "${var.prefix}-${var.cluster_name}-blob-endpoint" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + subnet_id = data.azurerm_subnet.subnet.id + tags = merge(var.tags_map, { "weka_cluster" : var.cluster_name }) + + private_dns_zone_group { + name = "${var.prefix}-${var.cluster_name}-dns-zone-group-blob" + private_dns_zone_ids = [azurerm_private_dns_zone.blob[0].id] + } + private_service_connection { + name = "${var.prefix}-${var.cluster_name}-privateBlobSvcCon" + is_manual_connection = false + private_connection_resource_id = local.deployment_storage_account_id + subresource_names = ["blob"] + } +} + +data "azurerm_storage_account" "weka_obs" { + count = var.tiering_obs_name != "" ? 1 : 0 + name = var.tiering_obs_name + resource_group_name = var.rg_name +} + +resource "azurerm_private_endpoint" "weka_obs_blob_endpoint" { + count = !var.allow_sa_public_network_access && var.create_storage_account_private_links && var.tiering_blob_obs_access_key != "" ? 1 : 0 + name = "${var.prefix}-${var.cluster_name}-obs-blob-endpoint" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + subnet_id = data.azurerm_subnet.subnet.id + tags = merge(var.tags_map, { "weka_cluster" : var.cluster_name }) + + private_dns_zone_group { + name = "${var.prefix}-${var.cluster_name}-dns-zone-group-obs-blob" + private_dns_zone_ids = [azurerm_private_dns_zone.blob[0].id] + } + private_service_connection { + name = "${var.prefix}-${var.cluster_name}-private-obs-BlobSvcCon" + is_manual_connection = false + private_connection_resource_id = data.azurerm_storage_account.weka_obs[0].id + subresource_names = ["blob"] + } + + lifecycle { + precondition { + condition = var.tiering_obs_name != "" + error_message = "Tiering OBS is not provided" + } + precondition { + condition = var.tiering_obs_container_name != "" + error_message = "Tiering OBS container name is not provided" + } + } +} + +data "azurerm_storage_account_blob_container_sas" "function_app_code_sas" { + count = var.allow_sa_public_network_access ? 0 : 1 + connection_string = local.deployment_sa_connection_string + container_name = local.deployment_container_name + start = timestamp() + expiry = formatdate("YYYY-MM-DD'T'hh:mm:ssZ", timeadd(timestamp(), "1h")) + permissions { + read = true + add = false + create = false + write = false + delete = false + list = false + } +} diff --git a/function-app/code/common/common.go b/function-app/code/common/common.go index cb130036..bb8b08d1 100644 --- a/function-app/code/common/common.go +++ b/function-app/code/common/common.go @@ -21,6 +21,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/lease" "github.com/google/uuid" @@ -234,6 +235,120 @@ func ReadBlobObject(ctx context.Context, bl BlobObjParams) (state []byte, err er } +func containerExists(ctx context.Context, containerClient *container.Client, storageName, containerName string) (bool, error) { + _, err := containerClient.GetProperties(ctx, nil) + if err != nil { + var responseErr *azcore.ResponseError + if errors.As(err, &responseErr) && responseErr.ErrorCode == "ContainerNotFound" { + return false, nil + } + err = fmt.Errorf("failed to get container properties: %w", err) + return false, err + } + return true, nil +} + +func ensureStorageContainer(ctx context.Context, storageAccountName, containerName string) (err error) { + logger := logging.LoggerFromCtx(ctx) + + credential, err := getCredential(ctx) + if err != nil { + return + } + + containerUrl := getContainerUrl(storageAccountName, containerName) + containerClient, err := container.NewClient(containerUrl, credential, nil) + if err != nil { + err = fmt.Errorf("failed to create container client: %v", err) + return err + } + + exists, err := containerExists(ctx, containerClient, storageAccountName, containerName) + if err != nil { + logger.Error().Err(err).Send() + return + } + + if exists { + logger.Info().Str("container", containerName).Msg("container already exists") + return + } + + logger.Info().Str("container", containerName).Msg("container does not exist, creating new container") + + _, err = containerClient.Create(ctx, nil) + if err != nil { + err = fmt.Errorf("failed to create container: %v", err) + logger.Error().Err(err).Send() + } + return +} + +func blobExists(ctx context.Context, blobClient *blob.Client) (bool, error) { + _, err := blobClient.GetProperties(ctx, nil) + if err != nil { + var responseErr *azcore.ResponseError + if errors.As(err, &responseErr) && responseErr.ErrorCode == "BlobNotFound" { + return false, nil + } + err = fmt.Errorf("failed to get blob properties: %w", err) + return false, err + } + return true, nil +} + +func EnsureStateIsCreated(ctx context.Context, p BlobObjParams, initialState protocol.ClusterState) (exists bool, err error) { + logger := logging.LoggerFromCtx(ctx) + + err = ensureStorageContainer(ctx, p.StorageName, p.ContainerName) + if err != nil { + return + } + + credential, err := getCredential(ctx) + if err != nil { + return + } + + url := getBlobFileUrl(p.StorageName, p.ContainerName, p.BlobName) + blobClient, err := blob.NewClient(url, credential, nil) + if err != nil { + logger.Error().Err(err).Msg("failed to create blob client") + return + } + + exists, err = blobExists(ctx, blobClient) + if err != nil { + logger.Error().Err(err).Send() + return + } + + if exists { + logger.Info().Msg("state already exists") + return + } + + logger.Info().Msg("state does not exist, creating new state") + + err = WriteState(ctx, p, initialState) + if err != nil { + logger.Error().Err(err).Msg("failed to write initial state") + } + return +} + +func ReadStateOrCreateNew(ctx context.Context, p BlobObjParams, initialState protocol.ClusterState) (state protocol.ClusterState, err error) { + exists, err := EnsureStateIsCreated(ctx, p, initialState) + if err != nil { + return + } + + if exists { + return ReadState(ctx, p) + } + return initialState, nil +} + func ReadState(ctx context.Context, stateParams BlobObjParams) (state protocol.ClusterState, err error) { logger := logging.LoggerFromCtx(ctx) @@ -287,6 +402,10 @@ func getBlobUrl(storageName string) string { return fmt.Sprintf("https://%s.blob.core.windows.net/", storageName) } +func getBlobFileUrl(storageName, containerName, blobName string) string { + return fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", storageName, containerName, blobName) +} + func getContainerUrl(storageName, containerName string) string { return fmt.Sprintf("https://%s.blob.core.windows.net/%s", storageName, containerName) } @@ -377,12 +496,16 @@ func CreateStorageAccount(ctx context.Context, subscriptionId, resourceGroupName } skuName := armstorage.SKUNameStandardZRS kind := armstorage.KindStorageV2 + // publicAccessDisabled := armstorage.PublicNetworkAccessDisabled _, err = client.BeginCreate(ctx, resourceGroupName, obsName, armstorage.AccountCreateParameters{ Kind: &kind, Location: &location, SKU: &armstorage.SKU{ Name: &skuName, }, + // Properties: &armstorage.AccountPropertiesCreateParameters{ + // PublicNetworkAccess: &publicAccessDisabled, + // }, }, nil) if err != nil { @@ -1455,16 +1578,19 @@ func GetAzureInstanceNameCmd() string { return "curl -s -H Metadata:true --noproxy * http://169.254.169.254/metadata/instance?api-version=2021-02-01 | jq '.compute.name' | cut -c2- | rev | cut -c2- | rev" } -func ReadVmssConfig(ctx context.Context, storageName, containerName string) (vmssConfig VMSSConfig, err error) { +func ReadVmssConfig(ctx context.Context, vmssConfigStr string) (vmssConfig VMSSConfig, err error) { logger := logging.LoggerFromCtx(ctx) - params := BlobObjParams{ - StorageName: storageName, - ContainerName: containerName, - BlobName: "vmss-config", + if vmssConfigStr == "" { + err = fmt.Errorf("vmss config is not set") + logger.Error().Err(err).Msg("cannot read vmss config") + return } - asByteArray, err := ReadBlobObject(ctx, params) + + asByteArray := []byte(vmssConfigStr) + err = json.Unmarshal(asByteArray, &vmssConfig) if err != nil { + logger.Error().Err(err).Msg("cannot unmarshal vmss config") return } @@ -1472,12 +1598,6 @@ func ReadVmssConfig(ctx context.Context, storageName, containerName string) (vms hash := sha256.Sum256(asByteArray) hashStr := fmt.Sprintf("%x", hash) - err = json.Unmarshal(asByteArray, &vmssConfig) - if err != nil { - logger.Error().Err(err).Send() - return - } - // take first 16 characters of the hash vmssConfig.ConfigHash = hashStr[:16] return diff --git a/function-app/code/functions/clusterize/clusterize.go b/function-app/code/functions/clusterize/clusterize.go index edda46b6..20a33f45 100644 --- a/function-app/code/functions/clusterize/clusterize.go +++ b/function-app/code/functions/clusterize/clusterize.go @@ -25,10 +25,11 @@ import ( ) type AzureObsParams struct { - Name string - ContainerName string - AccessKey string - TieringSsdPercent string + Name string + ContainerName string + AccessKey string + TieringSsdPercent string + PublicAccessDisabled bool } func GetObsScript(obsParams AzureObsParams) string { @@ -93,6 +94,12 @@ func HandleLastClusterVm(ctx context.Context, state protocol.ClusterState, p Clu if p.Cluster.SetObs { if p.Obs.AccessKey == "" { + if p.Obs.PublicAccessDisabled { + err = fmt.Errorf("public access is disabled for the storage account, please provide pre-created storage account info in terraform") + logger.Error().Err(err).Send() + return + } + p.Obs.AccessKey, err = common.CreateStorageAccount( ctx, p.SubscriptionId, p.ResourceGroupName, p.Obs.Name, p.Location, ) @@ -348,6 +355,7 @@ func Handler(w http.ResponseWriter, r *http.Request) { obsName := os.Getenv("OBS_NAME") obsContainerName := os.Getenv("OBS_CONTAINER_NAME") obsAccessKey := os.Getenv("OBS_ACCESS_KEY") + obsPublicAccessDisabled, _ := strconv.ParseBool(os.Getenv("OBS_PUBLIC_ACCESS_DISABLED")) location := os.Getenv("LOCATION") tieringSsdPercent := os.Getenv("TIERING_SSD_PERCENT") tieringTargetSsdRetention, _ := strconv.Atoi(os.Getenv("TIERING_TARGET_SSD_RETENTION")) @@ -429,10 +437,11 @@ func Handler(w http.ResponseWriter, r *http.Request) { TieringStartDemote: tieringStartDemote, }, Obs: AzureObsParams{ - Name: obsName, - ContainerName: obsContainerName, - AccessKey: obsAccessKey, - TieringSsdPercent: tieringSsdPercent, + Name: obsName, + ContainerName: obsContainerName, + AccessKey: obsAccessKey, + TieringSsdPercent: tieringSsdPercent, + PublicAccessDisabled: obsPublicAccessDisabled, }, NFSStateParams: common.BlobObjParams{StorageName: stateStorageName, ContainerName: nfsStateContainerName, BlobName: nfsStateBlobName}, FunctionAppName: functionAppName, diff --git a/init-script.sh b/function-app/code/functions/scale_up/init_script.go old mode 100755 new mode 100644 similarity index 67% rename from init-script.sh rename to function-app/code/functions/scale_up/init_script.go index 43b977e6..f02368a5 --- a/init-script.sh +++ b/function-app/code/functions/scale_up/init_script.go @@ -1,38 +1,40 @@ -function retry { - local retry_max=$1 - local retry_sleep=$2 - shift 2 - local count=$retry_max - while [ $count -gt 0 ]; do - "$@" && break - count=$(($count - 1)) - sleep $retry_sleep - done - [ $count -eq 0 ] && { - echo "Retry failed [$retry_max]" - shutdown -h now - return 1 - } - return 0 -} +package scale_up + +import "fmt" + +var ( + initScript = `#!/bin/bash +set -ex + +# user data +%s -# retry for 2 minutes -# NOTE: in some cases it takes time for all access policies to be applied -retry 12 10 curl --fail ${report_url}?code="${function_app_default_key}" -H "Content-Type:application/json" -d "{\"hostname\": \"$HOSTNAME\", \"type\": \"progress\", \"message\": \"Running init script\"}" +DISK_SIZE=%d +NICS_NUM=%d +SUBNET_RANGE="%s" +APT_REPO_SERVER="%s" + +# report function definition +%s + +# deploy function definition +%s + +report "{\"hostname\": \"$HOSTNAME\", \"type\": \"progress\", \"message\": \"Running init script\"}" while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 2 done # set apt private repo -if [[ "${apt_repo_server}" ]]; then +if [[ "$APT_REPO_SERVER" ]]; then mv /etc/apt/sources.list /etc/apt/sources.list.bak - echo "deb ${apt_repo_server} focal main restricted universe" > /etc/apt/sources.list - echo "deb ${apt_repo_server} focal-updates main restricted" >> /etc/apt/sources.list + echo "deb $APT_REPO_SERVER focal main restricted universe" > /etc/apt/sources.list + echo "deb $APT_REPO_SERVER focal-updates main restricted" >> /etc/apt/sources.list apt update -y fi -for(( i=0; i<${nics_num}; i++ )); do +for(( i=0; i<$NICS_NUM; i++ )); do cat <<-EOF | sed -i "/ eth$i/r /dev/stdin" /etc/netplan/50-cloud-init.yaml mtu: 3900 EOF @@ -47,7 +49,7 @@ gateway=$(ip r | grep default | awk '{print $3}') eth=$(ifconfig | grep eth0 -C2 | grep 'inet ' | awk '{print $2}') cat <<-EOF | sed -i "/ set-name: eth0/r /dev/stdin" /etc/netplan/50-cloud-init.yaml routes: - - to: ${subnet_range} + - to: $SUBNET_RANGE via: $gateway metric: 200 table: 200 @@ -63,9 +65,9 @@ EOF netplan apply -if [[ ${nics_num} -gt 1 ]]; then +if [[ $NICS_NUM -gt 1 ]]; then are_routes_ready='ip route | grep eth1' - for(( i=2; i<${nics_num}; i++ )); do + for(( i=2; i<$NICS_NUM; i++ )); do are_routes_ready=$are_routes_ready' && ip route | grep eth'"$i" done cat >>/usr/sbin/remove-routes.sh < /tmp/deploy.sh 2>/tmp/deploy_err.log || [ ! -s /tmp/deploy.sh ]; do +while ! deploy "{\"name\": \"$compute_name:$HOSTNAME\"}" > /tmp/deploy.sh 2>/tmp/deploy_err.log || [ ! -s /tmp/deploy.sh ]; do echo "Retry $retry: waiting for deploy script generation success" cat /tmp/deploy_err.log retry=$((retry + 1)) @@ -155,10 +158,16 @@ mv /root/weka-prepackaged $weka_dir if [ $retry -gt 0 ]; then msg="Deploy script generation retried $retry times" echo "$msg" - curl -i "${report_url}?code=${function_app_default_key}" -H "Content-Type:application/json" -d "{\"hostname\": \"$HOSTNAME\", \"type\": \"debug\", \"message\": \"$msg\"}" + report "{\"hostname\": \"$HOSTNAME\", \"type\": \"debug\", \"message\": \"$msg\"}" fi echo "$(date -u): running deploy script" chmod +x /tmp/deploy.sh /tmp/deploy.sh 2>&1 | tee /tmp/weka_deploy.log +` +) + +func getInitScript(userData string, diskSize int, nicsNum int, subnetRange string, aptRepoServer string, reportFuncDef string, deployFuncDef string) string { + return fmt.Sprintf(initScript, userData, diskSize, nicsNum, subnetRange, aptRepoServer, reportFuncDef, deployFuncDef) +} diff --git a/function-app/code/functions/scale_up/scale_up.go b/function-app/code/functions/scale_up/scale_up.go index f0987998..5e21db8e 100644 --- a/function-app/code/functions/scale_up/scale_up.go +++ b/function-app/code/functions/scale_up/scale_up.go @@ -2,12 +2,16 @@ package scale_up import ( "context" + "encoding/base64" "fmt" "net/http" "os" + "strconv" "time" "weka-deployment/common" + "weka-deployment/functions/azure_functions_def" + "github.com/weka/go-cloud-lib/functions_def" "github.com/weka/go-cloud-lib/logging" "github.com/weka/go-cloud-lib/protocol" ) @@ -23,9 +27,51 @@ var ( nfsContainerName = os.Getenv("NFS_STATE_CONTAINER_NAME") nfsStateBlobName = os.Getenv("NFS_STATE_BLOB_NAME") nfsScaleSetName = os.Getenv("NFS_VMSS_NAME") - keyVaultUri = os.Getenv("KEY_VAULT_URI") + vmssConfigStr = os.Getenv("VMSS_CONFIG") + // initial state of the cluster + initialClusterSize, _ = strconv.Atoi(os.Getenv("INITIAL_CLUSTER_SIZE")) + clusterizeTarget, _ = strconv.Atoi(os.Getenv("CLUSTERIZATION_TARGET")) + initialNfsSize, _ = strconv.Atoi(os.Getenv("NFS_PROTOCOL_GATEWAYS_NUM")) + clusterInitialState = protocol.ClusterState{ + InitialSize: initialClusterSize, + DesiredSize: initialClusterSize, + ClusterizationTarget: clusterizeTarget, + } + nfsInitialState = protocol.ClusterState{ + InitialSize: initialNfsSize, + DesiredSize: initialNfsSize, + ClusterizationTarget: initialNfsSize, + } ) +func getBackendCustomDataScript(ctx context.Context) (customData string, err error) { + functionAppName := os.Getenv("FUNCTION_APP_NAME") + keyVaultUri := os.Getenv("KEY_VAULT_URI") + diskSize, _ := strconv.Atoi(os.Getenv("DISK_SIZE")) + nicsNum, _ := strconv.Atoi(os.Getenv("NICS_NUM")) + subnet := os.Getenv("SUBNET") + aptRepo := os.Getenv("APT_REPO_SERVER") + userData := os.Getenv("USER_DATA") + + logger := logging.LoggerFromCtx(ctx) + + functionAppKey, err := common.GetKeyVaultValue(ctx, keyVaultUri, "function-app-default-key") + if err != nil { + logger.Error().Err(err).Msg("cannot get function app key") + return + } + + baseFunctionUrl := fmt.Sprintf("https://%s.azurewebsites.net/api/", functionAppName) + funcDef := azure_functions_def.NewFuncDef(baseFunctionUrl, functionAppKey) + reportFunction := funcDef.GetFunctionCmdDefinition(functions_def.Report) + deployFunction := funcDef.GetFunctionCmdDefinition(functions_def.Deploy) + + customDataStr := getInitScript(userData, diskSize, nicsNum, subnet, aptRepo, reportFunction, deployFunction) + // base64 encode the custom data + customData = base64.StdEncoding.EncodeToString([]byte(customDataStr)) + return +} + func Handler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() logger := logging.LoggerFromCtx(ctx) @@ -37,7 +83,7 @@ func Handler(w http.ResponseWriter, r *http.Request) { BlobName: stateBlobName, } - state, err := common.ReadState(ctx, stateParams) + state, err := common.ReadStateOrCreateNew(ctx, stateParams, clusterInitialState) if err != nil { logger.Error().Err(err).Msg("cannot read state") common.WriteErrorResponse(w, err) @@ -59,7 +105,7 @@ func Handler(w http.ResponseWriter, r *http.Request) { } // get expected vmss config - vmssConfig, err := common.ReadVmssConfig(ctx, stateStorageName, stateContainerName) + vmssConfig, err := common.ReadVmssConfig(ctx, vmssConfigStr) if err != nil { logger.Error().Err(err).Msgf("cannot read vmss config") common.WriteErrorResponse(w, err) @@ -139,7 +185,7 @@ func handleNFSScaleUp(ctx context.Context) (message string, err error) { ContainerName: nfsContainerName, BlobName: nfsStateBlobName, } - nfsState, err := common.ReadState(ctx, nfsStateParams) + nfsState, err := common.ReadStateOrCreateNew(ctx, nfsStateParams, nfsInitialState) if err != nil { logger.Error().Err(err).Msg("cannot read NFS state") return @@ -168,12 +214,18 @@ func handleNFSScaleUp(ctx context.Context) (message string, err error) { return } -func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssName string, vmssSize int) error { +func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssName string, vmssSize int) (err error) { logger := logging.LoggerFromCtx(ctx) vmssConfigHash := vmssConfig.ConfigHash + vmssConfig.CustomData, err = getBackendCustomDataScript(ctx) + if err != nil { + logger.Error().Err(err).Msg("cannot get custom data script") + return err + } + logger.Info().Msgf("creating new vmss %s of size %d", vmssName, vmssSize) - _, err := common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, vmssName, vmssConfigHash, *vmssConfig, vmssSize) + _, err = common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, vmssName, vmssConfigHash, *vmssConfig, vmssSize) if err != nil { return err } @@ -181,7 +233,7 @@ func createVmss(ctx context.Context, vmssConfig *common.VMSSConfig, vmssName str return nil } -func handleVmssUpdate(ctx context.Context, currentConfig, newConfig *common.VMSSConfig, stateParams common.BlobObjParams, desiredSize int) error { +func handleVmssUpdate(ctx context.Context, currentConfig, newConfig *common.VMSSConfig, stateParams common.BlobObjParams, desiredSize int) (err error) { logger := logging.LoggerFromCtx(ctx) newConfigHash := newConfig.ConfigHash @@ -201,7 +253,13 @@ func handleVmssUpdate(ctx context.Context, currentConfig, newConfig *common.VMSS return common.AddClusterUpdate(ctx, stateParams, update) } - _, err := common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, currentConfig.Name, newConfigHash, *newConfig, desiredSize) + newConfig.CustomData, err = getBackendCustomDataScript(ctx) + if err != nil { + logger.Error().Err(err).Msg("cannot get custom data script") + return err + } + + _, err = common.CreateOrUpdateVmss(ctx, subscriptionId, resourceGroupName, currentConfig.Name, newConfigHash, *newConfig, desiredSize) if err != nil { logger.Error().Err(err).Msgf("cannot update vmss %s", currentConfig.Name) errStr := err.Error() diff --git a/function-app/code/functions/status/status.go b/function-app/code/functions/status/status.go index 6f23d725..fc66c053 100644 --- a/function-app/code/functions/status/status.go +++ b/function-app/code/functions/status/status.go @@ -180,8 +180,8 @@ func GetClusterStatus(ctx context.Context, vmssParams *common.ScaleSetParams, st return } -func GetRefreshStatus(ctx context.Context, vmssParams *common.ScaleSetParams, stateParams common.BlobObjParams, extended bool) (*common.VMSSStateVerbose, error) { - vmssConfig, err := common.ReadVmssConfig(ctx, stateParams.StorageName, stateParams.ContainerName) +func GetRefreshStatus(ctx context.Context, vmssParams *common.ScaleSetParams, stateParams common.BlobObjParams, vmssConfigStr string, extended bool) (*common.VMSSStateVerbose, error) { + vmssConfig, err := common.ReadVmssConfig(ctx, vmssConfigStr) if err != nil { return nil, err } @@ -235,6 +235,7 @@ func Handler(w http.ResponseWriter, r *http.Request) { nfsStateContainerName := os.Getenv("NFS_STATE_CONTAINER_NAME") nfsStateBlobName := os.Getenv("NFS_STATE_BLOB_NAME") nfsScaleSetName := os.Getenv("NFS_VMSS_NAME") + vmssConfigStr := os.Getenv("VMSS_CONFIG") ctx := r.Context() logger := logging.LoggerFromCtx(ctx) @@ -298,9 +299,9 @@ func Handler(w http.ResponseWriter, r *http.Request) { } else if requestBody.Type == "progress" { result, err = GetReports(ctx, stateParams, vmssParams) } else if requestBody.Type == "vmss" { - result, err = GetRefreshStatus(ctx, vmssParams, stateParams, false) + result, err = GetRefreshStatus(ctx, vmssParams, stateParams, vmssConfigStr, false) } else if requestBody.Type == "vmss-extended" { - result, err = GetRefreshStatus(ctx, vmssParams, stateParams, true) + result, err = GetRefreshStatus(ctx, vmssParams, stateParams, vmssConfigStr, true) } else { result = "Invalid status type" } diff --git a/functions.tf b/functions.tf index 0cac27a4..84dea970 100644 --- a/functions.tf +++ b/functions.tf @@ -1,28 +1,185 @@ locals { - create_private_function = var.function_access_restriction_enabled ? 1 : 0 - stripe_width_calculated = var.cluster_size - var.protection_level - 1 - stripe_width = local.stripe_width_calculated < 16 ? local.stripe_width_calculated : 16 - location = data.azurerm_resource_group.rg.location - function_app_zip_name = "${var.function_app_dist}/${var.function_app_version}.zip" - weka_sa = "${var.function_app_storage_account_prefix}${local.location}" - weka_sa_container = "${var.function_app_storage_account_container_prefix}${local.location}" - function_code_path = "${path.module}/function-app/code" - function_app_code_hash = md5(join("", [for f in fileset(local.function_code_path, "**") : filemd5("${local.function_code_path}/${f}")])) - get_compute_memory_index = var.set_dedicated_fe_container ? 1 : 0 - deployment_storage_account_id = var.deployment_storage_account_name == "" ? azurerm_storage_account.deployment_sa[0].id : data.azurerm_storage_account.deployment_blob[0].id - deployment_storage_account_name = var.deployment_storage_account_name == "" ? azurerm_storage_account.deployment_sa[0].name : var.deployment_storage_account_name - deployment_container_name = var.deployment_container_name == "" ? azurerm_storage_container.deployment[0].name : var.deployment_container_name - obs_storage_account_name = var.tiering_obs_name == "" ? "${substr("${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}", 0, 21)}obs" : var.tiering_obs_name - obs_container_name = var.tiering_obs_container_name == "" ? "${var.prefix}-${var.cluster_name}-obs" : var.tiering_obs_container_name - function_app_name = "${local.alphanumeric_prefix_name}-${local.alphanumeric_cluster_name}-function-app" - install_weka_url = var.install_weka_url != "" ? var.install_weka_url : "https://$TOKEN@get.weka.io/dist/v1/install/${var.weka_version}/${var.weka_version}" - supported_regions = split("\n", replace(chomp(file("${path.module}/supported_regions/${var.function_app_dist}.txt")), "\r", "")) + create_private_function = var.function_access_restriction_enabled ? 1 : 0 + stripe_width_calculated = var.cluster_size - var.protection_level - 1 + stripe_width = local.stripe_width_calculated < 16 ? local.stripe_width_calculated : 16 + location = data.azurerm_resource_group.rg.location + function_app_zip_name = var.allow_sa_public_network_access ? "${var.function_app_dist}/${var.function_app_version}.zip" : var.deployment_function_app_code_blob + weka_sa = var.allow_sa_public_network_access ? "${var.function_app_storage_account_prefix}${local.location}" : var.deployment_storage_account_name + weka_sa_container = var.allow_sa_public_network_access ? "${var.function_app_storage_account_container_prefix}${local.location}" : var.deployment_container_name + function_app_blob_sas = var.allow_sa_public_network_access ? "" : data.azurerm_storage_account_blob_container_sas.function_app_code_sas[0].sas + function_code_path = "${path.module}/function-app/code" + function_app_code_hash = md5(join("", [for f in fileset(local.function_code_path, "**") : filemd5("${local.function_code_path}/${f}")])) + get_compute_memory_index = var.set_dedicated_fe_container ? 1 : 0 + obs_storage_account_name = var.tiering_obs_name == "" ? "${substr("${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}", 0, 21)}obs" : var.tiering_obs_name + obs_container_name = var.tiering_obs_container_name == "" ? "${var.prefix}-${var.cluster_name}-obs" : var.tiering_obs_container_name + function_app_name = "${local.alphanumeric_prefix_name}-${local.alphanumeric_cluster_name}-function-app" + install_weka_url = var.install_weka_url != "" ? var.install_weka_url : "https://$TOKEN@get.weka.io/dist/v1/install/${var.weka_version}/${var.weka_version}" + supported_regions = split("\n", replace(chomp(file("${path.module}/supported_regions/${var.function_app_dist}.txt")), "\r", "")) # log analytics for function app log_analytics_workspace_id = var.enable_application_insights ? var.log_analytics_workspace_id == "" ? azurerm_log_analytics_workspace.la_workspace[0].id : var.log_analytics_workspace_id : "" application_insights_id = var.enable_application_insights ? var.application_insights_name == "" ? azurerm_application_insights.application_insights[0].id : data.azurerm_application_insights.application_insights[0].id : "" insights_instrumenation_key = var.enable_application_insights ? var.application_insights_name == "" ? azurerm_application_insights.application_insights[0].instrumentation_key : data.azurerm_application_insights.application_insights[0].instrumentation_key : "" # nfs autoscaling - nfs_deployment_container_name = var.nfs_deployment_container_name == "" ? azurerm_storage_container.nfs_deployment[0].name : var.nfs_deployment_container_name + nfs_deployment_container_name = var.nfs_deployment_container_name == "" ? "${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}-protocol-deployment" : var.nfs_deployment_container_name + + clusterization_target = var.clusterization_target != null ? var.clusterization_target : min(var.cluster_size, max(20, ceil(var.cluster_size * 0.8))) + # fields that depend on LB creation + vmss_health_probe_id = var.create_lb ? azurerm_lb_probe.backend_lb_probe[0].id : null + lb_backend_pool_ids = var.create_lb ? [azurerm_lb_backend_address_pool.lb_backend_pool[0].id] : [] + + vmss_config = jsonencode({ + name = "${var.prefix}-${var.cluster_name}-vmss" + location = data.azurerm_resource_group.rg.location + zones = var.zone != null ? [var.zone] : [] + resource_group_name = var.rg_name + sku = var.instance_type + upgrade_mode = "Manual" + health_probe_id = local.vmss_health_probe_id + admin_username = var.vm_username + ssh_public_key = local.public_ssh_key + computer_name_prefix = "${var.prefix}-${var.cluster_name}-backend" + custom_data = "" + disable_password_authentication = true + proximity_placement_group_id = local.placement_group_id + single_placement_group = var.vmss_single_placement_group + source_image_id = var.source_image_id + overprovision = false + orchestration_mode = "Uniform" + tags = merge(var.tags_map, { + "weka_cluster" : var.cluster_name, + "user_id" : data.azurerm_client_config.current.object_id, + }) + + os_disk = { + caching = "ReadWrite" + storage_account_type = "Premium_LRS" + } + + data_disk = { + lun = 0 + caching = "None" + create_option = "Empty" + disk_size_gb = local.disk_size + storage_account_type = "Premium_LRS" + } + + identity = { + type = "UserAssigned" + identity_ids = [local.vmss_identity_id] + } + + primary_nic = { + name = "${var.prefix}-${var.cluster_name}-backend-nic-0" + network_security_group_id = local.sg_id + enable_accelerated_networking = var.install_cluster_dpdk + + ip_configurations = [{ + primary = true + subnet_id = data.azurerm_subnet.subnet.id + load_balancer_backend_address_pool_ids = local.lb_backend_pool_ids + public_ip_address = { + assign = local.assign_public_ip + name = "${var.prefix}-${var.cluster_name}-public-ip" + domain_name_label = "${var.prefix}-${var.cluster_name}-backend" + } + }] + } + + secondary_nics = { + number = local.nics_numbers - 1 + name_prefix = "${var.prefix}-${var.cluster_name}-backend-nic" + network_security_group_id = local.sg_id + enable_accelerated_networking = var.install_cluster_dpdk + ip_configurations = [{ + primary = true + subnet_id = data.azurerm_subnet.subnet.id + load_balancer_backend_address_pool_ids = local.lb_backend_pool_ids + }] + } + }) + + # function app settings + initial_app_settings = { + "USER_ASSIGNED_CLIENT_ID" = local.function_app_identity_client_id + "STATE_STORAGE_NAME" = local.deployment_storage_account_name + "STATE_CONTAINER_NAME" = local.deployment_container_name + "STATE_BLOB_NAME" = "state" + "HOSTS_NUM" = var.cluster_size + "CLUSTER_NAME" = var.cluster_name + "PROTECTION_LEVEL" = var.protection_level + "STRIPE_WIDTH" = var.stripe_width != -1 ? var.stripe_width : local.stripe_width + "HOTSPARE" = var.hotspare + "VM_USERNAME" = var.vm_username + "SUBSCRIPTION_ID" = var.subscription_id + "RESOURCE_GROUP_NAME" = data.azurerm_resource_group.rg.name + "LOCATION" = data.azurerm_resource_group.rg.location + "SET_OBS" = var.tiering_enable_obs_integration + "CREATE_CONFIG_FS" = (var.smbw_enabled && var.smb_setup_protocol) || var.s3_setup_protocol + "OBS_NAME" = local.obs_storage_account_name + "OBS_CONTAINER_NAME" = local.obs_container_name + "OBS_ACCESS_KEY" = var.tiering_blob_obs_access_key + "OBS_PUBLIC_ACCESS_DISABLED" = !var.allow_sa_public_network_access + DRIVE_CONTAINER_CORES_NUM = var.containers_config_map[var.instance_type].drive + COMPUTE_CONTAINER_CORES_NUM = var.set_dedicated_fe_container == false ? var.containers_config_map[var.instance_type].compute + 1 : var.containers_config_map[var.instance_type].compute + FRONTEND_CONTAINER_CORES_NUM = var.set_dedicated_fe_container == false ? 0 : var.containers_config_map[var.instance_type].frontend + COMPUTE_MEMORY = var.containers_config_map[var.instance_type].memory[local.get_compute_memory_index] + DISK_SIZE = local.disk_size + "NVMES_NUM" = var.containers_config_map[var.instance_type].nvme + "TIERING_SSD_PERCENT" = var.tiering_enable_ssd_percent + "TIERING_TARGET_SSD_RETENTION" = var.tiering_obs_target_ssd_retention + "TIERING_START_DEMOTE" = var.tiering_obs_start_demote + "PREFIX" = var.prefix + "KEY_VAULT_URI" = azurerm_key_vault.key_vault.vault_uri + "INSTALL_DPDK" = var.install_cluster_dpdk + "NICS_NUM" = var.containers_config_map[var.instance_type].nics + "INSTALL_URL" = local.install_weka_url + "LOG_LEVEL" = var.function_app_log_level + "SUBNET" = local.subnet_range + FUNCTION_APP_NAME = local.function_app_name + PROXY_URL = var.proxy_url + WEKA_HOME_URL = var.weka_home_url + POST_CLUSTER_CREATION_SCRIPT = var.script_post_cluster_creation + PRE_START_IO_SCRIPT = var.script_pre_start_io + DOWN_BACKENDS_REMOVAL_TIMEOUT = var.debug_down_backends_removal_timeout + + https_only = true + FUNCTION_APP_EDIT_MODE = "readonly" + HASH = var.function_app_version + WEBSITE_RUN_FROM_PACKAGE = "https://${local.weka_sa}.blob.core.windows.net/${local.weka_sa_container}/${local.function_app_zip_name}${local.function_app_blob_sas}" + + NFS_STATE_CONTAINER_NAME = local.nfs_deployment_container_name + NFS_STATE_BLOB_NAME = "nfs_state" + NFS_INTERFACE_GROUP_NAME = var.nfs_interface_group_name + NFS_SECONDARY_IPS_NUM = var.nfs_protocol_gateway_secondary_ips_per_nic + NFS_PROTOCOL_GATEWAY_FE_CORES_NUM = var.nfs_protocol_gateway_fe_cores_num + NFS_PROTOCOL_GATEWAYS_NUM = var.nfs_protocol_gateways_number + NFS_VMSS_NAME = var.nfs_protocol_gateways_number > 0 ? "${var.prefix}-${var.cluster_name}-nfs-protocol-gateway-vmss" : "" + NFS_DISK_SIZE = var.nfs_protocol_gateway_disk_size + SMB_DISK_SIZE = var.smb_protocol_gateway_disk_size + S3_DISK_SIZE = var.s3_protocol_gateway_disk_size + SMB_PROTOCOL_GATEWAY_FE_CORES_NUM = var.smb_protocol_gateway_fe_cores_num + S3_PROTOCOL_GATEWAY_FE_CORES_NUM = var.s3_protocol_gateway_fe_cores_num + TRACES_PER_FRONTEND = var.traces_per_ionode + + BACKEND_LB_IP = var.create_lb ? azurerm_lb.backend_lb[0].private_ip_address : "" + # state + INITIAL_CLUSTER_SIZE = var.cluster_size + CLUSTERIZATION_TARGET = local.clusterization_target + VMSS_CONFIG = local.vmss_config + # init script inputs + APT_REPO_SERVER = var.apt_repo_server + USER_DATA = var.user_data + } + + secured_storage_account_app_settings = { + # "AzureWebJobsStorage" and "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" are not needed as we set storage_account_access_key + "WEBSITE_CONTENTSHARE" = "${local.deployment_container_name}-share" + "WEBSITE_CONTENTOVERVNET" = 1 + "WEBSITE_VNET_ROUTE_ALL" = 1 + "WEBSITE_DNS_SERVER" = "168.63.129.16" + } + + app_settings = var.allow_sa_public_network_access ? local.initial_app_settings : merge(local.initial_app_settings, local.secured_storage_account_app_settings) } resource "azurerm_log_analytics_workspace" "la_workspace" { @@ -118,11 +275,16 @@ resource "azurerm_linux_function_app" "function_app" { location = data.azurerm_resource_group.rg.location service_plan_id = azurerm_service_plan.app_service_plan.id storage_account_name = local.deployment_storage_account_name - storage_account_access_key = var.deployment_storage_account_access_key == "" ? azurerm_storage_account.deployment_sa[0].primary_access_key : var.deployment_storage_account_access_key + storage_account_access_key = local.deployment_sa_access_key https_only = true virtual_network_subnet_id = var.function_app_subnet_delegation_id == "" ? azurerm_subnet.subnet_delegation[0].id : var.function_app_subnet_delegation_id + site_config { - vnet_route_all_enabled = true + vnet_route_all_enabled = true + application_insights_key = local.insights_instrumenation_key + application_stack { + use_custom_runtime = true + } dynamic "ip_restriction" { for_each = range(local.create_private_function) content { @@ -133,7 +295,7 @@ resource "azurerm_linux_function_app" "function_app" { } } dynamic "ip_restriction" { - for_each = range(local.create_private_function) + for_each = range(var.allow_sa_public_network_access ? local.create_private_function : 0) content { virtual_network_subnet_id = var.logic_app_subnet_delegation_id == "" ? azurerm_subnet.logicapp_subnet_delegation[0].id : var.logic_app_subnet_delegation_id action = "Allow" @@ -143,73 +305,7 @@ resource "azurerm_linux_function_app" "function_app" { } } - app_settings = { - "USER_ASSIGNED_CLIENT_ID" = local.function_app_identity_client_id - "APPINSIGHTS_INSTRUMENTATIONKEY" = local.insights_instrumenation_key - "STATE_STORAGE_NAME" = local.deployment_storage_account_name - "STATE_CONTAINER_NAME" = local.deployment_container_name - "STATE_BLOB_NAME" = "state" - "HOSTS_NUM" = var.cluster_size - "CLUSTER_NAME" = var.cluster_name - "PROTECTION_LEVEL" = var.protection_level - "STRIPE_WIDTH" = var.stripe_width != -1 ? var.stripe_width : local.stripe_width - "HOTSPARE" = var.hotspare - "VM_USERNAME" = var.vm_username - "SUBSCRIPTION_ID" = var.subscription_id - "RESOURCE_GROUP_NAME" = data.azurerm_resource_group.rg.name - "LOCATION" = data.azurerm_resource_group.rg.location - "SET_OBS" = var.tiering_enable_obs_integration - "CREATE_CONFIG_FS" = (var.smbw_enabled && var.smb_setup_protocol) || var.s3_setup_protocol - "OBS_NAME" = local.obs_storage_account_name - "OBS_CONTAINER_NAME" = local.obs_container_name - "OBS_ACCESS_KEY" = var.tiering_blob_obs_access_key - DRIVE_CONTAINER_CORES_NUM = var.containers_config_map[var.instance_type].drive - COMPUTE_CONTAINER_CORES_NUM = var.set_dedicated_fe_container == false ? var.containers_config_map[var.instance_type].compute + 1 : var.containers_config_map[var.instance_type].compute - FRONTEND_CONTAINER_CORES_NUM = var.set_dedicated_fe_container == false ? 0 : var.containers_config_map[var.instance_type].frontend - COMPUTE_MEMORY = var.containers_config_map[var.instance_type].memory[local.get_compute_memory_index] - DISK_SIZE = local.disk_size - "NVMES_NUM" = var.containers_config_map[var.instance_type].nvme - "TIERING_SSD_PERCENT" = var.tiering_enable_ssd_percent - "TIERING_TARGET_SSD_RETENTION" = var.tiering_obs_target_ssd_retention - "TIERING_START_DEMOTE" = var.tiering_obs_start_demote - "PREFIX" = var.prefix - "KEY_VAULT_URI" = azurerm_key_vault.key_vault.vault_uri - "INSTALL_DPDK" = var.install_cluster_dpdk - "NICS_NUM" = var.containers_config_map[var.instance_type].nics - "INSTALL_URL" = local.install_weka_url - "LOG_LEVEL" = var.function_app_log_level - "SUBNET" = data.azurerm_subnet.subnet.address_prefix - FUNCTION_APP_NAME = local.function_app_name - PROXY_URL = var.proxy_url - WEKA_HOME_URL = var.weka_home_url - POST_CLUSTER_CREATION_SCRIPT = var.script_post_cluster_creation - PRE_START_IO_SCRIPT = var.script_pre_start_io - DOWN_BACKENDS_REMOVAL_TIMEOUT = var.debug_down_backends_removal_timeout - - https_only = true - FUNCTIONS_EXTENSION_VERSION = "~4" - FUNCTIONS_WORKER_RUNTIME = "custom" - FUNCTION_APP_EDIT_MODE = "readonly" - HASH = var.function_app_version - WEBSITE_RUN_FROM_PACKAGE = "https://${local.weka_sa}.blob.core.windows.net/${local.weka_sa_container}/${local.function_app_zip_name}" - WEBSITE_VNET_ROUTE_ALL = true - - NFS_STATE_CONTAINER_NAME = local.nfs_deployment_container_name - NFS_STATE_BLOB_NAME = "nfs_state" - NFS_INTERFACE_GROUP_NAME = var.nfs_interface_group_name - NFS_SECONDARY_IPS_NUM = var.nfs_protocol_gateway_secondary_ips_per_nic - NFS_PROTOCOL_GATEWAY_FE_CORES_NUM = var.nfs_protocol_gateway_fe_cores_num - NFS_PROTOCOL_GATEWAYS_NUM = var.nfs_protocol_gateways_number - NFS_VMSS_NAME = var.nfs_protocol_gateways_number > 0 ? "${var.prefix}-${var.cluster_name}-nfs-protocol-gateway-vmss" : "" - NFS_DISK_SIZE = var.nfs_protocol_gateway_disk_size - SMB_DISK_SIZE = var.smb_protocol_gateway_disk_size - S3_DISK_SIZE = var.s3_protocol_gateway_disk_size - SMB_PROTOCOL_GATEWAY_FE_CORES_NUM = var.smb_protocol_gateway_fe_cores_num - S3_PROTOCOL_GATEWAY_FE_CORES_NUM = var.s3_protocol_gateway_fe_cores_num - TRACES_PER_FRONTEND = var.traces_per_ionode - - BACKEND_LB_IP = var.create_lb ? azurerm_lb.backend_lb[0].private_ip_address : "" - } + app_settings = local.app_settings identity { type = "UserAssigned" @@ -229,5 +325,5 @@ resource "azurerm_linux_function_app" "function_app" { } } - depends_on = [module.network, azurerm_storage_account.deployment_sa, azurerm_subnet.logicapp_subnet_delegation] + depends_on = [module.network, azurerm_storage_account.deployment_sa, azurerm_private_endpoint.file_endpoint, azurerm_private_endpoint.blob_endpoint] } diff --git a/main.tf b/main.tf index 95682ab5..f35ac974 100644 --- a/main.tf +++ b/main.tf @@ -9,21 +9,7 @@ locals { alphanumeric_prefix_name = lower(replace(var.prefix, "/\\W|_|\\s/", "")) subnet_range = data.azurerm_subnet.subnet.address_prefix nics_numbers = var.install_cluster_dpdk ? var.containers_config_map[var.instance_type].nics : 1 - init_script = templatefile("${path.module}/init-script.sh", { - apt_repo_server = var.apt_repo_server - user = var.vm_username - subnet_range = local.subnet_range - nics_num = local.nics_numbers - deploy_url = "https://${azurerm_linux_function_app.function_app.name}.azurewebsites.net/api/deploy" - report_url = "https://${azurerm_linux_function_app.function_app.name}.azurewebsites.net/api/report" - function_app_default_key = data.azurerm_function_app_host_keys.function_keys.default_function_key - disk_size = local.disk_size - }) - custom_data_script = templatefile("${path.module}/user-data.sh", { - user_data = var.user_data - init_script = local.init_script - }) - placement_group_id = var.placement_group_id != "" ? var.placement_group_id : var.vmss_single_placement_group ? azurerm_proximity_placement_group.ppg[0].id : null + placement_group_id = var.placement_group_id != "" ? var.placement_group_id : var.vmss_single_placement_group ? azurerm_proximity_placement_group.ppg[0].id : null } # ===================== SSH key ++++++++++++++++++++++++= # diff --git a/modules/iam/README.md b/modules/iam/README.md index 69b3f2db..702efb0e 100644 --- a/modules/iam/README.md +++ b/modules/iam/README.md @@ -62,6 +62,7 @@ No modules. | [obs\_container\_name](#input\_obs\_container\_name) | The name of the container for the OBS. | `string` | n/a | yes | | [prefix](#input\_prefix) | Prefix for all resources | `string` | n/a | yes | | [rg\_name](#input\_rg\_name) | A predefined resource group in the Azure subscription. | `string` | n/a | yes | +| [support\_logic\_app](#input\_support\_logic\_app) | Enable support for logic app. | `bool` | `true` | no | | [tiering\_enable\_obs\_integration](#input\_tiering\_enable\_obs\_integration) | Enable OBS integration for tiering. | `bool` | n/a | yes | | [tiering\_obs\_name](#input\_tiering\_obs\_name) | Name of existing obs storage account. | `string` | `""` | no | | [vmss\_identity\_name](#input\_vmss\_identity\_name) | The user assigned identity name for the vmss instances (if empty - new one is created). | `string` | `""` | no | diff --git a/modules/iam/logic_app.tf b/modules/iam/logic_app.tf index 46986aa5..226617aa 100644 --- a/modules/iam/logic_app.tf +++ b/modules/iam/logic_app.tf @@ -1,32 +1,32 @@ data "azurerm_user_assigned_identity" "logic_app" { - count = var.logic_app_identity_name != "" ? 1 : 0 + count = var.logic_app_identity_name != "" && var.support_logic_app ? 1 : 0 name = var.logic_app_identity_name resource_group_name = data.azurerm_resource_group.rg.name } resource "azurerm_user_assigned_identity" "logic_app" { - count = var.logic_app_identity_name == "" ? 1 : 0 + count = var.logic_app_identity_name == "" && var.support_logic_app ? 1 : 0 location = data.azurerm_resource_group.rg.location name = "${var.prefix}-${var.cluster_name}-logic-app-identity" resource_group_name = data.azurerm_resource_group.rg.name } resource "azurerm_role_assignment" "logic_app_standard_reader" { - count = var.logic_app_identity_name == "" ? 1 : 0 + count = var.logic_app_identity_name == "" && var.support_logic_app ? 1 : 0 scope = data.azurerm_resource_group.rg.id role_definition_name = "Reader" principal_id = azurerm_user_assigned_identity.logic_app[0].principal_id } resource "azurerm_role_assignment" "logic_app_standard_reader_secret" { - count = var.logic_app_identity_name == "" ? 1 : 0 + count = var.logic_app_identity_name == "" && var.support_logic_app ? 1 : 0 scope = var.key_vault_id role_definition_name = "Key Vault Secrets User" principal_id = azurerm_user_assigned_identity.logic_app[0].principal_id } resource "azurerm_role_assignment" "logic_app_standard_reader_smb_data" { - count = var.logic_app_identity_name == "" ? 1 : 0 + count = var.logic_app_identity_name == "" && var.support_logic_app ? 1 : 0 scope = var.logic_app_storage_account_id role_definition_name = "Storage File Data SMB Share Contributor" principal_id = azurerm_user_assigned_identity.logic_app[0].principal_id diff --git a/modules/iam/outputs.tf b/modules/iam/outputs.tf index a5ea994a..fd3075db 100644 --- a/modules/iam/outputs.tf +++ b/modules/iam/outputs.tf @@ -1,10 +1,10 @@ output "logic_app_identity_id" { - value = var.logic_app_identity_name == "" ? azurerm_user_assigned_identity.logic_app[0].id : data.azurerm_user_assigned_identity.logic_app[0].id + value = var.support_logic_app ? var.logic_app_identity_name == "" ? azurerm_user_assigned_identity.logic_app[0].id : data.azurerm_user_assigned_identity.logic_app[0].id : null description = "The ID of the managed identity for the logic app" } output "logic_app_identity_principal_id" { - value = var.logic_app_identity_name == "" ? azurerm_user_assigned_identity.logic_app[0].principal_id : data.azurerm_user_assigned_identity.logic_app[0].principal_id + value = var.support_logic_app ? var.logic_app_identity_name == "" ? azurerm_user_assigned_identity.logic_app[0].principal_id : data.azurerm_user_assigned_identity.logic_app[0].principal_id : null description = "The principal ID of the managed identity for the logic app" } diff --git a/modules/iam/variables.tf b/modules/iam/variables.tf index 3c750c3d..09758334 100644 --- a/modules/iam/variables.tf +++ b/modules/iam/variables.tf @@ -33,6 +33,12 @@ variable "function_app_identity_name" { default = "" } +variable "support_logic_app" { + type = bool + description = "Enable support for logic app." + default = true +} + variable "logic_app_identity_name" { type = string description = "The user assigned identity name for the logic app (if empty - new one is created)." diff --git a/modules/logic_app/README.md b/modules/logic_app/README.md new file mode 100644 index 00000000..0bf36ebd --- /dev/null +++ b/modules/logic_app/README.md @@ -0,0 +1,63 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.4.6 | +| [azurerm](#requirement\_azurerm) | ~>3.75.0 | +| [local](#requirement\_local) | ~>2.4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | ~>3.75.0 | +| [local](#provider\_local) | ~>2.4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurerm_key_vault_access_policy.standard_logic_app_get_secret_permission](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_logic_app_standard.logic_app_standard](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/logic_app_standard) | resource | +| [azurerm_service_plan.logicapp_service_plan](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) | resource | +| [azurerm_storage_share_directory.share_directory_scale_down](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_directory) | resource | +| [azurerm_storage_share_directory.share_directory_scale_up](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_directory) | resource | +| [azurerm_storage_share_file.connections_share_file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_file) | resource | +| [azurerm_storage_share_file.scale_down_share_file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_file) | resource | +| [azurerm_storage_share_file.scale_up_share_file](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_share_file) | resource | +| [local_file.connections_workflow_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.scale_down_workflow_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.scale_up_workflow_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source | +| [azurerm_storage_account.logicapp](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_account) | data source | +| [azurerm_storage_share.storage_share](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_share) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_name](#input\_cluster\_name) | Cluster name | `string` | n/a | yes | +| [function\_app\_id](#input\_function\_app\_id) | The ID of the function app. | `string` | n/a | yes | +| [function\_app\_key](#input\_function\_app\_key) | The key of the function app. | `string` | n/a | yes | +| [function\_app\_name](#input\_function\_app\_name) | The name of the function app. | `string` | n/a | yes | +| [key\_vault\_id](#input\_key\_vault\_id) | The id of the Azure Key Vault. | `string` | n/a | yes | +| [key\_vault\_uri](#input\_key\_vault\_uri) | The URI of the Azure Key Vault. | `string` | n/a | yes | +| [location](#input\_location) | The Azure region to deploy all resources to. | `string` | n/a | yes | +| [logic\_app\_identity\_id](#input\_logic\_app\_identity\_id) | The ID of the managed identity for the logic app | `string` | n/a | yes | +| [logic\_app\_identity\_principal](#input\_logic\_app\_identity\_principal) | The principal ID of the managed identity for the logic app | `string` | n/a | yes | +| [logic\_app\_subnet\_delegation\_id](#input\_logic\_app\_subnet\_delegation\_id) | Required to specify if subnet\_name were used to specify pre-defined subnets for weka. Logicapp subnet delegation requires an additional subnet, and in the case of pre-defined networking this one also should be pre-defined | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix for all resources | `string` | n/a | yes | +| [restricted\_inbound\_access](#input\_restricted\_inbound\_access) | Restrict inbound access to internal VNet | `bool` | n/a | yes | +| [rg\_name](#input\_rg\_name) | A predefined resource group in the Azure subscription. | `string` | n/a | yes | +| [storage\_account\_name](#input\_storage\_account\_name) | The name of the storage account. | `string` | n/a | yes | +| [subnet\_id](#input\_subnet\_id) | The ID of the cluster subnet. | `string` | n/a | yes | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/logic_app/connections.json b/modules/logic_app/connections.json similarity index 100% rename from logic_app/connections.json rename to modules/logic_app/connections.json diff --git a/logic_app.tf b/modules/logic_app/main.tf similarity index 65% rename from logic_app.tf rename to modules/logic_app/main.tf index 80ab431a..0480c3f9 100644 --- a/logic_app.tf +++ b/modules/logic_app/main.tf @@ -1,30 +1,13 @@ -resource "azurerm_storage_account" "logicapp" { - name = substr("${local.alphanumeric_prefix_name}${local.alphanumeric_cluster_name}logicappsa", 0, 24) - resource_group_name = var.rg_name - location = local.location - account_tier = "Standard" - account_replication_type = "LRS" -} +data "azurerm_client_config" "current" {} -resource "azurerm_subnet" "logicapp_subnet_delegation" { - count = var.logic_app_subnet_delegation_id == "" ? 1 : 0 - name = "${var.prefix}-${var.cluster_name}-logicapp-delegation" - resource_group_name = local.vnet_rg_name - virtual_network_name = local.vnet_name - address_prefixes = [var.logic_app_subnet_delegation_cidr] - service_endpoints = ["Microsoft.KeyVault", "Microsoft.Web"] - delegation { - name = "logic-delegation" - service_delegation { - name = "Microsoft.Web/serverFarms" - actions = ["Microsoft.Network/virtualNetworks/subnets/action"] - } - } +data "azurerm_storage_account" "logicapp" { + name = var.storage_account_name + resource_group_name = var.rg_name } resource "azurerm_service_plan" "logicapp_service_plan" { name = "${var.prefix}-${var.cluster_name}-logic-app-service-plan" - location = local.location + location = var.location resource_group_name = var.rg_name os_type = "Windows" sku_name = "WS1" @@ -35,23 +18,23 @@ resource "azurerm_service_plan" "logicapp_service_plan" { resource "azurerm_logic_app_standard" "logic_app_standard" { name = "${var.prefix}-${var.cluster_name}-logic-app" - location = local.location + location = var.location resource_group_name = var.rg_name app_service_plan_id = azurerm_service_plan.logicapp_service_plan.id - storage_account_name = azurerm_storage_account.logicapp.name - storage_account_access_key = azurerm_storage_account.logicapp.primary_access_key + storage_account_name = data.azurerm_storage_account.logicapp.name + storage_account_access_key = data.azurerm_storage_account.logicapp.primary_access_key version = "~4" # sets FUNCTIONS_EXTENSION_VERSION (should be same as for function app) identity { type = "UserAssigned" - identity_ids = [local.logic_app_identity_id] + identity_ids = [var.logic_app_identity_id] } site_config { vnet_route_all_enabled = true dynamic "ip_restriction" { - for_each = range(local.create_private_function) + for_each = range(var.restricted_inbound_access ? 1 : 0) content { - virtual_network_subnet_id = data.azurerm_subnet.subnet.id + virtual_network_subnet_id = var.subnet_id action = "Allow" priority = 300 name = "VirtualNetwork" @@ -61,55 +44,52 @@ resource "azurerm_logic_app_standard" "logic_app_standard" { app_settings = { "FUNCTIONS_WORKER_RUNTIME" = "node" "WEBSITE_NODE_DEFAULT_VERSION" = "~18" - "function_app_key" = data.azurerm_function_app_host_keys.function_keys.default_function_key - "keyVaultUri" = azurerm_key_vault.key_vault.vault_uri + "function_app_key" = var.function_app_key + "keyVaultUri" = var.key_vault_uri } - virtual_network_subnet_id = var.logic_app_subnet_delegation_id == "" ? azurerm_subnet.logicapp_subnet_delegation[0].id : var.logic_app_subnet_delegation_id - depends_on = [azurerm_service_plan.logicapp_service_plan, azurerm_subnet.logicapp_subnet_delegation, azurerm_storage_account.logicapp] + virtual_network_subnet_id = var.logic_app_subnet_delegation_id + depends_on = [azurerm_service_plan.logicapp_service_plan] } resource "azurerm_key_vault_access_policy" "standard_logic_app_get_secret_permission" { - key_vault_id = azurerm_key_vault.key_vault.id + key_vault_id = var.key_vault_id tenant_id = data.azurerm_client_config.current.tenant_id - object_id = local.logic_app_identity_principal + object_id = var.logic_app_identity_principal secret_permissions = [ "Get", ] - depends_on = [azurerm_key_vault.key_vault] } resource "azurerm_storage_share_directory" "share_directory_scale_down" { name = "site/wwwroot/scale-down" share_name = "${azurerm_logic_app_standard.logic_app_standard.name}-content" - storage_account_name = azurerm_storage_account.logicapp.name - depends_on = [azurerm_storage_account.logicapp] + storage_account_name = data.azurerm_storage_account.logicapp.name } resource "azurerm_storage_share_directory" "share_directory_scale_up" { name = "site/wwwroot/scale-up" share_name = "${azurerm_logic_app_standard.logic_app_standard.name}-content" - storage_account_name = azurerm_storage_account.logicapp.name - depends_on = [azurerm_storage_account.logicapp] + storage_account_name = data.azurerm_storage_account.logicapp.name } data "azurerm_storage_share" "storage_share" { name = "${azurerm_logic_app_standard.logic_app_standard.name}-content" - storage_account_name = azurerm_storage_account.logicapp.name + storage_account_name = data.azurerm_storage_account.logicapp.name } locals { - connections_workflow_path = "${path.module}/logic_app/connections.json" + connections_workflow_path = "${path.module}/connections.json" connections_workflow = templatefile(local.connections_workflow_path, { - function_name = azurerm_linux_function_app.function_app.name - function_id = azurerm_linux_function_app.function_app.id + function_name = var.function_app_name + function_id = var.function_app_id }) connections_workflow_hash = md5(join("", [for f in fileset(local.connections_workflow, "**") : filemd5("${local.connections_workflow}/${f}")])) connections_workflow_filename = "/tmp/${var.prefix}_${var.cluster_name}_connections_workflow_${local.connections_workflow_hash}" - scale_up_workflow_path = "${path.module}/logic_app/scale_up.json" + scale_up_workflow_path = "${path.module}/scale_up.json" scale_up_workflow_hash = md5(join("", [for f in fileset(local.scale_up_workflow_path, "**") : filemd5("${local.scale_up_workflow_path}/${f}")])) scale_up_workflow_filename = "/tmp/${var.prefix}_${var.cluster_name}_scale_up_workflow_${local.scale_up_workflow_hash}" - scale_down_workflow_path = "${path.module}/logic_app/scale_down.json" + scale_down_workflow_path = "${path.module}/scale_down.json" scale_down_workflow_hash = md5(join("", [for f in fileset(local.scale_down_workflow_path, "**") : filemd5("${local.scale_down_workflow_path}/${f}")])) scale_down_workflow_filename = "/tmp/${var.prefix}_${var.cluster_name}_scale_down_workflow_${local.scale_down_workflow_hash}" } diff --git a/modules/logic_app/outputs.tf b/modules/logic_app/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/logic_app/scale_down.json b/modules/logic_app/scale_down.json similarity index 100% rename from logic_app/scale_down.json rename to modules/logic_app/scale_down.json diff --git a/logic_app/scale_up.json b/modules/logic_app/scale_up.json similarity index 100% rename from logic_app/scale_up.json rename to modules/logic_app/scale_up.json diff --git a/modules/logic_app/variables.tf b/modules/logic_app/variables.tf new file mode 100644 index 00000000..a4682b75 --- /dev/null +++ b/modules/logic_app/variables.tf @@ -0,0 +1,75 @@ +variable "prefix" { + type = string + description = "Prefix for all resources" +} + +variable "cluster_name" { + type = string + description = "Cluster name" +} + +variable "rg_name" { + type = string + description = "A predefined resource group in the Azure subscription." +} + +variable "subnet_id" { + type = string + description = "The ID of the cluster subnet." +} + +variable "location" { + type = string + description = "The Azure region to deploy all resources to." +} + +variable "logic_app_subnet_delegation_id" { + type = string + description = "Required to specify if subnet_name were used to specify pre-defined subnets for weka. Logicapp subnet delegation requires an additional subnet, and in the case of pre-defined networking this one also should be pre-defined" +} + +variable "storage_account_name" { + type = string + description = "The name of the storage account." +} + +variable "logic_app_identity_id" { + type = string + description = "The ID of the managed identity for the logic app" +} + +variable "logic_app_identity_principal" { + type = string + description = "The principal ID of the managed identity for the logic app" + +} + +variable "restricted_inbound_access" { + type = bool + description = "Restrict inbound access to internal VNet" +} + +variable "function_app_name" { + type = string + description = "The name of the function app." +} + +variable "function_app_id" { + type = string + description = "The ID of the function app." +} + +variable "function_app_key" { + type = string + description = "The key of the function app." +} + +variable "key_vault_id" { + type = string + description = "The id of the Azure Key Vault." +} + +variable "key_vault_uri" { + type = string + description = "The URI of the Azure Key Vault." +} diff --git a/modules/logic_app/versions.tf b/modules/logic_app/versions.tf new file mode 100644 index 00000000..a964232c --- /dev/null +++ b/modules/logic_app/versions.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.4.6" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.75.0" + } + local = { + source = "hashicorp/local" + version = "~>2.4.0" + } + } +} diff --git a/prerequisites.tf b/prerequisites.tf index e460ac1e..b834e25c 100644 --- a/prerequisites.tf +++ b/prerequisites.tf @@ -26,8 +26,9 @@ module "iam" { cluster_name = var.cluster_name vmss_identity_name = var.vmss_identity_name function_app_identity_name = var.function_app_identity_name + support_logic_app = var.allow_sa_public_network_access logic_app_identity_name = var.logic_app_identity_name - logic_app_storage_account_id = azurerm_storage_account.logicapp.id + logic_app_storage_account_id = var.allow_sa_public_network_access ? azurerm_storage_account.logicapp[0].id : "" key_vault_id = azurerm_key_vault.key_vault.id weka_tar_storage_account_id = var.weka_tar_storage_account_id deployment_storage_account_id = local.deployment_storage_account_id @@ -65,6 +66,45 @@ module "peering" { depends_on = [module.network] } + +resource "azurerm_subnet" "logicapp_subnet_delegation" { + count = var.logic_app_subnet_delegation_id == "" && var.allow_sa_public_network_access ? 1 : 0 + name = "${var.prefix}-${var.cluster_name}-logicapp-delegation" + resource_group_name = local.vnet_rg_name + virtual_network_name = local.vnet_name + address_prefixes = [var.logic_app_subnet_delegation_cidr] + service_endpoints = ["Microsoft.KeyVault", "Microsoft.Web"] + delegation { + name = "logic-delegation" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +module "logicapp" { + count = var.allow_sa_public_network_access ? 1 : 0 + source = "./modules/logic_app" + rg_name = var.rg_name + location = local.location + prefix = var.prefix + cluster_name = var.cluster_name + logic_app_subnet_delegation_id = var.logic_app_subnet_delegation_id == "" ? azurerm_subnet.logicapp_subnet_delegation[0].id : var.logic_app_subnet_delegation_id + storage_account_name = azurerm_storage_account.logicapp[0].name + logic_app_identity_id = local.logic_app_identity_id + logic_app_identity_principal = local.logic_app_identity_principal + restricted_inbound_access = var.function_access_restriction_enabled + subnet_id = data.azurerm_subnet.subnet.id + function_app_id = azurerm_linux_function_app.function_app.id + function_app_name = azurerm_linux_function_app.function_app.name + function_app_key = data.azurerm_function_app_host_keys.function_keys.default_function_key + key_vault_id = azurerm_key_vault.key_vault.id + key_vault_uri = azurerm_key_vault.key_vault.vault_uri + + depends_on = [azurerm_storage_account.logicapp, module.network] +} + data "azurerm_subnet" "subnet" { resource_group_name = local.vnet_rg_name virtual_network_name = local.vnet_name diff --git a/user-data.sh b/user-data.sh deleted file mode 100644 index c2aa50c6..00000000 --- a/user-data.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -ex - -${user_data} - -${init_script} diff --git a/variables.tf b/variables.tf index a953e5f5..ded7bae6 100644 --- a/variables.tf +++ b/variables.tf @@ -387,7 +387,7 @@ variable "function_app_storage_account_container_prefix" { variable "function_app_version" { type = string description = "Function app code version (hash)" - default = "0e787ecc4b4936228cdb784e52ec408a" + default = "144dd946da08cb0cf72a8ce1947c1a71" } variable "function_app_dist" { @@ -582,11 +582,10 @@ variable "deployment_container_name" { description = "Name of exising deployment container" } -variable "deployment_storage_account_access_key" { +variable "deployment_function_app_code_blob" { type = string - description = "The access key of the existing Blob object store container." - sensitive = true - default = "" + description = "The path to the function app code blob file." + default = "function_app_code.zip" } variable "zone" { @@ -828,3 +827,15 @@ variable "debug_down_backends_removal_timeout" { default = "3h" description = "Don't change this value without consulting weka support team. Timeout for removing down backends. Valid time units are ns, us (or µs), ms, s, m, h." } + +variable "allow_sa_public_network_access" { + type = bool + default = true + description = "Allow public network access to the storage account." +} + +variable "create_storage_account_private_links" { + type = bool + default = true + description = "Create private links for storage accounts (in case if public network access for the storage account is disabled)." +} diff --git a/zip_function_app_creation/write_function_hash_to_variables.sh b/zip_function_app_creation/write_function_hash_to_variables.sh index 52fbb0e4..930d07d6 100755 --- a/zip_function_app_creation/write_function_hash_to_variables.sh +++ b/zip_function_app_creation/write_function_hash_to_variables.sh @@ -1,17 +1,21 @@ #!/bin/bash # Usage: -# write_function_hash_to_variables.sh +# write_function_hash_to_variables.sh -os_name="$1" -function_code_path="$2" +dist="$1" +os_name="$2" +function_code_path="$3" new_function_app_zip_version=$(./zip_function_app_creation/get_function_app_hash.sh ${os_name} ${function_code_path}) old_function_app_zip_version=$(awk '/Function app code version/{getline;print $NF;}' variables.tf | tr -d \") +old_dist=$(awk '/Function app code dist/{getline;print $NF;}' variables.tf | tr -d \") echo "Replacing '$old_function_app_zip_version' function_app_version to '$new_function_app_zip_version'" if [ $os_name == "darwin" ]; then sed -i '' "s/$old_function_app_zip_version/$new_function_app_zip_version/" variables.tf + sed -i '' "s/$old_dist\/$old_function_app_zip_version/$dist\/$new_function_app_zip_version/" README.md else sed -i "s/$old_function_app_zip_version/$new_function_app_zip_version/" variables.tf + sed -i "s/$old_dist\/$old_function_app_zip_version/$dist\/$new_function_app_zip_version/" README.md fi