Skip to content

Commit cfaf0a8

Browse files
mmcfarlandelayRob Emanuele
authored
Function identity and storage network access (#228)
* upgrade to linux app and use identity based storage connection for function app * add back config * remove settings for remote build, enable local build * Set default deny network action on SA * Add IP to tf state storage firewall * Add subscription to shared access key setting * Function debugging in new premium service plan * Assign function subnet to output storage * Cleanup --------- Co-authored-by: elay <[email protected]> Co-authored-by: Rob Emanuele <[email protected]>
1 parent a08f05e commit cfaf0a8

File tree

9 files changed

+167
-124
lines changed

9 files changed

+167
-124
lines changed

deployment/bin/deploy

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ function usage() {
1414
Deploys the project infrastructure.
1515
1616
-t TERRAFORM_DIR: The terraform directory. Required.
17+
-y: Auto approve the terraform changes.
1718
--plan: Only run Terraform plan.
1819
--skip-tf: Skips Terraform apply. Will still gather terraform output
1920
"
@@ -37,6 +38,10 @@ while [[ "$#" -gt 0 ]]; do case $1 in
3738
PLAN_ONLY=1
3839
shift
3940
;;
41+
-y)
42+
AUTO_APPROVE=-auto-approve
43+
shift
44+
;;
4045
--help)
4146
usage
4247
exit 0
@@ -64,6 +69,14 @@ SAK_STORAGE_ACCOUNTS=(
6469
["pcfilestest"]="pc-test-manual-resources"
6570
)
6671

72+
# Add client IP to firewall for storage accounts that must have properties read
73+
# [storage_account]=resource_group
74+
declare -A FW_STORAGE_ACCOUNTS
75+
FW_STORAGE_ACCOUNTS=(
76+
["pctesttfstate"]="pc-test-manual-resources"
77+
["pctapisstagingsa"]="pct-apis-westeurope-staging_rg"
78+
)
79+
6780
if [[ -z ${TERRAFORM_DIR} ]]; then
6881
echo "Must pass in TERRAFORM_DIR with -t"
6982
exit 1
@@ -95,10 +108,10 @@ fi
95108
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
96109

97110
#########################
98-
# Add IP to KV firewall #
111+
# Add IP to firewalls #
99112
#########################
100113

101-
bin/kv_add_ip
114+
add_ip_to_firewalls
102115

103116
#####################
104117
# Deploy Terraform #
@@ -118,7 +131,7 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
118131
exit 0
119132
fi
120133

121-
terraform apply -auto-approve
134+
terraform apply "$AUTO_APPROVE"
122135
fi
123136

124137
# Gather terraform output
@@ -127,10 +140,10 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
127140
popd
128141

129142
##############################
130-
# Remove IP from KV firewall #
143+
# Remove IP from firewalls #
131144
##############################
132145

133-
bin/kv_rmv_ip
146+
remove_ip_from_firewalls
134147

135148
############################
136149
# Render Helm chart values #

deployment/bin/kv_add_ip

Lines changed: 0 additions & 38 deletions
This file was deleted.

deployment/bin/kv_rmv_ip

Lines changed: 0 additions & 38 deletions
This file was deleted.

deployment/bin/lib

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ function disable_shared_access_keys() {
142142
--name ${SAK_STORAGE_ACCOUNT} \
143143
--resource-group ${SAK_RESOURCE_GROUP} \
144144
--allow-shared-key-access false \
145+
--subscription ${ARM_SUBSCRIPTION_ID} \
145146
--output none
146147

147148
if [ $? -ne 0 ]; then
@@ -154,20 +155,72 @@ function disable_shared_access_keys() {
154155
}
155156

156157
function enable_shared_access_keys() {
157-
echo "Enabling shared key access for storage account..."
158158
# Terraform isn't able to read all resources from a storage account if shared key access is disabled
159159
# so while we're deploying, we need to enable it. Since we haven't run TF yet, we don't have the name of the account
160160
# so they are hardcoded here. This is a temporary workaround until this is resolved
161161
# https://github.com/hashicorp/terraform-provider-azurerm/issues/25218
162162

163+
echo "Enabling shared key access for storage accounts..."
163164
for SAK_STORAGE_ACCOUNT in "${!SAK_STORAGE_ACCOUNTS[@]}"; do
164165
SAK_RESOURCE_GROUP=${SAK_STORAGE_ACCOUNTS[$SAK_STORAGE_ACCOUNT]}
165166

166-
echo " - enabling ${SAK_STORAGE_ACCOUNT} / ${SAK_RESOURCE_GROUP}"
167+
echo " - ${SAK_RESOURCE_GROUP}.${SAK_STORAGE_ACCOUNT}"
167168
az storage account update \
168169
--name ${SAK_STORAGE_ACCOUNT} \
169170
--resource-group ${SAK_RESOURCE_GROUP} \
170171
--allow-shared-key-access true \
172+
--subscription ${ARM_SUBSCRIPTION_ID} \
173+
--output none
174+
done
175+
176+
sleep 10
177+
}
178+
179+
function add_ip_to_firewalls() {
180+
cidr=$(get_cidr_range)
181+
182+
echo "Adding IP $cidr to Key Vault firewall allow list..."
183+
az keyvault network-rule add \
184+
-g "${KEY_VAULT_RESOURCE_GROUP_NAME}" \
185+
-n "${KEY_VAULT_NAME}" \
186+
--ip-address "$cidr" \
187+
--subscription "${ARM_SUBSCRIPTION_ID}" \
188+
--output none
189+
190+
# Also add the IP to the terraform state storage account
191+
for FW_STORAGE_ACCOUNT in "${!FW_STORAGE_ACCOUNTS[@]}"; do
192+
FW_RESOURCE_GROUP=${FW_STORAGE_ACCOUNTS[$FW_STORAGE_ACCOUNT]}
193+
echo "Adding IP $cidr to ${FW_STORAGE_ACCOUNT} Storage firewall allow list..."
194+
az storage account network-rule add \
195+
-g "${FW_RESOURCE_GROUP}" \
196+
-n "${FW_STORAGE_ACCOUNT}" \
197+
--ip-address "$cidr" \
198+
--subscription "${ARM_SUBSCRIPTION_ID}" \
199+
--output none
200+
done
201+
202+
sleep 10
203+
}
204+
205+
function remove_ip_from_firewalls() {
206+
cidr=$(get_cidr_range)
207+
208+
echo "Removing IP $cidr from Key Vault firewall allow list..."
209+
az keyvault network-rule remove \
210+
-g ${KEY_VAULT_RESOURCE_GROUP_NAME} \
211+
-n ${KEY_VAULT_NAME} \
212+
--ip-address $cidr \
213+
--subscription ${ARM_SUBSCRIPTION_ID} \
214+
--output none
215+
216+
for FW_STORAGE_ACCOUNT in "${!FW_STORAGE_ACCOUNTS[@]}"; do
217+
FW_RESOURCE_GROUP=${FW_STORAGE_ACCOUNTS[$FW_STORAGE_ACCOUNT]}
218+
echo "Removing IP $cidr from ${FW_STORAGE_ACCOUNT} Storage firewall allow list..."
219+
az storage account network-rule remove \
220+
-g ${FW_RESOURCE_GROUP} \
221+
-n ${FW_STORAGE_ACCOUNT} \
222+
--ip-address $cidr \
223+
--subscription ${ARM_SUBSCRIPTION_ID} \
171224
--output none
172225
done
173226
}
Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,42 @@
1-
resource "azurerm_app_service_plan" "pc" {
2-
name = "plan-${local.prefix}"
1+
resource "azurerm_service_plan" "pc" {
2+
name = "app-plan-${local.prefix}"
33
location = azurerm_resource_group.pc.location
44
resource_group_name = azurerm_resource_group.pc.name
5-
kind = "functionapp"
6-
reserved = true
5+
os_type = "Linux"
6+
7+
sku_name = "EP1"
78

8-
sku {
9-
tier = "Dynamic"
10-
size = "Y1"
11-
}
129
}
1310

14-
resource "azurerm_function_app" "pcfuncs" {
15-
name = "func-${local.prefix}"
16-
location = azurerm_resource_group.pc.location
17-
resource_group_name = azurerm_resource_group.pc.name
18-
app_service_plan_id = azurerm_app_service_plan.pc.id
19-
storage_account_name = azurerm_storage_account.pc.name
20-
storage_account_access_key = azurerm_storage_account.pc.primary_access_key
21-
https_only = true
11+
resource "azurerm_linux_function_app" "pcfuncs" {
12+
name = "func-${local.prefix}"
13+
location = azurerm_resource_group.pc.location
14+
resource_group_name = azurerm_resource_group.pc.name
15+
service_plan_id = azurerm_service_plan.pc.id
16+
storage_account_name = azurerm_storage_account.pc.name
17+
18+
virtual_network_subnet_id = azurerm_subnet.function_subnet.id
19+
20+
ftp_publish_basic_authentication_enabled = false
21+
webdeploy_publish_basic_authentication_enabled = false
22+
23+
24+
storage_uses_managed_identity = true
25+
https_only = true
2226

2327
identity {
2428
type = "SystemAssigned"
2529
}
2630

2731
app_settings = {
28-
"ENABLE_ORYX_BUILD" = "true",
29-
"SCM_DO_BUILD_DURING_DEPLOYMENT" = "true",
30-
"FUNCTIONS_WORKER_RUNTIME" = "python",
31-
"APP_INSIGHTS_IKEY" = azurerm_application_insights.pc_application_insights.instrumentation_key,
32-
"APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.pc_application_insights.instrumentation_key,
32+
"FUNCTIONS_WORKER_RUNTIME" = "python",
33+
"APP_INSIGHTS_IKEY" = azurerm_application_insights.pc_application_insights.instrumentation_key,
34+
35+
# Remote build
36+
"BUILD_FLAGS" = "UseExpressBuild",
37+
"ENABLE_ORYX_BUILD" = "true"
38+
"SCM_DO_BUILD_DURING_DEPLOYMENT" = "1",
39+
"XDG_CACHE_HOME" = "/tmp/.cache"
3340
"AzureWebJobsDisableHomepage" = true,
3441

3542
# Animation Function
@@ -48,48 +55,50 @@ resource "azurerm_function_app" "pcfuncs" {
4855
"LOG_ANALYTICS_WORKSPACE_ID" = var.prod_log_analytics_workspace_id,
4956
}
5057

51-
os_type = "linux"
52-
version = "~4"
5358
site_config {
54-
linux_fx_version = "PYTHON|3.9"
55-
use_32_bit_worker_process = false
56-
ftps_state = "Disabled"
59+
vnet_route_all_enabled = true
60+
application_insights_key = azurerm_application_insights.pc_application_insights.instrumentation_key
61+
ftps_state = "Disabled"
5762

5863
cors {
5964
allowed_origins = ["*"]
6065
}
66+
application_stack {
67+
python_version = "3.9"
68+
}
6169
}
62-
6370
lifecycle {
6471
ignore_changes = [
6572
tags
6673
]
6774
}
6875
}
6976

70-
# Note: this must be in the same subscription as the rest of the deployed infrastructure
71-
data "azurerm_storage_container" "output" {
72-
name = var.output_container_name
73-
storage_account_name = var.output_storage_account_name
77+
78+
79+
resource "azurerm_role_assignment" "function-app-storage-account-access" {
80+
scope = azurerm_storage_account.pc.id
81+
role_definition_name = "Storage Blob Data Owner"
82+
principal_id = azurerm_linux_function_app.pcfuncs.identity[0].principal_id
7483
}
7584

7685
resource "azurerm_role_assignment" "function-app-animation-container-access" {
77-
scope = data.azurerm_storage_container.output.resource_manager_id
86+
scope = data.azurerm_storage_account.output-storage-account.id
7887
role_definition_name = "Storage Blob Data Contributor"
79-
principal_id = azurerm_function_app.pcfuncs.identity[0].principal_id
88+
principal_id = azurerm_linux_function_app.pcfuncs.identity[0].principal_id
8089

8190
depends_on = [
82-
azurerm_function_app.pcfuncs
91+
azurerm_linux_function_app.pcfuncs
8392
]
8493
}
8594

8695
resource "azurerm_role_assignment" "function-app-storage-table-data-contributor" {
8796
scope = azurerm_storage_account.pc.id
8897
role_definition_name = "Storage Table Data Contributor"
89-
principal_id = azurerm_function_app.pcfuncs.identity[0].principal_id
98+
principal_id = azurerm_linux_function_app.pcfuncs.identity[0].principal_id
9099

91100
depends_on = [
92-
azurerm_function_app.pcfuncs
101+
azurerm_linux_function_app.pcfuncs
93102
]
94103
}
95104

@@ -102,9 +111,9 @@ data "azurerm_log_analytics_workspace" "prod_log_analytics_workspace" {
102111
resource "azurerm_role_assignment" "function-app-log-analytics-access" {
103112
scope = data.azurerm_log_analytics_workspace.prod_log_analytics_workspace.id
104113
role_definition_name = "Log Analytics Reader"
105-
principal_id = azurerm_function_app.pcfuncs.identity[0].principal_id
114+
principal_id = azurerm_linux_function_app.pcfuncs.identity[0].principal_id
106115

107116
depends_on = [
108-
azurerm_function_app.pcfuncs
117+
azurerm_linux_function_app.pcfuncs
109118
]
110-
}
119+
}

deployment/terraform/resources/output.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,5 @@ output "redis_port" {
137137
# Functions
138138

139139
output "function_app_name" {
140-
value = azurerm_function_app.pcfuncs.name
140+
value = azurerm_linux_function_app.pcfuncs.name
141141
}

0 commit comments

Comments
 (0)