I'm trying to deploy a Python Azure Function to Azure Portal using an Azure DevOps Build Pipeline. For some reason the code is deployed to the server, but I'm getting a 404 trying to hit the endpoint. I'm getting the an error that says 1 functions found (Custom) 0 functions loaded
as well as an error that says ModuleNotFoundError: No module named 'requests'
on the server. I've been at this for hours, but no luck. I have the following details pertaining to the error I'm getting (found below). I'm wondering if anyone can please help me troubleshoot to see if I can get this DevOps pipeline to deploy correctly. Please let me know, thank you!
function_app.py:
import logging
import azure.functions as func
from src.api.controllers.etl_controller import ETLController
from src.helpers.configuration_helper import fetch_configurations
# Define your function
app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)
# Define your routes
@app.route(route="run", methods=["POST"])
def run(req: func.HttpRequest) -> func.HttpResponse:
# Fetch updated configurations
fetch_configurations()
# Process the request
logging.info(f'Processing request: {req.method} {req.url}')
controller = ETLController()
return controller.run_elphi_etl(req)
function.json:
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["post"]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
host.json:
{
"version": "2.0",
"functionTimeout": "00:30:00",
"logging": {
"logLevel": {
"default": "Debug"
},
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
requirements.txt:
asgiref==3.8.1
asn1crypto==1.5.1
azure-appconfiguration==1.6.0
azure-core==1.30.2
azure-core-tracing-opentelemetry==1.0.0b11
azure-functions==1.20.0
azure-identity==1.17.1
azure-keyvault-secrets==4.8.0
azure-monitor-opentelemetry==1.6.0
azure-monitor-opentelemetry-exporter==1.0.0b27
certifi==2024.7.4
cffi==1.16.0
cfgv==3.4.0
charset-normalizer==3.3.2
colorama==0.4.6
coverage==7.5.4
cryptography==42.0.8
debugpy==1.8.2
Deprecated==1.2.14
distlib==0.3.8
filelock==3.15.4
fixedint==0.1.6
flatten-json==0.1.14
identify==2.6.0
idna==3.7
importlib_metadata==8.0.0
iniconfig==2.0.0
isodate==0.6.1
msal==1.30.0
msal-extensions==1.2.0
msrest==0.7.1
nodeenv==1.9.1
oauthlib==3.2.2
opentelemetry-api==1.26.0
opentelemetry-instrumentation==0.47b0
opentelemetry-instrumentation-asgi==0.47b0
opentelemetry-instrumentation-dbapi==0.47b0
opentelemetry-instrumentation-django==0.47b0
opentelemetry-instrumentation-fastapi==0.47b0
opentelemetry-instrumentation-flask==0.47b0
opentelemetry-instrumentation-psycopg2==0.47b0
opentelemetry-instrumentation-requests==0.47b0
opentelemetry-instrumentation-urllib==0.47b0
opentelemetry-instrumentation-urllib3==0.47b0
opentelemetry-instrumentation-wsgi==0.47b0
opentelemetry-resource-detector-azure==0.1.5
opentelemetry-sdk==1.26.0
opentelemetry-semantic-conventions==0.47b0
opentelemetry-util-http==0.47b0
packaging==24.1
platformdirs==4.2.2
pluggy==1.5.0
portalocker==2.10.1
pre-commit==3.7.1
psutil==5.9.8
pycparser==2.22
PyJWT==2.8.0
pyOpenSSL==24.2.1
pytest==8.2.2
pytest-cov==5.0.0
python-dotenv==1.0.1
pytz==2024.1
pywin32==306; sys_platform == 'win32'
PyYAML==6.0.1
requests==2.32.3
requests-oauthlib==2.0.0
six==1.16.0
snowflake-connector-python==3.11.0
sortedcontainers==2.4.0
tomlkit==0.13.0
typing_extensions==4.12.2
urllib3==2.2.2
virtualenv==20.26.3
wrapt==1.16.0
zipp==3.19.2
azure-pipelines-dev.yaml:
trigger:
branches:
include:
- develop
pool:
vmImage: "ubuntu-latest"
variables:
PYTHON_VERSION: "3.11.8"
FUNCTIONAPP_NAME: "fn-elphi-etl-dev"
SERVICE_CONNECTION: "Elphi-ETL-Dev"
RESOURCE_GROUP: "fn-elphi-etl-dev"
PACKAGE_PATH: "$(Build.ArtifactStagingDirectory)/fn_elphi_etl.zip"
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: "$(PYTHON_VERSION)"
addToPath: true
displayName: "Install Python $(PYTHON_VERSION)"
- script: |
python -m pip install --upgrade pip
displayName: "Upgrade pip"
- script: |
python -m pip install -r requirements.txt
displayName: "Install dependencies"
- script: |
python -m pytest -v --cov=src --cov-report=term --cov-report=html --cov-report=xml:coverage.xml --junitxml=test-results.xml
displayName: "Run tests with coverage"
- task: PublishTestResults@2
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "test-results.xml"
failTaskOnFailedTests: true
testRunTitle: "Pytest Results"
displayName: "Publish Pytest Results"
- script: |
mkdir -p $(Build.ArtifactStagingDirectory)/src
shopt -s dotglob
cp -r * $(Build.ArtifactStagingDirectory)/src/
ls -l $(Build.ArtifactStagingDirectory)/src
displayName: "Copy sources and all directories to artifact staging directory and list contents"
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: "$(Build.ArtifactStagingDirectory)/src"
includeRootFolder: false
archiveType: "zip"
archiveFile: "$(PACKAGE_PATH)"
replaceExistingArchive: true
displayName: "Archive files"
- task: PublishPipelineArtifact@1
inputs:
targetPath: "$(Build.ArtifactStagingDirectory)/src"
artifact: "$(FUNCTIONAPP_NAME)"
displayName: "Publish Python project as artifact"
- task: PublishCodeCoverageResults@2
inputs:
summaryFileLocation: "$(Build.SourcesDirectory)/coverage.xml"
displayName: "Publish code coverage results"
- task: AzureFunctionApp@1
inputs:
azureSubscription: "$(SERVICE_CONNECTION)"
appType: "functionAppLinux"
appName: "$(FUNCTIONAPP_NAME)"
package: "$(PACKAGE_PATH)"
runtimeStack: "python|3.11"
startUpCommand: "func start"
displayName: "Deploy Azure Function App"
Server logs:
2024-07-26T17:36:54.860 [Information] Starting JobHost
2024-07-26T17:36:54.861 [Information] Starting Host (HostId=fn-elphi-etl-dev, InstanceId=f791376b-5871-4ad4-ab78-47b08af12162, Version=4.34.2.2, ProcessId=26, AppDomainId=1, InDebugMode=True, InDiagnosticMode=False, FunctionsExtensionVersion=~4)
2024-07-26T17:36:54.863 [Information] Loading functions metadata
2024-07-26T17:36:54.869 [Information] Reading functions metadata (Custom)
2024-07-26T17:36:54.871 [Information] 1 functions found (Custom)
2024-07-26T17:36:54.873 [Information] 0 functions loaded
2024-07-26T17:36:54.875 [Debug] FUNCTIONS_WORKER_RUNTIME value: 'python'
2024-07-26T17:36:54.876 [Debug] Adding Function descriptor provider for language python.
2024-07-26T17:36:54.876 [Debug] Creating function descriptors.
2024-07-26T17:36:54.877 [Debug] Function descriptors created.
2024-07-26T17:36:54.878 [Debug] Placeholder mode is enabled: False
2024-07-26T17:36:54.878 [Debug] RpcFunctionInvocationDispatcher received no functions
2024-07-26T17:36:54.878 [Information] Generating 0 job function(s)
2024-07-26T17:36:54.885 [Warning] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
2024-07-26T17:36:54.888 [Information] Initializing function HTTP routes
2024-07-26T21:00:17.863 [Debug] Hosting starting
2024-07-26T21:00:17.950 [Information] Traceback (most recent call last):
2024-07-26T21:00:17.950 [Information] File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 44, in call
2024-07-26T21:00:17.950 [Information] return func(*args, **kwargs)
2024-07-26T21:00:17.950 [Information] ^^^^^^^^^^^^^^^^^^^^^
2024-07-26T21:00:17.950 [Information] File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/loader.py", line 238, in index_function_app
2024-07-26T21:00:17.950 [Information] imported_module = importlib.import_module(module_name)
2024-07-26T21:00:17.950 [Information] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-07-26T21:00:17.950 [Information] File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
2024-07-26T21:00:17.950 [Information] return _bootstrap._gcd_import(name[level:], package, level)
2024-07-26T21:00:17.950 [Information] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap_external>", line 940, in exec_module
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/function_app.py", line 4, in <module>
2024-07-26T21:00:17.950 [Information] from src.api.controllers.etl_controller import ETLController
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/api/controllers/etl_controller.py", line 6, in <module>
2024-07-26T21:00:17.950 [Information] from src.api.services.etl_service import ETLService
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/api/services/etl_service.py", line 4, in <module>
2024-07-26T21:00:17.950 [Information] from src.api.services.elphi_service import ElphiService
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/api/services/elphi_service.py", line 3, in <module>
2024-07-26T21:00:17.950 [Information] from src.clients.elphi_client import ElphiClient
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/clients/elphi_client.py", line 3, in <module>
2024-07-26T21:00:17.950 [Information] import requests
2024-07-26T21:00:17.950 [Error] ModuleNotFoundError: No module named 'requests'
And here's the file structure on the server post-deploy:
Also, if I run func azure functionapp publish fn-elphi-etl-dev --build remote
on my local machine it works correctly and I get the following folder structure instead:
Based on your screenshots, when you deploy the function app on your local machine, it contains .python_packages
folder. The required packages(include request package) will be saved in this folder.
But when you deploy the function app via Azure Pipelines, it doesn't contain the .python_packages
. In this case, the packages can not be found when running the azure function.
To solve this issue, you can add --target
argument to pip install command to force the packages saving in the correct folder and make sure that the folder can be included in the zip package:
For example:
python -m pip install --target="./.python_packages/lib/site-packages" -r requirements.txt
Pipeline task:
- script: |
python -m pip install --target="./.python_packages/lib/site-packages" -r requirements.txt
displayName: "Install dependencies"
For more detailed info, you can refer to this doc: Example YAML build pipelines