-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcreateFunctionApp.json
173 lines (173 loc) · 14.8 KB
/
createFunctionApp.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"storageAccountName": {
"type": "string"
},
"hostingPlanName": {
"type": "string"
},
"functionAppName": {
"type": "string"
},
"functionName": {
"type": "string"
},
"storageApiVersion": {
"type": "string",
"defaultValue": "2016-12-01"
},
"appServiceApiVersion": {
"type": "string",
"defaultValue": "2016-08-01"
},
"hostingPlanApiVersion": {
"type": "string",
"defaultValue": "2016-09-01"
}
},
"variables": {
"functionMainScriptBody": "/*\r\n* Function: GetToken\r\n* Created: 4030 B.C.\r\n* Created By: Ra\r\n* Description:\r\n A helper function for getting an bearer access token that can be used in AAD-based API calls\r\n to Azure resources or Azure Resource Manager (ARM) functionality.\r\n* Usage:\r\n 1) Get the access token (Token string in the response body):\r\n https://{function-name}.azurewebsites.net/api/GetToken?code={API-code}\r\n 2) Get the access token and other information (JSON in the response body):\r\n https://{function-name}.azurewebsites.net/api/GetToken?showdetails&code={API-code}\r\n\r\n The above 2 examples get an access token for the https://management.azure.com resource.\r\n 3) Get the access token for a specific resource (Token string in the response body):\r\n https://{function-name}.azurewebsites.net/api/GetToken?msiresource=https://rest.media.azure.net&code={API-code}\r\n\r\n This gets an access token that can be used for Azure Media Services (AMS) API calls.\r\n\r\n* Background:\r\n To enable MSI on the Function App:\r\n https://docs.microsoft.com/en-us/azure/app-service/app-service-managed-service-identity\r\n*/\r\n\r\n#r \"Newtonsoft.Json\"\r\n\r\nusing System.Configuration;\r\nusing System.Net;\r\nusing System.Net.Http;\r\nusing System.Net.Http.Headers;\r\nusing System.Text;\r\nusing System.Xml;\r\nusing Newtonsoft.Json;\r\n\r\nconst string DefaultResource = \"https://management.azure.com/\";\r\nconst string MsiApiVersion = \"2017-09-01\";\r\n\r\npublic static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, ExecutionContext context)\r\n{\r\n log.Info(\"GetToken C# HTTP trigger function processed a request.\");\r\n\r\n // Do nothing if this a warmup call.\r\n if (req.GetQueryNameValuePairs().Any(q => string.Compare(q.Key, \"iswarmup\", true) == 0))\r\n {\r\n log.Info(\"Processed a warmup request.\");\r\n return new HttpResponseMessage(HttpStatusCode.OK);\r\n }\r\n\r\n // Validate that MSI is enabled.\r\n string msiEndpoint = Environment.GetEnvironmentVariable(\"MSI_ENDPOINT\");\r\n string msiSecret = Environment.GetEnvironmentVariable(\"MSI_SECRET\");\r\n if (string.IsNullOrWhiteSpace(msiEndpoint) || string.IsNullOrWhiteSpace(msiSecret))\r\n {\r\n return new HttpResponseMessage(HttpStatusCode.BadRequest)\r\n {\r\n Content = new StringContent(\"MSI is not enabled. If MSI was just enabled, make sure to restart the function before trying again.\", Encoding.UTF8, \"application/json\")\r\n };\r\n }\r\n\r\n // See if a resource is specified as a query parameter. If not, use default resource.\r\n // Examples: AMS: https://rest.media.azure.net\r\n string msiResource = req.GetQueryNameValuePairs().FirstOrDefault(q => string.Compare(q.Key, \"msiresource\", true) == 0).Value;\r\n msiResource = msiResource ?? DefaultResource;\r\n bool showDetails = req.GetQueryNameValuePairs().Any(q => string.Compare(q.Key, \"showdetails\", true) == 0);\r\n\r\n try\r\n {\r\n string token = await GetToken(msiResource, MsiApiVersion, msiEndpoint, msiSecret, log);\r\n if (string.IsNullOrWhiteSpace(token))\r\n {\r\n return new HttpResponseMessage(HttpStatusCode.BadRequest)\r\n {\r\n Content = new StringContent(\"Failed to get a token.\", Encoding.UTF8, \"application/json\")\r\n };\r\n }\r\n else\r\n {\r\n HttpContent content = null;\r\n if (showDetails)\r\n {\r\n content = new StringContent(token, Encoding.UTF8, \"application/json\");\r\n }\r\n else\r\n {\r\n string accessToken = JsonConvert.DeserializeObject<Token>(token).access_token;\r\n content = new StringContent(accessToken, Encoding.UTF8, \"application/json\");\r\n }\r\n\r\n return new HttpResponseMessage(HttpStatusCode.OK)\r\n {\r\n Content = content\r\n };\r\n }\r\n }\r\n catch (Exception ex)\r\n {\r\n return new HttpResponseMessage(HttpStatusCode.BadRequest)\r\n {\r\n Content = new StringContent($\"Failed to get a token. Exception: {ex.Message}\", Encoding.UTF8, \"application/json\")\r\n };\r\n }\r\n}\r\n\r\n// Returns a JSON string of the form (see Token class definition):\r\n// {\"access_token\":\"eyJ0...s1DZw\",\"expires_on\":\"12/12/2017 10:20:00 AM +00:00\",\"resource\":\"https://management.azure.com\",\"token_type\":\"Bearer\"}\r\n// Bearer tokens returned are typically valid for only 1 hour.\r\npublic static async Task<string> GetToken(string resource, string apiversion, string msiEndpoint, string msiSecret, TraceWriter log)\r\n{\r\n string msiUrl = $\"{msiEndpoint}?resource={resource}&api-version={apiversion}\";\r\n log.Info($\"MSI Endpoint={msiEndpoint}\");\r\n //log.Info($\"MSI secret={msiSecret}\");\r\n log.Info($\"MSI Url={msiUrl}\");\r\n\r\n var headers = new Dictionary<string, string>();\r\n headers.Add(\"Secret\", msiSecret);\r\n var tokenPayload = await InvokeRestMethodAsync(msiUrl, log, HttpMethod.Get, null, null, null, headers);\r\n log.Info($\"Token Payload={tokenPayload}\");\r\n\r\n return tokenPayload;\r\n}\r\n\r\npublic static async Task<string> InvokeRestMethodAsync(string url, TraceWriter log, HttpMethod httpMethod, string body = null, string authorizationToken = null, string authorizationScheme = \"Bearer\", IDictionary<string, string> headers = null)\r\n{\r\n HttpClient client = new HttpClient();\r\n if (!string.IsNullOrWhiteSpace(authorizationToken))\r\n {\r\n client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authorizationScheme, authorizationToken);\r\n log.Info($\"Authorization: {client.DefaultRequestHeaders.Authorization.Parameter}\");\r\n }\r\n \r\n HttpRequestMessage request = new HttpRequestMessage(httpMethod, url);\r\n if (headers != null && headers.Count > 0)\r\n {\r\n foreach (var header in headers)\r\n {\r\n request.Headers.Add(header.Key, header.Value);\r\n }\r\n }\r\n\r\n if (!string.IsNullOrWhiteSpace(body))\r\n {\r\n request.Content = new StringContent(body, Encoding.UTF8, \"application/json\");\r\n }\r\n\r\n HttpResponseMessage response = await client.SendAsync(request);\r\n if (response.IsSuccessStatusCode)\r\n {\r\n return await response.Content.ReadAsStringAsync();\r\n }\r\n\r\n string statusCodeName = response.StatusCode.ToString();\r\n int statusCodeValue = (int)response.StatusCode;\r\n string content = await response.Content.ReadAsStringAsync();\r\n log.Info($\"Status Code: {statusCodeName} ({statusCodeValue}). Body: {content}\");\r\n\r\n throw new Exception($\"Status Code: {statusCodeName} ({statusCodeValue}). Body: {content}\");\r\n}\r\n\r\npublic class Token\r\n{\r\n public string access_token { get; set; }\r\n public DateTime expires_on { get; set; }\r\n public string resource { get; set; }\r\n public string token_type { get; set; }\r\n}\r\n"
},
"resources": [
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "[parameters('hostingPlanApiVersion')]",
"name": "[parameters('hostingPlanName')]",
"location": "[parameters('location')]",
"tags": {
"displayName": "AppServicePlan",
"applicationName": "MSITokenFunctionApp"
},
"properties": {
"name": "[parameters('hostingPlanName')]",
"computeMode": "Shared",
"kind": "functionapp"
},
"sku": {
"name": "Y1",
"tier": "Dynamic",
"size": "Y1",
"family": "Y",
"capacity": 0
}
},
{
"apiVersion": "[parameters('appServiceApiVersion')]",
"name": "[parameters('functionAppName')]",
"type": "Microsoft.Web/sites",
"location": "[parameters('location')]",
"kind": "functionapp",
"tags": {
"displayName": "TokenFunctionApp",
"applicationName": "MSITokenFunctionApp"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
],
"identity": {
"type": "SystemAssigned"
},
"properties": {
"name": "[parameters('functionAppName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsDashboard",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('storageApiVersion')).keys[0].value)]"
},
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('storageApiVersion')).keys[0].value)]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~1"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('storageApiVersion')).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[concat(toLower(parameters('functionAppName')), '6d6f6a616c6c')]"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "6.5.0"
},
{
"name": "WEBSITE_USE_PLACEHOLDER",
"value": "0"
}
]
},
"clientAffinityEnabled": false
}
},
{
"apiVersion": "[parameters('appServiceApiVersion')]",
"name": "[concat(parameters('functionAppName'), '/', parameters('functionName'))]",
"type": "Microsoft.Web/sites/functions",
"tags": {
"displayName": "TokenFunction",
"applicationName": "MSITokenFunctionApp"
},
"dependsOn": [
"[resourceId('Microsoft.Web/Sites', parameters('functionAppName'))]"
],
"properties": {
"config": {
"bindings": [
{
"name": "req",
"authLevel": "function",
"direction": "in",
"type": "httpTrigger"
},
{
"name": "res",
"direction": "out",
"type": "http"
}
],
"disabled": false
},
"files": {
"run.csx": "[variables('functionMainScriptBody')]"
}
}
}
],
"outputs": {
"functionAppId": {
"type": "string",
"value": "[resourceId('Microsoft.Web/Sites', parameters('functionAppName'))]"
},
"getDefaultTokenEndpoint": {
"type": "string",
"value": "[concat('https://', parameters('functionAppName'), '.azurewebsites.net/api/GetToken?code=', listsecrets(resourceId('Microsoft.Web/sites/functions', parameters('functionAppName'), parameters('functionName')), parameters('appServiceApiVersion')).key)]"
},
"getAMSTokenEndpoint": {
"type": "string",
"value": "[concat('https://', parameters('functionAppName'), '.azurewebsites.net/api/GetToken?msiresource=https://rest.media.azure.net&code=', listsecrets(resourceId('Microsoft.Web/sites/functions', parameters('functionAppName'), parameters('functionName')), parameters('appServiceApiVersion')).key)]"
},
"getDefaultTokenEndpointWithDetails": {
"type": "string",
"value": "[concat('https://', parameters('functionAppName'), '.azurewebsites.net/api/GetToken?showdetails&code=', listsecrets(resourceId('Microsoft.Web/sites/functions', parameters('functionAppName'), parameters('functionName')), parameters('appServiceApiVersion')).key)]"
},
"getAMSTokenEndpointWithDetails": {
"type": "string",
"value": "[concat('https://', parameters('functionAppName'), '.azurewebsites.net/api/GetToken?msiresource=https://rest.media.azure.net&showdetails&code=', listsecrets(resourceId('Microsoft.Web/sites/functions', parameters('functionAppName'), parameters('functionName')), parameters('appServiceApiVersion')).key)]"
},
"functionTriggerUrl": {
"type": "string",
"value": "[listsecrets(resourceId('Microsoft.Web/sites/functions', parameters('functionAppName'), parameters('functionName')), parameters('appServiceApiVersion')).trigger_url]"
}
}
}