Skip to content

Commit

Permalink
feat: added support for invoking manual sync on function app. This wi…
Browse files Browse the repository at this point in the history
…ll ensure that the function app runs after creation. Instructions are added to handle the case where the manual sync fails in terraform
  • Loading branch information
rhartnett-zscaler committed May 14, 2024
1 parent 5bb18b6 commit 0eddce8
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ override.tf.json

examples/base_cc_vmss/terraform.tfvars.sample
modules/terraform-zscc-ccvmss-azure/zscaler_cc_function_app_*
modules/terraform-zscc-function-app-azure/exitstatus
modules/terraform-zscc-function-app-azure/stderr
modules/terraform-zscc-function-app-azure/stdout
64 changes: 63 additions & 1 deletion examples/base_cc_vmss/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,74 @@ Bastion Public IP:
${module.bastion.public_ip}
TB

testbedconfig_manual_sync_failed = <<TB
***Disclaimer***
By default, these templates store two critical files to the "examples" directory. DO NOT delete/lose these files:
1. Terraform State file (terraform.tfstate) - Terraform must store state about your managed infrastructure and configuration.
This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
Terraform uses state to determine which changes to make to your infrastructure.
Prior to any operation, Terraform does a refresh to update the state with the real infrastructure.
If this file is missing, you will NOT be able to make incremental changes to the environment resources without first importing state back to terraform manually.
2. SSH Private Key (.pem) file - Zscaler templates will attempt to create a new local private/public key pair for VM access (if a pre-existing one is not specified).
You (and subsequently Zscaler) will NOT be able to remotely access these VMs once deployed without valid SSH access.
***Disclaimer***
1) Copy the SSH key to the bastion host
scp -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.bastion.public_ip}:/home/centos/.
2) SSH to the bastion host
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.bastion.public_ip}
3) SSH to the workload host
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.workload.private_ip[0]} -o "proxycommand ssh -W %h:%p -i ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.bastion.public_ip}"
All Workload IPs. Replace private IP below with centos@"ip address" in ssh example command above.
${join("\n", module.workload.private_ip)}
Resource Group:
${module.network.resource_group_name}
LB IP:
${module.cc_lb.lb_ip}
VMSS Names:
${join("\n", module.cc_vmss.vmss_names)}
VMSS IDs:
${join("\n", module.cc_vmss.vmss_ids)}
Function App ID:
${module.cc_functionapp.function_app_id}
Function App Outbound IPs:
${join("\n", module.cc_functionapp.function_app_outbound_ip_address_list)}
All NAT GW IPs:
${join("\n", module.network.public_ip_address)}
Bastion Public IP:
${module.bastion.public_ip}
**IMPORTANT (ONLY APPLICABLE FOR INITIATIAL CREATE OF FUNCTION APP)**
Based on the recorded output, the manual sync to start your Azure Function App failed. To perform this manual sync perform one of the following steps:
1. Navigate to the Azure Function App ${module.cc_functionapp.function_app_id} on the Azure Portal. The loading of the Function App page triggers the manual sync and will start your Function App.
2. Attempt to rerun the manual_sync.sh script manually using the following command (path to file is based on root of the repo):
../../modules/terraform-zscc-function-app-azure/manual_sync.sh ${module.cc_functionapp.subscription_id} ${module.network.resource_group_name} ${module.cc_functionapp.function_app_name}
**IMPORTANT (ONLY APPLICABLE FOR INITIATIAL CREATE OF FUNCTION APP)**
TB
}

output "testbedconfig" {
description = "Azure Testbed results"
value = local.testbedconfig
value = module.cc_functionapp.manual_sync_exit_status != "1" ? local.testbedconfig : local.testbedconfig_manual_sync_failed
}

resource "local_file" "testbed" {
Expand Down
64 changes: 63 additions & 1 deletion examples/base_cc_vmss_zpa/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,74 @@ Bastion Public IP:
${module.bastion.public_ip}
TB

testbedconfig_manual_sync_failed = <<TB
***Disclaimer***
By default, these templates store two critical files to the "examples" directory. DO NOT delete/lose these files:
1. Terraform State file (terraform.tfstate) - Terraform must store state about your managed infrastructure and configuration.
This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
Terraform uses state to determine which changes to make to your infrastructure.
Prior to any operation, Terraform does a refresh to update the state with the real infrastructure.
If this file is missing, you will NOT be able to make incremental changes to the environment resources without first importing state back to terraform manually.
2. SSH Private Key (.pem) file - Zscaler templates will attempt to create a new local private/public key pair for VM access (if a pre-existing one is not specified).
You (and subsequently Zscaler) will NOT be able to remotely access these VMs once deployed without valid SSH access.
***Disclaimer***
1) Copy the SSH key to the bastion host
scp -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.bastion.public_ip}:/home/centos/.
2) SSH to the bastion host
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.bastion.public_ip}
3) SSH to the workload host
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.workload.private_ip[0]} -o "proxycommand ssh -W %h:%p -i ${var.name_prefix}-key-${random_string.suffix.result}.pem centos@${module.bastion.public_ip}"
All Workload IPs. Replace private IP below with centos@"ip address" in ssh example command above.
${join("\n", module.workload.private_ip)}
Resource Group:
${module.network.resource_group_name}
LB IP:
${module.cc_lb.lb_ip}
VMSS Names:
${join("\n", module.cc_vmss.vmss_names)}
VMSS IDs:
${join("\n", module.cc_vmss.vmss_ids)}
Function App ID:
${module.cc_functionapp.function_app_id}
Function App Outbound IPs:
${join("\n", module.cc_functionapp.function_app_outbound_ip_address_list)}
All NAT GW IPs:
${join("\n", module.network.public_ip_address)}
Bastion Public IP:
${module.bastion.public_ip}
**IMPORTANT (ONLY APPLICABLE FOR INITIATIAL CREATE OF FUNCTION APP)**
Based on the recorded output, the manual sync to start your Azure Function App failed. To perform this manual sync perform one of the following steps:
1. Navigate to the Azure Function App ${module.cc_functionapp.function_app_id} on the Azure Portal. The loading of the Function App page triggers the manual sync and will start your Function App.
2. Attempt to rerun the manual_sync.sh script manually using the following command (path to file is based on root of the repo):
../../modules/terraform-zscc-function-app-azure/manual_sync.sh ${module.cc_functionapp.subscription_id} ${module.network.resource_group_name} ${module.cc_functionapp.function_app_name}
**IMPORTANT (ONLY APPLICABLE FOR INITIATIAL CREATE OF FUNCTION APP)**
TB
}

output "testbedconfig" {
description = "Azure Testbed results"
value = local.testbedconfig
value = module.cc_functionapp.manual_sync_exit_status != "1" ? local.testbedconfig : local.testbedconfig_manual_sync_failed
}

resource "local_file" "testbed" {
Expand Down
14 changes: 14 additions & 0 deletions modules/terraform-zscc-function-app-azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,18 @@ resource "azurerm_linux_function_app" "vmss_orchestration_app" {
}

tags = var.global_tags

provisioner "local-exec" {
command = "${path.module}/manual_sync.sh ${data.azurerm_subscription.current.subscription_id} ${var.resource_group} ${azurerm_linux_function_app.vmss_orchestration_app.name} 2>${path.module}/stderr >${path.module}/stdout; echo $? >${path.module}/exitstatus"
when = create
}
}


resource "null_resource" "contents" {
triggers = {
stdout = file("${path.module}/stdout")
stderr = file("${path.module}/stderr")
exitstatus = file("${path.module}/exitstatus")
}
}
56 changes: 56 additions & 0 deletions modules/terraform-zscc-function-app-azure/manual_sync.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash


# read in subcription ID, resource group, and function app as arguments
subscription_id=$1
resource_group=$2
function_app=$3
if [[ $subscription_id == "" || $resource_group == "" || $function_app == "" ]]; then
echo "Arguments are missing. Expected arguments are Subscription ID, Resource Group, Function App"
exit 1
fi

if [[ "$ARM_TENANT_ID" != "" ]]; then
echo "ARM_TENANT_ID ENV variable is set, using ENV variables to get access token."
# if env variable is set, take credential values from env variables
tenant_id="$ARM_TENANT_ID"
client_id="$ARM_CLIENT_ID"
client_secret="$ARM_CLIENT_SECRET"
# make call to get access token
auth_response=$(curl --location "https://login.microsoftonline.com/${tenant_id}/oauth2/token" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=${client_id}" \
--data-urlencode "client_secret=${client_secret}" \
--data-urlencode "resource=https://management.azure.com/")
# extract access token
access_token=$(jq --argjson j "$auth_response" -n '$j.accessToken' | cut -d "\"" -f 2)
else
echo "ARM_TENANT_ID ENV variable is not set, attempting to use AZ CLI to get access token."
az account set --subscription ${subscription_id}
auth_response=$(az account get-access-token)
if [[ $auth_response == "" ]]; then
echo "AZ CLI not installed on system, please install AZ CLI or set ENV variables ARM_TENANT_ID, ARM_CLIENT_ID, " \
"ARM_CLIENT_SECRET to their respective values. Error: ${account_response}"
exit 1
fi
access_token=$(jq --argjson j "$auth_response" -n '$j.accessToken' | cut -d "\"" -f 2)
fi

if [[ $access_token == "" ]]; then
echo "Failed to obtain access token. Error: ${auth_response}"
exit 1
fi

# make POST call to manually sync function
output=$(curl --request POST "https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.Web/sites/${function_app}/syncfunctiontriggers?api-version=2016-08-01" \
--header "Authorization: Bearer ${access_token}" \
--header "Content-Length: 0")
if [[ $output == "{\"status\":\"success\"}" ]]; then
echo "success"
else
echo "Failed. Output: ${output}"
exit 1
fi

exit 0
15 changes: 15 additions & 0 deletions modules/terraform-zscc-function-app-azure/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,22 @@ output "function_app_id" {
value = azurerm_linux_function_app.vmss_orchestration_app.id
}

output "function_app_name" {
description = "Function App ID"
value = "${var.name_prefix}-ccvmss-${var.resource_tag}-function-app"
}

output "function_app_outbound_ip_address_list" {
description = "A list of outbound IP addresses used by the function"
value = azurerm_linux_function_app.vmss_orchestration_app.outbound_ip_address_list
}

output "manual_sync_exit_status" {
description = "Exit status of the operation to manually sync the Azure Function App after deployment."
value = chomp(null_resource.contents.triggers["exitstatus"])
}

output "subscription_id" {
description = "Subscription ID."
value = data.azurerm_subscription.current.subscription_id
}

0 comments on commit 0eddce8

Please sign in to comment.