I'm using Python and Pulumi to deploy version 2 Azure Functions with Python runtime v4 and Linux OS. The functions I want to create are HTTP triggered and have some additional functionality including database calls and some extra configs. The structure of the folder I am using:
Initially I tried to deploy an example function (taken from here after adapting the example to version 2) everything works fine but when I add imports (from another Python module because I want my code to be clear and organized) the functions are not visible after deployment. My functions are version 2, so they are declared in the same file using decorators. After searching I saw the following options:
declare additional functions in additional_functions.py using blueprints as described here -not necessary an option for me because I don't need additional routes inside my app
I tried to pack my additional functions inside a custom python package, based on a .whl file. I added the .whl file in the folder and the dependency in requirements.txt. I did the deployment, the functions are still not visible
the third option is to declare the additional functions as internal functions inside the function app - not necessary a good practice, I want my code modular
I can show a limited code snippet I have used. The main file:
import pulumi
import pulumi_azure_native as azure
resource_group = azure.resources.get_resource_group(...) # get existing resource group
account = azure.storage.get_storage_account(...) # get existing storage account
primary_key = pulumi.Output.all(resource_group.name, account.name).apply(
lambda args: azure.storage.list_storage_account_keys(resource_group_name=args[0], account_name=args[1])
).apply(lambda accountKeys: accountKeys.keys[0].value) # get connection string to storage account
app_container = azure.storage.BlobContainer(
"container-name",
account_name=account.name,
resource_group_name=resource_group.name,
public_access=azure.storage.PublicAccess.NONE,
) # create a storage container
app_blob = azure.storage.Blob(
"blob-name",
account_name=account.name,
resource_group_name=resource_group.name,
container_name=app_container.name,
source=pulumi.FileArchive("."), # upload current folder, including the .whl file
) # upload the serverless app to the storage container
signature = (
pulumi.Output.all(resource_group.name, account.name, app_container.name)
.apply(
....
)
)
.apply(lambda result: result.service_sas_token)
) # create a shared access signature to give the function app access to the code
plan = azure.web.AppServicePlan(
"plan",
resource_group_name=resource_group.name,
kind="Linux",
reserved=True,
sku=azure.web.SkuDescriptionArgs(
name="Y1",
tier="Dynamic",
),
) # create service plan
app = azure.web.WebApp(
"app",
resource_group_name=resource_group.name,
server_farm_id=plan.id,
kind="FunctionApp",
site_config=azure.web.SiteConfigArgs(
linux_fx_version="PYTHON|3.11",
app_settings=[
azure.web.NameValuePairArgs(
name="FUNCTIONS_WORKER_RUNTIME",
value="python",
),
azure.web.NameValuePairArgs(
name="FUNCTIONS_EXTENSION_VERSION",
value="~4",
),
azure.web.NameValuePairArgs(
name="WEBSITE_RUN_FROM_PACKAGE",
value=pulumi.Output.all(
account.name, app_container.name, app_blob.name, signature
).apply(
lambda args: f"https://{args[0]}.blob.core.windows.net/{args[1]}/{args[2]}?{args[3]}"
),
),
azure.web.NameValuePairArgs(
name="AzureWebJobsStorage",
value=pulumi.Output.all(
account.name, primary_key
).apply(
lambda args: f"DefaultEndpointsProtocol=https;AccountName={args[0]};AccountKey={args[1]};EndpointSuffix=core.windows.net",
),
),
azure.web.NameValuePairArgs(
name="AzureWebJobsFeatureFlags",
value="EnableWorkerIndexing"
),
azure.web.NameValuePairArgs(
name="ENABLE_ORYX_BUILD",
value="true",
),
azure.web.NameValuePairArgs(
name="SCM_DO_BUILD_DURING_DEPLOYMENT",
value="true",
),
],
cors=azure.web.CorsSettingsArgs(
allowed_origins=["*"],
),
),
) # create function app
And the function app:
import azure.functions as func
import logging
import custom_package
app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)
@app.function_name(name="name")
@app.route(route="route/{arg1}/{arg2}", auth_level=func.AuthLevel.ANONYMOUS, methods=[func.HttpMethod.POST])
async def example(req: func.HttpRequest) -> func.HttpResponse:
logging.info('HTTP trigger function')
res = custom_package.custom_function(arg1, arg2)
return func.HttpResponse(f"Success: {res}", status_code=200)
Are there any good practices to follow when you want to add custom functionality to functions without having to use additional routes or blueprints? The problem has been discussed but looks like there's no certain solution from Azure. Thanks!

To import custom packages in
Python model V2. you can simply create a file with any name<filename>.pyand import it infunction_app.pyusing thisfrom <filename> import <user defined function>.To Import custom packages when you have named it
__main__.pyin thefunction_app.py. You need to add the__main__.pyfile inside a folder and import it using thisfrom <folder name> import __main__and call the function inside package using__main__.<user defined function>.Note:Packages installed with
pipsuch aspymongoas you mentioned in the comment, you need to mention it in therequirements.txtfile. all the packages which are mentioned in therequiremnets.txtare installed in the azure function during deployment. which helps you function to run properly using required addition packages require in the main code.Example:

OUTPUT:<filename>.py# My Directory

custom_package.py:function_app.py__main__.pyfile name.**#My Directory

__main__.py:function_app.py