Resolve issues and concerns with resource naming strategy and naming collisions (#281)

* *Refactor single-service template to use locals block
*Resolve names of resources that can be overridden in tfvars
*Refactor HW template to use locals block
*Moved provider to commons.tf
*Following same pattern as other templates for resource naming
*Compute the basename consistently
*Fallback to admin RG for ACR if not specified in TF vars
*Randomization element for HW template resource names
*Randomization element for simple single-service template
*Terraform insists on determinism for count metaproperty
*Randomization element for ISO template
*Include randomized string in shortened names

* Tdevani workingbranch (#274)

* adding commits to pull out resource name into the templates

* updating az-isolated template for single region to update app service module

* *Use name prefixes on app services too
*updating the iso template documentation for inputs & outputs based on the recent changes
*updating readme for single region with the required variables
*Unit test fixes
*Remove resource names from asserts
*Remove resource names that are indeterminate
*Deterministic trigger for webhook creation
*Set ACR flag in ISO template
*Fix webhook URLs
*Remove hardcoded name checks from integration tests
*Webhook Name must be <= 50 chars
*Escaped res group param
*Resolves problems with AZ CLI commands for management of app service ACR pull and authentication

* Missed during rebase

* Global Web App name is capped at 60 characters total (https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions#web)

* Proper name of trigger
This commit is contained in:
Keith Rome 2019-09-12 15:47:16 -05:00 коммит произвёл GitHub
Родитель 6f50be7ce0
Коммит 37722f874f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
24 изменённых файлов: 329 добавлений и 171 удалений

Просмотреть файл

@ -18,7 +18,7 @@ data "azurerm_app_service_plan" "appsvc" {
}
resource "azurerm_app_service" "appsvc" {
name = format("%s-%s", lower(local.app_names[count.index]), lower(terraform.workspace))
name = format("%s-%s", var.app_service_name_prefix, lower(local.app_names[count.index]))
resource_group_name = data.azurerm_resource_group.appsvc.name
location = data.azurerm_resource_group.appsvc.location
app_service_plan_id = data.azurerm_app_service_plan.appsvc.id
@ -47,25 +47,23 @@ resource "azurerm_app_service" "appsvc" {
}
resource "null_resource" "acr_webhook_creation" {
count = var.docker_enable_ci == true && var.azure_container_registry_name != "" ? length(local.app_names) : 0
count = var.docker_enable_ci == true && var.uses_acr ? length(local.app_names) : 0
depends_on = [azurerm_app_service.appsvc]
triggers = {
images_to_deploy = "${join(",", [for config in local.app_configs : config.image])}"
acr_name = var.azure_container_registry_name
uses_acr = var.uses_acr
}
provisioner "local-exec" {
command = "az acr webhook create --registry $ACRNAME --name $APPNAME$WRKSPACE$WEBHOOKNAME --actions push --uri $(az webapp deployment container show-cd-url -n $APPNAME_URL-$WRKSPACE_URL -g $APPSVCNAME --query CI_CD_URL -o tsv)"
command = "az acr webhook create --registry \"$ACRNAME\" --name \"$APPNAME$WEBHOOKNAME\" --actions push --uri $(az webapp deployment container show-cd-url -n $APPNAME_URL -g $APPSVCNAME --query CI_CD_URL -o tsv)"
environment = {
ACRNAME = var.azure_container_registry_name
APPNAME = replace(lower(local.app_names[count.index]), "-", "")
WRKSPACE = replace(lower(terraform.workspace), "-", "")
APPNAME_URL = lower(local.app_names[count.index])
WRKSPACE_URL = lower(terraform.workspace)
WEBHOOKNAME = local.acr_webhook_name
APPSVCNAME = data.azurerm_resource_group.appsvc.name
ACRNAME = var.azure_container_registry_name
APPNAME = replace(lower(local.app_names[count.index]), "-", "")
APPNAME_URL = format("%s-%s", var.app_service_name_prefix, lower(local.app_names[count.index]))
WEBHOOKNAME = local.acr_webhook_name
APPSVCNAME = data.azurerm_resource_group.appsvc.name
}
}
@ -73,7 +71,7 @@ resource "null_resource" "acr_webhook_creation" {
resource "azurerm_app_service_slot" "appsvc_staging_slot" {
name = "staging"
app_service_name = format("%s-%s", lower(local.app_names[count.index]), lower(terraform.workspace))
app_service_name = format("%s-%s", var.app_service_name_prefix, lower(local.app_names[count.index]))
count = length(local.app_names)
location = data.azurerm_resource_group.appsvc.location
resource_group_name = data.azurerm_resource_group.appsvc.name
@ -89,11 +87,11 @@ data "azurerm_app_service" "all" {
resource "azurerm_template_deployment" "access_restriction" {
name = "access_restriction"
count = var.vnet_name == "" ? 0 : length(local.app_names)
count = var.uses_vnet ? length(local.app_names) : 0
resource_group_name = data.azurerm_resource_group.appsvc.name
parameters = {
service_name = format("%s-%s", lower(local.app_names[count.index]), lower(terraform.workspace))
service_name = format("%s-%s", var.app_service_name_prefix, lower(local.app_names[count.index]))
vnet_subnet_id = var.vnet_subnet_id
access_restriction_name = local.access_restriction_name
access_restriction_description = local.access_restriction_description

Просмотреть файл

@ -8,6 +8,17 @@ variable "service_plan_name" {
type = string
}
variable "app_service_name_prefix" {
description = "String value prepended to the name of each app service"
type = string
}
variable "uses_acr" {
description = "Determines whether or not an Azure container registry is being used"
type = bool
default = false
}
variable "azure_container_registry_name" {
description = "The name of the azure container registry resource"
type = string
@ -52,6 +63,12 @@ variable "site_config_always_on" {
default = true
}
variable "uses_vnet" {
description = "Determines whether or not a virtual network is being used"
type = bool
default = false
}
variable "vnet_name" {
description = "The vnet integration name."
type = string

Просмотреть файл

@ -0,0 +1,45 @@
module "provider" {
source = "../../modules/providers/azure/provider"
}
resource "random_string" "workspace_scope" {
keepers = {
# Generate a new id each time we switch to a new workspace or app id
ws_name = replace(trimspace(lower(terraform.workspace)), "_", "-")
app_id = replace(trimspace(lower(var.name)), "_", "-")
}
length = max(1, var.randomization_level) // error for zero-length
special = false
upper = false
}
locals {
// sanitize names
app_id = random_string.workspace_scope.keepers.app_id
region = replace(trimspace(lower(var.resource_group_location)), "_", "-")
ws_name = random_string.workspace_scope.keepers.ws_name
suffix = var.randomization_level > 0 ? "-${random_string.workspace_scope.result}" : ""
// base name for resources, name constraints documented here: https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions
base_name = "${local.app_id}-${local.ws_name}${local.suffix}"
base_name_21 = length(local.base_name) < 22 ? local.base_name : "${substr(local.base_name, 0, 21 - length(local.suffix))}${local.suffix}"
base_name_46 = length(local.base_name) < 47 ? local.base_name : "${substr(local.base_name, 0, 46 - length(local.suffix))}${local.suffix}"
base_name_60 = length(local.base_name) < 61 ? local.base_name : "${substr(local.base_name, 0, 60 - length(local.suffix))}${local.suffix}"
base_name_76 = length(local.base_name) < 77 ? local.base_name : "${substr(local.base_name, 0, 76 - length(local.suffix))}${local.suffix}"
base_name_83 = length(local.base_name) < 84 ? local.base_name : "${substr(local.base_name, 0, 83 - length(local.suffix))}${local.suffix}"
// Resolved resource names
app_rg_name = "${local.base_name_83}-app-rg" // app resource group (max 90 chars)
sp_name = "${local.base_name}-sp" // service plan
app_svc_name_prefix = local.base_name_21
// Resolved TF Vars
reg_url = var.docker_registry_server_url
app_services = {
for target in var.deployment_targets :
target.app_name => {
image = "${target.image_name}:${target.image_release_tag_prefix}"
}
}
}

Просмотреть файл

@ -1,29 +1,20 @@
module "provider" {
source = "../../modules/providers/azure/provider"
}
resource "azurerm_resource_group" "main" {
name = var.prefix
location = var.resource_group_location
name = local.app_rg_name
location = local.region
}
module "service_plan" {
source = "../../modules/providers/azure/service-plan"
resource_group_name = azurerm_resource_group.main.name
service_plan_name = "${azurerm_resource_group.main.name}-sp"
service_plan_name = local.sp_name
}
module "app_service" {
source = "../../modules/providers/azure/app-service"
app_service_name_prefix = local.app_svc_name_prefix
service_plan_name = module.service_plan.service_plan_name
service_plan_resource_group_name = azurerm_resource_group.main.name
docker_registry_server_url = var.docker_registry_server_url
app_service_config = {
for target in var.deployment_targets :
target.app_name => {
image = "${target.image_name}:${target.image_release_tag_prefix}"
}
}
docker_registry_server_url = local.reg_url
app_service_config = local.app_services
}

Просмотреть файл

@ -3,14 +3,15 @@
# responsibility to choose the values that make sense for your application.
#
# Note: These values will impact the names of resources. If your deployment
# fails due to a resource name colision, consider using different values for
# the `prefix` variable.
# fails due to a resource name collision, consider using different values for
# the `name` variable or increasing the value for `randomization_level`.
resource_group_location = "eastus"
prefix = "az-hello-world"
name = "az-hello-world"
randomization_level = 8
deployment_targets = [{
app_name = "cobalt-backend-api",
image_name = "appsvcsample/static-site",
image_release_tag_prefix = "latest"
}]

Просмотреть файл

@ -1,7 +1,6 @@
package test
import (
"fmt"
"os"
"strings"
"testing"
@ -44,14 +43,10 @@ func httpGetRespondsWith200(goTest *testing.T, output infratests.TerraformOutput
}
func TestAzureSimple(t *testing.T) {
workspace := terraform.RunTerraformCommand(t, tfOptions, "workspace", "show")
testFixture := infratests.IntegrationTestFixture{
GoTest: t,
TfOptions: tfOptions,
ExpectedTfOutputCount: 1,
ExpectedTfOutput: infratests.TerraformOutput{
"app_service_default_hostname": strings.ToLower(fmt.Sprintf("https://cobalt-backend-api-%s.azurewebsites.net", workspace)),
},
TfOutputAssertions: []infratests.TerraformOutputValidation{
httpGetRespondsWith200,
},

Просмотреть файл

@ -18,7 +18,7 @@ var tfOptions = &terraform.Options{
TerraformDir: "../../",
Upgrade: true,
Vars: map[string]interface{}{
"prefix": prefix,
"name": prefix,
"resource_group_location": datacenter,
},
BackendConfig: map[string]interface{}{
@ -31,12 +31,11 @@ func TestAzureSimple(t *testing.T) {
testFixture := infratests.UnitTestFixture{
GoTest: t,
TfOptions: tfOptions,
ExpectedResourceCount: 9,
ExpectedResourceCount: 10,
PlanAssertions: nil,
Workspace: workspace,
ExpectedResourceAttributeValues: infratests.ResourceDescription{
"module.app_service.azurerm_app_service.appsvc[0]": map[string]interface{}{
"resource_group_name": prefix,
"app_settings": map[string]interface{}{
"WEBSITES_ENABLE_APP_SERVICE_STORAGE": "false",
},
@ -53,7 +52,6 @@ func TestAzureSimple(t *testing.T) {
},
"azurerm_resource_group.main": map[string]interface{}{
"location": datacenter,
"name": prefix,
},
},
}

Просмотреть файл

@ -1,13 +1,25 @@
variable "prefix" {
description = "The prefix used for all resources in this example"
// ---- General Configuration ----
variable "name" {
description = "An identifier used to construct the names of all resources in this template."
type = string
}
variable "randomization_level" {
description = "Number of additional random characters to include in resource names to insulate against unexpected resource name collisions."
type = number
default = 8
}
variable "resource_group_location" {
description = "The Azure location where all resources in this example should be created."
description = "The Azure region where all resources in this template should be created."
type = string
}
// ---- App Service Configuration ----
variable "deployment_targets" {
description = "Metadata about apps to deploy, such as image metadata."
type = list(object({

Просмотреть файл

@ -10,12 +10,12 @@ provider "azurerm" {
resource "azurerm_resource_group" "app_rg" {
name = local.app_rg_name
location = var.resource_group_location
location = local.region
provider = azurerm.app_dev
}
resource "azurerm_management_lock" "app_rg_lock" {
name = format("%s-delete-lock", local.app_rg_name)
name = local.app_rg_lock
scope = azurerm_resource_group.app_rg.id
lock_level = "CanNotDelete"
provider = azurerm.app_dev
@ -64,7 +64,7 @@ module "container_registry" {
module "app_service_principal_contributor" {
source = "../../modules/providers/azure/service-principal"
create_for_rbac = true
display_name = local.svc_principal_name
display_name = local.svc_princ_name
role_name = "Contributor"
role_scope = "${module.container_registry.container_registry_id}"
}
@ -90,7 +90,7 @@ module "app_service_principal_secrets" {
module "acr_service_principal_acrpull" {
source = "../../modules/providers/azure/service-principal"
create_for_rbac = true
display_name = local.acr_svc_principal_name
display_name = local.acr_svc_princ_name
role_name = "acrpull"
role_scope = "${module.container_registry.container_registry_id}"
}

Просмотреть файл

@ -10,12 +10,12 @@ provider "azurerm" {
resource "azurerm_resource_group" "admin_rg" {
name = local.admin_rg_name
location = var.resource_group_location
location = local.region
provider = azurerm.admin
}
resource "azurerm_management_lock" "admin_rg_lock" {
name = format("%s-delete-lock", local.admin_rg_name)
name = local.admin_rg_lock
scope = azurerm_resource_group.admin_rg.id
lock_level = "CanNotDelete"
provider = azurerm.admin
@ -53,8 +53,10 @@ module "service_plan" {
module "app_service" {
source = "../../modules/providers/azure/app-service"
service_plan_name = module.service_plan.service_plan_name
app_service_name_prefix = local.app_svc_name_prefix
service_plan_resource_group_name = azurerm_resource_group.admin_rg.name
app_insights_instrumentation_key = module.app_insights.app_insights_instrumentation_key
uses_acr = true
vault_uri = module.keyvault.keyvault_uri
azure_container_registry_name = module.container_registry.container_registry_name
docker_registry_server_url = module.container_registry.container_registry_login_server
@ -85,9 +87,11 @@ module "app_service_keyvault_access_policy" {
module "authn_app_service" {
source = "../../modules/providers/azure/app-service"
service_plan_name = module.service_plan.service_plan_name
app_service_name_prefix = local.auth_svc_name_prefix
service_plan_resource_group_name = azurerm_resource_group.admin_rg.name
vault_uri = module.keyvault.keyvault_uri
app_insights_instrumentation_key = module.app_insights.app_insights_instrumentation_key
uses_acr = true
azure_container_registry_name = module.container_registry.container_registry_name
docker_registry_server_url = module.container_registry.container_registry_login_server
docker_registry_server_username = module.container_registry.admin_username
@ -143,7 +147,7 @@ resource "null_resource" "auth" {
}
provisioner "local-exec" {
command = "az webapp auth update -g $RES_GRP -n $APPNAME --enabled true --action LoginWithAzureActiveDirectory --aad-token-issuer-url \"$ISSUER\" --aad-client-id \"$APPID\""
command = "az webapp auth update -g \"$RES_GRP\" -n \"$APPNAME\" --enabled true --action LoginWithAzureActiveDirectory --aad-token-issuer-url \"$ISSUER\" --aad-client-id \"$APPID\""
environment = {
RES_GRP = azurerm_resource_group.admin_rg.name

Просмотреть файл

@ -6,17 +6,60 @@ data "azurerm_subscription" "current" {}
data "azurerm_client_config" "current" {}
resource "random_string" "workspace_scope" {
keepers = {
# Generate a new id each time we switch to a new workspace or app id
ws_name = replace(trimspace(lower(terraform.workspace)), "_", "-")
app_id = replace(trimspace(lower(var.name)), "_", "-")
}
length = max(1, var.randomization_level) // error for zero-length
special = false
upper = false
}
locals {
prefix = "${lower(var.name)}-${lower(terraform.workspace)}"
tenant_id = data.azurerm_client_config.current.tenant_id
admin_rg_name = "${local.prefix}-admin-rg" // name of resource group used for admin resources
app_rg_name = "${local.prefix}-app-rg" // name of app resource group
sp_name = "${local.prefix}-sp" // name of service plan
ai_name = "${local.prefix}-ai" // name of app insights
kv_name = format("%s%s-kv", format("%.10s", lower(var.name)), format("%.10s", lower(terraform.workspace))) // name of key vault
acr_name = replace(format("%s%sacr", format("%.10s", lower(var.name)), format("%.10s", lower(terraform.workspace))), "/\\W/", "") // name of acr
ase_sub_id = var.ase_subscription_id == "" ? data.azurerm_subscription.current.subscription_id : var.ase_subscription_id
app_sub_id = var.app_dev_subscription_id == "" ? data.azurerm_subscription.current.subscription_id : var.app_dev_subscription_id
// sanitize names
app_id = random_string.workspace_scope.keepers.app_id
region = replace(trimspace(lower(var.resource_group_location)), "_", "-")
ws_name = random_string.workspace_scope.keepers.ws_name
suffix = var.randomization_level > 0 ? "-${random_string.workspace_scope.result}" : ""
// base name for resources, name constraints documented here: https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions
base_name = "${local.app_id}-${local.ws_name}${local.suffix}"
base_name_21 = length(local.base_name) < 22 ? local.base_name : "${substr(local.base_name, 0, 21 - length(local.suffix))}${local.suffix}"
base_name_46 = length(local.base_name) < 47 ? local.base_name : "${substr(local.base_name, 0, 46 - length(local.suffix))}${local.suffix}"
base_name_60 = length(local.base_name) < 61 ? local.base_name : "${substr(local.base_name, 0, 60 - length(local.suffix))}${local.suffix}"
base_name_76 = length(local.base_name) < 77 ? local.base_name : "${substr(local.base_name, 0, 76 - length(local.suffix))}${local.suffix}"
base_name_83 = length(local.base_name) < 84 ? local.base_name : "${substr(local.base_name, 0, 83 - length(local.suffix))}${local.suffix}"
tenant_id = data.azurerm_client_config.current.tenant_id
// Resource names
admin_rg_name = "${local.base_name_83}-adm-rg" // resource group used for admin resources (max 90 chars)
app_rg_name = "${local.base_name_83}-app-rg" // app resource group (max 90 chars)
admin_rg_lock = "${local.base_name_83}-adm-rg-delete-lock" // management lock to prevent deletes
app_rg_lock = "${local.base_name_83}-app-rg-delete-lock" // management lock to prevent deletes
sp_name = "${local.base_name}-sp" // service plan
ai_name = "${local.base_name}-ai" // app insights
kv_name = "${local.base_name_21}-kv" // key vault (max 24 chars)
acr_name = "${replace(local.base_name_46, "-", "")}acr" // container registry (max 50 chars, alphanumeric *only*)
vnet_name = "${local.base_name_60}-net" // virtual network (max 64 chars)
tm_profile_name = "${local.base_name_60}-tf" // traffic manager profile (max 63 chars)
tm_endpoint_name = "${local.region}_${local.app_id}" // traffic manager endpoint
tm_dns_name = "${local.base_name}-dns" // traffic manager dns relative name
appgateway_name = "${local.base_name_76}-gw" // app gateway (max 80 chars)
public_pip_name = "${local.base_name_76}-ip" // public IP (max 80 chars)
svc_princ_name = "${local.base_name}-svc-principal" // service principal
acr_svc_princ_name = "${local.base_name}-acr-svc-principal" // container registry service principal
app_svc_name_prefix = local.base_name_21
auth_svc_name_prefix = "${local.base_name_21}-au"
// Resolved TF Vars
ase_sub_id = var.ase_subscription_id == "" ? data.azurerm_subscription.current.subscription_id : var.ase_subscription_id
app_sub_id = var.app_dev_subscription_id == "" ? data.azurerm_subscription.current.subscription_id : var.app_dev_subscription_id
// id of App Service Environment
ase_id = "/subscriptions/${local.ase_sub_id}/resourceGroups/${var.ase_resource_group}/providers/Microsoft.Web/hostingEnvironments/${var.ase_name}"
app_secrets = {
"app-service-principal-object-id" = module.app_service_principal_contributor.service_principal_object_id,
@ -33,8 +76,4 @@ locals {
acr_password = {
"acr-service-principal-password" = module.acr_service_principal_acrpull.service_principal_password
}
svc_principal_name = "${local.prefix}-svc-principal"
acr_svc_principal_name = "${local.prefix}-acr-svc-principal"
// id of App Service Environment
ase_id = "/subscriptions/${local.ase_sub_id}/resourceGroups/${var.ase_resource_group}/providers/Microsoft.Web/hostingEnvironments/${var.ase_name}"
}

Просмотреть файл

@ -79,18 +79,48 @@ Only one terraform template can be deployed within a given workspace. The use ca
## Module and Template Deep Dive
### Template Inputs
General Configuration
| name | type | default | description |
|---|---|---|---|
| `resource_group_location` | string | | The deployment location of resource group container all the resources |
| `name` | string | | The name of the deployment. This will be used across the resource created in this solution |
| `app_service_containers` | map(str) | | The name key value pair where the key is the name assigned to the app service and value is the source container |
| `metrics_configuration` | MetricsConfig **see below** | `[]` | A list of metrics configuration that should be configured for the app service plan |
| `resource_group_location` | string | | The Azure region where all resources in this template should be created |
| `name` | string | | It serves as an identifier used to construct the names of all resources in this template
| `randomization_level` | number | | Number of additional random characters to include in resource names to insulate against unexpected resource name collisions |
App Service Environment Configuration
| name | type | default | description |
|---|---|---|---|
| `app_service_containers` | string | | The ID of the subscription in which the ASE lives in |
| `ase_name` | string | | The name of the App Service Environment in which to deploy the app services |
| `ase_resource_group` | string | | The resource group of the App Service Environment in which to deploy the app services |
| `monitoring_dimension_values` | `["*"]` | | Dimensions used to determine service plan scaling |
| `ase_vnet_name` | string | | The name of the VNET in which the ASE lives |
| `service_plan_scaling_rules` | list(rule) **see below** | `[]` | A list of scaline rules to apply to the service plan
| `provision_sql` | bool | `false` | True if SQL Server should be deployed, false otherwise
| `provision_storage_account` | bool | `false` | True if s storage account should be deployed, false otherwise
| `authenticated_clients` | list(string) | `[]` | List of clients to authenticate if `enable_auth` is true
| `ase_vnet_name` | string | | The name of the VNET that the App Service Environment is deployed to |
App Service Configuration
| name | type | default | description |
|---|---|---|---|
| `unauthn_deployment_targets` | list(obj) | | DMetadata about apps to deploy, such as repository location, docker file metadata and image names |
| `authn_deployment_targets` | list(obj) | | Metadata about apps to deploy that also require authentication |
Service Plan Configuration
| name | type | default | description |
|---|---|---|---|
| `monitoring_dimension_values` | `["*"]` | | Dimensions used to determine service plan scaling |
| `service_plan_size` | string | | The size of the service plan instance. Valid values are I1, I2, I3 |
| `service_plan_kind` | string | | The kind of Service Plan to be created. Possible values are Windows/Linux/FunctionApp/App |
| `scaling_rules` | list(obj) | | The scaling rules for the app service plan. |
App Service Authentication Configuration
| name | type | default | description |
|---|---|---|---|
| `auth_suffix` | string | | easy-auth | A name to be appended to all azure ad applications
| `graph_role_id` | string | | 00000002-0000-0000-c000-000000000000The size of the service plan instance. Valid values are I1, I2, I3 |
| `graph_id` | string | 00000002-0000-0000-c000-000000000000 | The kind of Service Plan to be created. Possible values are Windows/Linux/FunctionApp/App |
App Dev Subscription and Networking
| name | type | default | description |
|---|---|---|---|
| `App Dev Subscription` | string | | Subscription in which the application dependencies will be deployed to |
| `resource_ip_whitelist` | list[string] | | A list of IPs and/or IP ranges that should have access to VNET isolated resources provisioned by this template |
**Notes**

Просмотреть файл

@ -3,8 +3,12 @@
# responsibility to choose the values that make sense for your application.
#
# Note: These values will impact the names of resources. If your deployment
# fails due to a resource name colision, consider using different values for
# the `name` variable.
# fails due to a resource name collision, consider using different values for
# the `name` variable or increasing the value for `randomization_level`.
resource_group_location = "eastus2"
name = "isolated-service"
randomization_level = 8
# Targets that will be configured to also setup AuthN with Easy Auth
authn_deployment_targets = [
@ -30,9 +34,7 @@ unauthn_deployment_targets = [
# Note: this is configured as such only to test IP Whitelists. This is a well
# known DNS address
resource_ip_whitelist = ["13.107.6.0/24", "13.107.9.0/24", "13.107.42.0/24", "13.107.43.0/24", "40.74.0.0/15", "40.76.0.0/14", "40.80.0.0/12", "40.96.0.0/12", "40.112.0.0/13", "40.120.0.0/14", "40.124.0.0/16", "40.125.0.0/17"]
ase_name = "co-static-ase"
name = "isolated-service"
ase_resource_group = "co-static-ase-rg"
ase_vnet_name = "co-static-ase-vnet"
resource_group_location = "eastus2"
resource_ip_whitelist = ["13.107.6.0/24", "13.107.9.0/24", "13.107.42.0/24", "13.107.43.0/24", "40.74.0.0/15", "40.76.0.0/14", "40.80.0.0/12", "40.96.0.0/12", "40.112.0.0/13", "40.120.0.0/14", "40.124.0.0/16", "40.125.0.0/17"]
ase_name = "co-static-ase"
ase_resource_group = "co-static-ase-rg"
ase_vnet_name = "co-static-ase-vnet"

Просмотреть файл

@ -54,8 +54,6 @@ var tfOptions = &terraform.Options{
}
func TestIsoSingleRegion(t *testing.T) {
workspace = terraform.RunTerraformCommand(t, tfOptions, "workspace", "show")
// Note: creating an App Service Plan configured with an Isolated SKU can take > 1.5
// hours. In order to prevent a very long test cycle this test uses a static environment
// that lives beyond the lifetime of this test. This is achieved using the
@ -68,12 +66,6 @@ func TestIsoSingleRegion(t *testing.T) {
GoTest: t,
TfOptions: tfOptions,
ExpectedTfOutputCount: 10,
ExpectedTfOutput: infratests.TerraformOutput{
"fqdns": []string{
"http://co-backend-api-1-" + workspace + "." + aseName + ".p.azurewebsites.net",
"http://co-frontend-api-1-" + workspace + "." + aseName + ".p.azurewebsites.net",
},
},
TfOutputAssertions: []infratests.TerraformOutputValidation{
// These are commented because we are using hosted build agents
// and would need to add all azure ips in whitelist. When we move to

Просмотреть файл

@ -65,17 +65,13 @@ func asMap(t *testing.T, jsonString string) map[string]interface{} {
func TestTemplate(t *testing.T) {
expectedStagingSlot := asMap(t, `{"name":"staging"}`)
expectedAppDevResourceGroup := asMap(t, `{
"name": "isolated-service-`+workspace+`-app-rg",
"location": "`+region+`"
}`)
expectedAdminResourceGroup := asMap(t, `{
"name": "isolated-service-`+workspace+`-admin-rg",
"location": "`+region+`"
}`)
expectedAppInsights := asMap(t, `{
"name": "isolated-service-`+workspace+`-ai",
"application_type": "Web",
"resource_group_name": "isolated-service-`+workspace+`-admin-rg"
"application_type": "Web"
}`)
// expectedKeyVault := asMap(t, `{
// "network_acls": [{
@ -149,11 +145,9 @@ func TestTemplate(t *testing.T) {
aseResourceGroup,
aseName)
expectedAppServicePlan := asMap(t, `{
"app_service_environment_id": "`+expectedAppServiceEnvID+`",
"app_service_environment_id": "`+expectedAppServiceEnvID+`",
"kind": "Linux",
"name": "isolated-service-`+workspace+`-sp",
"reserved": true,
"resource_group_name": "isolated-service-`+workspace+`-admin-rg",
"sku": [{ "capacity": 1, "size": "I1", "tier": "Isolated" }]
}`)
expectedAutoScalePlan := asMap(t, `{
@ -202,22 +196,18 @@ func TestTemplate(t *testing.T) {
}`)
expectedAppServiceSchema := `{
"identity": [{ "type": "SystemAssigned" }],
"name": "co-backend-api-%d-%s",
"resource_group_name": "isolated-service-%s-admin-rg",
"site_config": [{
"always_on": true
}]
}`
expectedAppServiceSchema2 := `{
"identity": [{ "type": "SystemAssigned" }],
"name": "co-frontend-api-%d-%s",
"resource_group_name": "isolated-service-%s-admin-rg",
"site_config": [{
"always_on": true
}]
}`
expectedAppService1 := asMap(t, fmt.Sprintf(expectedAppServiceSchema, 1, workspace, workspace))
expectedAppService2 := asMap(t, fmt.Sprintf(expectedAppServiceSchema2, 1, workspace, workspace))
expectedAppService1 := asMap(t, expectedAppServiceSchema)
expectedAppService2 := asMap(t, expectedAppServiceSchema2)
testFixture := infratests.UnitTestFixture{
GoTest: t,

Просмотреть файл

@ -1,12 +1,18 @@
// ---- General Configuration ----
variable "resource_group_location" {
description = "The deployment location of resource group container all the resources"
variable "name" {
description = "An identifier used to construct the names of all resources in this template."
type = string
}
variable "name" {
description = "The name of the deployment. This will be used across the resource created in this solution"
variable "randomization_level" {
description = "Number of additional random characters to include in resource names to insulate against unexpected resource name collisions."
type = number
default = 8
}
variable "resource_group_location" {
description = "The Azure region where all resources in this template should be created."
type = string
}

Просмотреть файл

@ -97,8 +97,8 @@ terraform destroy
#### Required Variables
1. `resource_group_location`: The deployment location of resource group container all the resources.
2. `name`: The name of the deployment. This will be used across the resource created in this solution.
1. `resource_group_location`: The deployment location of resource group container all the resource
2. `name`: An identifier used to construct the names of all resources in this template.
3. `app_service_name`: The name key value pair where the key is representative to the app service name and value is the source container.
#### Optional Variables

Просмотреть файл

@ -1,20 +1,6 @@
locals {
prefix = "${lower(var.name)}-${lower(terraform.workspace)}"
prefix_short = format("%.20s", local.prefix)
tm_profile_name = "${local.prefix}-tf"
vnet_name = "${local.prefix}-vnet"
tm_endpoint_name = "${var.resource_group_location}_${var.name}"
tm_dns_name = "${local.prefix}-dns"
appgateway_name = "${local.prefix}-gateway"
public_pip_name = "${local.prefix}-ip"
kv_name = "${local.prefix_short}kv"
acr_name = "${replace(local.prefix, "-", "")}acr"
resource_group_name = "${local.prefix}"
}
resource "azurerm_resource_group" "svcplan" {
name = local.resource_group_name
location = var.resource_group_location
name = local.admin_rg_name
location = local.region
}
module "vnet" {
@ -65,8 +51,8 @@ module "app_gateway" {
module "container_registry" {
source = "../../modules/providers/azure/container-registry"
container_registry_name = var.azure_container_resource_name == "" ? local.acr_name : var.azure_container_resource_name
resource_group_name = var.azure_container_resource_group == "" ? azurerm_resource_group.svcplan.name : var.azure_container_resource_group
container_registry_name = local.resolved_acr_name
resource_group_name = local.resolved_acr_rg_name
container_registry_admin_enabled = true
container_registry_tags = var.azure_container_tags
}

Просмотреть файл

@ -1,11 +1,6 @@
locals {
service_plan_name = "${local.prefix}-sp"
app_insights_name = "${local.prefix}-ai"
}
data "azurerm_container_registry" "acr" {
name = module.container_registry.container_registry_name
resource_group_name = var.azure_container_resource_group == "" ? azurerm_resource_group.svcplan.name : var.azure_container_resource_group
name = local.resolved_acr_name
resource_group_name = local.resolved_acr_rg_name
depends_on = ["azurerm_resource_group.svcplan", "module.container_registry"]
}
@ -32,25 +27,24 @@ resource "null_resource" "acr_image_deploy" {
}
}
module "provider" {
source = "../../modules/providers/azure/provider"
}
module "service_plan" {
source = "../../modules/providers/azure/service-plan"
resource_group_name = azurerm_resource_group.svcplan.name
service_plan_name = local.service_plan_name
service_plan_name = local.sp_name
}
module "app_service" {
source = "../../modules/providers/azure/app-service"
app_service_name = local.app_service_name
service_plan_name = module.service_plan.service_plan_name
service_plan_resource_group_name = azurerm_resource_group.svcplan.name
app_insights_instrumentation_key = module.app_insight.app_insights_instrumentation_key
uses_acr = true
azure_container_registry_name = module.container_registry.container_registry_name
docker_registry_server_url = module.container_registry.container_registry_login_server
docker_registry_server_username = data.azurerm_container_registry.acr.admin_username
docker_registry_server_password = data.azurerm_container_registry.acr.admin_password
uses_vnet = true
vnet_name = module.vnet.vnet_name
vnet_subnet_id = module.vnet.vnet_subnet_ids[0]
vault_uri = module.keyvault.keyvault_uri
@ -72,7 +66,7 @@ resource "azurerm_role_assignment" "acr_pull" {
module "app_insight" {
source = "../../modules/providers/azure/app-insights"
service_plan_resource_group_name = azurerm_resource_group.svcplan.name
appinsights_name = local.app_insights_name
appinsights_name = local.ai_name
appinsights_application_type = var.application_type
}

Просмотреть файл

@ -0,0 +1,51 @@
module "provider" {
source = "../../modules/providers/azure/provider"
}
resource "random_string" "workspace_scope" {
keepers = {
# Generate a new id each time we switch to a new workspace or app id
ws_name = replace(trimspace(lower(terraform.workspace)), "_", "-")
app_id = replace(trimspace(lower(var.name)), "_", "-")
}
length = max(1, var.randomization_level) // error for zero-length
special = false
upper = false
}
locals {
// sanitize names
app_id = random_string.workspace_scope.keepers.app_id
region = replace(trimspace(lower(var.resource_group_location)), "_", "-")
ws_name = random_string.workspace_scope.keepers.ws_name
suffix = var.randomization_level > 0 ? "-${random_string.workspace_scope.result}" : ""
// base name for resources, name constraints documented here: https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions
base_name = "${local.app_id}-${local.ws_name}${local.suffix}"
base_name_21 = length(local.base_name) < 22 ? local.base_name : "${substr(local.base_name, 0, 21 - length(local.suffix))}${local.suffix}"
base_name_46 = length(local.base_name) < 47 ? local.base_name : "${substr(local.base_name, 0, 46 - length(local.suffix))}${local.suffix}"
base_name_60 = length(local.base_name) < 61 ? local.base_name : "${substr(local.base_name, 0, 60 - length(local.suffix))}${local.suffix}"
base_name_76 = length(local.base_name) < 77 ? local.base_name : "${substr(local.base_name, 0, 76 - length(local.suffix))}${local.suffix}"
base_name_83 = length(local.base_name) < 84 ? local.base_name : "${substr(local.base_name, 0, 83 - length(local.suffix))}${local.suffix}"
// Resource names
admin_rg_name = "${local.base_name_83}-adm-rg" // resource group used for admin resources (max 90 chars)
app_rg_name = "${local.base_name_83}-app-rg" // app resource group (max 90 chars)
sp_name = "${local.base_name}-sp" // service plan
ai_name = "${local.base_name}-ai" // app insights
kv_name = "${local.base_name_21}-kv" // key vault (max 24 chars)
acr_name = "${replace(local.base_name_46, "-", "")}acr" // container registry (max 50 chars, alphanumeric *only*)
vnet_name = "${local.base_name_60}-net" // virtual network (max 64 chars)
tm_profile_name = "${local.base_name_60}-tf" // traffic manager profile (max 63 chars)
tm_endpoint_name = "${local.region}_${local.app_id}" // traffic manager endpoint
tm_dns_name = "${local.base_name}-dns" // traffic manager dns relative name
appgateway_name = "${local.base_name_76}-gw" // app gateway (max 80 chars)
public_pip_name = "${local.base_name_76}-ip" // public IP (max 80 chars)
app_svc_name_prefix = local.base_name
// Resolved TF Vars
resolved_acr_rg_name = var.azure_container_resource_group == "" ? azurerm_resource_group.svcplan.name : var.azure_container_resource_group
resolved_acr_name = var.azure_container_resource_name == "" ? local.acr_name : var.azure_container_resource_name
}

Просмотреть файл

@ -3,11 +3,13 @@
# responsibility to choose the values that make sense for your application.
#
# Note: These values will impact the names of resources. If your deployment
# fails due to a resource name colision, consider using different values for
# the `name` variable.
# fails due to a resource name collision, consider using different values for
# the `name` variable or increasing the value for `randomization_level`.
resource_group_location = "eastus"
name = "az-simple"
randomization_level = 8
deployment_targets = [{
app_name = "cobalt-backend-api",
image_name = "msftcse/az-service-single-region",

Просмотреть файл

@ -86,14 +86,12 @@ func verifyRequestFails(goTest *testing.T, host string, protocol string, client
func TestAzureSimple(t *testing.T) {
azure.CliServicePrincipalLogin(t)
workspace := terraform.RunTerraformCommand(t, tfOptions, "workspace", "show")
testFixture := infratests.IntegrationTestFixture{
GoTest: t,
TfOptions: tfOptions,
ExpectedTfOutputCount: 5,
ExpectedTfOutput: infratests.TerraformOutput{
"app_gateway_health_probe_backend_address": "cobalt-backend-api-" + workspace + ".azurewebsites.net",
"app_gateway_health_probe_backend_status": "Healthy",
"app_gateway_health_probe_backend_status": "Healthy",
},
TfOutputAssertions: []infratests.TerraformOutputValidation{
verifyHTTPSSuccessOnFrontEnd,

Просмотреть файл

@ -35,7 +35,6 @@ func asMap(t *testing.T, jsonString string) map[string]interface{} {
func TestTemplate(t *testing.T) {
expectedAppServicePlan := asMap(t, `{
"name": "cobalt-backend-api-`+workspace+`"
}`)
expectedAppGatewayPlan := asMap(t, `{
@ -97,18 +96,13 @@ func TestTemplate(t *testing.T) {
TfOptions: tfOptions,
Workspace: workspace,
PlanAssertions: nil,
ExpectedResourceCount: 35,
ExpectedResourceCount: 36,
ExpectedResourceAttributeValues: infratests.ResourceDescription{
"azurerm_resource_group.svcplan": map[string]interface{}{
"location": region,
"name": "az-simple-" + workspace,
},
"module.app_gateway.data.azurerm_resource_group.appgateway": map[string]interface{}{
"name": "az-simple-" + workspace,
},
"module.app_insight.data.azurerm_resource_group.appinsights": map[string]interface{}{
"name": "az-simple-" + workspace,
},
"module.app_gateway.data.azurerm_resource_group.appgateway": map[string]interface{}{},
"module.app_insight.data.azurerm_resource_group.appinsights": map[string]interface{}{},
"module.app_service.azurerm_app_service_slot.appsvc_staging_slot[0]": map[string]interface{}{
"name": "staging",
},

Просмотреть файл

@ -1,24 +1,41 @@
# Resource Group
variable "resource_group_location" {
description = "The deployment location of resource group container all the resources"
// ---- General Configuration ----
variable "name" {
description = "An identifier used to construct the names of all resources in this template."
type = string
}
variable "randomization_level" {
description = "Number of additional random characters to include in resource names to insulate against unexpected resource name collisions."
type = number
default = 8
}
variable "resource_group_location" {
description = "The Azure region where all resources in this template should be created."
type = string
}
// ---- App Service Configuration ----
variable "application_type" {
description = "Type of the App Insights Application. Valid values are ios for iOS, java for Java web, MobileCenter for App Center, Node.JS for Node.js, other for General, phone for Windows Phone, store for Windows Store and web for ASP.NET."
default = "Web"
}
variable "name" {
description = "The name of the deployment. This will be used across the resource created in this solution"
type = string
}
variable "acr_build_git_source_url" {
description = "The URL to a git repository (e.g., 'https://github.com/Azure-Samples/acr-build-helloworld-node.git') containing the docker build manifests."
type = string
}
variable "acr_build_docker_file" {
description = "The relative path of the the docker file to the source code root folder. Default to 'Dockerfile'."
type = string
default = "Dockerfile"
}
variable "deployment_targets" {
description = "Metadata about apps to deploy, such as image metadata."
type = list(object({
@ -67,7 +84,9 @@ variable "subnet_prefixes" {
default = ["10.0.1.0/24"]
}
# Monitoring Service
// ---- Monitoring Service Configuration ----
variable "action_group_name" {
description = "The name of the action group."
type = string
@ -133,9 +152,3 @@ variable "monitoring_dimension_values" {
type = list(string)
default = ["*"]
}
variable "acr_build_docker_file" {
description = "The relative path of the the docker file to the source code root folder. Default to 'Dockerfile'."
type = string
default = "Dockerfile"
}