From 73f58b9c4fe376532db6e20adcb2269c05233de8 Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Mon, 18 Apr 2022 17:41:32 -0700 Subject: [PATCH 1/8] updating deploy.py for dotnet packages --- src/deployment/deploy.py | 53 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index cb5e6b3df2..462728f691 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -1026,6 +1026,44 @@ def deploy_app(self) -> None: if error is not None: raise error + def deploy_dotnet_app(self) -> None: + logger.info("deploying function app %s", self.app_zip) + with tempfile.TemporaryDirectory() as tmpdirname: + with zipfile.ZipFile(self.app_zip, "r") as zip_ref: + func = shutil.which("func") + assert func is not None + + zip_ref.extractall(tmpdirname) + error: Optional[subprocess.CalledProcessError] = None + max_tries = 5 + for i in range(max_tries): + try: + subprocess.check_output( + [ + func, + "azure", + "functionapp", + "publish", + self.application_name, + "--dotnet", + "--no-build", + ], + env=dict(os.environ, CLI_DEBUG="1"), + cwd=tmpdirname, + ) + return + except subprocess.CalledProcessError as err: + error = err + if i + 1 < max_tries: + logger.debug("func failure error: %s", err) + logger.warning( + "function failed to deploy, waiting 60 " + "seconds and trying again" + ) + time.sleep(60) + if error is not None: + raise error + def update_registration(self) -> None: if not self.create_registration: return @@ -1085,7 +1123,6 @@ def main() -> None: ("add_instance_id", Client.add_instance_id), ("instance-specific-setup", Client.upload_instance_setup), ("third-party", Client.upload_third_party), - ("api", Client.deploy_app), ("export_appinsights", Client.add_log_export), ("update_registration", Client.update_registration), ] @@ -1192,6 +1229,11 @@ def main() -> None: nargs="*", help="Set additional AAD tenants beyond the tenant the app is deployed in", ) + parser.add_argument( + "--dotnet_deploy", + action="store_true", + help="deploys the dotnet version of the app, instead of python version", + ) args = parser.parse_args() @@ -1238,7 +1280,14 @@ def main() -> None: ) states = rbac_only_states else: - states = full_deployment_states + if args.dotnet_deploy: + logger.info( + "deploying dotnet services for Azure functions instead of python" + ) + states = full_deployment_states + [("api", Client.deploy_dotnet_app)] + else: + states = full_deployment_states + [("api", Client.deploy_app)] + if args.start_at != states[0][0]: logger.warning( From 1e311fbba08d5f54e6c1b3ee12e71d1ac9b62c83 Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Fri, 22 Apr 2022 10:59:19 -0700 Subject: [PATCH 2/8] dotnet deployment needs to be along side python if flagged --- src/deployment/deploy.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index 462728f691..1a90436127 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -1123,6 +1123,7 @@ def main() -> None: ("add_instance_id", Client.add_instance_id), ("instance-specific-setup", Client.upload_instance_setup), ("third-party", Client.upload_third_party), + ("api", Client.deploy_app), ("export_appinsights", Client.add_log_export), ("update_registration", Client.update_registration), ] @@ -1232,7 +1233,7 @@ def main() -> None: parser.add_argument( "--dotnet_deploy", action="store_true", - help="deploys the dotnet version of the app, instead of python version", + help="deploys the dotnet version of the app along with the python version", ) args = parser.parse_args() @@ -1282,11 +1283,11 @@ def main() -> None: else: if args.dotnet_deploy: logger.info( - "deploying dotnet services for Azure functions instead of python" + "deploying dotnet and python services for Azure functions" ) states = full_deployment_states + [("api", Client.deploy_dotnet_app)] else: - states = full_deployment_states + [("api", Client.deploy_app)] + states = full_deployment_states if args.start_at != states[0][0]: From e746b27df27b6e725eedd5b66e694584379372e3 Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Fri, 22 Apr 2022 11:17:04 -0700 Subject: [PATCH 3/8] indivdual deployment state for dotnet api since this will deploy alongside python api --- src/deployment/deploy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index 1a90436127..5e4603e375 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -1285,7 +1285,7 @@ def main() -> None: logger.info( "deploying dotnet and python services for Azure functions" ) - states = full_deployment_states + [("api", Client.deploy_dotnet_app)] + states = full_deployment_states + [("dotnet-api", Client.deploy_dotnet_app)] else: states = full_deployment_states From e3c41c804d14f577f33aa8dd587218d72d1f2284 Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:28:02 -0700 Subject: [PATCH 4/8] correcting state order for dotnet-api deployment step to follow python api deployment --- src/deployment/deploy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index 5e4603e375..41441cf0b5 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -1285,9 +1285,9 @@ def main() -> None: logger.info( "deploying dotnet and python services for Azure functions" ) - states = full_deployment_states + [("dotnet-api", Client.deploy_dotnet_app)] - else: - states = full_deployment_states + after_python = (full_deployment_states.index(("api", Client.deploy_app)) + 1) + full_deployment_states.insert(after_python, ("dotnet-api", Client.deploy_dotnet_app)) + states = full_deployment_states if args.start_at != states[0][0]: From 1c0f693b8de2be06681f35f9192e51b25fc7c95d Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Sun, 24 Apr 2022 15:56:40 -0700 Subject: [PATCH 5/8] cleanup deploy.py formatting --- src/deployment/deploy.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index 41441cf0b5..f2c7694374 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -1282,14 +1282,13 @@ def main() -> None: states = rbac_only_states else: if args.dotnet_deploy: - logger.info( - "deploying dotnet and python services for Azure functions" + logger.info("deploying dotnet and python services for Azure functions") + after_python = full_deployment_states.index(("api", Client.deploy_app)) + 1 + full_deployment_states.insert( + after_python, ("dotnet-api", Client.deploy_dotnet_app) ) - after_python = (full_deployment_states.index(("api", Client.deploy_app)) + 1) - full_deployment_states.insert(after_python, ("dotnet-api", Client.deploy_dotnet_app)) states = full_deployment_states - if args.start_at != states[0][0]: logger.warning( "*** Starting at a non-standard deployment state. " From d413f655b1bc7b2c2cdc40f86d9a3fc01f0787a2 Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Wed, 27 Apr 2022 18:14:11 -0700 Subject: [PATCH 6/8] Adding dotnet's zip package arguments to deploy and fixing func command flags --- src/deployment/deploy.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index f2c7694374..b8a1be8339 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -138,6 +138,7 @@ def __init__( client_id: Optional[str], client_secret: Optional[str], app_zip: str, + app_net_zip: str, tools: str, instance_specific: str, third_party: str, @@ -159,6 +160,7 @@ def __init__( self.owner = owner self.nsg_config = nsg_config self.app_zip = app_zip + self.app_net_zip = app_net_zip self.tools = tools self.instance_specific = instance_specific self.third_party = third_party @@ -1027,9 +1029,9 @@ def deploy_app(self) -> None: raise error def deploy_dotnet_app(self) -> None: - logger.info("deploying function app %s", self.app_zip) + logger.info("deploying function app %s ", self.app_net_zip) with tempfile.TemporaryDirectory() as tmpdirname: - with zipfile.ZipFile(self.app_zip, "r") as zip_ref: + with zipfile.ZipFile(self.app_net_zip, "r") as zip_ref: func = shutil.which("func") assert func is not None @@ -1044,8 +1046,7 @@ def deploy_dotnet_app(self) -> None: "azure", "functionapp", "publish", - self.application_name, - "--dotnet", + self.application_name + '-net', "--no-build", ], env=dict(os.environ, CLI_DEBUG="1"), @@ -1153,6 +1154,12 @@ def main() -> None: default="api-service.zip", help="(default: %(default)s)", ) + parser.add_argument( + "--app-net-zip", + type=arg_file, + default="api-service-net.zip", + help="(default: %(default)s)", + ) parser.add_argument( "--tools", type=arg_dir, default="tools", help="(default: %(default)s)" ) @@ -1251,6 +1258,7 @@ def main() -> None: client_id=args.client_id, client_secret=args.client_secret, app_zip=args.app_zip, + app_net_zip=args.app_net_zip, tools=args.tools, instance_specific=args.instance_specific, third_party=args.third_party, From 3ca4d895f4d0ab8b0b5008b45583899880bf042e Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Wed, 27 Apr 2022 19:35:57 -0700 Subject: [PATCH 7/8] changed quotes to pass black check --- src/deployment/deploy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deployment/deploy.py b/src/deployment/deploy.py index b8a1be8339..618eaa846f 100644 --- a/src/deployment/deploy.py +++ b/src/deployment/deploy.py @@ -1046,7 +1046,7 @@ def deploy_dotnet_app(self) -> None: "azure", "functionapp", "publish", - self.application_name + '-net', + self.application_name + "-net", "--no-build", ], env=dict(os.environ, CLI_DEBUG="1"), From 36052287f2a6c24215637020593fd9280872facd Mon Sep 17 00:00:00 2001 From: AdamLeclerc-Microsoft <103067949+AdamLeclerc-Microsoft@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:58:05 -0700 Subject: [PATCH 8/8] reconfiguring bicep templates to deploy function app settings correctly by moving app settings into a separate template --- src/deployment/azuredeploy.bicep | 76 ++++++++++++------- .../bicep-templates/function-settings.bicep | 61 +++++++++++++++ src/deployment/bicep-templates/function.bicep | 54 ------------- 3 files changed, 111 insertions(+), 80 deletions(-) create mode 100644 src/deployment/bicep-templates/function-settings.bicep diff --git a/src/deployment/azuredeploy.bicep b/src/deployment/azuredeploy.bicep index 0e36af7fbf..138758aa01 100644 --- a/src/deployment/azuredeploy.bicep +++ b/src/deployment/azuredeploy.bicep @@ -162,7 +162,7 @@ module eventGrid 'bicep-templates/event-grid.bicep' = { ] } -// try to make role assignments to deploy as late as possible in order to has principalId ready +// try to make role assignments to deploy as late as possible in order to have principalId ready resource roleAssigmentsPy 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = [for r in roleAssignmentsParams: { name: guid('${resourceGroup().id}${r.suffix}-python') properties: { @@ -176,7 +176,7 @@ resource roleAssigmentsPy 'Microsoft.Authorization/roleAssignments@2020-10-01-pr ] }] -// try to make role assignments to deploy as late as possible in order to has principalId ready +// try to make role assignments to deploy as late as possible in order to have principalId ready resource roleAssigmentsNet 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = [for r in roleAssignmentsParams: { name: guid('${resourceGroup().id}${r.suffix}-net') properties: { @@ -191,7 +191,7 @@ resource roleAssigmentsNet 'Microsoft.Authorization/roleAssignments@2020-10-01-p }] -// try to make role assignments to deploy as late as possible in order to has principalId ready +// try to make role assignments to deploy as late as possible in order to have principalId ready resource readBlobUserAssignment 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = { name: guid('${resourceGroup().id}-user_managed_idenity_read_blob') properties: { @@ -209,63 +209,87 @@ resource readBlobUserAssignment 'Microsoft.Authorization/roleAssignments@2020-10 module pythonFunction 'bicep-templates/function.bicep' = { name: 'pythonFunction' params: { - functions_worker_runtime: 'python' - linux_fx_version: 'Python|3.8' - functions_extension_version: '~3' name: name + linux_fx_version: 'Python|3.8' - instance_name: name app_logs_sas_url: storage.outputs.FuncSasUrlBlobAppLogs app_func_audiences: app_func_audiences app_func_issuer: app_func_issuer - app_insights_app_id: operationalInsights.outputs.appInsightsAppId - app_insights_key: operationalInsights.outputs.appInsightsInstrumentationKey - client_id: clientId - client_secret: clientSecret + diagnostics_log_level: diagnosticsLogLevel - func_sas_url: storage.outputs.FuncSasUrl - func_storage_resource_id: storage.outputs.FuncId - fuzz_storage_resource_id: storage.outputs.FuzzId - keyvault_name: keyVaultName location: location log_retention: log_retention - monitor_account_name: operationalInsights.outputs.monitorAccountName - multi_tenant_domain: multi_tenant_domain owner: owner server_farm_id: serverFarms.outputs.id - signal_r_connection_string: signalR.outputs.connectionString + client_id: clientId } } module netFunction 'bicep-templates/function.bicep' = { name: 'netFunction' params: { - functions_worker_runtime: 'dotnet-isolated' linux_fx_version: 'DOTNET-ISOLATED|6.0' - functions_extension_version: '~4' name: '${name}-net' - instance_name: name app_logs_sas_url: storage.outputs.FuncSasUrlBlobAppLogs app_func_audiences: app_func_audiences app_func_issuer: app_func_issuer + client_id: clientId + diagnostics_log_level: diagnosticsLogLevel + location: location + log_retention: log_retention + owner: owner + server_farm_id: serverFarms.outputs.id + } +} + +module pythonFunctionSettings 'bicep-templates/function-settings.bicep' = { + name: 'pythonFunctionSettings' + params: { + name: name + owner: owner + functions_worker_runtime: 'python' + functions_extension_version: '~3' + instance_name: name app_insights_app_id: operationalInsights.outputs.appInsightsAppId app_insights_key: operationalInsights.outputs.appInsightsInstrumentationKey - client_id: clientId client_secret: clientSecret - diagnostics_log_level: diagnosticsLogLevel + signal_r_connection_string: signalR.outputs.connectionString func_sas_url: storage.outputs.FuncSasUrl func_storage_resource_id: storage.outputs.FuncId fuzz_storage_resource_id: storage.outputs.FuzzId keyvault_name: keyVaultName - location: location - log_retention: log_retention monitor_account_name: operationalInsights.outputs.monitorAccountName multi_tenant_domain: multi_tenant_domain + } + dependsOn: [ + pythonFunction + ] +} + + +module netFunctionSettings 'bicep-templates/function-settings.bicep' = { + name: 'netFunctionSettings' + params: { owner: owner - server_farm_id: serverFarms.outputs.id + name: '${name}-net' signal_r_connection_string: signalR.outputs.connectionString + app_insights_app_id: operationalInsights.outputs.appInsightsAppId + app_insights_key: operationalInsights.outputs.appInsightsInstrumentationKey + functions_worker_runtime: 'dotnet-isolated' + functions_extension_version: '~4' + instance_name: name + client_secret: clientSecret + func_sas_url: storage.outputs.FuncSasUrl + func_storage_resource_id: storage.outputs.FuncId + fuzz_storage_resource_id: storage.outputs.FuzzId + keyvault_name: keyVaultName + monitor_account_name: operationalInsights.outputs.monitorAccountName + multi_tenant_domain: multi_tenant_domain } + dependsOn: [ + netFunction + ] } diff --git a/src/deployment/bicep-templates/function-settings.bicep b/src/deployment/bicep-templates/function-settings.bicep new file mode 100644 index 0000000000..1a6203781b --- /dev/null +++ b/src/deployment/bicep-templates/function-settings.bicep @@ -0,0 +1,61 @@ +param name string +param instance_name string +param owner string +param app_insights_app_id string +@secure() +param app_insights_key string + +@secure() +param func_sas_url string + +param multi_tenant_domain string + +@secure() +param signal_r_connection_string string + +param func_storage_resource_id string +param fuzz_storage_resource_id string + +param keyvault_name string + +@secure() +param client_secret string + +param monitor_account_name string + +param functions_worker_runtime string +param functions_extension_version string + +var telemetry = 'd7a73cf4-5a1a-4030-85e1-e5b25867e45a' + +resource function 'Microsoft.Web/sites@2021-02-01' existing = { + name: name +} + +resource functionSettings 'Microsoft.Web/sites/config@2021-03-01' = { + parent: function + name: 'appsettings' + properties: { + 'FUNCTIONS_EXTENSION_VERSION': functions_extension_version + 'FUNCTIONS_WORKER_RUNTIME': functions_worker_runtime + 'FUNCTIONS_WORKER_PROCESS_COUNT': '1' + 'APPINSIGHTS_INSTRUMENTATIONKEY': app_insights_key + 'APPINSIGHTS_APPID': app_insights_app_id + 'ONEFUZZ_TELEMETRY': telemetry + 'AzureWebJobsStorage': func_sas_url + 'MULTI_TENANT_DOMAIN': multi_tenant_domain + 'AzureWebJobsDisableHomepage': 'true' + 'AzureSignalRConnectionString': signal_r_connection_string + 'AzureSignalRServiceTransportType': 'Transient' + 'ONEFUZZ_INSTANCE_NAME': instance_name + 'ONEFUZZ_INSTANCE': 'https://${name}.azurewebsites.net' + 'ONEFUZZ_RESOURCE_GROUP': resourceGroup().id + 'ONEFUZZ_DATA_STORAGE': fuzz_storage_resource_id + 'ONEFUZZ_FUNC_STORAGE': func_storage_resource_id + 'ONEFUZZ_MONITOR': monitor_account_name + 'ONEFUZZ_KEYVAULT': keyvault_name + 'ONEFUZZ_OWNER': owner + 'ONEFUZZ_CLIENT_SECRET': client_secret + } +} + diff --git a/src/deployment/bicep-templates/function.bicep b/src/deployment/bicep-templates/function.bicep index c5f3b2beb3..54936f3e41 100644 --- a/src/deployment/bicep-templates/function.bicep +++ b/src/deployment/bicep-templates/function.bicep @@ -1,5 +1,4 @@ param name string -param instance_name string param location string param owner string @@ -11,18 +10,6 @@ param app_func_audiences array @secure() param app_logs_sas_url string -param app_insights_app_id string -@secure() -param app_insights_key string - -@secure() -param func_sas_url string - -param multi_tenant_domain string - -@secure() -param signal_r_connection_string string - @description('The degree of severity for diagnostics logs.') @allowed([ 'Verbose' @@ -32,22 +19,8 @@ param signal_r_connection_string string ]) param diagnostics_log_level string param log_retention int - -param func_storage_resource_id string -param fuzz_storage_resource_id string - -param keyvault_name string - -@secure() -param client_secret string - -param monitor_account_name string - param linux_fx_version string -param functions_worker_runtime string -param functions_extension_version string -var telemetry = 'd7a73cf4-5a1a-4030-85e1-e5b25867e45a' resource function 'Microsoft.Web/sites@2021-03-01' = { name: name @@ -123,31 +96,4 @@ resource funcLogs 'Microsoft.Web/sites/config@2021-03-01' = { parent: function } -resource pythonFunctionSettings 'Microsoft.Web/sites/config@2021-03-01' = { - name: 'appsettings' - parent: function - properties: { - 'FUNCTIONS_EXTENSION_VERSION': functions_extension_version - 'FUNCTIONS_WORKER_RUNTIME': functions_worker_runtime - 'FUNCTIONS_WORKER_PROCESS_COUNT': '1' - 'APPINSIGHTS_INSTRUMENTATIONKEY': app_insights_key - 'APPINSIGHTS_APPID': app_insights_app_id - 'ONEFUZZ_TELEMETRY': telemetry - 'AzureWebJobsStorage': func_sas_url - 'MULTI_TENANT_DOMAIN': multi_tenant_domain - 'AzureWebJobsDisableHomepage': 'true' - 'AzureSignalRConnectionString': signal_r_connection_string - 'AzureSignalRServiceTransportType': 'Transient' - 'ONEFUZZ_INSTANCE_NAME': instance_name - 'ONEFUZZ_INSTANCE': 'https://${name}.azurewebsites.net' - 'ONEFUZZ_RESOURCE_GROUP': resourceGroup().id - 'ONEFUZZ_DATA_STORAGE': fuzz_storage_resource_id - 'ONEFUZZ_FUNC_STORAGE': func_storage_resource_id - 'ONEFUZZ_MONITOR': monitor_account_name - 'ONEFUZZ_KEYVAULT': keyvault_name - 'ONEFUZZ_OWNER': owner - 'ONEFUZZ_CLIENT_SECRET': client_secret - } -} - output principalId string = reference(function.id, function.apiVersion, 'Full').identity.principalId