Skip to content

Commit 216003e

Browse files
authored
Switch use of storage account keys by Terraform to use Azure AD authentication (#4104)
* Switch use of storage account keys to use Entra authentication where possible Fixes #4103 * Fix health-services bundle * Increase core version * Add tags to fix linting. * Fix linting * # tflint-ignore-file: azurerm_resource_missing_tags * Revert linting fixes
1 parent f91bdd1 commit 216003e

File tree

72 files changed

+503
-263
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+503
-263
lines changed

.github/actions/devcontainer_run_command/action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ runs:
186186
-e TF_INPUT="0" \
187187
-e TF_IN_AUTOMATION="1" \
188188
-e USE_ENV_VARS_NOT_FILES="true" \
189+
-e ARM_STORAGE_USE_AZUREAD="true" \
190+
-e ARM_USE_AZUREAD="true" \
191+
-e ARM_USE_OIDC="true" \
189192
-e BUNDLE_TYPE="${{ inputs.BUNDLE_TYPE }}" \
190193
-e WORKSPACE_SERVICE_NAME="${{ inputs.WORKSPACE_SERVICE_NAME }}" \
191194
-e ARM_ENVIRONMENT="${{ env.ARM_ENVIRONMENT }}" \

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ FEATURES:
77

88
ENHANCEMENTS:
99
* Expose APP_SERVICE_SKU build variable to allow enablement of App Gateway WAF ([#4111](https://github.com/microsoft/AzureTRE/pull/4111))
10+
* Update Terraform to use Azure AD authentication rather than storage account keys ([#4103](https://github.com/microsoft/AzureTRE/issues/4103))
11+
1012
BUG FIXES:
1113

1214
COMPONENTS:

core/terraform/migrate.sh

Lines changed: 35 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ set -o pipefail
55
set -o nounset
66
# set -o xtrace
77

8-
terraform_wrapper_path="../../devops/scripts/terraform_wrapper.sh"
8+
# Configure AzureRM provider to user Azure AD to connect to storage accounts
9+
export ARM_STORAGE_USE_AZUREAD=true
10+
11+
# Configure AzureRM backend to user Azure AD to connect to storage accounts
12+
export ARM_USE_AZUREAD=true
13+
export ARM_USE_OIDC=true
14+
15+
# terraform_wrapper_path="../../devops/scripts/terraform_wrapper.sh"
916

1017
# This variables are loaded in for us
1118
# shellcheck disable=SC2154
@@ -16,214 +23,42 @@ terraform init -input=false -backend=true -reconfigure \
1623
-backend-config="key=${TRE_ID}"
1724

1825
echo "*** Migrating TF Resources... ***"
19-
# terraform show might fail if provider schema has changed. Since we don't call apply at this stage a refresh is needed
20-
terraform refresh
2126

22-
# 1. Check we have a root_module in state
23-
# 2. Grab the Resource ID
24-
# 3. Delete the old resource from state
25-
# 4. Import the new resource type in using the existing Azure Resource ID
2627

2728
terraform_show_json=$(terraform show -json)
2829

29-
# azurerm_app_service_plan -> azurerm_service_plan
30-
core_app_service_plan_id=$(echo "${terraform_show_json}" \
31-
| jq -r 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_app_service_plan.core") | .values.id')
32-
if [ -n "${core_app_service_plan_id}" ]; then
33-
echo "Migrating ${core_app_service_plan_id}"
34-
terraform state rm azurerm_app_service_plan.core
35-
if [[ $(az resource list --query "[?id=='${core_app_service_plan_id}'] | length(@)") == 0 ]];
36-
then
37-
echo "The resource doesn't exist on Azure. Skipping importing it back to state."
38-
else
39-
terraform import azurerm_service_plan.core "${core_app_service_plan_id}"
40-
fi
41-
fi
42-
43-
# azurerm_app_service -> azurerm_linux_web_app
44-
api_app_service_id=$(echo "${terraform_show_json}" \
45-
| jq -r 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_app_service.api") | .values.id')
46-
if [ -n "${api_app_service_id}" ]; then
47-
echo "Migrating ${api_app_service_id}"
48-
terraform state rm azurerm_app_service.api
49-
if [[ $(az resource list --query "[?id=='${api_app_service_id}'] | length(@)") == 0 ]];
50-
then
51-
echo "The resource doesn't exist on Azure. Skipping importing it back to state."
52-
else
53-
terraform import azurerm_linux_web_app.api "${api_app_service_id}"
54-
fi
55-
fi
56-
57-
# app insights via -> native tf resource
58-
app_insights_via_arm=$(echo "${terraform_show_json}" \
59-
| jq -r 'select(.values.root_module.child_modules != null) .values.root_module.child_modules[] | select (.address=="module.azure_monitor") | .resources[] | select(.address=="module.azure_monitor.azurerm_resource_group_template_deployment.app_insights_core") | .values.id')
60-
if [ -n "${app_insights_via_arm}" ]; then
61-
echo "Migrating ${app_insights_via_arm}"
62-
63-
PLAN_FILE="tfplan$$"
64-
TS=$(date +"%s")
65-
LOG_FILE="${TS}-tre-core-migrate.log"
66-
67-
# This variables are loaded in for us
68-
# shellcheck disable=SC2154
69-
"${terraform_wrapper_path}" \
70-
-g "${TF_VAR_mgmt_resource_group_name}" \
71-
-s "${TF_VAR_mgmt_storage_account_name}" \
72-
-n "${TF_VAR_terraform_state_container_name}" \
73-
-k "${TRE_ID}" \
74-
-l "${LOG_FILE}" \
75-
-c "terraform plan -target module.azure_monitor.azurerm_resource_group_template_deployment.app_insights_core -target module.azure_monitor.azurerm_resource_group_template_deployment.ampls_core -out ${PLAN_FILE} && \
76-
terraform apply -input=false -auto-approve ${PLAN_FILE}"
77-
fi
78-
79-
# support downgrading core app service plan
80-
core_plan=$(echo "${terraform_show_json}" \
81-
| jq -r 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_service_plan.core") | .values.id')
82-
api_diag=$(echo "${terraform_show_json}" \
83-
| jq -r 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_monitor_diagnostic_setting.webapp_api") | .values.id')
84-
if [ -n "${core_plan}" ] && [ -n "${api_diag}" ]; then
85-
set +o errexit
86-
terraform plan -target "azurerm_service_plan.core" -detailed-exitcode
87-
plan_exit_code=$?
88-
set -o errexit
89-
90-
if [ "${plan_exit_code}" == "2" ]; then
91-
echo "Migrating ${api_diag}"
92-
PLAN_FILE="tfplan$$"
93-
TS=$(date +"%s")
94-
LOG_FILE="${TS}-tre-core-migrate.log"
95-
96-
# This variables are loaded in for us
97-
# shellcheck disable=SC2154
98-
"${terraform_wrapper_path}" \
99-
-g "${TF_VAR_mgmt_resource_group_name}" \
100-
-s "${TF_VAR_mgmt_storage_account_name}" \
101-
-n "${TF_VAR_terraform_state_container_name}" \
102-
-k "${TRE_ID}" \
103-
-l "${LOG_FILE}" \
104-
-c "terraform plan -destroy -target azurerm_monitor_diagnostic_setting.webapp_api -out ${PLAN_FILE} && \
105-
terraform apply -input=false -auto-approve ${PLAN_FILE}"
106-
fi
107-
fi
108-
109-
# remove app insights profiler storage account
110-
app_insights_byo_storage=$(echo "${terraform_show_json}" \
111-
| jq -r 'select(.values.root_module.child_modules != null) .values.root_module.child_modules[] | select (.address=="module.azure_monitor") | .resources[] | select(.address=="module.azure_monitor.azurerm_resource_group_template_deployment.app_insights_byo_storage") | .values.id')
112-
if [ -n "${app_insights_byo_storage}" ]; then
113-
echo "Removing state of app_insights_byo_storage"
114-
terraform state rm module.azure_monitor.azurerm_resource_group_template_deployment.app_insights_byo_storage
115-
fi
116-
117-
# airlock inline vnet integration (instead of via swift)
118-
airlock_vnet_integration=$(echo "${terraform_show_json}" \
119-
| jq -r 'select(.values.root_module.child_modules != null) .values.root_module.child_modules[] | select (.address=="module.airlock_resources") | .resources[] | select(.address=="module.airlock_resources.azurerm_app_service_virtual_network_swift_connection.airlock_integrated_vnet") | .values.id')
120-
if [ -n "${airlock_vnet_integration}" ]; then
121-
echo "Migrating ${airlock_vnet_integration}"
122-
123-
PLAN_FILE="tfplan$$"
124-
TS=$(date +"%s")
125-
LOG_FILE="${TS}-tre-core-migrate.log"
126-
127-
# This variables are loaded in for us
128-
# shellcheck disable=SC2154
129-
"${terraform_wrapper_path}" \
130-
-g "${TF_VAR_mgmt_resource_group_name}" \
131-
-s "${TF_VAR_mgmt_storage_account_name}" \
132-
-n "${TF_VAR_terraform_state_container_name}" \
133-
-k "${TRE_ID}" \
134-
-l "${LOG_FILE}" \
135-
-c "terraform plan -target module.airlock_resources.azurerm_app_service_virtual_network_swift_connection.airlock_integrated_vnet -out ${PLAN_FILE} && \
136-
terraform apply -input=false -auto-approve ${PLAN_FILE}"
137-
fi
138-
139-
# api inline vnet integration (instead of via swift)
140-
api_vnet_integration=$(echo "${terraform_show_json}" \
141-
| jq -r 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_app_service_virtual_network_swift_connection.api_integrated_vnet") | .values.id')
142-
if [ -n "${api_vnet_integration}" ]; then
143-
echo "Migrating ${api_vnet_integration}"
144-
145-
PLAN_FILE="tfplan$$"
146-
TS=$(date +"%s")
147-
LOG_FILE="${TS}-tre-core-migrate.log"
30+
# Remove cnab-state legacy state path form state. Needs to be run before refresh, as refresh will fail.
31+
state_store_legacy_path=$(echo "${terraform_show_json}" \
32+
| jq 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_storage_share.storage_state_path") | .values.id')
14833

149-
# This variables are loaded in for us
150-
# shellcheck disable=SC2154
151-
"${terraform_wrapper_path}" \
152-
-g "${TF_VAR_mgmt_resource_group_name}" \
153-
-s "${TF_VAR_mgmt_storage_account_name}" \
154-
-n "${TF_VAR_terraform_state_container_name}" \
155-
-k "${TRE_ID}" \
156-
-l "${LOG_FILE}" \
157-
-c "terraform plan -target azurerm_app_service_virtual_network_swift_connection.api_integrated_vnet -out ${PLAN_FILE} && \
158-
terraform apply -input=false -auto-approve ${PLAN_FILE}"
34+
if [ -n "${state_store_legacy_path}" ]; then
35+
echo -e "\n\e[96mRemoving legacy state path from TF state\e[0m..."
36+
terraform state rm azurerm_storage_share.storage_state_path
15937
fi
16038

161-
# support changing the resource processor subnet size
162-
rp_subnet=$(echo "${terraform_show_json}" \
163-
| jq -r 'select(.values.root_module.child_modules != null) .values.root_module.child_modules[] | select (.address=="module.network") | .resources[] | select(.address=="module.network.azurerm_subnet.resource_processor") | .values.id')
164-
if [ -n "${rp_subnet}" ]; then
165-
set +o errexit
166-
terraform plan -target "module.network.azurerm_subnet.resource_processor" -detailed-exitcode
167-
plan_exit_code=$?
168-
set -o errexit
169-
170-
if [ "${plan_exit_code}" == "2" ]; then
171-
echo "Migrating ${rp_subnet}"
172-
PLAN_FILE="tfplan$$"
173-
TS=$(date +"%s")
174-
LOG_FILE="${TS}-tre-core-migrate-rp-subnet.log"
175-
176-
# This variables are loaded in for us
177-
# shellcheck disable=SC2154
178-
"${terraform_wrapper_path}" \
179-
-g "${TF_VAR_mgmt_resource_group_name}" \
180-
-s "${TF_VAR_mgmt_storage_account_name}" \
181-
-n "${TF_VAR_terraform_state_container_name}" \
182-
-k "${TRE_ID}" \
183-
-l "${LOG_FILE}" \
184-
-c "terraform plan -destroy -target module.resource_processor_vmss_porter[0].azurerm_linux_virtual_machine_scale_set.vm_linux \
185-
-target azurerm_private_endpoint.sbpe \
186-
-target azurerm_private_endpoint.mongo \
187-
-out ${PLAN_FILE} && \
188-
terraform apply -input=false -auto-approve ${PLAN_FILE}"
189-
fi
190-
fi
191-
192-
# DNS Zones migration. We can't use a moved block due the the vars being used.
193-
nexus_dns_zone=$(echo "${terraform_show_json}" \
194-
| jq -r 'select(.values.root_module.child_modules != null) .values.root_module.child_modules[] | select (.address=="module.network") | .resources[] | select(.address=="module.network.azurerm_private_dns_zone.nexus") | .values.id')
195-
if [ -n "${nexus_dns_zone}" ]; then
196-
terraform state rm module.network.azurerm_private_dns_zone.nexus
197-
terraform import azurerm_private_dns_zone.non_core[\""nexus-${TRE_ID}.${LOCATION}.cloudapp.azure.com"\"] "${nexus_dns_zone}"
198-
fi
39+
# terraform show might fail if provider schema has changed. Since we don't call apply at this stage a refresh is needed
40+
terraform refresh
19941

200-
# Additional DNS Zones migration. We changed the name for the nexus dns zone hence we need to apply the change.
201-
NEXUS_DNS_NAME="nexus-${TRE_ID}.${LOCATION}.cloudapp.azure.com"
202-
nexus_dns_zone_changed=$(echo "${terraform_show_json}" \
203-
| jq -r --arg nexus_dns_name "$NEXUS_DNS_NAME" 'select(.values.root_module.resources != null) .values.root_module.resources[] | select (.address=="azurerm_private_dns_zone.non_core[\""+$nexus_dns_name+"\"]") | .values.id')
204-
if [ -n "${nexus_dns_zone_changed}" ]; then
205-
terraform state rm azurerm_private_dns_zone.non_core[\""nexus-${TRE_ID}.${LOCATION}.cloudapp.azure.com"\"]
206-
terraform import azurerm_private_dns_zone.nexus "${nexus_dns_zone_changed}"
207-
fi
42+
# 1. Check we have a root_module in state
43+
# 2. Grab the Resource ID
44+
# 3. Delete the old resource from state
45+
# 4. Import the new resource type in using the existing Azure Resource ID
20846

209-
# this isn't a classic migration, but impacts how terraform handles the deployment in the next phase
210-
state_store_serverless=$(echo "${terraform_show_json}" \
211-
| jq 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_cosmosdb_account.tre_db_account") | any(.values.capabilities[]; .name=="EnableServerless")')
212-
# false = resource EXITS in the state WITHOUT the serverless capability.
213-
# true = exists with the capability, empty value = resource doesn't exist.
214-
if [ "${state_store_serverless}" == "false" ]; then
215-
echo "Identified CosmosDB with defined throughput."
216-
TF_VAR_is_cosmos_defined_throughput="true"
217-
export TF_VAR_is_cosmos_defined_throughput
218-
fi
47+
terraform_show_json=$(terraform show -json)
21948

220-
# prep for migration of azurerm_servicebus_namespace_network_rule_set https://github.com/microsoft/AzureTRE/pull/3858
221-
# as described https://github.com/hashicorp/terraform-provider-azurerm/issues/23954
222-
state_store_servicebus_network_rule_set=$(echo "${terraform_show_json}" \
223-
| jq 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_servicebus_namespace_network_rule_set.servicebus_network_rule_set") | .values.id')
224-
if [ -n "${state_store_servicebus_network_rule_set}" ]; then
225-
echo "Removing state of azurerm_servicebus_namespace_network_rule_set"
226-
terraform state rm azurerm_servicebus_namespace_network_rule_set.servicebus_network_rule_set
227-
fi
49+
# example migration
50+
# # azurerm_app_service_plan -> azurerm_service_plan
51+
# core_app_service_plan_id=$(echo "${terraform_show_json}" \
52+
# | jq -r 'select(.values.root_module.resources != null) | .values.root_module.resources[] | select(.address=="azurerm_app_service_plan.core") | .values.id')
53+
# if [ -n "${core_app_service_plan_id}" ]; then
54+
# echo "Migrating ${core_app_service_plan_id}"
55+
# terraform state rm azurerm_app_service_plan.core
56+
# if [[ $(az resource list --query "[?id=='${core_app_service_plan_id}'] | length(@)") == 0 ]];
57+
# then
58+
# echo "The resource doesn't exist on Azure. Skipping importing it back to state."
59+
# else
60+
# terraform import azurerm_service_plan.core "${core_app_service_plan_id}"
61+
# fi
62+
# fi
22863

22964
echo "*** Migration is done. ***"

core/terraform/resource_processor/vmss_porter/data.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ data "template_cloudinit_config" "config" {
4545
}
4646
}
4747

48+
data "azurerm_storage_account" "mgmt_storage" {
49+
name = var.mgmt_storage_account_name
50+
resource_group_name = var.mgmt_resource_group_name
51+
}

core/terraform/resource_processor/vmss_porter/main.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ resource "terraform_data" "vm_linux_reimage" {
151151
]
152152
}
153153

154+
resource "azurerm_role_assignment" "mgmt_storage_account_blob_contributor" {
155+
scope = data.azurerm_storage_account.mgmt_storage.id
156+
role_definition_name = "Storage Blob Data Contributor"
157+
principal_id = azurerm_user_assigned_identity.vmss_msi.principal_id
158+
}
159+
154160
resource "azurerm_role_assignment" "vmss_acr_pull" {
155161
scope = var.acr_id
156162
role_definition_name = "AcrPull"

core/terraform/storage.tf

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@ resource "azurerm_storage_account" "stg" {
99
lifecycle { ignore_changes = [tags] }
1010
}
1111

12-
resource "azurerm_storage_share" "storage_state_path" {
13-
name = "cnab-state"
14-
storage_account_name = azurerm_storage_account.stg.name
15-
quota = 50
16-
}
17-
1812
resource "azurerm_private_endpoint" "blobpe" {
1913
name = "pe-blob-${var.tre_id}"
2014
location = azurerm_resource_group.core.location

core/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.10.10"
1+
__version__ = "0.10.11"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
3+
# Directory containing the porter.yaml files
4+
TEMPLATES_DIR="./templates"
5+
6+
# Function to increment the version number
7+
increment_version() {
8+
local version=$1
9+
local major minor patch
10+
IFS='.' read -r major minor patch <<< "$version"
11+
patch=$((patch + 1))
12+
echo "$major.$minor.$patch"
13+
}
14+
15+
# Find all porter.yaml files in the templates directory
16+
find "$TEMPLATES_DIR" -name "porter.yaml" | while read -r file; do
17+
# Read the current version from the file
18+
current_version=$(grep -E '^version: ' "$file" | awk '{print $2}')
19+
20+
# Increment the version number
21+
new_version=$(increment_version "$current_version")
22+
23+
# Update the version number in the file
24+
sed -i "s/version: $current_version/version: $new_version/" "$file"
25+
26+
echo "Updated $file from version $current_version to $new_version"
27+
done

devops/scripts/load_and_validate_env.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ else
8383

8484
TRE_URL=$(construct_tre_url "${TRE_ID}" "${LOCATION}" "${AZURE_ENVIRONMENT}")
8585
export TRE_URL
86+
87+
# Configure AzureRM provider and backend to use Azure AD to connect to storage accounts
88+
export ARM_STORAGE_USE_AZUREAD=true
89+
export ARM_USE_AZUREAD=true
90+
export ARM_USE_OIDC=true
8691
fi
8792

8893
# if local debugging is configured, then set vars required by ~/.porter/config.yaml

devops/scripts/terraform_wrapper.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ do
107107

108108
# upload the log file?
109109
if [[ $TF_LOG == "DEBUG" ]] ; then
110-
az storage blob upload --file $tf_logfile \
110+
az storage blob upload --file "$tf_logfile" \
111111
--container-name "tflogs" \
112112
--account-name "$mgmt_storage_account_name" \
113113
--auth-mode key
114114
fi
115115

116-
LOCKED_STATE=$(cat ${tf_logfile} | grep -c 'Error acquiring the state lock') || true;
117-
TF_ERROR=$(cat ${tf_logfile} | grep -c 'COMMAND_EXIT_CODE="1"') || true;
116+
LOCKED_STATE=$(cat < "$tf_logfile" | grep -c 'Error acquiring the state lock') || true;
117+
TF_ERROR=$(cat < "$tf_logfile" | grep -c 'COMMAND_EXIT_CODE="1"') || true;
118118
if [[ $LOCKED_STATE -gt 0 ]];
119119
then
120120
RUN_COMMAND=1

0 commit comments

Comments
 (0)