Skip to content

Commit bdcc79f

Browse files
authored
Merge pull request #154476 from stefanushinardi/bringing-dependency
Bringing third party dependecies to azure functions
2 parents 1a41244 + 66be774 commit bdcc79f

File tree

3 files changed

+268
-0
lines changed

3 files changed

+268
-0
lines changed

articles/azure-functions/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@
343343
href: functions-bindings-error-pages.md
344344
- name: Manually run a non HTTP-triggered function
345345
href: functions-manually-run-non-http.md
346+
- name: Bring dependencies to function apps
347+
href: bring-dependency-to-functions.md
346348
- name: Deploy
347349
items:
348350
- name: Continuous deployment
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
---
2+
title: Bring dependencies and third-party libraries to Azure Functions
3+
description: Learn how to bring files or third party library
4+
ms.date: 4/6/2020
5+
ms.topic: article
6+
zone_pivot_groups: "bring-third-party-dependency-programming-functions"
7+
---
8+
9+
# Bring dependencies or third party library to Azure Functions
10+
11+
In this article, you learn to bring in third-party dependencies into your functions apps. Examples of third-party dependencies are json files, binary files and machine learning models.
12+
13+
In this article, you learn how to:
14+
> [!div class="checklist"]
15+
> * Bring in dependencies via Functions Code project
16+
::: zone pivot="programming-language-python"
17+
> [!div class="checklist"]
18+
> * Bring in dependencies via mounting Azure Fileshare
19+
::: zone-end
20+
21+
## Bring in dependencies from the project directory
22+
::: zone pivot="programming-language-python"
23+
One of the simplest ways to bring in dependencies is to put the files/artifact together with the functions app code in Functions project directory structure. Here's an example of the directory samples in a Python functions project:
24+
```
25+
<project_root>/
26+
| - my_first_function/
27+
| | - __init__.py
28+
| | - function.json
29+
| | - example.py
30+
| - dependencies/
31+
| | - dependency1
32+
| - .funcignore
33+
| - host.json
34+
| - local.settings.json
35+
```
36+
By putting the dependencies in a folder inside functions app project directory, the dependencies folder will get deployed together with the code. As a result, your function code can access the dependencies in the cloud via file system api.
37+
38+
### Accessing the dependencies in your code
39+
40+
Here's an example to access and execute ```ffmpeg``` dependency that is put into ```<project_root>/ffmpeg_lib``` directory.
41+
42+
43+
```python
44+
import logging
45+
46+
import azure.functions as func
47+
import subprocess
48+
49+
FFMPEG_RELATIVE_PATH = "../ffmpeg_lib/ffmpeg"
50+
51+
def main(req: func.HttpRequest,
52+
context: func.Context) -> func.HttpResponse:
53+
logging.info('Python HTTP trigger function processed a request.')
54+
55+
command = req.params.get('command')
56+
# If no command specified, set the command to help
57+
if not command:
58+
command = "-h"
59+
60+
# context.function_directory returns the current directory in which functions is executed
61+
ffmpeg_path = "/".join([str(context.function_directory), FFMPEG_RELATIVE_PATH])
62+
63+
try:
64+
byte_output = subprocess.check_output([ffmpeg_path, command])
65+
return func.HttpResponse(byte_output.decode('UTF-8').rstrip(),status_code=200)
66+
except Exception as e:
67+
return func.HttpResponse("Unexpected exception happened when executing ffmpeg. Error message:" + str(e),status_code=200)
68+
```
69+
>[!NOTE]
70+
> You may need to use `chmod` to provide `Execute` rights to the ffmpeg binary in a Linux environment
71+
::: zone-end
72+
73+
::: zone pivot="programming-language-java"
74+
One of the simplest ways to bring in dependencies is to put the files/artifact together with the functions app code in functions project directory structure. Here's an example of the directory samples in a Java functions project:
75+
```
76+
<project_root>/
77+
| - src/
78+
| | - main/java/com/function
79+
| | | - Function.java
80+
| | - test/java/com/function
81+
| - artifacts/
82+
| | - dependency1
83+
| - host.json
84+
| - local.settings.json
85+
| - pom.xml
86+
```
87+
For java specifically, you need to specifically include the artifacts into the build/target folder when copying resources. Here's an example on how to do it in Maven:
88+
89+
```xml
90+
...
91+
<execution>
92+
<id>copy-resources</id>
93+
<phase>package</phase>
94+
<goals>
95+
<goal>copy-resources</goal>
96+
</goals>
97+
<configuration>
98+
<overwrite>true</overwrite>
99+
<outputDirectory>${stagingDirectory}</outputDirectory>
100+
<resources>
101+
<resource>
102+
<directory>${project.basedir}</directory>
103+
<includes>
104+
<include>host.json</include>
105+
<include>local.settings.json</include>
106+
<include>artifacts/**</include>
107+
</includes>
108+
</resource>
109+
</resources>
110+
</configuration>
111+
</execution>
112+
...
113+
```
114+
By putting the dependencies in a folder inside functions app project directory, the dependencies folder will get deployed together with the code. As a result, your function code can access the dependencies in the cloud via file system api.
115+
116+
### Accessing the dependencies in your code
117+
118+
Here's an example to access and execute ```ffmpeg``` dependency that is put into ```<project_root>/ffmpeg_lib``` directory.
119+
120+
121+
```java
122+
public class Function {
123+
final static String BASE_PATH = "BASE_PATH";
124+
final static String FFMPEG_PATH = "/artifacts/ffmpeg/ffmpeg.exe";
125+
final static String HELP_FLAG = "-h";
126+
final static String COMMAND_QUERY = "command";
127+
128+
@FunctionName("HttpExample")
129+
public HttpResponseMessage run(
130+
@HttpTrigger(
131+
name = "req",
132+
methods = {HttpMethod.GET, HttpMethod.POST},
133+
authLevel = AuthorizationLevel.ANONYMOUS)
134+
HttpRequestMessage<Optional<String>> request,
135+
final ExecutionContext context) throws IOException{
136+
context.getLogger().info("Java HTTP trigger processed a request.");
137+
138+
// Parse query parameter
139+
String flags = request.getQueryParameters().get(COMMAND_QUERY);
140+
141+
if (flags == null || flags.isBlank()) {
142+
flags = HELP_FLAG;
143+
}
144+
145+
Runtime rt = Runtime.getRuntime();
146+
String[] commands = { System.getenv(BASE_PATH) + FFMPEG_PATH, flags};
147+
Process proc = rt.exec(commands);
148+
149+
BufferedReader stdInput = new BufferedReader(new
150+
InputStreamReader(proc.getInputStream()));
151+
152+
String out = stdInput.lines().collect(Collectors.joining("\n"));
153+
if(out.isEmpty()) {
154+
BufferedReader stdError = new BufferedReader(new
155+
InputStreamReader(proc.getErrorStream()));
156+
out = stdError.lines().collect(Collectors.joining("\n"));
157+
}
158+
return request.createResponseBuilder(HttpStatus.OK).body(out).build();
159+
160+
}
161+
```
162+
>[!NOTE]
163+
> To get this snippet of code to work in Azure, you need to specify a custom application setting of "BASE_PATH" with value of "/home/site/wwwroot"
164+
::: zone-end
165+
166+
167+
::: zone pivot="programming-language-python"
168+
## Bring dependencies by mounting a file share
169+
170+
When running your function app on Linux, there's another way to bring in third-party dependencies. Functions lets you mount a file share hosted in Azure Files. Consider this approach when you want to decouple dependencies or artifacts from your application code.
171+
172+
First, you need to create an Azure Storage Account. In the account, you also need to create file share in Azure files. To create these resources, follow this [guide](../storage/files/storage-how-to-use-files-portal.md)
173+
174+
After you created the storage account and file share, use the [az webapp config storage-account add](/cli/azure/webapp/config/storage-account#az_webapp_config_storage_account_add) command to attach the file share to your functions app, as shown in the following example.
175+
176+
```console
177+
az webapp config storage-account add \
178+
--name < Function-App-Name > \
179+
--resource-group < Resource-Group > \
180+
--subscription < Subscription-Id > \
181+
--custom-id < Unique-Custom-Id > \
182+
--storage-type AzureFiles \
183+
--account-name < Storage-Account-Name > \
184+
--share-name < File-Share-Name > \
185+
--access-key < Storage-Account-AccessKey > \
186+
--mount-path </path/to/mount>
187+
```
188+
189+
190+
191+
| Flag | Value |
192+
| ------------- | ---------------------------------- |
193+
| custom-id | Any unique string |
194+
| storage-type | Only AzureFiles is supported currently |
195+
| share-name | Pre-existing share |
196+
| mount-path | Path at which the share will be accessible inside the container. Value has to be of the format `/dir-name` and it can't start with `/home` |
197+
198+
More commands to modify/delete the file share configuration can be found [here](/cli/azure/webapp/config/storage-account#az-webapp-config-storage-account-update)
199+
200+
201+
### Uploading the dependencies to Azure Files
202+
203+
One option to upload your dependency into Azure Files is through Azure portal. Refer to this [guide](../storage/files/storage-how-to-use-files-portal.md#upload-a-file) for instruction to upload dependencies using portal. Other options to upload your dependencies into Azure Files are through [Azure CLI](../storage/files/storage-how-to-use-files-cli.md#upload-a-file) and [PowerShell](../storage/files/storage-how-to-use-files-powershell.md#upload-a-file).
204+
205+
206+
### Accessing the dependencies in your code
207+
208+
After your dependencies are uploaded in the file share, you can access the dependencies from your code. The mounted share is available at the specified *mount-path*, such as ```/path/to/mount```. You can access the target directory by using file system APIs.
209+
210+
The following example shows HTTP trigger code that accesses the `ffmpeg` library, which is stored in a mounted file share.
211+
212+
```python
213+
import logging
214+
215+
import azure.functions as func
216+
import subprocess
217+
218+
FILE_SHARE_MOUNT_PATH = os.environ['FILE_SHARE_MOUNT_PATH']
219+
FFMPEG = "ffmpeg"
220+
221+
def main(req: func.HttpRequest) -> func.HttpResponse:
222+
logging.info('Python HTTP trigger function processed a request.')
223+
224+
command = req.params.get('command')
225+
# If no command specified, set the command to help
226+
if not command:
227+
command = "-h"
228+
229+
try:
230+
byte_output = subprocess.check_output(["/".join(FILE_SHARE_MOUNT_PATH, FFMPEG), command])
231+
return func.HttpResponse(byte_output.decode('UTF-8').rstrip(),status_code=200)
232+
except Exception as e:
233+
return func.HttpResponse("Unexpected exception happened when executing ffmpeg. Error message:" + str(e),status_code=200)
234+
```
235+
236+
When you deploy this code to a function app in Azure, you need to [create an app setting](functions-how-to-use-azure-function-app-settings.md#settings) with a key name of `FILE_SHARE_MOUNT_PATH` and value of the mounted file share path, which for this example is `/azure-files-share`. To do local debugging, you need to populate the `FILE_SHARE_MOUNT_PATH` with the file path where your dependencies are stored in your local machine. Here's an example to set `FILE_SHARE_MOUNT_PATH` using `local.settings.json`:
237+
238+
```json
239+
{
240+
"IsEncrypted": false,
241+
"Values": {
242+
"AzureWebJobsStorage": "",
243+
"FUNCTIONS_WORKER_RUNTIME": "python",
244+
"FILE_SHARE_MOUNT_PATH" : "PATH_TO_LOCAL_FFMPEG_DIR"
245+
}
246+
}
247+
248+
```
249+
::: zone-end
250+
251+
252+
253+
## Next steps
254+
255+
+ [Azure Functions Python developer guide](functions-reference-python.md)
256+
+ [Azure Functions Java developer guide](functions-reference-java.md)
257+
+ [Azure Functions developer reference](functions-reference.md)

articles/zone-pivot-groups.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,15 @@ groups:
417417
title: Command line
418418
- id: development-environment-vs
419419
title: Visual Studio
420+
#Owner: shinardi
421+
- id: bring-third-party-dependency-programming-functions
422+
title: Programming Languages
423+
prompt: Choose a programming language
424+
pivots:
425+
- id: programming-language-python
426+
title: Python
427+
- id: programming-language-java
428+
title: Java
420429
- id: programming-languages-metrics-monitor
421430
title: Programming languages
422431
prompt: Choose a programming language.

0 commit comments

Comments
 (0)