page_type | languages | products | description | urlFragment | ||||||
---|---|---|---|---|---|---|---|---|---|---|
sample |
|
|
This sample demonstrates how Azure services can be used to upload static content and make it immediately available for fast downloads all over the world. |
functions-java-push-static-contents-to-cdn |
This sample demonstrates how Azure services can be used to upload static content and make it immediately available for fast downloads all over the world.
This sample shows how to use Azure Functions to create a restful backend API for uploading content to Azure storage via a standard HTML form. After a completed upload, the new file is preloaded to Azure CDN for immediate local availability.
The steps below require Java 8 or later, Maven 3.3 or later, and either Azure CLI or PowerShell with the Az
module. Azure CloudShell can be used if these dependencies are not available locally.
- Copy the file
deployment.properties.template
todeployment.properties
and fill in the values. For example:
location=eastus
functionAppResourceGroup=contentUpload
functionAppName=contentuploader
cdnSubscriptionId=00000000-0000-0000-0000-000000000000
cdnResourceGroup=contentUpload
cdnProfileName=uploadcdnprofile
cdnEndpointName=uploadendpoint
storageSubscriptionId=00000000-0000-0000-0000-000000000000
storageResourceGroup=contentUpload
storageAccountName=uploadstorageaccount
storageContainerName=uploads
-
Run
mvn package azure-functions:deploy
. This will create the resource group specified byfunctionAppResourceGroup
in step 1, and upload the Azure Functions application. It will also popul -
Provision the storage account for content and the CDN infrastructure by running one of the following:
setup.ps1
in PowerShell. This requires theAz
PowerShell module, which can be install by runningInstall-Module Az
. You must be logged into your azure account (viaConnect-AzAccount
) prior to running.setup.sh
in a *nix shell. This requires Azure CLI to be installed. You must be logged into your azure account (viaaz login
) prior to running.
At the end of the script, a webpage will be generated (upload.html
) through which you can test the file upload.
Note: when an Azure Endpoint is first provisioned using Verizon as the CDN provider, it may take about 90 minutes for the registration to complete.
Open upload.html
, which will be generated by the setup script. This is a standard HTML form that, when submitted, will make a POST request containing the file content to the Azure Function.
Use the Network Inspector in your browser's web tools to observe that the function returns HTTP status code 200, indicating success.
Once the upload completes, you should see your file become available at the CDN endpoint. Its URL will be https://<endpoint name>.azureedge.net/<storage container name>/<file name>
. If, when attempting to access that URL, you get a 404/Resource Not Found error, it likely means that the CDN registration has not been completed.
To accept a POST request with binary content, we a create a POJO with a public method annotated as follows:
@FunctionName("uploadFile")
public HttpResponseMessage run(@HttpTrigger(name = "requestTrigger", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<byte[]>> request,
final ExecutionContext context) {
...
}
This creates an HTTP endpoint that accepts a POST request at the URL https://<functionAppName>.azurewebsites.net/api/uploadFile
. Note: uploadFile
is the annotated FunctionName
.
We use the Azure SDK to perform the core workloud of the function: writing file contents to Azure Blob Storage, and preloading the newly written files to Azure CDN.
We use Azure Managed Identity to enable Azure Functions to inject credentials to access Azure. To use Azure Managed Identity, we first enable it from the setup scripts:
PowerShell:
Set-AzWebApp -AssignIdentity $true -Name $config.functionAppName -ResourceGroupName $config.functionAppResourceGroup
Azure CLI:
az webapp identity assign --name "$functionAppName" --resource-group "$functionAppResourceGroup"
This creates a Service Principal for the Functions application. In order to authorize the functions application to write to Azure Blob Storage and preload to Azure CDN, we assign this service principal a Contributor
role on these resources:
PowerShell:
$appServicePrincipal = Get-AzADServicePrincipal -DisplayName $config.functionAppName
New-AzRoleAssignment -Scope $storageAccount.Id -RoleDefinitionName Contributor -ApplicationId $appServicePrincipal.ApplicationId
New-AzRoleAssignment -Scope $cdnProfile.Id -RoleDefinitionName Contributor -ApplicationId $appServicePrincipal.ApplicationId
Azure CLI:
functionSpAppId=$(az ad sp list --display-name "$functionAppName" --query '[].appId' -otsv)
az role assignment create --scope "$storageAccountId" --assignee "$functionSpAppId" --role Contributor
az role assignment create --scope "$cdnProfileId" --assignee "$functionSpAppId" --role Contributor
With the requisite roles now granted to our Function Application's service principal, we can now create an authenticated Azure SDK client within our function's code:
Azure azure = Azure.authenticate(new AppServiceMSICredentials(AzureEnvironment.AZURE))
.withSubscription(cdnSubscriptionId);
We use App Settings of Azure Functions to inject identifying information of outside resources, such as the CDN profile and the Blob Storage container.
Because we use the Azure Functions Maven plugin to deploy the application, we use the same plugin to publish App Settings:
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<configuration>
<resourceGroup>${functionAppResourceGroup}</resourceGroup>
<appName>${functionAppName}</appName>
<region>${location}</region>
<appSettings>
...
<property>
<name>STORAGE_SUBSCRIPTION_ID</name>
<value>${storageSubscriptionId}</value>
</property>
<property>
<name>STORAGE_RESOURCE_GROUP</name>
<value>${storageResourceGroup}</value>
</property>
<property>
<name>STORAGE_ACCOUNT_NAME</name>
<value>${storageAccountName}</value>
</property>
<property>
<name>STORAGE_CONTAINER_NAME</name>
<value>${storageContainerName}</value>
</property>
<property>
<name>CDN_SUBSCRIPTION_ID</name>
<value>${cdnSubscriptionId}</value>
</property>
<property>
<name>CDN_RESOURCE_GROUP</name>
<value>${cdnResourceGroup}</value>
</property>
<property>
<name>CDN_PROFILE_NAME</name>
<value>${cdnProfileName}</value>
</property>
<property>
<name>CDN_ENDPOINT_NAME</name>
<value>${cdnEndpointName}</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
From inside the Azure function code, the values of App Settings can be read as environment variables:
String storageAccountSubscriptionId = System.getenv("STORAGE_SUBSCRIPTION_ID");
String storageAccountName = System.getenv("STORAGE_ACCOUNT_NAME");
String storageContainerName = System.getenv("STORAGE_CONTAINER_NAME");
String storageResourceGroup = System.getenv("STORAGE_RESOURCE_GROUP");