Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function identity and storage network access #228

Merged
merged 10 commits into from
Jul 1, 2024
23 changes: 18 additions & 5 deletions deployment/bin/deploy
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function usage() {
Deploys the project infrastructure.

-t TERRAFORM_DIR: The terraform directory. Required.
-y: Auto approve the terraform changes.
--plan: Only run Terraform plan.
--skip-tf: Skips Terraform apply. Will still gather terraform output
"
Expand All @@ -37,6 +38,10 @@ while [[ "$#" -gt 0 ]]; do case $1 in
PLAN_ONLY=1
shift
;;
-y)
AUTO_APPROVE=-auto-approve
shift
;;
--help)
usage
exit 0
Expand Down Expand Up @@ -64,6 +69,14 @@ SAK_STORAGE_ACCOUNTS=(
["pcfilestest"]="pc-test-manual-resources"
)

# Add client IP to firewall for storage accounts that must have properties read
# [storage_account]=resource_group
declare -A FW_STORAGE_ACCOUNTS
FW_STORAGE_ACCOUNTS=(
["pctesttfstate"]="pc-test-manual-resources"
["pctapisstagingsa"]="pct-apis-westeurope-staging_rg"
)

if [[ -z ${TERRAFORM_DIR} ]]; then
echo "Must pass in TERRAFORM_DIR with -t"
exit 1
Expand Down Expand Up @@ -95,10 +108,10 @@ fi
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then

#########################
# Add IP to KV firewall #
# Add IP to firewalls #
#########################

bin/kv_add_ip
add_ip_to_firewalls

#####################
# Deploy Terraform #
Expand All @@ -118,7 +131,7 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
exit 0
fi

terraform apply -auto-approve
terraform apply "$AUTO_APPROVE"
fi

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

##############################
# Remove IP from KV firewall #
# Remove IP from firewalls #
##############################

bin/kv_rmv_ip
remove_ip_from_firewalls

############################
# Render Helm chart values #
Expand Down
38 changes: 0 additions & 38 deletions deployment/bin/kv_add_ip

This file was deleted.

38 changes: 0 additions & 38 deletions deployment/bin/kv_rmv_ip

This file was deleted.

57 changes: 55 additions & 2 deletions deployment/bin/lib
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ function disable_shared_access_keys() {
--name ${SAK_STORAGE_ACCOUNT} \
--resource-group ${SAK_RESOURCE_GROUP} \
--allow-shared-key-access false \
--subscription ${ARM_SUBSCRIPTION_ID} \
--output none

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

function enable_shared_access_keys() {
echo "Enabling shared key access for storage account..."
# Terraform isn't able to read all resources from a storage account if shared key access is disabled
# 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
# so they are hardcoded here. This is a temporary workaround until this is resolved
# https://github.com/hashicorp/terraform-provider-azurerm/issues/25218

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

echo " - enabling ${SAK_STORAGE_ACCOUNT} / ${SAK_RESOURCE_GROUP}"
echo " - ${SAK_RESOURCE_GROUP}.${SAK_STORAGE_ACCOUNT}"
az storage account update \
--name ${SAK_STORAGE_ACCOUNT} \
--resource-group ${SAK_RESOURCE_GROUP} \
--allow-shared-key-access true \
--subscription ${ARM_SUBSCRIPTION_ID} \
--output none
done

sleep 10
}

function add_ip_to_firewalls() {
cidr=$(get_cidr_range)

echo "Adding IP $cidr to Key Vault firewall allow list..."
az keyvault network-rule add \
-g "${KEY_VAULT_RESOURCE_GROUP_NAME}" \
-n "${KEY_VAULT_NAME}" \
--ip-address "$cidr" \
--subscription "${ARM_SUBSCRIPTION_ID}" \
--output none

# Also add the IP to the terraform state storage account
for FW_STORAGE_ACCOUNT in "${!FW_STORAGE_ACCOUNTS[@]}"; do
FW_RESOURCE_GROUP=${FW_STORAGE_ACCOUNTS[$FW_STORAGE_ACCOUNT]}
echo "Adding IP $cidr to ${FW_STORAGE_ACCOUNT} Storage firewall allow list..."
az storage account network-rule add \
-g "${FW_RESOURCE_GROUP}" \
-n "${FW_STORAGE_ACCOUNT}" \
--ip-address "$cidr" \
--subscription "${ARM_SUBSCRIPTION_ID}" \
--output none
done

sleep 10
}

function remove_ip_from_firewalls() {
cidr=$(get_cidr_range)

echo "Removing IP $cidr from Key Vault firewall allow list..."
az keyvault network-rule remove \
-g ${KEY_VAULT_RESOURCE_GROUP_NAME} \
-n ${KEY_VAULT_NAME} \
--ip-address $cidr \
--subscription ${ARM_SUBSCRIPTION_ID} \
--output none

for FW_STORAGE_ACCOUNT in "${!FW_STORAGE_ACCOUNTS[@]}"; do
FW_RESOURCE_GROUP=${FW_STORAGE_ACCOUNTS[$FW_STORAGE_ACCOUNT]}
echo "Removing IP $cidr from ${FW_STORAGE_ACCOUNT} Storage firewall allow list..."
az storage account network-rule remove \
-g ${FW_RESOURCE_GROUP} \
-n ${FW_STORAGE_ACCOUNT} \
--ip-address $cidr \
--subscription ${ARM_SUBSCRIPTION_ID} \
--output none
done
}
87 changes: 48 additions & 39 deletions deployment/terraform/resources/functions.tf
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
resource "azurerm_app_service_plan" "pc" {
name = "plan-${local.prefix}"
resource "azurerm_service_plan" "pc" {
name = "app-plan-${local.prefix}"
location = azurerm_resource_group.pc.location
resource_group_name = azurerm_resource_group.pc.name
kind = "functionapp"
reserved = true
os_type = "Linux"

sku_name = "EP1"

sku {
tier = "Dynamic"
size = "Y1"
}
}

resource "azurerm_function_app" "pcfuncs" {
name = "func-${local.prefix}"
location = azurerm_resource_group.pc.location
resource_group_name = azurerm_resource_group.pc.name
app_service_plan_id = azurerm_app_service_plan.pc.id
storage_account_name = azurerm_storage_account.pc.name
storage_account_access_key = azurerm_storage_account.pc.primary_access_key
https_only = true
resource "azurerm_linux_function_app" "pcfuncs" {
name = "func-${local.prefix}"
location = azurerm_resource_group.pc.location
resource_group_name = azurerm_resource_group.pc.name
service_plan_id = azurerm_service_plan.pc.id
storage_account_name = azurerm_storage_account.pc.name

virtual_network_subnet_id = azurerm_subnet.function_subnet.id

ftp_publish_basic_authentication_enabled = false
webdeploy_publish_basic_authentication_enabled = false


storage_uses_managed_identity = true
https_only = true

identity {
type = "SystemAssigned"
}

app_settings = {
"ENABLE_ORYX_BUILD" = "true",
"SCM_DO_BUILD_DURING_DEPLOYMENT" = "true",
"FUNCTIONS_WORKER_RUNTIME" = "python",
"APP_INSIGHTS_IKEY" = azurerm_application_insights.pc_application_insights.instrumentation_key,
"APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.pc_application_insights.instrumentation_key,
"FUNCTIONS_WORKER_RUNTIME" = "python",
"APP_INSIGHTS_IKEY" = azurerm_application_insights.pc_application_insights.instrumentation_key,

# Remote build
"BUILD_FLAGS" = "UseExpressBuild",
"ENABLE_ORYX_BUILD" = "true"
"SCM_DO_BUILD_DURING_DEPLOYMENT" = "1",
"XDG_CACHE_HOME" = "/tmp/.cache"
"AzureWebJobsDisableHomepage" = true,

# Animation Function
Expand All @@ -48,48 +55,50 @@ resource "azurerm_function_app" "pcfuncs" {
"LOG_ANALYTICS_WORKSPACE_ID" = var.prod_log_analytics_workspace_id,
}

os_type = "linux"
version = "~4"
site_config {
linux_fx_version = "PYTHON|3.9"
use_32_bit_worker_process = false
ftps_state = "Disabled"
vnet_route_all_enabled = true
application_insights_key = azurerm_application_insights.pc_application_insights.instrumentation_key
ftps_state = "Disabled"

cors {
allowed_origins = ["*"]
}
application_stack {
python_version = "3.9"
}
}

lifecycle {
ignore_changes = [
tags
]
}
}

# Note: this must be in the same subscription as the rest of the deployed infrastructure
data "azurerm_storage_container" "output" {
name = var.output_container_name
storage_account_name = var.output_storage_account_name


resource "azurerm_role_assignment" "function-app-storage-account-access" {
scope = azurerm_storage_account.pc.id
role_definition_name = "Storage Blob Data Owner"
principal_id = azurerm_linux_function_app.pcfuncs.identity[0].principal_id
}

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

depends_on = [
azurerm_function_app.pcfuncs
azurerm_linux_function_app.pcfuncs
]
}

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

depends_on = [
azurerm_function_app.pcfuncs
azurerm_linux_function_app.pcfuncs
]
}

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

depends_on = [
azurerm_function_app.pcfuncs
azurerm_linux_function_app.pcfuncs
]
}
}
2 changes: 1 addition & 1 deletion deployment/terraform/resources/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,5 @@ output "redis_port" {
# Functions

output "function_app_name" {
value = azurerm_function_app.pcfuncs.name
value = azurerm_linux_function_app.pcfuncs.name
}
Loading
Loading