diff --git a/.github/workflows/.template.terraform.yml b/.github/workflows/.template.terraform.yml index 8afc1580..3714608b 100644 --- a/.github/workflows/.template.terraform.yml +++ b/.github/workflows/.template.terraform.yml @@ -7,7 +7,7 @@ on: type: string description: 'Terraform version' required: true - default: '1.3.9' + default: '1.9.7' modulePath: type: string description: 'Path to the Terraform module' @@ -94,4 +94,4 @@ jobs: backendStorageContainerName: ${{ secrets.AZURE_TF_STATE_STORAGE_CONTAINER_NAME }} ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION }} - ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} \ No newline at end of file diff --git a/.github/workflows/scenario1.terraform.yml b/.github/workflows/scenario1.terraform.yml index 0acf68af..d7a3c4fb 100644 --- a/.github/workflows/scenario1.terraform.yml +++ b/.github/workflows/scenario1.terraform.yml @@ -1,3 +1,4 @@ + name: 'Scenario 1: Terraform Multi-Tenant ASEv3 Secure Baseline' concurrency: @@ -40,9 +41,9 @@ permissions: env: modulePath: 'scenarios/secure-baseline-multitenant/terraform' - terraformVersion: 1.5.2 # must be greater than or equal to 1.2 for OIDC - backendStateKey: 'scenario1.hub.tfstate' - tfvarPath: 'parameters/ase-multitenant.parameters.tfvars' + terraformVersion: 1.9.7 # must be greater than or equal to 1.2 for OIDC + backendStateKey: 'scenario1.appsvclza.tfstate' + tfvarPath: '_parameters/ase-multitenant.parameters.tfvars' jobs: prepare-environment: @@ -56,31 +57,17 @@ jobs: backendStateKey: ${{ env.backendStateKey }} tfvarPath: ${{ env.tfvarPath }} - terraform-deploy-hub: - name: 'Terraform CICD (Hub Multi-tenant Secure Baseline)' + terraform-deploy: + name: 'Terraform CICD (Hub & Spoke Multi-tenant Secure Baseline)' needs: - prepare-environment uses: ./.github/workflows/.template.terraform.yml with: - modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/hub + modulePath: ${{ needs.prepare-environment.outputs.modulePath }} terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} - backendStateKey: 'scenario1.hub.tfstate' + backendStateKey: ${{ needs.prepare-environment.outputs.backendStateKey }} tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} # Ensure this value is a boolean destroy: ${{ github.event.inputs.destroy == 'true' }} secrets: inherit - terraform-deploy-spoke: - name: 'Terraform CICD (Spoke Multi-tenant Secure Baseline)' - needs: - - prepare-environment - - terraform-deploy-hub - uses: ./.github/workflows/.template.terraform.yml - with: - modulePath: ${{ needs.prepare-environment.outputs.modulePath }}/spoke - terraformVersion: ${{ needs.prepare-environment.outputs.terraformVersion }} - backendStateKey: 'scenario1.spoke.tfstate' - tfvarPath: ${{ needs.prepare-environment.outputs.tfvarPath }} - # Ensure this value is a boolean - destroy: ${{ github.event.inputs.destroy == 'true' }} - secrets: inherit \ No newline at end of file diff --git a/.gitignore b/.gitignore index 53e3c7a8..d95e33e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +backend.hcl deployment/bicep/localparam*.json deployment/bicep/localmain.bicep deployment/bicep/localtestscript.ps1 @@ -7,6 +8,8 @@ deployment/bicep/main.json # local .vscode folder **/.vscode/* +backend.hcl + # Local .terraform directories .terraform **/.terraform/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 058a9482..4b7eb072 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,8 @@ repos: rev: v1.74.1 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: - id: terraform_fmt - - id: terraform_validate + # Currently disabled due to the openai module not being able to be validated + # - id: terraform_validate - id: terraform_docs args: - --hook-config=--path-to-file=README.md # Valid UNIX path. I.e. ../TFDOC.md or docs/README.md etc. diff --git a/scenarios/secure-baseline-multitenant/.gitignore b/scenarios/secure-baseline-multitenant/.gitignore index 8e843802..d39999b9 100644 --- a/scenarios/secure-baseline-multitenant/.gitignore +++ b/scenarios/secure-baseline-multitenant/.gitignore @@ -1 +1,2 @@ .azure +backend.hcl \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index b5d30b4b..cf8e5532 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -40,8 +40,11 @@ tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" entra_admin_group_object_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" entra_admin_group_name = "Microsoft Entra ID SQL Admins" vm_entra_admin_username = "bob@contoso.com" +entra_admin_group_object_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +entra_admin_group_name = "Microsoft Entra ID SQL Admins" +vm_entra_admin_username = "bob@contoso.com" -# Optionally provide non-Entra admin credentials for the VM +# Optionally provide non-entra admin credentials for the VM # vm_admin_username = "daniem" # vm_admin_password = "**************" @@ -172,7 +175,7 @@ az upgrade az network bastion rdp --name bast-bastion --resource-group rg-hub --target-resource-id /subscriptions/{subscription-id}/resourceGroups/{rg-name}/providers/Microsoft.Compute/virtualMachines/{vm-name} --disable-gateway ``` -If you experience issues connecting to the DevOps VM using your Microsoft Entra ID credentials, see [Unable to connect to DevOps VM using Microsoft Entra ID credentials](#unable-to-connect-to-devops-vm-using-microsoft-entra-id-credentials) +If you experience issues connecting to the DevOps VM using your Microsoft Entra ID credentials, see [Unable to connect to DevOps VM using Microsoft Entra ID credentials](#unable-to-connect-to-devops-vm-using-entra-credentials) Once completed, you should be able to connect to the SQL Server using the Microsoft Entra ID account from SQL Server Management Studio. On the sample database (sample-db by default), run the following commands to create the user and grant minimal permissions (the exact command will be provided in the output of the Terraform deployment): @@ -207,7 +210,7 @@ az network front-door frontend-endpoint show --front-door-name ### Unable to connect to DevOps VM using Microsoft Entra ID credentials The Microsoft Entra ID enrollment can take a few minutes to complete. Check: [https://portal.manage-beta.microsoft.com/devices](https://portal.manage-beta.microsoft.com/devices) -Verify in the Azure Portal if the `aad-login-for-windows` VM extension was deployed successfully. +Verify in the Azure Portal if the `entra-login-for-windows` VM extension was deployed successfully. Connect to the VM using the local VM admin credentials and run `dsregcmd /status`. The output should look similar to this: @@ -261,3 +264,133 @@ Connect to the VM using the local VM admin credentials and run `dsregcmd /status ``` If the VM is Microsoft Entra ID joined, try to login in with the Microsoft Entra ID credentials again after a few minutes. If it's not Microsoft Entra ID joined, attempt to re-install the VM extension or manually enroll the VM to Microsoft Entra ID by following the steps in Edge: open Edge and click "Sign in to sync data", select "Work or school account", and then press OK on "Allow my organization to manage my device". It takes a few minutes for the policies to be applied, device scanned and confirmed as secure to access corporate resources. You will know that the process is complete. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >=1.3 | +| [azurecaf](#requirement\_azurecaf) | >=1.2.23 | +| [azurerm](#requirement\_azurerm) | 4.5.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [hub](#module\_hub) | ./hub | n/a | +| [spoke](#module\_spoke) | ./spoke | n/a | + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/resource_group) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | +| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = optional(list(string))

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1",
"zone_redundant": true
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [ase\_subnet\_cidr](#input\_ase\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26 | `list(string)` |
[
"10.240.5.0/24"
]
| no | +| [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | +| [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | +| [deployment\_options](#input\_deployment\_options) | [Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
deploy_asev3 = bool
})
|
{
"deploy_app_config": false,
"deploy_asev3": false,
"deploy_bastion": false,
"deploy_openai": false,
"deploy_redis": false,
"deploy_sql_database": false,
"deploy_vm": false,
"enable_diagnostic_settings": false,
"enable_egress_lockdown": false,
"enable_waf": false
}
| no | +| [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | [Required] The name of the Entra group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | [Required] The object ID of the Entra group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | +| [firewall\_subnet\_cidr](#input\_firewall\_subnet\_cidr) | [Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26 | `list(string)` |
[
"10.242.0.0/26"
]
| no | +| [firewall\_subnet\_name](#input\_firewall\_subnet\_name) | [Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet' | `string` | `"AzureFirewallSubnet"` | no | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | +| [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | +| [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"S0"` | no | +| [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | +| [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | +| [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | +| [tenant\_id](#input\_tenant\_id) | The Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Entra object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Entra username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | + +## Outputs + +No outputs. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >=1.3 | +| [azurecaf](#requirement\_azurecaf) | >=1.2.23 | +| [azurerm](#requirement\_azurerm) | ~>4.5.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [hub](#module\_hub) | ./hub | n/a | +| [spoke](#module\_spoke) | ./spoke | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | +| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = optional(list(string))

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "I1v2",
"zone_redundant": true
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "v6.0"
},
"slots": []
}
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [ase\_subnet\_cidr](#input\_ase\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26 | `list(string)` |
[
"10.240.5.0/24"
]
| no | +| [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | +| [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | +| [deployment\_options](#input\_deployment\_options) | [Optional] Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
deploy_asev3 = bool
})
|
{
"deploy_app_config": false,
"deploy_asev3": false,
"deploy_bastion": false,
"deploy_openai": false,
"deploy_redis": false,
"deploy_sql_database": false,
"deploy_vm": false,
"enable_diagnostic_settings": false,
"enable_egress_lockdown": false,
"enable_waf": false
}
| no | +| [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | [Required] The name of the Entra group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | [Required] The object ID of the Entra group that should be granted SQL Admin permissions to the SQL Server | `string` | `null` | no | +| [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | +| [firewall\_subnet\_cidr](#input\_firewall\_subnet\_cidr) | [Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26 | `list(string)` |
[
"10.242.0.0/26"
]
| no | +| [firewall\_subnet\_name](#input\_firewall\_subnet\_name) | [Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet' | `string` | `"AzureFirewallSubnet"` | no | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with appropriate naming standards.
- name\_prefix: A string to prepend to all resource names.
- name\_suffix: A string to append to all resource names.
- use\_slug: Whether to use a random slug in resource names for uniqueness.
- random\_length: The length of the random string to use in the slug.
- resource\_prefixes: A map of resource type to prefix string.
- resource\_suffixes: A map of resource type to suffix string. | `map(any)` | `{}` | no | +| [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | +| [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"S0"` | no | +| [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | +| [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | +| [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | +| [tenant\_id](#input\_tenant\_id) | The Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Entra object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Entra username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | + +## Outputs + +No outputs. + diff --git a/scenarios/secure-baseline-multitenant/terraform/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/_locals.tf new file mode 100644 index 00000000..a456a036 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/_locals.tf @@ -0,0 +1,33 @@ +locals { + deployment_name = "sec-baseline-1" + + global_settings = merge({ + environment = try(var.global_settings.environment, var.environment) + passthrough = try(var.global_settings.passthrough, false) + prefixes = try(var.global_settings.prefixes, [local.deployment_name, local.short_location]) + suffixes = try(var.global_settings.suffixes, [var.environment]) + random_length = try(var.global_settings.random_length, 0) + regions = try(var.global_settings.regions, null) + tags = try(var.global_settings.tags, null) + use_slug = try(var.global_settings.use_slug, true) + }, var.global_settings) + + short_location_map = { + "eastus" : "eus" + "eastus2" : "eus2" + "westus" : "wus" + "westus2" : "wus2" + "westeurope" : "weu" + "easteurope" : "eeu" + "southcentralus" : "scus" + } + + short_location = try(local.short_location_map[var.location], var.location) + + base_tags = merge({ + "Terraform" = true + "Environment" = local.global_settings.environment + "Owner" = var.owner + "Project" = "[Scenario 1] App Service Landing Zone Accelerator" + }, var.tags) +} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars b/scenarios/secure-baseline-multitenant/terraform/_parameters/ase-multitenant.parameters.tfvars similarity index 58% rename from scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars rename to scenarios/secure-baseline-multitenant/terraform/_parameters/ase-multitenant.parameters.tfvars index 76d70cac..dc37d077 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/parameters/ase-multitenant.parameters.tfvars +++ b/scenarios/secure-baseline-multitenant/terraform/_parameters/ase-multitenant.parameters.tfvars @@ -1,78 +1,73 @@ -application_name = "eslz2" -environment = "prod" -location = "westus3" -owner = "cloudops@contoso.com" - -# For the hub state, use the same settings as the remote state config on the hub deployment from /hub/main.tf -hub_state_resource_group_name = "backend-appsrvc-dev-westus2-001" -hub_state_storage_account_name = "stbackendappsrwestus2001" -hub_state_container_name = "tfstate" -hub_state_key = "scenario1.hub.tfstate" - -entra_admin_group_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" -entra_admin_group_name = "AppSvcLZA Microsoft Entra SQL Admins" - -## Lookup the Microsoft Entra User -# vm_entra_admin_username = "my-user@contoso.com" -## Reference an existing Microsoft Entra User/Group Object ID to bypass lookup -vm_entra_admin_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" # "AppSvcLZA Microsoft Entra SQL Admins" - - -## Optionally provide non-Entra ID admin credentials for the VM -# vm_admin_username = "daniem" -# vm_admin_password = "**************" - -## These settings are used for peering the spoke to the hub. Fill in the appropriate settings for your environment -hub_settings = { - rg_name = "rg-hub-scenario1-wus3" - vnet_name = "vnet-hub-scenario1-wus3" - - firewall = { - private_ip = "10.242.0.4" - } -} - -## Toggle deployment of optional features and services for the Landing Zone -deployment_options = { - enable_waf = true - enable_egress_lockdown = true - enable_diagnostic_settings = true - deploy_bastion = true - deploy_redis = true - deploy_sql_database = true - deploy_app_config = true - deploy_vm = false - deploy_openai = true -} - -## Optionally deploy a Github runner, DevOps agent, or both to the VM. -# devops_settings = { -# github_runner = { -# repository_url = "https://github.com/{organization}/{repository}" -# token = "runner_registration_token" # See: https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28 -# } -# -# devops_agent = { -# organization_url = "https://dev.azure.com/{organization}/" -# token = "pat_token" -# } -# } - -appsvc_options = { - service_plan = { - os_type = "Windows" - sku_name = "S1" - - # Optionally configure zone redundancy (requires a minimum of three workers and Premium SKU service plan) - # worker_count = 3 - # zone_redundant = true - } - - web_app = { - application_stack = { - current_stack = "dotnet" - dotnet_version = "v6.0" - } - slots = ["staging"] - } -} \ No newline at end of file +application_name = "eslztest" +environment = "dev" +location = "westus3" +owner = "cloudops@contoso.com" + +# entra_admin_group_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" +# entra_admin_group_name = "AppSvcLZA Entra SQL Admins" + +## Lookup the Entra User +# vm_entra_admin_username = "my-user@contoso.com" +## Reference an existing Entra User/Group Object ID to bypass lookup +vm_entra_admin_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" # "AppSvcLZA Entra SQL Admins" + +## Optionally provide non-entra admin credentials for the VM +# vm_admin_username = "daniem" +# vm_admin_password = "**************" + +## Toggle deployment of optional features and services for the Landing Zone +deployment_options = { + deploy_asev3 = true + enable_waf = true + enable_egress_lockdown = true + enable_diagnostic_settings = true + deploy_bastion = true + deploy_redis = true + deploy_sql_database = true + deploy_app_config = true + deploy_vm = false + deploy_openai = true +} + +## OpenAI Deployment Models +oai_deployment_models = { + "text-embedding-ada-002" = { + name = "text-embedding-ada-002" + model_format = "OpenAI" + model_name = "text-embedding-ada-002" + model_version = "2" + sku_name = "Standard" + } +} + +## Optionally deploy a Github runner, DevOps agent, or both to the VM. +# devops_settings = { +# github_runner = { +# repository_url = "https://github.com/{organization}/{repository}" +# token = "runner_registration_token" # See: https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28 +# } +# +# devops_agent = { +# organization_url = "https://dev.azure.com/{organization}/" +# token = "pat_token" +# } +# } + +appsvc_options = { + service_plan = { + os_type = "Windows" + sku_name = "I1v2" + + # Optionally configure zone redundancy (requires a minimum of three workers and Premium SKU service plan) + # worker_count = 3 + # zone_redundant = true + } + + web_app = { + application_stack = { + current_stack = "dotnet" + dotnet_version = "v6.0" + } + slots = ["staging"] + } +} diff --git a/scenarios/secure-baseline-multitenant/terraform/backend.hcl.template b/scenarios/secure-baseline-multitenant/terraform/backend.hcl.template new file mode 100644 index 00000000..62dc2d20 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/backend.hcl.template @@ -0,0 +1,10 @@ +### To use this template, rename the file to `backend.tf` +### and update the values below to match your remote state config +### +### To use this file as part of your deployment, provide the following flag as you initialize: +### terraform init -backend-config=backend.tf + +resource_group_name = "my-tf-remote-state-rg-name" +storage_account_name = "my-tf-remote-state-sa-name" +container_name = "my-tf-remote-state-container-name" +key = "my-state-file-name.tfstate" \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/README.md b/scenarios/secure-baseline-multitenant/terraform/hub/README.md index 5745f2a8..e5dd6333 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/hub/README.md @@ -1,20 +1,20 @@ -# hub - - +# hub + + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >=1.3 | | [azurecaf](#requirement\_azurecaf) | >=1.2.23 | -| [azurerm](#requirement\_azurerm) | >=3.66.0 | +| [azurerm](#requirement\_azurerm) | 4.5.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.85.0 | +| [azurecaf](#provider\_azurecaf) | >=1.2.23 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules @@ -29,7 +29,7 @@ | Name | Type | |------|------| | [azurecaf_name.caf_name_hub_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | -| [azurerm_resource_group.hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_resource_group.hub](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/resource_group) | resource | ## Inputs @@ -54,10 +54,10 @@ | Name | Description | |------|-------------| -| [bastion\_name](#output\_bastion\_name) | n/a | | [firewall\_private\_ip](#output\_firewall\_private\_ip) | n/a | | [firewall\_rules](#output\_firewall\_rules) | n/a | -| [rg\_name](#output\_rg\_name) | n/a | -| [vnet\_id](#output\_vnet\_id) | n/a | -| [vnet\_name](#output\_vnet\_name) | n/a | - +| [resource\_group\_name](#output\_resource\_group\_name) | n/a | +| [virtual\_network](#output\_virtual\_network) | n/a | +| [virtual\_network\_id](#output\_virtual\_network\_id) | n/a | +| [virtual\_network\_name](#output\_virtual\_network\_name) | n/a | + diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf index 6df104aa..402a1cc1 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/_locals.tf @@ -1,5 +1,5 @@ locals { - deployment_name = "sec-baseline-1-hub" + deployment_name = "sec-baseline-hub" global_settings = merge({ environment = try(var.global_settings.environment, var.environment) diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/main.tf b/scenarios/secure-baseline-multitenant/terraform/hub/main.tf index 822b642b..535a8d1c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/main.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/main.tf @@ -4,14 +4,16 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = ">=3.66.0" + version = ">=4.0" } azurecaf = { source = "aztfmod/azurecaf" version = ">=1.2.23" } } - backend "azurerm" {} + + # If called as a module, this backend configuration block will have no effect. + # backend "azurerm" {} } provider "azurerm" { @@ -32,5 +34,21 @@ provider "azurerm" { partner_id = "cf7e9f0a-f872-49db-b72f-f2e318189a6d" } -provider "azurecaf" {} +## Create Hub Resource Group with the name generated from global_settings +resource "azurecaf_name" "caf_name_hub_rg" { + name = var.application_name + resource_type = "azurerm_resource_group" + prefixes = local.global_settings.prefixes + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length + clean_input = true + passthrough = local.global_settings.passthrough + use_slug = local.global_settings.use_slug +} + +resource "azurerm_resource_group" "hub" { + name = azurecaf_name.caf_name_hub_rg.result + location = var.location + tags = local.base_tags +} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/hub-network.tf b/scenarios/secure-baseline-multitenant/terraform/hub/network.tf similarity index 76% rename from scenarios/secure-baseline-multitenant/terraform/hub/hub-network.tf rename to scenarios/secure-baseline-multitenant/terraform/hub/network.tf index 0face5eb..2fb49664 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/hub-network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/network.tf @@ -1,22 +1,14 @@ # Hub network config +# ----- +# - VNet +# - Firewall Subnet +# - Bastion Subnet +# - Azure Firewall [optional] +# - Bastion [optional] -resource "azurecaf_name" "caf_name_hub_rg" { - name = var.application_name - resource_type = "azurerm_resource_group" - prefixes = local.global_settings.prefixes - random_length = local.global_settings.random_length - clean_input = true - passthrough = local.global_settings.passthrough - use_slug = local.global_settings.use_slug -} - -resource "azurerm_resource_group" "hub" { - name = azurecaf_name.caf_name_hub_rg.result - location = var.location - tags = local.base_tags -} +## Deploy Hub VNet with Firewall and Bastion subnets module "network" { source = "../../../shared/terraform-modules/network" @@ -41,6 +33,7 @@ module "network" { tags = local.base_tags } +## Deploy Azure Firewall (enabled via deployment option) module "firewall" { count = var.deployment_options.enable_egress_lockdown ? 1 : 0 @@ -60,6 +53,7 @@ module "firewall" { tags = local.base_tags } +## Deploy Bastion (enabled via deployment option) module "bastion" { count = var.deployment_options.deploy_bastion ? 1 : 0 diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf b/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf index 70e6037c..1bc344e3 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/outputs.tf @@ -1,17 +1,17 @@ -output "rg_name" { +output "resource_group_name" { value = azurerm_resource_group.hub.name } -output "vnet_name" { +output "virtual_network_name" { value = module.network.vnet_name } -output "vnet_id" { +output "virtual_network_id" { value = module.network.vnet_id } -output "bastion_name" { - value = var.deployment_options.deploy_bastion ? module.bastion[0].name : null +output "virtual_network" { + value = module.network.vnet } output "firewall_private_ip" { diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/parameters/ase-multitenant.parameters.tfvars b/scenarios/secure-baseline-multitenant/terraform/hub/parameters/ase-multitenant.parameters.tfvars deleted file mode 100644 index 95d28bed..00000000 --- a/scenarios/secure-baseline-multitenant/terraform/hub/parameters/ase-multitenant.parameters.tfvars +++ /dev/null @@ -1,18 +0,0 @@ -application_name = "eslz2" -environment = "prod" -location = "westus2" -# location_short = "wus3" -owner = "cloudops@contoso.com" - -# Toggle deployment of optional features and services for the Landing Zone -deployment_options = { - enable_waf = true - enable_egress_lockdown = true - enable_diagnostic_settings = true - deploy_bastion = true - deploy_redis = true - deploy_sql_database = true - deploy_app_config = true - deploy_vm = true - deploy_openai = true -} diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf b/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf index cb81a7e6..44eccd7a 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/hub/variables.tf @@ -22,7 +22,7 @@ variable "owner" { # variable "tenant_id" { # type = string -# description = "[Required] The Microsoft Entra tenant ID for the identities" +# description = "[Required] The Entra tenant ID for the identities" # } variable "tags" { diff --git a/scenarios/secure-baseline-multitenant/terraform/main.tf b/scenarios/secure-baseline-multitenant/terraform/main.tf new file mode 100644 index 00000000..ee026fe2 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/main.tf @@ -0,0 +1,37 @@ +terraform { + # must be greater than or equal to 1.2 for OIDC + # must be greater than or equal to 1.3 for OpenAI + required_version = ">=1.3" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>4.5.0" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = ">=1.2.23" + } + } + + # If called as a module, this backend configuration block will have no effect. + backend "azurerm" {} +} + +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + + virtual_machine { + delete_os_disk_on_deletion = true + graceful_shutdown = false + skip_shutdown_and_force_delete = true + } + } + + # DO NOT CHANGE THE BELOW VALUES + disable_terraform_partner_id = false + partner_id = "cf7e9f0a-f872-49db-b72f-f2e318189a6d" +} diff --git a/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf new file mode 100644 index 00000000..01b02312 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/multitenant-asev3.tf @@ -0,0 +1,63 @@ + +# Create Hub via module +module "hub" { + source = "./hub" + + application_name = var.application_name + environment = var.environment + location = var.location + owner = var.owner + + # Optional Network Config Variables + hub_vnet_cidr = var.hub_vnet_cidr + spoke_vnet_cidr = var.spoke_vnet_cidr + firewall_subnet_name = var.firewall_subnet_name + firewall_subnet_cidr = var.firewall_subnet_cidr + bastion_subnet_name = var.bastion_subnet_name + bastion_subnet_cidr = var.bastion_subnet_cidr + devops_subnet_cidr = var.devops_subnet_cidr + + # Optional Deployment Variables + deployment_options = var.deployment_options + global_settings = var.global_settings + tags = var.tags +} + +module "spoke" { + source = "./spoke" + + application_name = var.application_name + environment = var.environment + location = var.location + owner = var.owner + tenant_id = var.tenant_id + + appsvc_options = var.appsvc_options + + oai_deployment_models = var.oai_deployment_models + oai_sku_name = var.oai_sku_name + + # Spoke Network Configuration Variables + hub_virtual_network = module.hub.virtual_network + firewall_private_ip = module.hub.firewall_private_ip + firewall_rules = module.hub.firewall_rules + spoke_vnet_cidr = var.spoke_vnet_cidr + ase_subnet_cidr = var.ase_subnet_cidr + devops_subnet_cidr = var.devops_subnet_cidr + appsvc_subnet_cidr = var.appsvc_subnet_cidr + front_door_subnet_cidr = var.front_door_subnet_cidr + private_link_subnet_cidr = var.private_link_subnet_cidr + + # Optional Self-hosted Agent Config Variables + vm_admin_username = var.vm_admin_username + vm_entra_admin_username = var.vm_entra_admin_username + vm_entra_admin_object_id = var.vm_entra_admin_object_id + + # Spoke Resource Configuration Variables + sql_databases = var.sql_databases + + # Optional Deployment Variables + deployment_options = var.deployment_options + global_settings = var.global_settings + tags = var.tags +} diff --git a/scenarios/secure-baseline-multitenant/terraform/outputs.tf b/scenarios/secure-baseline-multitenant/terraform/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index 6326e594..e1924ba5 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -1,13 +1,13 @@ # spoke - + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >=1.3 | | [azurecaf](#requirement\_azurecaf) | >=1.2.23 | -| [azurerm](#requirement\_azurerm) | >=3.66.0 | +| [azurerm](#requirement\_azurerm) | 4.5.0 | ## Providers @@ -16,7 +16,6 @@ | [azurecaf](#provider\_azurecaf) | 1.2.26 | | [azurerm](#provider\_azurerm) | 3.85.0 | | [random](#provider\_random) | 3.6.0 | -| [terraform](#provider\_terraform) | n/a | ## Modules @@ -39,54 +38,151 @@ | Name | Type | |------|------| | [azurecaf_name.appsvc_subnet](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_asev3](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_id_contributor](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_id_reader](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_app_service_environment_v3.this](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/app_service_environment_v3) | resource | +| [azurerm_log_analytics_workspace.law](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/log_analytics_workspace) | resource | +| [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/resource_group) | resource | +| [azurerm_user_assigned_identity.contributor](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/user_assigned_identity) | resource | +| [azurerm_user_assigned_identity.reader](https://registry.terraform.io/providers/hashicorp/azurerm/4.5.0/docs/resources/user_assigned_identity) | resource | +| [random_integer.unique_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | +| [appsvc\_options](#input\_appsvc\_options) | [Optional] The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": null
}
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [ase\_subnet\_cidr](#input\_ase\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26 | `list(string)` |
[
"10.240.5.0/24"
]
| no | +| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_asev3 = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_asev3": false,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | [Required] The name of the Entra group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | [Required] The object ID of the Entra group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | +| [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | +| [firewall\_private\_ip](#input\_firewall\_private\_ip) | n/a | `string` | n/a | yes | +| [firewall\_rules](#input\_firewall\_rules) | n/a | `any` | n/a | yes | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | +| [hub\_virtual\_network](#input\_hub\_virtual\_network) | [Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual\_network) to provide this value | `any` | n/a | yes | +| [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"S0"` | no | +| [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | +| [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | +| [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | +| [tenant\_id](#input\_tenant\_id) | The Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [vm\_admin\_password](#input\_vm\_admin\_password) | [Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Entra object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Entra username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | +| [zone\_redundant](#input\_zone\_redundant) | [Optional] Enable zone redundancy for the app service environment. Defaults to true | `bool` | `true` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [devops\_vm\_id](#output\_devops\_vm\_id) | n/a | +| [key\_vault\_name](#output\_key\_vault\_name) | n/a | +| [key\_vault\_uri](#output\_key\_vault\_uri) | n/a | +| [redis\_connection\_string](#output\_redis\_connection\_string) | n/a | +| [rg\_name](#output\_rg\_name) | n/a | +| [sql\_db\_connection\_string](#output\_sql\_db\_connection\_string) | n/a | +| [vnet\_id](#output\_vnet\_id) | n/a | +| [vnet\_name](#output\_vnet\_name) | n/a | +| [web\_app\_name](#output\_web\_app\_name) | n/a | +| [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | +| [web\_app\_uri](#output\_web\_app\_uri) | n/a | + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >=1.3 | +| [azurecaf](#requirement\_azurecaf) | >=1.2.23 | +| [azurerm](#requirement\_azurerm) | >=4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | >=1.2.23 | +| [azurerm](#provider\_azurerm) | >=4.0 | +| [random](#provider\_random) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [app\_configuration](#module\_app\_configuration) | ../../../shared/terraform-modules/app-configuration | n/a | +| [app\_service](#module\_app\_service) | ../../../shared/terraform-modules/app-service | n/a | +| [devops\_vm](#module\_devops\_vm) | ../../../shared/terraform-modules/windows-vm | n/a | +| [frontdoor](#module\_frontdoor) | ../../../shared/terraform-modules/frontdoor | n/a | +| [key\_vault](#module\_key\_vault) | ../../../shared/terraform-modules/key-vault | n/a | +| [network](#module\_network) | ../../../shared/terraform-modules/network | n/a | +| [openai](#module\_openai) | ../../../shared/terraform-modules/cognitive-services/openai | n/a | +| [private\_dns\_zones](#module\_private\_dns\_zones) | ../../../shared/terraform-modules/private-dns-zone | n/a | +| [redis\_cache](#module\_redis\_cache) | ../../../shared/terraform-modules/redis | n/a | +| [sql\_database](#module\_sql\_database) | ../../../shared/terraform-modules/sql-database | n/a | +| [user\_defined\_routes](#module\_user\_defined\_routes) | ../../../shared/terraform-modules/user-defined-routes | n/a | + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.appsvc_subnet](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_asev3](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_id_contributor](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_id_reader](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_spoke_rg](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.law](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_app_service_environment_v3.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_environment_v3) | resource | | [azurerm_log_analytics_workspace.law](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/log_analytics_workspace) | resource | | [azurerm_resource_group.spoke](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | | [azurerm_user_assigned_identity.contributor](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | | [azurerm_user_assigned_identity.reader](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | | [random_integer.unique_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource | -| [azurerm_virtual_network.hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/virtual_network) | data source | -| [terraform_remote_state.hub](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [aad\_admin\_group\_name](#input\_aad\_admin\_group\_name) | The name of the Microsoft Entra ID group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | -| [aad\_admin\_group\_object\_id](#input\_aad\_admin\_group\_object\_id) | The object ID of the Microsoft Entra ID group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | -| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | -| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | -| [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | -| [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | -| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | -| [devops\_settings](#input\_devops\_settings) | The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | +| [appsvc\_options](#input\_appsvc\_options) | [Optional] The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "v6.0"
},
"slots": null
}
}
| no | +| [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | +| [ase\_subnet\_cidr](#input\_ase\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26 | `list(string)` |
[
"10.240.5.0/24"
]
| no | +| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_asev3 = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_asev3": false,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [devops\_settings](#input\_devops\_settings) | [Optional] The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | | [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | -| [firewall\_subnet\_cidr](#input\_firewall\_subnet\_cidr) | [Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26 | `list(string)` |
[
"10.242.0.0/26"
]
| no | -| [firewall\_subnet\_name](#input\_firewall\_subnet\_name) | [Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet' | `string` | `"AzureFirewallSubnet"` | no | -| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | +| [firewall\_private\_ip](#input\_firewall\_private\_ip) | n/a | `string` | n/a | yes | +| [firewall\_rules](#input\_firewall\_rules) | n/a | `any` | n/a | yes | +| [front\_door\_subnet\_cidr](#input\_front\_door\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.64/26"
]
| no | | [global\_settings](#input\_global\_settings) | [Optional] Global settings to configure each module with the appropriate naming standards. | `map(any)` | `{}` | no | -| [hub\_settings](#input\_hub\_settings) | The settings for the hub virtual network. |
object({
rg_name = string
vnet_name = string

firewall = object({
private_ip = optional(string)
})
})
| `null` | no | -| [hub\_state\_container\_name](#input\_hub\_state\_container\_name) | The name of the container that holds the Terraform state for the hub | `string` | n/a | yes | -| [hub\_state\_key](#input\_hub\_state\_key) | The key of the Terraform state for the hub | `string` | n/a | yes | -| [hub\_state\_resource\_group\_name](#input\_hub\_state\_resource\_group\_name) | The name of the resource group that holds the Terraform state for the hub | `string` | n/a | yes | -| [hub\_state\_storage\_account\_name](#input\_hub\_state\_storage\_account\_name) | The name of the storage account that holds the Terraform state for the hub | `string` | n/a | yes | -| [hub\_vnet\_cidr](#input\_hub\_vnet\_cidr) | [Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20 | `list(string)` |
[
"10.242.0.0/20"
]
| no | +| [hub\_virtual\_network](#input\_hub\_virtual\_network) | [Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual\_network) to provide this value | `any` | n/a | yes | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [oai\_deployment\_models](#input\_oai\_deployment\_models) | [Optional] Map to specify deployment models for the OpenAI resource | `any` |
{
"gpt-35-turbo": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"model_version": "0613",
"name": "gpt-35-turbo",
"scale_type": "Standard"
},
"text-embedding-ada-002": {
"model_format": "OpenAI",
"model_name": "text-embedding-ada-002",
"model_version": "2",
"name": "text-embedding-ada-002",
"scale_type": "Standard"
}
}
| no | +| [oai\_sku\_name](#input\_oai\_sku\_name) | [Optional] The SKU name for the OpenAI resource | `string` | `"S0"` | no | | [owner](#input\_owner) | [Required] Owner of the deployment. | `string` | n/a | yes | -| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | +| [private\_link\_subnet\_cidr](#input\_private\_link\_subnet\_cidr) | [Optional] The CIDR block for the subnet. | `list(string)` |
[
"10.240.11.0/24"
]
| no | | [spoke\_vnet\_cidr](#input\_spoke\_vnet\_cidr) | [Optional] The CIDR block(s) for the virtual network for whitelisting on the firewall. Defaults to 10.240.0.0/20 | `list(string)` |
[
"10.240.0.0/20"
]
| no | +| [sql\_databases](#input\_sql\_databases) | [Optional] The settings for the SQL databases. |
list(object({
name = string
sku_name = string
}))
|
[
{
"name": "sample-db",
"sku_name": "S0"
}
]
| no | | [tags](#input\_tags) | [Optional] Additional tags to assign to your resources | `map(string)` | `{}` | no | -| [tenant\_id](#input\_tenant\_id) | The Microsoft Entra ID tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | -| [vm\_aad\_admin\_object\_id](#input\_vm\_aad\_admin\_object\_id) | The Microsoft Entra ID object ID for the VM admin user/group. If vm\_aad\_admin\_username is not specified, this value will be used. | `string` | `null` | no | -| [vm\_aad\_admin\_username](#input\_vm\_aad\_admin\_username) | [Optional] The Microsoft Entra ID username for the VM admin account. If vm\_aad\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | -| [vm\_admin\_password](#input\_vm\_admin\_password) | The password for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra ID admin account. | `string` | `null` | no | -| [vm\_admin\_username](#input\_vm\_admin\_username) | The username for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra ID admin account. | `string` | `null` | no | +| [tenant\_id](#input\_tenant\_id) | The Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [vm\_admin\_password](#input\_vm\_admin\_password) | [Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account. | `string` | `null` | no | +| [vm\_admin\_username](#input\_vm\_admin\_username) | [Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account. | `string` | `null` | no | +| [vm\_entra\_admin\_object\_id](#input\_vm\_entra\_admin\_object\_id) | [Optional] The Entra object ID for the VM admin user/group. If vm\_entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [vm\_entra\_admin\_username](#input\_vm\_entra\_admin\_username) | [Optional] The Entra username for the VM admin account. If vm\_entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | +| [zone\_redundant](#input\_zone\_redundant) | [Optional] Enable zone redundancy for the app service environment. Defaults to true | `bool` | `true` | no | ## Outputs diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf index 199aad07..32d74dc9 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf @@ -1,5 +1,5 @@ locals { - deployment_name = "sec-baseline-1-spoke" + deployment_name = "sec-baseline-spoke" # used in spoke-network.tf private_dns_zones = [for each in diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf index bf7e25b8..e049831b 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf @@ -7,30 +7,19 @@ module "openai" { resource_group_name = azurerm_resource_group.spoke.name location = azurerm_resource_group.spoke.location - deployment = { - "text-embedding-ada-002" = { - name = "text-embedding-ada-002" - model_format = "OpenAI" - model_name = "text-embedding-ada-002" - model_version = "2" - scale_type = "Standard" - } - "gpt-35-turbo" = { - name = "gpt-35-turbo" - model_format = "OpenAI" - model_name = "gpt-35-turbo" - model_version = "0613" - scale_type = "Standard" - } - } - pe_private_link_subnet_id = module.network.subnets["privateLink"].id private_dns_zone = local.provisioned_dns_zones["privatelink.openai.azure.com"] + sku_name = var.oai_sku_name + deployment = var.oai_deployment_models network_acls = [ { default_action = "Deny" virtual_network_rules = [ + var.deployment_options.deploy_asev3 ? { + subnet_id = module.network.subnets["hostingEnvironments"].id + ignore_missing_vnet_service_endpoint = true + } : null, { subnet_id = module.network.subnets["serverFarm"].id ignore_missing_vnet_service_endpoint = true diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf index c04c88ac..fd479b7c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf @@ -1,8 +1,16 @@ +# Spoke application deployment +# ------ +# - App Service +# - locals { sql_connstring = length(module.sql_database) > 0 ? module.sql_database[0].sql_db_connection_string : "SQL_NOT_PROVISIONED" redis_connstring = length(module.redis_cache) > 0 ? module.redis_cache[0].redis_connection_string : "REDIS_NOT_PROVISIONED" + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + is_isolated_sku = can(regex("(?i)^I.*v2$", var.appsvc_options.service_plan.sku_name)) } +# Deploy the App Service module "app_service" { source = "../../../shared/terraform-modules/app-service" @@ -13,10 +21,11 @@ module "app_service" { log_analytics_workspace_id = azurerm_log_analytics_workspace.law.id enable_diagnostic_settings = var.deployment_options.enable_diagnostic_settings - appsvc_subnet_id = module.network.subnets["serverFarm"].id - frontend_subnet_id = module.network.subnets["ingress"].id - service_plan_options = var.appsvc_options.service_plan - + # If isolated SKU (using App Service Environment) then do not enable vnet integration + appsvc_subnet_id = local.is_isolated_sku == true ? null : module.network.subnets["serverFarm"].id + frontend_subnet_id = module.network.subnets["ingress"].id + service_plan_options = var.appsvc_options.service_plan + app_service_environment_id = var.deployment_options.deploy_asev3 ? azurerm_app_service_environment_v3.this[0].id : null identity = { type = "UserAssigned" identity_ids = [ @@ -40,32 +49,6 @@ module "app_service" { tags = local.base_tags } -module "sql_database" { - count = var.deployment_options.deploy_sql_database ? 1 : 0 - - source = "../../../shared/terraform-modules/sql-database" - - resource_group = azurerm_resource_group.spoke.name - application_name = var.application_name - environment = var.environment - location = var.location - unique_id = random_integer.unique_id.result - tenant_id = var.tenant_id - entra_admin_group_object_id = var.entra_admin_group_object_id - entra_admin_group_name = var.entra_admin_group_name - private_link_subnet_id = module.network.subnets["privateLink"].id - global_settings = local.global_settings - tags = local.base_tags - sql_databases = [ - { - name = "sample-db" - sku_name = "S0" - } - ] - - private_dns_zone = local.provisioned_dns_zones["privatelink.database.windows.net"] -} - module "key_vault" { source = "../../../shared/terraform-modules/key-vault" @@ -89,6 +72,27 @@ module "key_vault" { private_dns_zone = local.provisioned_dns_zones["privatelink.vaultcore.azure.net"] } + +module "sql_database" { + count = var.deployment_options.deploy_sql_database ? 1 : 0 + + source = "../../../shared/terraform-modules/sql-database" + + resource_group = azurerm_resource_group.spoke.name + application_name = var.application_name + environment = var.environment + location = var.location + unique_id = random_integer.unique_id.result + tenant_id = var.tenant_id + private_link_subnet_id = module.network.subnets["privateLink"].id + global_settings = local.global_settings + tags = local.base_tags + sql_databases = var.sql_databases + key_vault_id = module.key_vault.vault_id + + private_dns_zone = local.provisioned_dns_zones["privatelink.database.windows.net"] +} + module "app_configuration" { count = var.deployment_options.deploy_app_config ? 1 : 0 diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf new file mode 100644 index 00000000..a2fff7be --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/asev3.tf @@ -0,0 +1,56 @@ +# Not broken into its own module as it is only used in this scenario +resource "azurecaf_name" "caf_name_asev3" { + count = var.deployment_options.deploy_asev3 ? 1 : 0 + + name = var.application_name + resource_type = "azurerm_app_service_environment" + prefixes = local.global_settings.prefixes + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length + clean_input = true + passthrough = local.global_settings.passthrough + + use_slug = local.global_settings.use_slug +} + +resource "azurerm_app_service_environment_v3" "this" { + count = var.deployment_options.deploy_asev3 ? 1 : 0 + + name = azurecaf_name.caf_name_asev3.0.result + resource_group_name = azurerm_resource_group.spoke.name + + # a /24 or larger CIDR is required. Once associated with an ASE, this size cannot be changed. + subnet_id = module.network.subnets["hostingEnvironments"].id + + # Possible values are None (for an External VIP Type), and "Web, Publishing" (for an Internal VIP Type). + internal_load_balancing_mode = "Web, Publishing" + + # Required for Private DNS and Endpoint configs + allow_new_private_endpoint_connections = true + + # You can only set either dedicated_host_count or zone_redundant but not both. + # Changing this forces a new resource to be created. + # dedicated_host_count = 2 + + # Changing this forces a new resource to be created. + zone_redundant = var.zone_redundant + + + cluster_setting { + name = "DisableTls1.0" + value = "1" + } + + ## Caution: Enabling internal encryption will add hours to your deployment time + # cluster_setting { + # name = "InternalEncryption" + # value = "true" + # } + + cluster_setting { + name = "FrontEndSSLCipherSuiteOrder" + value = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + } + + tags = local.base_tags +} \ No newline at end of file diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf deleted file mode 100644 index 48fe4423..00000000 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/data.tf +++ /dev/null @@ -1,14 +0,0 @@ -data "terraform_remote_state" "hub" { - backend = "azurerm" - config = { - resource_group_name = var.hub_state_resource_group_name - storage_account_name = var.hub_state_storage_account_name - container_name = var.hub_state_container_name - key = var.hub_state_key - } -} - -data "azurerm_virtual_network" "hub" { - name = data.terraform_remote_state.hub.outputs.vnet_name - resource_group_name = data.terraform_remote_state.hub.outputs.rg_name -} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/devops.tf similarity index 76% rename from scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf rename to scenarios/secure-baseline-multitenant/terraform/spoke/devops.tf index 875fe2ec..a9d72020 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/devops.tf @@ -3,7 +3,7 @@ locals { az_cli_commands = <<-EOT az login --identity --username ${azurerm_user_assigned_identity.contributor.principal_id} --allow-no-subscriptions az keyvault secret set --vault-name ${module.key_vault.vault_name} --name 'redis-connstring' --value '${local.redis_connstring}' - az appconfig kv set --auth-mode login --endpoint ${module.app_configuration[0].endpoint} --key 'sql-connstring' --value '${local.sql_connstring}' --label '${var.environment}' -y + ${var.deployment_options.deploy_app_config ? "az appconfig kv set --auth-mode login --endpoint ${module.app_configuration[0].endpoint} --key 'sql-connstring' --value '${local.sql_connstring}' --label '${var.environment}' -y" : ""} EOT } @@ -22,8 +22,7 @@ module "devops_vm" { entra_admin_username = var.vm_entra_admin_username entra_admin_object_id = var.vm_entra_admin_object_id global_settings = local.global_settings - - tags = local.base_tags + tags = local.base_tags identity = { type = "UserAssigned" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf index db18967a..47d1b99a 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/identity.tf @@ -1,3 +1,6 @@ +# Creates two user-assigned-identities for Reader and Contributor roles +# to be consumed in app.tf + resource "azurecaf_name" "caf_name_id_reader" { name = var.application_name resource_type = "azurerm_user_assigned_identity" diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf index 11ab98f5..e5bdf3d2 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf @@ -6,14 +6,17 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = ">=3.66.0" + version = ">=4.0" } azurecaf = { source = "aztfmod/azurecaf" version = ">=1.2.23" } } - backend "azurerm" {} + + # If called as a module, this backend configuration block will have no effect. + # Uncomment the below block to use the AzureRM backend for a spoke-specific deplployment + # backend "azurerm" {} } provider "azurerm" { @@ -34,12 +37,14 @@ provider "azurerm" { partner_id = "cf7e9f0a-f872-49db-b72f-f2e318189a6d" } -provider "azurecaf" {} - +## Create Spoke Resource Group with the name generated from global_settings resource "azurecaf_name" "caf_name_spoke_rg" { name = var.application_name resource_type = "azurerm_resource_group" - prefixes = concat(["spoke"], local.global_settings.prefixes) + # prefixes = concat(["spoke"], local.global_settings.prefixes) + prefixes = local.global_settings.prefixes + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length clean_input = true passthrough = local.global_settings.passthrough @@ -51,4 +56,4 @@ resource "azurerm_resource_group" "spoke" { location = var.location tags = local.base_tags -} \ No newline at end of file +} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/law.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/monitoring.tf similarity index 100% rename from scenarios/secure-baseline-multitenant/terraform/spoke/law.tf rename to scenarios/secure-baseline-multitenant/terraform/spoke/monitoring.tf diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index 8c10272e..ed13c420 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -1,20 +1,28 @@ # Spoke network config - -resource "random_integer" "unique_id" { - min = 1 - max = 9999 -} +# ----- +# - VNet +# - Server Farm Subnet (App Service/compute resources) +# - Ingress Subnet (Azure Front Door network ingress subnet) +# - Private Link Subnet (Private DNS Zones) +# - DevOps Subnet (optional Self Hosted CICD agent) +# - Private DNS Zones +# - User Defined Routes [optional] +# - Azure FrontDoor resource "azurecaf_name" "appsvc_subnet" { name = var.application_name resource_type = "azurerm_subnet" prefixes = concat(["spoke"], local.global_settings.prefixes) + suffixes = local.global_settings.suffixes + random_length = local.global_settings.random_length clean_input = true passthrough = local.global_settings.passthrough use_slug = local.global_settings.use_slug } + +## Deploy Spoke VNet with Server Farm, Ingress, Private Link and DevOps subnets module "network" { source = "../../../shared/terraform-modules/network" @@ -24,11 +32,12 @@ module "network" { name = var.application_name vnet_cidr = var.spoke_vnet_cidr peering_vnet = { - id = data.azurerm_virtual_network.hub.id - name = data.azurerm_virtual_network.hub.name - resource_group = data.azurerm_virtual_network.hub.resource_group_name + id = var.hub_virtual_network.id + name = var.hub_virtual_network.name + resource_group = var.hub_virtual_network.resource_group_name } + subnets = [ { name = "serverFarm" @@ -41,6 +50,17 @@ module "network" { } } }, + var.deployment_options.deploy_asev3 ? { + name = "hostingEnvironments" + subnet_cidr = var.ase_subnet_cidr + delegation = { + name = "Microsoft.Web.hostingEnvironments" + service_delegation = { + name = "Microsoft.Web/hostingEnvironments" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } + } : null, { name = "ingress" subnet_cidr = var.front_door_subnet_cidr @@ -61,46 +81,30 @@ module "network" { tags = local.base_tags } +## Deploy Private DNS Zones module "private_dns_zones" { source = "../../../shared/terraform-modules/private-dns-zone" count = length(local.private_dns_zones) - resource_group = data.terraform_remote_state.hub.outputs.rg_name + resource_group = var.hub_virtual_network.resource_group_name global_settings = local.global_settings dns_zone_name = local.private_dns_zones[count.index].name dns_records = lookup(local.private_dns_zones[count.index], "records", []) vnet_links = [ - data.azurerm_virtual_network.hub.id + var.hub_virtual_network.id ] tags = local.base_tags } -module "user_defined_routes" { - count = var.deployment_options.enable_egress_lockdown ? 1 : 0 - - source = "../../../shared/terraform-modules/user-defined-routes" - - resource_group = azurerm_resource_group.spoke.name - location = var.location - route_table_name = "egress-lockdown" - global_settings = local.global_settings - - routes = [ - { - name = "defaultRoute" - address_prefix = "0.0.0.0/0" - next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = data.terraform_remote_state.hub.outputs.firewall_private_ip - } - ] - - subnet_ids = module.network.subnet_ids - tags = local.base_tags +# TODO: Deprecate the random_integer unique_id logic +resource "random_integer" "unique_id" { + min = 1 + max = 9999 } - +## Deploy Azure Front Door with basic endpoint configuration for the web app module "frontdoor" { source = "../../../shared/terraform-modules/frontdoor" @@ -129,3 +133,27 @@ module "frontdoor" { module.app_service ] } + +## Deploy User Defined Routes (UDR) to route all traffic to the Azure Firewall (enabled via deployment option) +module "user_defined_routes" { + count = var.deployment_options.enable_egress_lockdown ? 1 : 0 + + source = "../../../shared/terraform-modules/user-defined-routes" + + resource_group = azurerm_resource_group.spoke.name + location = var.location + route_table_name = "egress-lockdown" + global_settings = local.global_settings + + routes = [ + { + name = "defaultRoute" + address_prefix = "0.0.0.0/0" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = var.firewall_private_ip + } + ] + + subnet_ids = module.network.subnet_ids + tags = local.base_tags +} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index 8c064392..d65e76d6 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -1,3 +1,8 @@ +# spoke variables.tf + +##################################### +# Common variables for naming and tagging +##################################### variable "global_settings" { type = map(any) description = "[Optional] Global settings to configure each module with the appropriate naming standards." @@ -9,26 +14,6 @@ variable "owner" { description = "[Required] Owner of the deployment." } -variable "hub_state_resource_group_name" { - type = string - description = "The name of the resource group that holds the Terraform state for the hub" -} - -variable "hub_state_storage_account_name" { - type = string - description = "The name of the storage account that holds the Terraform state for the hub" -} - -variable "hub_state_container_name" { - type = string - description = "The name of the container that holds the Terraform state for the hub" -} - -variable "hub_state_key" { - type = string - description = "The key of the Terraform state for the hub" -} - variable "application_name" { type = string description = "The name of your application" @@ -49,7 +34,7 @@ variable "location" { variable "tenant_id" { type = string - description = "The Microsoft Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant." + description = "The Entra tenant ID for the identities. If no value provided, will use current deployment environment tenant." default = null } @@ -62,43 +47,17 @@ variable "tags" { ##################################### # Spoke Resource Configuration Variables ##################################### -variable "entra_admin_group_object_id" { - type = string - description = "The object ID of the Microsoft Entra group that should be granted SQL Admin permissions to the SQL Server" -} - -variable "entra_admin_group_name" { - type = string - description = "The name of the Microsoft Entra group that should be granted SQL Admin permissions to the SQL Server" -} - -variable "bastion_subnet_name" { - type = string - description = "[Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet'" - default = "AzureBastionSubnet" +variable "hub_virtual_network" { + type = any + description = "[Required] Hub virtual network object that is live in Azure. Use either a data block or output of the `Hub` module (virtual_network) to provide this value" } -variable "firewall_subnet_name" { - type = string - description = "[Optional] Name of the subnet for firewall resources. Defaults to 'AzureFirewallSubnet'" - default = "AzureFirewallSubnet" -} -variable "hub_vnet_cidr" { - type = list(string) - description = "[Optional] The CIDR block(s) for the hub virtual network. Defaults to 10.242.0.0/20" - default = ["10.242.0.0/20"] -} - -variable "firewall_subnet_cidr" { - type = list(string) - description = "[Optional] The CIDR block(s) for the firewall subnet. Defaults to 10.242.0.0/26" - default = ["10.242.0.0/26"] +variable "firewall_private_ip" { + type = string } -variable "bastion_subnet_cidr" { - type = list(string) - description = "[Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26" - default = ["10.242.0.64/26"] +variable "firewall_rules" { + type = any } variable "spoke_vnet_cidr" { @@ -113,67 +72,110 @@ variable "devops_subnet_cidr" { default = ["10.240.10.128/26"] } +variable "ase_subnet_cidr" { + type = list(string) + description = "[Optional] The CIDR block for the subnet. Defaults to 10.241.0.0/26" + default = ["10.240.5.0/24"] +} variable "appsvc_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.0.0/26"] } variable "front_door_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.0.64/26"] } variable "private_link_subnet_cidr" { type = list(string) - description = "The CIDR block for the subnet." + description = "[Optional] The CIDR block for the subnet." default = ["10.240.11.0/24"] } -variable "hub_settings" { - type = object({ - rg_name = string - vnet_name = string - - firewall = object({ - private_ip = optional(string) - }) - }) - - description = "The settings for the hub virtual network." - - default = null -} variable "vm_admin_username" { type = string - description = "The username for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra admin account." + description = "[Optional] The username for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account." default = null } variable "vm_admin_password" { type = string - description = "The password for the local VM admin account. Autogenerated if null. Prefer using the Microsoft Entra admin account." + description = "[Optional] The password for the local VM admin account. Autogenerated if null. Prefer using the Entra admin account." default = null } variable "vm_entra_admin_username" { type = string - description = "[Optional] The Microsoft Entra username for the VM admin account. If vm_entra_admin_object_id is not specified, this value will be used." + description = "[Optional] The Entra username for the VM admin account. If vm_entra_admin_object_id is not specified, this value will be used." default = null } + variable "vm_entra_admin_object_id" { type = string - description = "The Microsoft Entra object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." + description = "[Optional] The Entra object ID for the VM admin user/group. If vm_entra_admin_username is not specified, this value will be used." default = null } + +variable "sql_databases" { + type = list(object({ + name = string + sku_name = string + })) + + description = "[Optional] The settings for the SQL databases." + + default = [ + { + name = "sample-db" + sku_name = "S0" + } + ] +} + +variable "zone_redundant" { + type = bool + description = "[Optional] Enable zone redundancy for the app service environment. Defaults to true" + default = true +} + +variable "oai_sku_name" { + description = "[Optional] The SKU name for the OpenAI resource" + type = string + default = "S0" +} + +variable "oai_deployment_models" { + description = "[Optional] Map to specify deployment models for the OpenAI resource" + type = any + default = { + "text-embedding-ada-002" = { + name = "text-embedding-ada-002" + model_format = "OpenAI" + model_name = "text-embedding-ada-002" + model_version = "2" + scale_type = "Standard" + } + "gpt-35-turbo" = { + name = "gpt-35-turbo" + model_format = "OpenAI" + model_name = "gpt-35-turbo" + model_version = "0613" + scale_type = "Standard" + } + } +} + variable "deployment_options" { type = object({ enable_waf = bool enable_egress_lockdown = bool enable_diagnostic_settings = bool + deploy_asev3 = bool deploy_bastion = bool deploy_redis = bool deploy_sql_database = bool @@ -188,6 +190,7 @@ variable "deployment_options" { enable_waf = true enable_egress_lockdown = true enable_diagnostic_settings = true + deploy_asev3 = false deploy_bastion = true deploy_redis = true deploy_sql_database = true @@ -200,10 +203,9 @@ variable "deployment_options" { variable "appsvc_options" { type = object({ service_plan = object({ - os_type = string - sku_name = string - worker_count = optional(number) - zone_redundant = optional(bool) + os_type = string + sku_name = string + worker_count = optional(number) }) web_app = object({ slots = list(string) @@ -226,7 +228,7 @@ variable "appsvc_options" { }) }) - description = "The options for the app service" + description = "[Optional] The options for the app service" default = { service_plan = { @@ -234,11 +236,11 @@ variable "appsvc_options" { sku_name = "S1" } web_app = { - slots = [] + slots = null application_stack = { current_stack = "dotnet" - dotnet_version = "6.0" + dotnet_version = "v6.0" } } } @@ -272,7 +274,7 @@ variable "devops_settings" { })) }) - description = "The settings for the Azure DevOps agent or GitHub runner" + description = "[Optional] The settings for the Azure DevOps agent or GitHub runner" default = { github_runner = null diff --git a/scenarios/secure-baseline-multitenant/terraform/variables.tf b/scenarios/secure-baseline-multitenant/terraform/variables.tf new file mode 100644 index 00000000..97ac6580 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/variables.tf @@ -0,0 +1,321 @@ +# terraform variables.tf + +##################################### +# Common variables for naming and tagging +##################################### +variable "global_settings" { + type = map(any) + description = < ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/app-configuration/main.tf b/scenarios/shared/terraform-modules/app-configuration/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-configuration/main.tf +++ b/scenarios/shared/terraform-modules/app-configuration/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/README.md b/scenarios/shared/terraform-modules/app-service/README.md index 50893e8d..cbcaeacd 100644 --- a/scenarios/shared/terraform-modules/app-service/README.md +++ b/scenarios/shared/terraform-modules/app-service/README.md @@ -3,14 +3,16 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.75.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules @@ -25,6 +27,7 @@ No requirements. |------|------| | [azurecaf_name.caf_name_appinsights](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.caf_name_asp](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_app_service_virtual_network_swift_connection.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_virtual_network_swift_connection) | resource | | [azurerm_application_insights.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/application_insights) | resource | | [azurerm_service_plan.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) | resource | @@ -32,6 +35,7 @@ No requirements. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [app\_service\_environment\_id](#input\_app\_service\_environment\_id) | The app service environment id | `string` | `null` | no | | [application\_name](#input\_application\_name) | The name of your application | `string` | n/a | yes | | [appsvc\_subnet\_id](#input\_appsvc\_subnet\_id) | The subnet id where the app service will be integrated | `string` | `null` | no | | [deploy\_web\_app](#input\_deploy\_web\_app) | Feature flag to deploy a web app as part of the module | `bool` | `true` | no | @@ -43,9 +47,9 @@ No requirements. | [log\_analytics\_workspace\_id](#input\_log\_analytics\_workspace\_id) | The log analytics workspace id | `string` | n/a | yes | | [private\_dns\_zone](#input\_private\_dns\_zone) | The private dns zone id where the app service will be integrated |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | | [resource\_group](#input\_resource\_group) | The name of the resource group where all resources in this example should be created. | `string` | n/a | yes | -| [service\_plan\_options](#input\_service\_plan\_options) | The options for the app service |
object({
os_type = string
sku_name = string
app_service_environment_id = optional(string)
worker_count = optional(number)
zone_redundant = optional(bool)
})
|
{
"os_type": "Windows",
"sku_name": "S1"
}
| no | +| [service\_plan\_options](#input\_service\_plan\_options) | The options for the app service |
object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
|
{
"os_type": "Windows",
"sku_name": "S1",
"worker_count": 3
}
| no | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
slots = optional(list(string))

application_stack = optional(object({
current_stack = optional(string) # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
}))
})
|
{
"application_stack": {},
"slots": []
}
| no | +| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
slots = list(string)
instrumentation_key = optional(string)
ai_connection_string = optional(string)
vnet_route_all_enabled = optional(bool)
use_32_bit_worker = optional(bool)
vnet_integration_enabled = optional(bool)

application_stack = optional(object({
current_stack = optional(string) # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
}))
})
|
{
"application_stack": {},
"slots": []
}
| no | ## Outputs @@ -57,6 +61,6 @@ No requirements. | [web\_app\_principal\_id](#output\_web\_app\_principal\_id) | n/a | | [web\_app\_slot\_hostname](#output\_web\_app\_slot\_hostname) | n/a | | [web\_app\_slot\_id](#output\_web\_app\_slot\_id) | n/a | +| [web\_app\_slot\_identities](#output\_web\_app\_slot\_identities) | n/a | | [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | -| [web\_app\_slot\_principal\_id](#output\_web\_app\_slot\_principal\_id) | n/a | diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md b/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md index 932a27fa..3dc6908c 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md @@ -1,16 +1,18 @@ -# linux-web-app - - +# linux-web-app + + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.75.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules @@ -58,8 +60,8 @@ No requirements. | [web\_app\_id](#output\_web\_app\_id) | n/a | | [web\_app\_name](#output\_web\_app\_name) | n/a | | [web\_app\_principal\_id](#output\_web\_app\_principal\_id) | n/a | -| [web\_app\_slot\_hostname](#output\_web\_app\_slot\_hostname) | n/a | -| [web\_app\_slot\_id](#output\_web\_app\_slot\_id) | n/a | -| [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | -| [web\_app\_slot\_principal\_id](#output\_web\_app\_slot\_principal\_id) | n/a | - +| [web\_app\_slot\_hostnames](#output\_web\_app\_slot\_hostnames) | n/a | +| [web\_app\_slot\_identities](#output\_web\_app\_slot\_identities) | n/a | +| [web\_app\_slot\_ids](#output\_web\_app\_slot\_ids) | n/a | +| [web\_app\_slot\_names](#output\_web\_app\_slot\_names) | n/a | + diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf index d9c5614d..5c4355bb 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf @@ -28,12 +28,11 @@ resource "azurerm_linux_web_app" "this" { use_32_bit_worker = false application_stack { - docker_image = var.webapp_options.application_stack.docker_image - docker_image_tag = var.webapp_options.application_stack.docker_image_tag - dotnet_version = var.webapp_options.application_stack.dotnet_version - java_version = var.webapp_options.application_stack.java_version - php_version = var.webapp_options.application_stack.php_version - node_version = var.webapp_options.application_stack.node_version + docker_image_name = "${var.webapp_options.application_stack.docker_image}:${var.webapp_options.application_stack.docker_image_tag}" + dotnet_version = var.webapp_options.application_stack.dotnet_version + java_version = var.webapp_options.application_stack.java_version + php_version = var.webapp_options.application_stack.php_version + node_version = var.webapp_options.application_stack.node_version } } @@ -91,22 +90,11 @@ resource "azurerm_monitor_diagnostic_setting" "this" { enabled_log { category_group = "allLogs" - ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention - # retention_policy { - # days = 0 - # enabled = false - # } } metric { category = "AllMetrics" enabled = false - - ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention - # retention_policy { - # days = 0 - # enabled = false - # } } } @@ -135,7 +123,8 @@ module "private_endpoint" { } resource "azurerm_linux_web_app_slot" "slot" { - name = var.webapp_options.slots[0] + count = length(var.webapp_options.slots) + name = var.webapp_options.slots[count.index] app_service_id = azurerm_linux_web_app.this.id virtual_network_subnet_id = var.appsvc_subnet_id https_only = true @@ -150,37 +139,32 @@ resource "azurerm_linux_web_app_slot" "slot" { use_32_bit_worker = false application_stack { - docker_image = var.webapp_options.application_stack.docker_image - docker_image_tag = var.webapp_options.application_stack.docker_image_tag - dotnet_version = var.webapp_options.application_stack.dotnet_version - java_version = var.webapp_options.application_stack.java_version - php_version = var.webapp_options.application_stack.php_version - node_version = var.webapp_options.application_stack.node_version + docker_image_name = "${var.webapp_options.application_stack.docker_image}:${var.webapp_options.application_stack.docker_image_tag}" + dotnet_version = var.webapp_options.application_stack.dotnet_version + java_version = var.webapp_options.application_stack.java_version + php_version = var.webapp_options.application_stack.php_version + node_version = var.webapp_options.application_stack.node_version } } } -resource "azurecaf_name" "slot" { - name = "${azurecaf_name.caf_name_linwebapp.result}-${var.webapp_options.slots[0]}" - resource_type = "azurerm_private_endpoint" -} - module "private_endpoint_slot" { source = "../../private-endpoint" + count = length(azurerm_linux_web_app_slot.slot) - name = azurecaf_name.slot.result + name = "${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot[count.index].name}" resource_group = var.resource_group location = var.location subnet_id = var.frontend_subnet_id - private_connection_resource_id = azurerm_linux_web_app.this.id + private_connection_resource_id = azurerm_linux_web_app.this.id // Change this line - subresource_names = ["sites-${var.webapp_options.slots[0]}"] + subresource_names = ["sites-${azurerm_linux_web_app_slot.slot[count.index].name}"] private_dns_zone = var.private_dns_zone private_dns_records = [ - lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot.name}"), - lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot.name}.scm") + lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot[count.index].name}"), + lower("${azurerm_linux_web_app.this.name}-${azurerm_linux_web_app_slot.slot[count.index].name}.scm") ] depends_on = [ diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf index 58dfad3a..266aeb5f 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/outputs.tf @@ -14,18 +14,18 @@ output "web_app_principal_id" { value = azurerm_linux_web_app.this.identity.0.principal_id } -output "web_app_slot_id" { - value = azurerm_linux_web_app_slot.slot.id +output "web_app_slot_ids" { + value = azurerm_linux_web_app_slot.slot.*.id } -output "web_app_slot_name" { - value = azurerm_linux_web_app_slot.slot.name +output "web_app_slot_names" { + value = azurerm_linux_web_app_slot.slot.*.name } -output "web_app_slot_hostname" { - value = azurerm_linux_web_app_slot.slot.default_hostname +output "web_app_slot_hostnames" { + value = azurerm_linux_web_app_slot.slot.*.default_hostname } -output "web_app_slot_principal_id" { - value = azurerm_linux_web_app_slot.slot.identity.0.principal_id +output "web_app_slot_identities" { + value = azurerm_linux_web_app_slot.slot.*.identity } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/main.tf b/scenarios/shared/terraform-modules/app-service/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-service/main.tf +++ b/scenarios/shared/terraform-modules/app-service/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/module.tf b/scenarios/shared/terraform-modules/app-service/module.tf index 760fffb6..69815895 100644 --- a/scenarios/shared/terraform-modules/app-service/module.tf +++ b/scenarios/shared/terraform-modules/app-service/module.tf @@ -3,6 +3,9 @@ locals { ai_connection_string = azurerm_application_insights.this.connection_string instrumentation_key = azurerm_application_insights.this.instrumentation_key }, var.webapp_options) + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + is_isolated_sku = can(regex("(?i)^I.*v2$", var.service_plan_options.sku_name)) } resource "azurecaf_name" "caf_name_asp" { @@ -24,10 +27,13 @@ resource "azurerm_service_plan" "this" { sku_name = var.service_plan_options.sku_name os_type = var.service_plan_options.os_type - app_service_environment_id = lookup(var.service_plan_options, "app_service_environment_id", null) - worker_count = coalesce(var.service_plan_options.worker_count, 1) - zone_balancing_enabled = coalesce(var.service_plan_options.zone_redundant, false) - tags = local.tags + app_service_environment_id = var.app_service_environment_id + worker_count = coalesce(var.service_plan_options.worker_count, 3) + + # For ASEv3 hosted ASP, zone redundancy is managed at the ASE level + zone_balancing_enabled = var.app_service_environment_id != null ? true : false + + tags = local.tags } module "windows_web_app" { @@ -37,17 +43,18 @@ module "windows_web_app" { resource_group = var.resource_group web_app_name = var.application_name - # environment = var.environment location = var.location service_plan_id = azurerm_service_plan.this.id service_plan_resource = azurerm_service_plan.this - appsvc_subnet_id = var.appsvc_subnet_id - frontend_subnet_id = var.frontend_subnet_id - webapp_options = local.webapp_options - private_dns_zone = var.private_dns_zone - identity = var.identity - global_settings = var.global_settings - tags = var.tags + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + appsvc_subnet_id = local.is_isolated_sku == true ? null : var.appsvc_subnet_id + frontend_subnet_id = var.frontend_subnet_id + webapp_options = local.webapp_options + private_dns_zone = var.private_dns_zone + identity = var.identity + global_settings = var.global_settings + tags = var.tags log_analytics_workspace_id = var.log_analytics_workspace_id enable_diagnostic_settings = var.enable_diagnostic_settings @@ -60,18 +67,26 @@ module "linux_web_app" { resource_group = var.resource_group web_app_name = var.application_name - # environment = var.environment location = var.location service_plan_id = azurerm_service_plan.this.id service_plan_resource = azurerm_service_plan.this - appsvc_subnet_id = var.appsvc_subnet_id - frontend_subnet_id = var.frontend_subnet_id - webapp_options = local.webapp_options - private_dns_zone = var.private_dns_zone - identity = var.identity - global_settings = var.global_settings - tags = var.tags + + # If isolated SKU (using App Service Environment) then do not enable vnet integration + appsvc_subnet_id = local.is_isolated_sku == true ? null : var.appsvc_subnet_id + frontend_subnet_id = var.frontend_subnet_id + webapp_options = local.webapp_options + private_dns_zone = var.private_dns_zone + identity = var.identity + global_settings = var.global_settings + tags = var.tags log_analytics_workspace_id = var.log_analytics_workspace_id enable_diagnostic_settings = var.enable_diagnostic_settings -} \ No newline at end of file +} + +# Only required for vnet integration (non ASE) +resource "azurerm_app_service_virtual_network_swift_connection" "this" { + count = local.is_isolated_sku == true ? 0 : 1 + app_service_id = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_id : module.linux_web_app[0].web_app_id : null + subnet_id = var.appsvc_subnet_id +} diff --git a/scenarios/shared/terraform-modules/app-service/outputs.tf b/scenarios/shared/terraform-modules/app-service/outputs.tf index f3719fb9..9e3ba6c8 100644 --- a/scenarios/shared/terraform-modules/app-service/outputs.tf +++ b/scenarios/shared/terraform-modules/app-service/outputs.tf @@ -15,17 +15,17 @@ output "web_app_principal_id" { } output "web_app_slot_id" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_id : module.linux_web_app[0].web_app_slot_id : null + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_ids : module.linux_web_app[0].web_app_slot_ids : null } output "web_app_slot_name" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_name : module.linux_web_app[0].web_app_slot_name : null + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_names : module.linux_web_app[0].web_app_slot_names : null } output "web_app_slot_hostname" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_hostname : module.linux_web_app[0].web_app_slot_hostname : null + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_hostnames : module.linux_web_app[0].web_app_slot_hostnames : null } -output "web_app_slot_principal_id" { - value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_principal_id : module.linux_web_app[0].web_app_slot_principal_id : null +output "web_app_slot_identities" { + value = var.deploy_web_app ? length(module.windows_web_app) > 0 ? module.windows_web_app[0].web_app_slot_identities : module.linux_web_app[0].web_app_slot_identities : null } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/variables.tf b/scenarios/shared/terraform-modules/app-service/variables.tf index 655dd2f3..e88f2bca 100644 --- a/scenarios/shared/terraform-modules/app-service/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/variables.tf @@ -42,20 +42,26 @@ variable "log_analytics_workspace_id" { description = "The log analytics workspace id" } +variable "app_service_environment_id" { + type = string + description = "The app service environment id" + default = null +} + variable "service_plan_options" { type = object({ - os_type = string - sku_name = string - app_service_environment_id = optional(string) - worker_count = optional(number) - zone_redundant = optional(bool) + os_type = string + sku_name = string + worker_count = optional(number) + zone_redundant = optional(bool) }) description = "The options for the app service" default = { - os_type = "Windows" - sku_name = "S1" + os_type = "Windows" + sku_name = "S1" + worker_count = 3 } validation { @@ -90,7 +96,12 @@ variable "identity" { variable "webapp_options" { type = object({ - slots = optional(list(string)) + slots = list(string) + instrumentation_key = optional(string) + ai_connection_string = optional(string) + vnet_route_all_enabled = optional(bool) + use_32_bit_worker = optional(bool) + vnet_integration_enabled = optional(bool) application_stack = optional(object({ current_stack = optional(string) # required for windows @@ -119,6 +130,8 @@ variable "webapp_options" { default = { slots = [] application_stack = {} + + } } diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md index dab6e024..dd80efbf 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md @@ -1,23 +1,24 @@ -# windows-web-app - - +# windows-web-app + + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.75.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules | Name | Source | Version | |------|--------|---------| | [private\_endpoint](#module\_private\_endpoint) | ../../private-endpoint | n/a | -| [private\_endpoint\_slot](#module\_private\_endpoint\_slot) | ../../private-endpoint | n/a | ## Resources @@ -48,7 +49,7 @@ No requirements. | [service\_plan\_resource](#input\_service\_plan\_resource) | The service plan resource where the web application will be created | `any` | n/a | yes | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | | [web\_app\_name](#input\_web\_app\_name) | The name of the web application | `string` | n/a | yes | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | +| [webapp\_options](#input\_webapp\_options) | [Required] The options for the app service |
object({
slots = list(string)
instrumentation_key = optional(string)
ai_connection_string = optional(string)
vnet_route_all_enabled = optional(bool)
use_32_bit_worker = optional(bool)
vnet_integration_enabled = optional(bool)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | ## Outputs @@ -58,8 +59,8 @@ No requirements. | [web\_app\_id](#output\_web\_app\_id) | n/a | | [web\_app\_name](#output\_web\_app\_name) | n/a | | [web\_app\_principal\_id](#output\_web\_app\_principal\_id) | n/a | -| [web\_app\_slot\_hostname](#output\_web\_app\_slot\_hostname) | n/a | -| [web\_app\_slot\_id](#output\_web\_app\_slot\_id) | n/a | -| [web\_app\_slot\_name](#output\_web\_app\_slot\_name) | n/a | -| [web\_app\_slot\_principal\_id](#output\_web\_app\_slot\_principal\_id) | n/a | - +| [web\_app\_slot\_hostnames](#output\_web\_app\_slot\_hostnames) | n/a | +| [web\_app\_slot\_identities](#output\_web\_app\_slot\_identities) | n/a | +| [web\_app\_slot\_ids](#output\_web\_app\_slot\_ids) | n/a | +| [web\_app\_slot\_names](#output\_web\_app\_slot\_names) | n/a | + diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf index ebf704f2..e71d1e87 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf @@ -11,7 +11,7 @@ resource "azurecaf_name" "caf_name_winwebapp" { } resource "azurerm_windows_web_app" "this" { - name = var.web_app_name + name = azurecaf_name.caf_name_winwebapp.result resource_group_name = var.resource_group location = var.location https_only = true @@ -24,8 +24,8 @@ resource "azurerm_windows_web_app" "this" { } site_config { - vnet_route_all_enabled = true - use_32_bit_worker = false + vnet_route_all_enabled = coalesce(var.webapp_options.vnet_route_all_enabled, true) + use_32_bit_worker = coalesce(var.webapp_options.use_32_bit_worker, true) application_stack { current_stack = coalesce(var.webapp_options.application_stack.current_stack, "dotnet") @@ -81,23 +81,11 @@ resource "azurerm_monitor_diagnostic_setting" "this" { enabled_log { category_group = "AllLogs" - - ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention - # retention_policy { - # days = 0 - # enabled = false - # } } metric { category = "AllMetrics" enabled = false - - ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention - # retention_policy { - # days = 0 - # enabled = false - # } } } @@ -126,17 +114,19 @@ module "private_endpoint" { } resource "azurerm_windows_web_app_slot" "slot" { - name = var.webapp_options.slots[0] + count = length(var.webapp_options.slots) + name = var.webapp_options.slots[count.index] app_service_id = azurerm_windows_web_app.this.id virtual_network_subnet_id = var.appsvc_subnet_id https_only = true identity { type = var.identity.type - identity_ids = var.identity.type == "SystemAssigned" ? [] : var.identity.identity_ids + identity_ids = var.identity.type == "SystemAssigned" ? null : var.identity.identity_ids } site_config { + vnet_route_all_enabled = true use_32_bit_worker = false @@ -149,26 +139,26 @@ resource "azurerm_windows_web_app_slot" "slot" { } } -resource "azurecaf_name" "slot" { - name = "${var.web_app_name}-${var.webapp_options.slots[0]}" - resource_type = "azurerm_private_endpoint" -} - module "private_endpoint_slot" { source = "../../private-endpoint" + count = length(azurerm_windows_web_app_slot.slot) - name = azurecaf_name.slot.result + name = "${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot[count.index].name}" resource_group = var.resource_group location = var.location subnet_id = var.frontend_subnet_id - private_connection_resource_id = azurerm_windows_web_app.this.id + private_connection_resource_id = azurerm_windows_web_app.this.id // Change this line - subresource_names = ["sites-${var.webapp_options.slots[0]}"] + subresource_names = ["sites-${azurerm_windows_web_app_slot.slot[count.index].name}"] private_dns_zone = var.private_dns_zone private_dns_records = [ - lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot.name}"), - lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot.name}.scm") + lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot[count.index].name}"), + lower("${azurerm_windows_web_app.this.name}-${azurerm_windows_web_app_slot.slot[count.index].name}.scm") + ] + + depends_on = [ + azurerm_windows_web_app_slot.slot ] } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf index 8f54e8fa..cc28ccd1 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/outputs.tf @@ -14,18 +14,18 @@ output "web_app_principal_id" { value = azurerm_windows_web_app.this.identity.0.principal_id } -output "web_app_slot_id" { - value = azurerm_windows_web_app_slot.slot.id +output "web_app_slot_ids" { + value = azurerm_windows_web_app_slot.slot.*.id } -output "web_app_slot_name" { - value = azurerm_windows_web_app_slot.slot.name +output "web_app_slot_names" { + value = azurerm_windows_web_app_slot.slot.*.name } -output "web_app_slot_hostname" { - value = azurerm_windows_web_app_slot.slot.default_hostname +output "web_app_slot_hostnames" { + value = azurerm_windows_web_app_slot.slot.*.default_hostname } -output "web_app_slot_principal_id" { - value = azurerm_windows_web_app_slot.slot.identity.0.principal_id +output "web_app_slot_identities" { + value = azurerm_windows_web_app_slot.slot.*.identity } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf index 2fd517b5..d1167858 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf @@ -91,9 +91,12 @@ variable "service_plan_options" { variable "webapp_options" { type = object({ - instrumentation_key = string - ai_connection_string = string - slots = list(string) + slots = list(string) + instrumentation_key = optional(string) + ai_connection_string = optional(string) + vnet_route_all_enabled = optional(bool) + use_32_bit_worker = optional(bool) + vnet_integration_enabled = optional(bool) application_stack = object({ current_stack = string # required for windows @@ -112,7 +115,7 @@ variable "webapp_options" { }) }) - description = "The options for the app service" + description = "[Required] The options for the app service" validation { condition = contains(["dotnet", "dotnetcore", "java", "php", "python", "node"], var.webapp_options.application_stack.current_stack) diff --git a/scenarios/shared/terraform-modules/bastion/README.md b/scenarios/shared/terraform-modules/bastion/README.md index 5431e5f5..2c906e1d 100644 --- a/scenarios/shared/terraform-modules/bastion/README.md +++ b/scenarios/shared/terraform-modules/bastion/README.md @@ -1,16 +1,18 @@ -# bastion - - +# bastion + + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules @@ -41,4 +43,4 @@ No modules. | Name | Description | |------|-------------| | [name](#output\_name) | n/a | - + diff --git a/scenarios/shared/terraform-modules/bastion/main.tf b/scenarios/shared/terraform-modules/bastion/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/bastion/main.tf +++ b/scenarios/shared/terraform-modules/bastion/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md index 14258195..acdd23e0 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md @@ -1,29 +1,101 @@ # openai + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [azurerm](#requirement\_azurerm) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 3.105.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [private\_endpoint](#module\_private\_endpoint) | ../../private-endpoint | n/a | + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_oai](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.priv_endpoint](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_cognitive_account.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account) | resource | +| [azurerm_cognitive_deployment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | +| [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | +| [default\_tags\_enabled](#input\_default\_tags\_enabled) | Determines whether or not default tags are applied to resources. If set to true, tags will be applied. If set to false, tags will not be applied. | `bool` | `false` | no | +| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
sku\_name = (Required) The name of the SKU. Possible values are `Standard`, `GlobalBatch`, `GlobalStandard`, and `ProvisionedManaged
sku_tier = (Optional) The tier of the SKU. Possible values are `Free`, `Basic`, `Standard`, `Premium`, and `Enterprise`

rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
}))
` |
map(object({
name = string
model_format = string
model_name = string
model_version = string
rai_policy_name = optional(string)
sku_name = optional(string)
sku_tier = optional(string)
sku_size = optional(string)
sku_family = optional(string)
sku_capacity = optional(number)
}))
| n/a | yes | +| [diagnostic\_setting](#input\_diagnostic\_setting) | A map of objects that represent the configuration for a diagnostic setting."
type = map(object({
name = (Required) Specifies the name of the diagnostic setting. Changing this forces a new resource to be created.
log\_analytics\_workspace\_id = (Optional) (Optional) Specifies the resource id of an Azure Log Analytics workspace where diagnostics data should be sent.
log\_analytics\_destination\_type = (Optional) Possible values are `AzureDiagnostics` and `Dedicated`. When set to Dedicated, logs sent to a Log Analytics workspace will go into resource specific tables, instead of the legacy `AzureDiagnostics` table.
eventhub\_name = (Optional) Specifies the name of the Event Hub where diagnostics data should be sent.
eventhub\_authorization\_rule\_id = (Optional) Specifies the resource id of an Event Hub Namespace Authorization Rule used to send diagnostics data.
storage\_account\_id = (Optional) Specifies the resource id of an Azure storage account where diagnostics data should be sent.
partner\_solution\_id = (Optional) The resource id of the market partner solution where diagnostics data should be sent. For potential partner integrations, click to learn more about partner integration.
audit\_log\_retention\_policy = (Optional) Specifies the retention policy for the audit log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
request\_response\_log\_retention\_policy = (Optional) Specifies the retention policy for the request response log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
trace\_log\_retention\_policy = (Optional) Specifies the retention policy for the trace log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
metric\_retention\_policy = (Optional) Specifies the retention policy for the metric. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
})) |
map(object({
name = string
log_analytics_workspace_id = optional(string)
log_analytics_destination_type = optional(string)
eventhub_name = optional(string)
eventhub_authorization_rule_id = optional(string)
storage_account_id = optional(string)
partner_solution_id = optional(string)
audit_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
request_response_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
trace_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
metric_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
}))
| `{}` | no | +| [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | +| [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [fqdns](#input\_fqdns) | List of FQDNs allowed for the Cognitive Account. | `list(string)` | `null` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
|
{
"type": "SystemAssigned"
}
| no | +| [local\_auth\_enabled](#input\_local\_auth\_enabled) | Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`. | `bool` | `true` | no | +| [location](#input\_location) | Azure OpenAI deployment region. Set this variable to `null` would use resource group's location. | `string` | n/a | yes | +| [network\_acls](#input\_network\_acls) | type = set(object({
default\_action = (Required) The Default Action to use when no rules match from ip\_rules / virtual\_network\_rules. Possible values are `Allow` and `Deny`.
ip\_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account.
virtual\_network\_rules = optional(set(object({
subnet\_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account.
ignore\_missing\_vnet\_service\_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`.
})))
})) |
set(object({
default_action = string
ip_rules = optional(set(string))
virtual_network_rules = optional(set(object({
subnet_id = string
ignore_missing_vnet_service_endpoint = optional(bool, false)
})))
}))
| `null` | no | +| [outbound\_network\_access\_restricted](#input\_outbound\_network\_access\_restricted) | Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [pe\_private\_link\_subnet\_id](#input\_pe\_private\_link\_subnet\_id) | The ID of the Subnet which the Private Endpoint should be created in. Changing this forces a new resource to be created. | `string` | n/a | yes | +| [pe\_subresource](#input\_pe\_subresource) | A list of subresource names which the Private Endpoint is able to connect to. `subresource_names` corresponds to `group_id`. Possible values are detailed in the product [documentation](https://docs.microsoft.com/azure/private-link/private-endpoint-overview#private-link-resource) in the `Subresources` column. Changing this forces a new resource to be created. | `list(string)` |
[
"account"
]
| no | +| [private\_dns\_zone](#input\_private\_dns\_zone) | The Private DNS Zone you'd like to use. |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | +| [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether public network access is allowed for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [resource\_group\_name](#input\_resource\_group\_name) | Name of the azure resource group to use. The resource group must exist. | `string` | n/a | yes | +| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"F0"` | no | +| [storage](#input\_storage) | type = list(object({
storage\_account\_id = (Required) Full resource id of a Microsoft.Storage resource.
identity\_client\_id = (Optional) The client ID of the managed identity associated with the storage resource.
})) |
list(object({
storage_account_id = string
identity_client_id = optional(string)
}))
| `[]` | no | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | +| [tracing\_tags\_enabled](#input\_tracing\_tags\_enabled) | Whether enable tracing tags that generated by BridgeCrew Yor. | `bool` | `false` | no | +| [tracing\_tags\_prefix](#input\_tracing\_tags\_prefix) | Default prefix for generated tracing tags | `string` | `"avm_"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [openai\_endpoint](#output\_openai\_endpoint) | The endpoint used to connect to the Cognitive Service Account. | +| [openai\_primary\_key](#output\_openai\_primary\_key) | The primary access key for the Cognitive Service Account. | +| [openai\_secondary\_key](#output\_openai\_secondary\_key) | The secondary access key for the Cognitive Service Account. | +| [openai\_subdomain](#output\_openai\_subdomain) | The subdomain used to connect to the Cognitive Service Account. | + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.0 | +| [azurerm](#requirement\_azurerm) | >= 4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.72.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >= 4.0 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [private\_endpoint](#module\_private\_endpoint) | ../../private-endpoint | n/a | ## Resources | Name | Type | |------|------| -| [azurecaf_name.caf_name_akv](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.caf_name_oai](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.priv_endpoint](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurerm_cognitive_account.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account) | resource | | [azurerm_cognitive_deployment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment) | resource | @@ -34,21 +106,28 @@ No modules. | [application\_name](#input\_application\_name) | Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | | [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | | [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | -| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model = {
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
}
scale = {
scale\_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created.
}
rai\_policy\_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
})) |
map(object({
name = string
model_format = string
model_name = string
model_version = string
scale_type = string
rai_policy_name = optional(string)
}))
| `{}` | no | +| [default\_tags\_enabled](#input\_default\_tags\_enabled) | Determines whether or not default tags are applied to resources. If set to true, tags will be applied. If set to false, tags will not be applied. | `bool` | `false` | no | +| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
sku\_name = (Required) The name of the SKU. Possible values are `Standard`, `GlobalBatch`, `GlobalStandard`, and `ProvisionedManaged
sku_tier = (Optional) The tier of the SKU. Possible values are `Free`, `Basic`, `Standard`, `Premium`, and `Enterprise`

rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
}))
` |
map(object({
name = string
model_format = string
model_name = string
model_version = optional(string)
rai_policy_name = optional(string)
sku_name = string
sku_tier = optional(string)
sku_size = optional(string)
sku_family = optional(string)
sku_capacity = optional(number)
}))
|
{
"default_deployment": {
"model_format": "OpenAI",
"model_name": "gpt-35-turbo",
"name": "default",
"sku_name": "Standard"
}
}
| no | +| [diagnostic\_setting](#input\_diagnostic\_setting) | A map of objects that represent the configuration for a diagnostic setting."
type = map(object({
name = (Required) Specifies the name of the diagnostic setting. Changing this forces a new resource to be created.
log\_analytics\_workspace\_id = (Optional) (Optional) Specifies the resource id of an Azure Log Analytics workspace where diagnostics data should be sent.
log\_analytics\_destination\_type = (Optional) Possible values are `AzureDiagnostics` and `Dedicated`. When set to Dedicated, logs sent to a Log Analytics workspace will go into resource specific tables, instead of the legacy `AzureDiagnostics` table.
eventhub\_name = (Optional) Specifies the name of the Event Hub where diagnostics data should be sent.
eventhub\_authorization\_rule\_id = (Optional) Specifies the resource id of an Event Hub Namespace Authorization Rule used to send diagnostics data.
storage\_account\_id = (Optional) Specifies the resource id of an Azure storage account where diagnostics data should be sent.
partner\_solution\_id = (Optional) The resource id of the market partner solution where diagnostics data should be sent. For potential partner integrations, click to learn more about partner integration.
audit\_log\_retention\_policy = (Optional) Specifies the retention policy for the audit log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
request\_response\_log\_retention\_policy = (Optional) Specifies the retention policy for the request response log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
trace\_log\_retention\_policy = (Optional) Specifies the retention policy for the trace log. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
metric\_retention\_policy = (Optional) Specifies the retention policy for the metric. This is a block with the following properties:
enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number.
days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number.
})) |
map(object({
name = string
log_analytics_workspace_id = optional(string)
log_analytics_destination_type = optional(string)
eventhub_name = optional(string)
eventhub_authorization_rule_id = optional(string)
storage_account_id = optional(string)
partner_solution_id = optional(string)
audit_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
request_response_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
trace_log_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
metric_retention_policy = optional(object({
enabled = optional(bool, true)
days = optional(number, 7)
}))
}))
| `{}` | no | | [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | | [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | | [fqdns](#input\_fqdns) | List of FQDNs allowed for the Cognitive Account. | `list(string)` | `null` | no | | [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | -| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
| `null` | no | +| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
|
{
"type": "SystemAssigned"
}
| no | | [local\_auth\_enabled](#input\_local\_auth\_enabled) | Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`. | `bool` | `true` | no | | [location](#input\_location) | Azure OpenAI deployment region. Set this variable to `null` would use resource group's location. | `string` | n/a | yes | | [network\_acls](#input\_network\_acls) | type = set(object({
default\_action = (Required) The Default Action to use when no rules match from ip\_rules / virtual\_network\_rules. Possible values are `Allow` and `Deny`.
ip\_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account.
virtual\_network\_rules = optional(set(object({
subnet\_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account.
ignore\_missing\_vnet\_service\_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`.
})))
})) |
set(object({
default_action = string
ip_rules = optional(set(string))
virtual_network_rules = optional(set(object({
subnet_id = string
ignore_missing_vnet_service_endpoint = optional(bool, false)
})))
}))
| `null` | no | | [outbound\_network\_access\_restricted](#input\_outbound\_network\_access\_restricted) | Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [pe\_private\_link\_subnet\_id](#input\_pe\_private\_link\_subnet\_id) | The ID of the Subnet which the Private Endpoint should be created in. Changing this forces a new resource to be created. | `string` | n/a | yes | +| [pe\_subresource](#input\_pe\_subresource) | A list of subresource names which the Private Endpoint is able to connect to. `subresource_names` corresponds to `group_id`. Possible values are detailed in the product [documentation](https://docs.microsoft.com/azure/private-link/private-endpoint-overview#private-link-resource) in the `Subresources` column. Changing this forces a new resource to be created. | `list(string)` |
[
"account"
]
| no | +| [private\_dns\_zone](#input\_private\_dns\_zone) | The Private DNS Zone you'd like to use. |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | | [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether public network access is allowed for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | | [resource\_group\_name](#input\_resource\_group\_name) | Name of the azure resource group to use. The resource group must exist. | `string` | n/a | yes | -| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"S0"` | no | +| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"F0"` | no | | [storage](#input\_storage) | type = list(object({
storage\_account\_id = (Required) Full resource id of a Microsoft.Storage resource.
identity\_client\_id = (Optional) The client ID of the managed identity associated with the storage resource.
})) |
list(object({
storage_account_id = string
identity_client_id = optional(string)
}))
| `[]` | no | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | +| [tracing\_tags\_enabled](#input\_tracing\_tags\_enabled) | Whether enable tracing tags that generated by BridgeCrew Yor. | `bool` | `false` | no | +| [tracing\_tags\_prefix](#input\_tracing\_tags\_prefix) | Default prefix for generated tracing tags | `string` | `"avm_"` | no | ## Outputs diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf index 4a4ebfe0..222aa939 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.0" + version = ">= 4.0" } azurecaf = { source = "aztfmod/azurecaf" diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf index 75c67de7..bfde6108 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf @@ -78,8 +78,13 @@ resource "azurerm_cognitive_deployment" "this" { name = each.value.model_name version = each.value.model_version } - scale { - type = each.value.scale_type + + sku { + name = each.value.sku_name + tier = each.value.sku_tier + size = each.value.sku_size + family = each.value.sku_family + capacity = each.value.sku_capacity } } diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf index 44f9a7e0..86f8d39c 100644 --- a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf @@ -46,23 +46,33 @@ variable "deployment" { name = string model_format = string model_name = string - model_version = string - scale_type = string + model_version = optional(string) rai_policy_name = optional(string) + sku_name = string + sku_tier = optional(string) + sku_size = optional(string) + sku_family = optional(string) + sku_capacity = optional(number) })) - default = {} + default = { + default_deployment = { + name = "default" + model_format = "OpenAI" + model_name = "gpt-35-turbo" + sku_name = "Standard" + } + } + description = <<-DESCRIPTION type = map(object({ name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created. cognitive_account_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created. - model = { - model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. - model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. - model_version = (Required) The version of Cognitive Services Account Deployment model. - } - scale = { - scale_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created. - } + model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. + model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. + model_version = (Required) The version of Cognitive Services Account Deployment model. + sku_name = (Required) The name of the SKU. Possible values are `Standard`, `GlobalBatch`, `GlobalStandard`, and `ProvisionedManaged + sku_tier = (Optional) The tier of the SKU. Possible values are `Free`, `Basic`, `Standard`, `Premium`, and `Enterprise` + rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created. })) DESCRIPTION @@ -92,7 +102,7 @@ variable "identity" { type = string identity_ids = optional(list(string)) }) - default = { + default = { type = "SystemAssigned" } description = <<-DESCRIPTION @@ -144,7 +154,7 @@ variable "pe_subresource" { } variable "pe_private_link_subnet_id" { - type = string + type = string description = "The ID of the Subnet which the Private Endpoint should be created in. Changing this forces a new resource to be created." } @@ -208,7 +218,7 @@ variable "public_network_access_enabled" { variable "sku_name" { type = string - default = "S0" + default = "F0" description = "Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`." } diff --git a/scenarios/shared/terraform-modules/firewall/README.md b/scenarios/shared/terraform-modules/firewall/README.md index bdc0596b..2c818f84 100644 --- a/scenarios/shared/terraform-modules/firewall/README.md +++ b/scenarios/shared/terraform-modules/firewall/README.md @@ -3,14 +3,16 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/firewall/main.tf b/scenarios/shared/terraform-modules/firewall/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/firewall/main.tf +++ b/scenarios/shared/terraform-modules/firewall/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/frontdoor/README.md b/scenarios/shared/terraform-modules/frontdoor/README.md index ba5d870c..820ec43c 100644 --- a/scenarios/shared/terraform-modules/frontdoor/README.md +++ b/scenarios/shared/terraform-modules/frontdoor/README.md @@ -3,14 +3,16 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/frontdoor/endpoint/README.md b/scenarios/shared/terraform-modules/frontdoor/endpoint/README.md index 9663ddd1..7b25be0f 100644 --- a/scenarios/shared/terraform-modules/frontdoor/endpoint/README.md +++ b/scenarios/shared/terraform-modules/frontdoor/endpoint/README.md @@ -1,46 +1,46 @@ # endpoint -## Requirements - -No requirements. - -## Providers - -| Name | Version | -|------|---------| -| [azurerm](#provider\_azurerm) | 3.60.0 | -| [null](#provider\_null) | 3.2.1 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [azurerm_cdn_frontdoor_endpoint.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_endpoint) | resource | -| [azurerm_cdn_frontdoor_origin.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_origin) | resource | -| [azurerm_cdn_frontdoor_origin_group.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_origin_group) | resource | -| [azurerm_cdn_frontdoor_route.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_route) | resource | -| [null_resource.web_app](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [endpoint\_name](#input\_endpoint\_name) | The name of the front door endpoint. | `string` | n/a | yes | -| [frontdoor\_profile\_id](#input\_frontdoor\_profile\_id) | The front door profile id | `string` | n/a | yes | -| [location](#input\_location) | The Azure region of the web app | `string` | n/a | yes | -| [private\_link\_target\_type](#input\_private\_link\_target\_type) | The private link target type | `string` | n/a | yes | -| [web\_app\_hostname](#input\_web\_app\_hostname) | The web app hostname | `string` | n/a | yes | -| [web\_app\_id](#input\_web\_app\_id) | The web app id | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [cdn\_frontdoor\_endpoint\_id](#output\_cdn\_frontdoor\_endpoint\_id) | n/a | -| [cdn\_frontdoor\_endpoint\_uri](#output\_cdn\_frontdoor\_endpoint\_uri) | n/a | +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | n/a | +| [null](#provider\_null) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurerm_cdn_frontdoor_endpoint.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_endpoint) | resource | +| [azurerm_cdn_frontdoor_origin.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_origin) | resource | +| [azurerm_cdn_frontdoor_origin_group.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_origin_group) | resource | +| [azurerm_cdn_frontdoor_route.web_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_route) | resource | +| [null_resource.web_app](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [endpoint\_name](#input\_endpoint\_name) | The name of the front door endpoint. | `string` | n/a | yes | +| [frontdoor\_profile\_id](#input\_frontdoor\_profile\_id) | The front door profile id | `string` | n/a | yes | +| [location](#input\_location) | The Azure region of the web app | `string` | n/a | yes | +| [private\_link\_target\_type](#input\_private\_link\_target\_type) | The private link target type | `string` | n/a | yes | +| [web\_app\_hostname](#input\_web\_app\_hostname) | The web app hostname | `string` | n/a | yes | +| [web\_app\_id](#input\_web\_app\_id) | The web app id | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [cdn\_frontdoor\_endpoint\_id](#output\_cdn\_frontdoor\_endpoint\_id) | n/a | +| [cdn\_frontdoor\_endpoint\_uri](#output\_cdn\_frontdoor\_endpoint\_uri) | n/a | diff --git a/scenarios/shared/terraform-modules/frontdoor/main.tf b/scenarios/shared/terraform-modules/frontdoor/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/frontdoor/main.tf +++ b/scenarios/shared/terraform-modules/frontdoor/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/key-vault/README.md b/scenarios/shared/terraform-modules/key-vault/README.md index 423b3f13..23b4f0b2 100644 --- a/scenarios/shared/terraform-modules/key-vault/README.md +++ b/scenarios/shared/terraform-modules/key-vault/README.md @@ -1,16 +1,74 @@ # key-vault + +## Requirements + +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_akv](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.private_endpoint](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_key_vault.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault) | resource | +| [azurerm_private_dns_a_record.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_a_record) | resource | +| [azurerm_private_endpoint.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_role_assignment.secrets_officer](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | +| [azurerm_role_assignment.secrets_user](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | +| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | The name of your application | `string` | n/a | yes | +| [environment](#input\_environment) | The environment (dev, test, prod...) | `string` | `"dev"` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [private\_dns\_zone](#input\_private\_dns\_zone) | The private dns zone id where the app service will be integrated |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | +| [private\_link\_subnet\_id](#input\_private\_link\_subnet\_id) | The subnet id | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | The name of the resource group where all resources in this example should be created. | `string` | n/a | yes | +| [secret\_officer\_identities](#input\_secret\_officer\_identities) | The list of identities that will be granted secret officer permissions | `list(string)` | `[]` | no | +| [secret\_reader\_identities](#input\_secret\_reader\_identities) | The list of identities that will be granted secret reader permissions | `list(string)` | n/a | yes | +| [sku\_name](#input\_sku\_name) | The sku name for the app service plan | `string` | `"standard"` | no | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | +| [tenant\_id](#input\_tenant\_id) | The Azure AD tenant ID for the identities. If no value provided, will use current deployment environment tenant. | `string` | `null` | no | +| [unique\_id](#input\_unique\_id) | The unique id | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [vault\_name](#output\_vault\_name) | n/a | +| [vault\_uri](#output\_vault\_uri) | n/a | + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules @@ -51,6 +109,7 @@ No modules. | Name | Description | |------|-------------| +| [vault\_id](#output\_vault\_id) | n/a | | [vault\_name](#output\_vault\_name) | n/a | | [vault\_uri](#output\_vault\_uri) | n/a | diff --git a/scenarios/shared/terraform-modules/key-vault/main.tf b/scenarios/shared/terraform-modules/key-vault/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/key-vault/main.tf +++ b/scenarios/shared/terraform-modules/key-vault/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/key-vault/outputs.tf b/scenarios/shared/terraform-modules/key-vault/outputs.tf index 6407c214..71f7d40b 100644 --- a/scenarios/shared/terraform-modules/key-vault/outputs.tf +++ b/scenarios/shared/terraform-modules/key-vault/outputs.tf @@ -4,4 +4,8 @@ output "vault_uri" { output "vault_name" { value = azurerm_key_vault.this.name +} + +output "vault_id" { + value = azurerm_key_vault.this.id } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/network/README.md b/scenarios/shared/terraform-modules/network/README.md index 6c22f608..50d4f626 100644 --- a/scenarios/shared/terraform-modules/network/README.md +++ b/scenarios/shared/terraform-modules/network/README.md @@ -3,14 +3,16 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/network/main.tf b/scenarios/shared/terraform-modules/network/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/network/main.tf +++ b/scenarios/shared/terraform-modules/network/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/network/module.tf b/scenarios/shared/terraform-modules/network/module.tf index c466e459..6730bcfe 100644 --- a/scenarios/shared/terraform-modules/network/module.tf +++ b/scenarios/shared/terraform-modules/network/module.tf @@ -20,22 +20,23 @@ resource "azurerm_virtual_network" "this" { } resource "azurerm_subnet" "this" { - count = length(var.subnets) + for_each = { for idx, subnet in var.subnets : subnet.name => subnet if subnet != null } - name = var.subnets[count.index].name - address_prefixes = var.subnets[count.index].subnet_cidr + name = each.key + address_prefixes = each.value.subnet_cidr resource_group_name = var.resource_group virtual_network_name = azurerm_virtual_network.this.name + dynamic "delegation" { - for_each = var.subnets[count.index].delegation == null ? [] : [var.subnets[count.index].delegation] + for_each = each.value.delegation == null ? [] : [each.value.delegation] content { - name = var.subnets[count.index].delegation.name + name = delegation.value.name service_delegation { - name = var.subnets[count.index].delegation.service_delegation.name - actions = var.subnets[count.index].delegation.service_delegation.actions + name = delegation.value.service_delegation.name + actions = delegation.value.service_delegation.actions } } } diff --git a/scenarios/shared/terraform-modules/openai/README.md b/scenarios/shared/terraform-modules/openai/README.md index 14258195..7fe5ccf0 100644 --- a/scenarios/shared/terraform-modules/openai/README.md +++ b/scenarios/shared/terraform-modules/openai/README.md @@ -1,19 +1,19 @@ # openai - + ## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.0 | +| [azurerm](#requirement\_azurerm) | >= 4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.72.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | ## Modules @@ -52,6 +52,65 @@ No modules. ## Outputs +| Name | Description | +|------|-------------| +| [openai\_endpoint](#output\_openai\_endpoint) | The endpoint used to connect to the Cognitive Service Account. | +| [openai\_primary\_key](#output\_openai\_primary\_key) | The primary access key for the Cognitive Service Account. | +| [openai\_secondary\_key](#output\_openai\_secondary\_key) | The secondary access key for the Cognitive Service Account. | +| [openai\_subdomain](#output\_openai\_subdomain) | The subdomain used to connect to the Cognitive Service Account. | + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [azurerm](#requirement\_azurerm) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >= 4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_oai](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_cognitive_account.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account) | resource | +| [azurerm_cognitive_deployment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | +| [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | +| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model = {
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
}
scale = {
scale\_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created.
}
rai\_policy\_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
})) |
map(object({
name = string
model_format = string
model_name = string
model_version = string
scale_type = string
rai_policy_name = optional(string)
}))
| `{}` | no | +| [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | +| [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [fqdns](#input\_fqdns) | List of FQDNs allowed for the Cognitive Account. | `list(string)` | `null` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
| `null` | no | +| [local\_auth\_enabled](#input\_local\_auth\_enabled) | Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`. | `bool` | `true` | no | +| [location](#input\_location) | Azure OpenAI deployment region. Set this variable to `null` would use resource group's location. | `string` | n/a | yes | +| [network\_acls](#input\_network\_acls) | type = set(object({
default\_action = (Required) The Default Action to use when no rules match from ip\_rules / virtual\_network\_rules. Possible values are `Allow` and `Deny`.
ip\_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account.
virtual\_network\_rules = optional(set(object({
subnet\_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account.
ignore\_missing\_vnet\_service\_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`.
})))
})) |
set(object({
default_action = string
ip_rules = optional(set(string))
virtual_network_rules = optional(set(object({
subnet_id = string
ignore_missing_vnet_service_endpoint = optional(bool, false)
})))
}))
| `null` | no | +| [outbound\_network\_access\_restricted](#input\_outbound\_network\_access\_restricted) | Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether public network access is allowed for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [resource\_group\_name](#input\_resource\_group\_name) | Name of the azure resource group to use. The resource group must exist. | `string` | n/a | yes | +| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"S0"` | no | +| [storage](#input\_storage) | type = list(object({
storage\_account\_id = (Required) Full resource id of a Microsoft.Storage resource.
identity\_client\_id = (Optional) The client ID of the managed identity associated with the storage resource.
})) |
list(object({
storage_account_id = string
identity_client_id = optional(string)
}))
| `[]` | no | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | + +## Outputs + | Name | Description | |------|-------------| | [openai\_endpoint](#output\_openai\_endpoint) | The endpoint used to connect to the Cognitive Service Account. | diff --git a/scenarios/shared/terraform-modules/openai/main.tf b/scenarios/shared/terraform-modules/openai/main.tf index 4a4ebfe0..222aa939 100644 --- a/scenarios/shared/terraform-modules/openai/main.tf +++ b/scenarios/shared/terraform-modules/openai/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.0" + version = ">= 4.0" } azurecaf = { source = "aztfmod/azurecaf" diff --git a/scenarios/shared/terraform-modules/openai/module.tf b/scenarios/shared/terraform-modules/openai/module.tf index 0ba5d222..8867c6bc 100644 --- a/scenarios/shared/terraform-modules/openai/module.tf +++ b/scenarios/shared/terraform-modules/openai/module.tf @@ -1,4 +1,4 @@ -resource "azurecaf_name" "caf_name_akv" { +resource "azurecaf_name" "caf_name_oai" { name = var.application_name resource_type = "azurerm_cognitive_account" prefixes = var.global_settings.prefixes @@ -10,13 +10,14 @@ resource "azurecaf_name" "caf_name_akv" { use_slug = var.global_settings.use_slug } + resource "azurerm_cognitive_account" "this" { kind = "OpenAI" location = var.location - name = azurecaf_name.caf_name_akv.result + name = azurecaf_name.caf_name_oai.result resource_group_name = var.resource_group_name sku_name = var.sku_name - custom_subdomain_name = var.custom_subdomain_name + custom_subdomain_name = coalesce(var.custom_subdomain_name, azurecaf_name.caf_name_oai.result) dynamic_throttling_enabled = var.dynamic_throttling_enabled fqdns = var.fqdns local_auth_enabled = var.local_auth_enabled @@ -37,14 +38,15 @@ resource "azurerm_cognitive_account" "this" { identity_ids = identity.value.identity_ids } } + dynamic "network_acls" { - for_each = var.network_acls != null ? [var.network_acls] : [] + for_each = var.network_acls != null ? var.network_acls : [] content { default_action = network_acls.value.default_action - ip_rules = network_acls.value.ip_rules + ip_rules = lookup(network_acls.value, "ip_rules", null) dynamic "virtual_network_rules" { - for_each = network_acls.value.virtual_network_rules != null ? network_acls.value.virtual_network_rules : [] + for_each = lookup(network_acls.value, "virtual_network_rules", []) content { subnet_id = virtual_network_rules.value.subnet_id ignore_missing_vnet_service_endpoint = virtual_network_rules.value.ignore_missing_vnet_service_endpoint @@ -52,6 +54,7 @@ resource "azurerm_cognitive_account" "this" { } } } + dynamic "storage" { for_each = var.storage content { @@ -75,7 +78,12 @@ resource "azurerm_cognitive_deployment" "this" { name = each.value.model_name version = each.value.model_version } - scale { - type = each.value.scale_type + + sku { + name = each.value.sku_name + tier = each.value.sku_tier + size = coalesce(each.value.sku_size, null) + family = coalesce(each.value.sku_family, null) + capacity = coalesce(each.value.sku_capacity, null) } -} \ No newline at end of file +} diff --git a/scenarios/shared/terraform-modules/private-dns-zone/README.md b/scenarios/shared/terraform-modules/private-dns-zone/README.md index a2c47a2b..74b76e62 100644 --- a/scenarios/shared/terraform-modules/private-dns-zone/README.md +++ b/scenarios/shared/terraform-modules/private-dns-zone/README.md @@ -3,13 +3,15 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/private-dns-zone/main.tf b/scenarios/shared/terraform-modules/private-dns-zone/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/private-dns-zone/main.tf +++ b/scenarios/shared/terraform-modules/private-dns-zone/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/private-endpoint/README.md b/scenarios/shared/terraform-modules/private-endpoint/README.md index 90dcab06..111dd9d2 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/README.md +++ b/scenarios/shared/terraform-modules/private-endpoint/README.md @@ -3,13 +3,15 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/private-endpoint/main.tf b/scenarios/shared/terraform-modules/private-endpoint/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/main.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/private-endpoint/module.tf b/scenarios/shared/terraform-modules/private-endpoint/module.tf index ef153253..ec7901aa 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/module.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/module.tf @@ -8,8 +8,9 @@ resource "azurerm_private_endpoint" "this" { private_service_connection { name = var.name private_connection_resource_id = var.private_connection_resource_id - subresource_names = var.subresource_names - is_manual_connection = false + + subresource_names = var.subresource_names + is_manual_connection = false } tags = local.tags diff --git a/scenarios/shared/terraform-modules/redis/README.md b/scenarios/shared/terraform-modules/redis/README.md index c21899e6..6f899408 100644 --- a/scenarios/shared/terraform-modules/redis/README.md +++ b/scenarios/shared/terraform-modules/redis/README.md @@ -3,14 +3,16 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/redis/main.tf b/scenarios/shared/terraform-modules/redis/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/redis/main.tf +++ b/scenarios/shared/terraform-modules/redis/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/redis/module.tf b/scenarios/shared/terraform-modules/redis/module.tf index 64dae234..70ff2d21 100644 --- a/scenarios/shared/terraform-modules/redis/module.tf +++ b/scenarios/shared/terraform-modules/redis/module.tf @@ -17,12 +17,12 @@ resource "azurerm_redis_cache" "this" { capacity = 2 family = "C" sku_name = var.sku_name - enable_non_ssl_port = false + non_ssl_port_enabled = false minimum_tls_version = "1.2" public_network_access_enabled = false redis_configuration { - enable_authentication = true + authentication_enabled = true } tags = local.tags diff --git a/scenarios/shared/terraform-modules/sql-database/README.md b/scenarios/shared/terraform-modules/sql-database/README.md index 15b594d7..2325dd98 100644 --- a/scenarios/shared/terraform-modules/sql-database/README.md +++ b/scenarios/shared/terraform-modules/sql-database/README.md @@ -1,16 +1,76 @@ # sql-database + +## Requirements + +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azuread](#provider\_azuread) | 3.0.2 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_sqlserver](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurecaf_name.private_endpoint](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_mssql_database.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/mssql_database) | resource | +| [azurerm_mssql_server.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/mssql_server) | resource | +| [azurerm_private_dns_a_record.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_a_record) | resource | +| [azurerm_private_endpoint.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azuread_group.sql_admin_group](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | The name of your application | `string` | n/a | yes | +| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | n/a | `string` | `null` | no | +| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | n/a | `string` | `null` | no | +| [environment](#input\_environment) | The environment (dev, test, prod...) | `string` | `"dev"` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | +| [private\_dns\_zone](#input\_private\_dns\_zone) | The private dns zone id where the app service will be integrated |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | +| [private\_link\_subnet\_id](#input\_private\_link\_subnet\_id) | The subnet id where the SQL database will be integrated | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | The name of the resource group where all resources in this example should be created. | `string` | n/a | yes | +| [sql\_databases](#input\_sql\_databases) | The list of SQL databases to be created |
list(object({
name = string
sku_name = string
}))
| n/a | yes | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | +| [tenant\_id](#input\_tenant\_id) | The tenant id where the resources will be created | `string` | n/a | yes | +| [unique\_id](#input\_unique\_id) | A unique identifier | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [sql\_db\_connection\_string](#output\_sql\_db\_connection\_string) | n/a | +| [sql\_db\_name](#output\_sql\_db\_name) | n/a | +| [sql\_server\_name](#output\_sql\_server\_name) | n/a | + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | +| [random](#provider\_random) | n/a | ## Modules @@ -22,20 +82,22 @@ No modules. |------|------| | [azurecaf_name.caf_name_sqlserver](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | | [azurecaf_name.private_endpoint](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_key_vault_secret.sql_admin_password](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | | [azurerm_mssql_database.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/mssql_database) | resource | | [azurerm_mssql_server.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/mssql_server) | resource | | [azurerm_private_dns_a_record.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_a_record) | resource | | [azurerm_private_endpoint.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [random_password.sql_admin_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [administrator\_login](#input\_administrator\_login) | The administrator login for the SQL Server | `string` | `null` | no | | [application\_name](#input\_application\_name) | The name of your application | `string` | n/a | yes | -| [entra\_admin\_group\_name](#input\_entra\_admin\_group\_name) | n/a | `string` | n/a | yes | -| [entra\_admin\_group\_object\_id](#input\_entra\_admin\_group\_object\_id) | n/a | `string` | n/a | yes | | [environment](#input\_environment) | The environment (dev, test, prod...) | `string` | `"dev"` | no | | [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [key\_vault\_id](#input\_key\_vault\_id) | The id of the key vault where the password will be stored | `string` | n/a | yes | | [location](#input\_location) | The Azure region where all resources in this example should be created | `string` | `"westus2"` | no | | [private\_dns\_zone](#input\_private\_dns\_zone) | The private dns zone id where the app service will be integrated |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | | [private\_link\_subnet\_id](#input\_private\_link\_subnet\_id) | The subnet id where the SQL database will be integrated | `string` | n/a | yes | diff --git a/scenarios/shared/terraform-modules/sql-database/main.tf b/scenarios/shared/terraform-modules/sql-database/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/sql-database/main.tf +++ b/scenarios/shared/terraform-modules/sql-database/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/sql-database/module.tf b/scenarios/shared/terraform-modules/sql-database/module.tf index c5d7ccf0..807e1f7a 100644 --- a/scenarios/shared/terraform-modules/sql-database/module.tf +++ b/scenarios/shared/terraform-modules/sql-database/module.tf @@ -10,6 +10,22 @@ resource "azurecaf_name" "caf_name_sqlserver" { use_slug = var.global_settings.use_slug } +# data "azurerm_client_config" "current" { } + +# data "azuread_user" "current_user" { +# object_id = data.azurerm_client_config.current.object_id +# } + +resource "random_password" "sql_admin_password" { + length = 16 + special = true +} + +resource "azurerm_key_vault_secret" "sql_admin_password" { + name = "sql-admin-password" + value = random_password.sql_admin_password.result + key_vault_id = var.key_vault_id +} # Create the SQL Server resource "azurerm_mssql_server" "this" { @@ -23,12 +39,14 @@ resource "azurerm_mssql_server" "this" { tags = local.tags - azuread_administrator { - login_username = var.entra_admin_group_name - object_id = var.entra_admin_group_object_id - azuread_authentication_only = true - tenant_id = var.tenant_id - } + administrator_login = var.administrator_login == null ? "sqladmin" : var.administrator_login + administrator_login_password = azurerm_key_vault_secret.sql_admin_password.value + # azuread_administrator { + # login_username = data.azuread_user.current_user.display_name + # object_id = data.azurerm_client_config.current.object_id + # azuread_authentication_only = true + # tenant_id = var.tenant_id + # } } # Create a the SQL database diff --git a/scenarios/shared/terraform-modules/sql-database/variables.tf b/scenarios/shared/terraform-modules/sql-database/variables.tf index 26c4a181..b0276fdc 100644 --- a/scenarios/shared/terraform-modules/sql-database/variables.tf +++ b/scenarios/shared/terraform-modules/sql-database/variables.tf @@ -1,3 +1,14 @@ +variable "administrator_login" { + type = string + description = "The administrator login for the SQL Server" + default = null +} + +variable "key_vault_id" { + type = string + description = "The id of the key vault where the password will be stored" +} + variable "application_name" { type = string description = "The name of your application" @@ -30,14 +41,6 @@ variable "tenant_id" { description = "The tenant id where the resources will be created" } -variable "entra_admin_group_object_id" { - type = string -} - -variable "entra_admin_group_name" { - type = string -} - variable "private_link_subnet_id" { type = string description = "The subnet id where the SQL database will be integrated" diff --git a/scenarios/shared/terraform-modules/user-defined-routes/README.md b/scenarios/shared/terraform-modules/user-defined-routes/README.md index ddf14097..30c15985 100644 --- a/scenarios/shared/terraform-modules/user-defined-routes/README.md +++ b/scenarios/shared/terraform-modules/user-defined-routes/README.md @@ -3,14 +3,16 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | ## Modules diff --git a/scenarios/shared/terraform-modules/user-defined-routes/main.tf b/scenarios/shared/terraform-modules/user-defined-routes/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/user-defined-routes/main.tf +++ b/scenarios/shared/terraform-modules/user-defined-routes/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/user-defined-routes/module.tf b/scenarios/shared/terraform-modules/user-defined-routes/module.tf index 9084f52d..a8ff6d73 100644 --- a/scenarios/shared/terraform-modules/user-defined-routes/module.tf +++ b/scenarios/shared/terraform-modules/user-defined-routes/module.tf @@ -7,7 +7,7 @@ resource "azurerm_route_table" "this" { name = azurecaf_name.route_table.result location = var.location resource_group_name = var.resource_group - disable_bgp_route_propagation = false + bgp_route_propagation_enabled = false tags = local.tags } diff --git a/scenarios/shared/terraform-modules/windows-vm-ext/README.md b/scenarios/shared/terraform-modules/windows-vm-ext/README.md index 55f0d3b0..4370d71b 100644 --- a/scenarios/shared/terraform-modules/windows-vm-ext/README.md +++ b/scenarios/shared/terraform-modules/windows-vm-ext/README.md @@ -1,5 +1,42 @@ # windows-vm-ext + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurerm_virtual_machine_extension.aad](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | +| [azurerm_virtual_machine_extension.install_ssms](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [enable\_microsoft\_entra\_join](#input\_enable\_microsoft\_entra\_join) | True to enable Microsoft Entra join of the VM. | `bool` | `true` | no | +| [enroll\_with\_mdm](#input\_enroll\_with\_mdm) | True to enroll the device with an approved MDM provider like Intune. | `bool` | `true` | no | +| [install\_extensions](#input\_install\_extensions) | n/a | `bool` | `false` | no | +| [mdm\_id](#input\_mdm\_id) | The default value is the MDM Id for Intune, but you can use your own MDM id if you want to use a different MDM service. | `string` | `"0000000a-0000-0000-c000-000000000000"` | no | +| [remote\_exec\_commands](#input\_remote\_exec\_commands) | values to pass to the remote-exec provisioner | `list(string)` | `[]` | no | +| [vm\_id](#input\_vm\_id) | value of the vm id | `string` | n/a | yes | + +## Outputs + +No outputs. + ## Requirements @@ -9,7 +46,7 @@ No requirements. | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | 3.59.0 | +| [azurerm](#provider\_azurerm) | n/a | ## Modules @@ -19,7 +56,7 @@ No modules. | Name | Type | |------|------| -| [azurerm_virtual_machine_extension.entra](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | +| [azurerm_virtual_machine_extension.aad](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | | [azurerm_virtual_machine_extension.install_ssms](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_extension) | resource | ## Inputs diff --git a/scenarios/shared/terraform-modules/windows-vm-ext/main.tf b/scenarios/shared/terraform-modules/windows-vm-ext/main.tf index 2d0bf743..d75b3128 100644 --- a/scenarios/shared/terraform-modules/windows-vm-ext/main.tf +++ b/scenarios/shared/terraform-modules/windows-vm-ext/main.tf @@ -1,4 +1,4 @@ -resource "azurerm_virtual_machine_extension" "entra" { +resource "azurerm_virtual_machine_extension" "aad" { count = var.enable_microsoft_entra_join ? 1 : 0 name = "aad-login-for-windows" diff --git a/scenarios/shared/terraform-modules/windows-vm/README.md b/scenarios/shared/terraform-modules/windows-vm/README.md index d9be0244..3e81b29a 100644 --- a/scenarios/shared/terraform-modules/windows-vm/README.md +++ b/scenarios/shared/terraform-modules/windows-vm/README.md @@ -1,18 +1,20 @@ # windows-vm - + ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | ## Providers | Name | Version | |------|---------| -| [azuread](#provider\_azuread) | 2.39.0 | -| [azurecaf](#provider\_azurecaf) | 1.2.25 | -| [azurerm](#provider\_azurerm) | 3.60.0 | -| [random](#provider\_random) | 3.5.1 | +| [azuread](#provider\_azuread) | 3.0.2 | +| [azurecaf](#provider\_azurecaf) | 1.2.28 | +| [azurerm](#provider\_azurerm) | 4.5.0 | +| [random](#provider\_random) | 3.6.3 | ## Modules @@ -55,6 +57,71 @@ No modules. ## Outputs +| Name | Description | +|------|-------------| +| [id](#output\_id) | n/a | +| [name](#output\_name) | n/a | +| [principal\_id](#output\_principal\_id) | n/a | +| [private\_ip\_address](#output\_private\_ip\_address) | n/a | +| [vm\_key\_vault\_secret\_ids](#output\_vm\_key\_vault\_secret\_ids) | n/a | + + +## Requirements + +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | >=4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azuread](#provider\_azuread) | n/a | +| [azurecaf](#provider\_azurecaf) | n/a | +| [azurerm](#provider\_azurerm) | >=4.0 | +| [random](#provider\_random) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_winvm](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_key_vault_secret.admin_password](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | +| [azurerm_key_vault_secret.admin_username](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | +| [azurerm_network_interface.vm_nic](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface) | resource | +| [azurerm_role_assignment.vm_admin_role_assignment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | +| [azurerm_windows_virtual_machine.vm](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/windows_virtual_machine) | resource | +| [random_password.password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [azuread_user.vm_admin](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/user) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [admin\_password](#input\_admin\_password) | n/a | `string` | `null` | no | +| [admin\_username](#input\_admin\_username) | n/a | `string` | n/a | yes | +| [entra\_admin\_object\_id](#input\_entra\_admin\_object\_id) | The Microsoft Entra ID for the VM admin user/group. If entra\_admin\_username is not specified, this value will be used. | `string` | `null` | no | +| [entra\_admin\_username](#input\_entra\_admin\_username) | [Optional] The Microsoft Entra ID username for the VM admin account. If entra\_admin\_object\_id is not specified, this value will be used. | `string` | `null` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [identity](#input\_identity) | The identity type and the list of identities ids |
object({
type = string
identity_ids = optional(list(string))
})
|
{
"identity_ids": [],
"type": "SystemAssigned"
}
| no | +| [key\_vault\_id](#input\_key\_vault\_id) | Optional ID of the key vault to store the VM password | `string` | `null` | no | +| [location](#input\_location) | The location (Azure region) where the resources should be created. | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | The name of the resource group where all resources should be created. | `string` | n/a | yes | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | +| [vm\_image\_offer](#input\_vm\_image\_offer) | n/a | `string` | `"windows-11"` | no | +| [vm\_image\_publisher](#input\_vm\_image\_publisher) | n/a | `string` | `"MicrosoftWindowsDesktop"` | no | +| [vm\_image\_sku](#input\_vm\_image\_sku) | n/a | `string` | `"win11-22h2-pro"` | no | +| [vm\_image\_version](#input\_vm\_image\_version) | n/a | `string` | `"latest"` | no | +| [vm\_name](#input\_vm\_name) | name of the virtual machine | `any` | n/a | yes | +| [vm\_size](#input\_vm\_size) | n/a | `string` | `"Standard_B2ms"` | no | +| [vm\_subnet\_id](#input\_vm\_subnet\_id) | n/a | `string` | n/a | yes | + +## Outputs + | Name | Description | |------|-------------| | [id](#output\_id) | n/a | diff --git a/scenarios/shared/terraform-modules/windows-vm/main.tf b/scenarios/shared/terraform-modules/windows-vm/main.tf index 132b090b..fdb92ba9 100644 --- a/scenarios/shared/terraform-modules/windows-vm/main.tf +++ b/scenarios/shared/terraform-modules/windows-vm/main.tf @@ -1,5 +1,9 @@ terraform { required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=4.0" + } azurecaf = { source = "aztfmod/azurecaf" } diff --git a/scenarios/shared/terraform-modules/windows-vm/variables.tf b/scenarios/shared/terraform-modules/windows-vm/variables.tf index 213bd502..b1e645c1 100644 --- a/scenarios/shared/terraform-modules/windows-vm/variables.tf +++ b/scenarios/shared/terraform-modules/windows-vm/variables.tf @@ -38,8 +38,7 @@ variable "key_vault_id" { } variable "admin_username" { - type = string - default = null + type = string } variable "admin_password" {