forked from Azure-Samples/functions-customer-reviews
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathAzure Functions HOL (JavaScript).html
847 lines (756 loc) · 45.8 KB
/
Azure Functions HOL (JavaScript).html
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
<!DOCTYPE html>
<html>
<head>
<title>Azure Functions HOL (JavaScript)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
<style type="text/css">
.highlight { background: #ffffff; }
.highlight .c { color: #999988; font-style: italic } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { font-weight: bold } /* Keyword */
.highlight .o { font-weight: bold } /* Operator */
.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #999999 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { font-weight: bold } /* Keyword.Constant */
.highlight .kd { font-weight: bold } /* Keyword.Declaration */
.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #009999 } /* Literal.Number */
.highlight .s { color: #d14 } /* Literal.String */
.highlight .na { color: #008080 } /* Name.Attribute */
.highlight .nb { color: #0086B3 } /* Name.Builtin */
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
.highlight .no { color: #008080 } /* Name.Constant */
.highlight .ni { color: #800080 } /* Name.Entity */
.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
.highlight .nn { color: #555555 } /* Name.Namespace */
.highlight .nt { color: #000080 } /* Name.Tag */
.highlight .nv { color: #008080 } /* Name.Variable */
.highlight .ow { font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #009999 } /* Literal.Number.Float */
.highlight .mh { color: #009999 } /* Literal.Number.Hex */
.highlight .mi { color: #009999 } /* Literal.Number.Integer */
.highlight .mo { color: #009999 } /* Literal.Number.Oct */
.highlight .sb { color: #d14 } /* Literal.String.Backtick */
.highlight .sc { color: #d14 } /* Literal.String.Char */
.highlight .sd { color: #d14 } /* Literal.String.Doc */
.highlight .s2 { color: #d14 } /* Literal.String.Double */
.highlight .se { color: #d14 } /* Literal.String.Escape */
.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
.highlight .si { color: #d14 } /* Literal.String.Interpol */
.highlight .sx { color: #d14 } /* Literal.String.Other */
.highlight .sr { color: #009926 } /* Literal.String.Regex */
.highlight .s1 { color: #d14 } /* Literal.String.Single */
.highlight .ss { color: #990073 } /* Literal.String.Symbol */
.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #008080 } /* Name.Variable.Class */
.highlight .vg { color: #008080 } /* Name.Variable.Global */
.highlight .vi { color: #008080 } /* Name.Variable.Instance */
.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
.pl-c {
color: #969896;
}
.pl-c1,.pl-mdh,.pl-mm,.pl-mp,.pl-mr,.pl-s1 .pl-v,.pl-s3,.pl-sc,.pl-sv {
color: #0086b3;
}
.pl-e,.pl-en {
color: #795da3;
}
.pl-s1 .pl-s2,.pl-smi,.pl-smp,.pl-stj,.pl-vo,.pl-vpf {
color: #333;
}
.pl-ent {
color: #63a35c;
}
.pl-k,.pl-s,.pl-st {
color: #a71d5d;
}
.pl-pds,.pl-s1,.pl-s1 .pl-pse .pl-s2,.pl-sr,.pl-sr .pl-cce,.pl-sr .pl-sra,.pl-sr .pl-sre,.pl-src,.pl-v {
color: #df5000;
}
.pl-id {
color: #b52a1d;
}
.pl-ii {
background-color: #b52a1d;
color: #f8f8f8;
}
.pl-sr .pl-cce {
color: #63a35c;
font-weight: bold;
}
.pl-ml {
color: #693a17;
}
.pl-mh,.pl-mh .pl-en,.pl-ms {
color: #1d3e81;
font-weight: bold;
}
.pl-mq {
color: #008080;
}
.pl-mi {
color: #333;
font-style: italic;
}
.pl-mb {
color: #333;
font-weight: bold;
}
.pl-md,.pl-mdhf {
background-color: #ffecec;
color: #bd2c00;
}
.pl-mdht,.pl-mi1 {
background-color: #eaffea;
color: #55a532;
}
.pl-mdr {
color: #795da3;
font-weight: bold;
}
.pl-mo {
color: #1d3e81;
}
.task-list {
padding-left:10px;
margin-bottom:0;
}
.task-list li {
margin-left: 20px;
}
.task-list-item {
list-style-type:none;
padding-left:10px;
}
.task-list-item label {
font-weight:400;
}
.task-list-item.enabled label {
cursor:pointer;
}
.task-list-item+.task-list-item {
margin-top:3px;
}
.task-list-item-checkbox {
display:inline-block;
margin-left:-20px;
margin-right:3px;
vertical-align:1px;
}
</style>
</head>
<body>
<p><a name="HOLTitle"></a></p>
<h1>Azure Functions</h1>
<hr>
<p><a name="Overview"></a></p>
<h2>Overview</h2>
<p>Functions have been the basic building blocks of software since the first lines of code were written and the need for code organization and reuse became a necessity. Azure Functions expand on these concepts by allowing developers to create "serverless", event-driven functions that run in the cloud and can be shared across a wide variety of services and systems, uniformly managed, and easily scaled based on demand. In addition, Azure Functions can be written in a variety of languages, including C#, JavaScript, Python, Bash, and PowerShell, and they're perfect for building apps and nanoservices that employ a compute-on-demand model.</p>
<p>In this lab, you will create an Azure Function that monitors a blob container in Azure Storage for new images, and then performs automated analysis of the images using the Microsoft Cognitive Services <a href="https://www.microsoft.com/cognitive-services/en-us/computer-vision-api">Computer Vision API</a>. Specifically, the Azure Function will analyze each image that is uploaded to the container for adult or racy content and create a copy of the image in another container. Images that contain adult or racy content will be copied to one container, and images that do not contain adult or racy content will be copied to another. In addition, the scores returned by the Computer Vision API will be stored in blob metadata.</p>
<p><a name="Objectives"></a></p>
<h3>Objectives</h3>
<p>In this hands-on lab, you will learn how to:</p>
<ul>
<li>Create an Azure Function App</li>
<li>Write an Azure Function that uses a blob trigger</li>
<li>Add application settings to an Azure Function App</li>
<li>Use Microsoft Cognitive Services to analyze images and store the results in blob metadata</li>
</ul>
<p><a name="Prerequisites"></a></p>
<h3>Prerequisites</h3>
<p>The following are required to complete this hands-on lab:</p>
<ul>
<li>An active Microsoft Azure subscription. If you don't have one, <a href="http://aka.ms/WATK-FreeTrial">sign up for a free trial</a>.</li>
<li><a href="http://storageexplorer.com">Microsoft Azure Storage Explorer</a> (optional)</li>
</ul>
<hr>
<p><a name="Exercises"></a></p>
<h2>Exercises</h2>
<p>This hands-on lab includes the following exercises:</p>
<ul>
<li><a href="#Exercise1">Exercise 1: Create an Azure Function App</a></li>
<li><a href="#Exercise2">Exercise 2: Add an Azure Function</a></li>
<li><a href="#Exercise3">Exercise 3: Add a subscription key to application settings</a></li>
<li><a href="#Exercise4">Exercise 4: Test the Azure Function</a></li>
<li><a href="#Exercise5">Exercise 5: View blob metadata (optional)</a></li>
</ul>
<p>Estimated time to complete this lab: <strong>45</strong> minutes.</p>
<p><a name="Exercise1"></a></p>
<h2>Exercise 1: Create an Azure Function App</h2>
<p>The first step in writing an Azure Function is to create an Azure Function App. In this exercise, you will create an Azure Function App using the Azure Portal. Then you will add the three blob containers to the storage account that is created for the Function App: one to store uploaded images, a second to store images that do not contain adult or racy content, and a third to contain images that <em>do</em> contain adult or racy content.</p>
<ol>
<li>
<p>Open the <a href="https://portal.azure.com">Azure Portal</a> in your browser. If asked to log in, do so using your Microsoft account.</p>
</li>
<li>
<p>Click <strong>+ New</strong>, followed by <strong>Compute</strong> and <strong>Function App</strong>.</p>
<p><a href="Images/new-function-app.png" target="_blank"><img src="Images/new-function-app.png" alt="Creating an Azure Function App" style="max-width:100%;"></a></p>
<p><em>Creating an Azure Function App</em></p>
</li>
<li>
<p>Enter an app name that is unique within Azure. Under <strong>Resource Group</strong>, select <strong>Create new</strong> and enter "FunctionsLabResourceGroup" (without quotation marks) as the resource-group name to create a resource group for the Function App. Choose the <strong>Location</strong> nearest you, and accept the default values for all other parameters. Then click <strong>Create</strong> to create a new Function App.</p>
<blockquote>
<p>The app name becomes part of a DNS name and therefore must be unique within Azure. Make sure a green check mark appears to the name indicating it is unique. You probably <strong>won't</strong> be able to use "functionslab" as the app name.</p>
</blockquote>
<p><a href="Images/function-app-name.png" target="_blank"><img src="Images/function-app-name.png" alt="Creating a Function App" style="max-width:100%;"></a></p>
<p><em>Creating a Function App</em></p>
</li>
<li>
<p>Click <strong>Resource groups</strong> in the ribbon on the left side of the portal, and then click the resource group created for the Function App.</p>
<p><a href="Images/open-resource-group.png" target="_blank"><img src="Images/open-resource-group.png" alt="Opening the resource group" style="max-width:100%;"></a></p>
<p><em>Opening the resource group</em></p>
</li>
<li>
<p>Periodically click the <strong>Refresh</strong> button at the top of the blade until "Deploying" changes to "Succeeded," indicating that the Function App has been deployed. Then click the storage account that was created for the Function App.</p>
<p><a href="Images/open-storage-account-after-deployment.png" target="_blank"><img src="Images/open-storage-account-after-deployment.png" alt="Opening the storage account" style="max-width:100%;"></a></p>
<p><em>Opening the storage account</em></p>
</li>
<li>
<p>Click <strong>Blobs</strong> to view the contents of blob storage.</p>
<p><a href="Images/open-blob-storage.png" target="_blank"><img src="Images/open-blob-storage.png" alt="Opening blob storage" style="max-width:100%;"></a></p>
<p><em>Opening blob storage</em></p>
</li>
<li>
<p>Click <strong>+ Container</strong>. Type "uploaded" into the <strong>Name</strong> box and set <strong>Access type</strong> to <strong>Private</strong>. Then click the <strong>OK</strong> button to create a new container.</p>
<p><a href="Images/add-container.png" target="_blank"><img src="Images/add-container.png" alt="Adding a container" style="max-width:100%;"></a></p>
<p><em>Adding a container</em></p>
</li>
<li>
<p>Repeat Step 7 to add containers named "accepted" and "rejected" to blob storage.</p>
</li>
<li>
<p>Confirm that all three containers were added to blob storage.</p>
<p><a href="Images/new-containers.png" target="_blank"><img src="Images/new-containers.png" alt="The new containers" style="max-width:100%;"></a></p>
<p><em>The new containers</em></p>
</li>
</ol>
<p>The Azure Function App has been created and you have added three containers to the storage account created for it. The next step is to add an Azure Function.</p>
<p><a name="Exercise2"></a></p>
<h2>Exercise 2: Add an Azure Function</h2>
<p>Once you have created an Azure Function App, you can add Azure Functions to it. In this exercise, you will add a function to the Function App you created in <a href="#Exercise1">Exercise 1</a> and write JavaScript code that uses the <a href="https://www.microsoft.com/cognitive-services/en-us/computer-vision-api">Computer Vision API</a> to analyze images added to the "uploaded" container for adult or racy content.</p>
<ol>
<li>
<p>Return to the blade for the "FunctionsLabResourceGroup" resource group and click the Azure Function App that you created in <a href="#Exercise1">Exercise 1</a>.</p>
<p><a href="Images/open-function-app.png" target="_blank"><img src="Images/open-function-app.png" alt="Opening the Function App" style="max-width:100%;"></a></p>
<p><em>Opening the Function App</em></p>
</li>
<li>
<p>Click the <strong>+</strong> sign to the right of <strong>Functions</strong>. Then click <strong>Custom function</strong>.</p>
<p><a href="Images/add-function.png" target="_blank"><img src="Images/add-function.png" alt="Adding a function" style="max-width:100%;"></a></p>
<p><em>Adding a function</em></p>
</li>
<li>
<p>Set <strong>Language</strong> to <strong>JavaScript</strong>. Then click <strong>BlobTrigger-JavaScript</strong>.</p>
<p><a href="Images/js-select-template.png" target="_blank"><img src="Images/js-select-template.png" alt="Selecting a function template" style="max-width:100%;"></a></p>
<p><em>Selecting a function template</em></p>
</li>
<li>
<p>Enter "BlobImageAnalysis" (without quotation marks) for the function name and "uploaded/{name}" into the <strong>Path</strong> box. (The latter applies the blob storage trigger to the "uploaded" container that you created in Exercise 1.) Then click the <strong>Create</strong> button to create the Azure Function.</p>
<p><a href="Images/create-azure-function.png" target="_blank"><img src="Images/create-azure-function.png" alt="Creating an Azure Function" style="max-width:100%;"></a></p>
<p><em>Creating an Azure Function</em></p>
</li>
<li>
<p>Replace the code shown in the code editor with the following statements:</p>
<div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> request <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>request-promise<span class="pl-pds">'</span></span>);
<span class="pl-k">var</span> azure <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>azure-storage<span class="pl-pds">'</span></span>);
<span class="pl-c1">module</span>.<span class="pl-en">exports</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span class="pl-smi">context</span>, <span class="pl-smi">myBlob</span>) {
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(<span class="pl-s"><span class="pl-pds">"</span>Analyzing uploaded image '<span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-smi">context</span>.<span class="pl-smi">bindingData</span>.<span class="pl-c1">name</span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>' for adult content...<span class="pl-pds">"</span></span>);
<span class="pl-k">var</span> options <span class="pl-k">=</span> <span class="pl-en">getAnalysisOptions</span>(myBlob, <span class="pl-c1">process</span>.<span class="pl-smi">env</span>.<span class="pl-smi">SubscriptionKey</span>);
<span class="pl-en">analyzeAndProcessImage</span>(context, options);
<span class="pl-k">function</span> <span class="pl-en">getAnalysisOptions</span>(<span class="pl-smi">image</span>, <span class="pl-smi">subscriptionKey</span>) {
<span class="pl-k">return</span> {
uri<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>https://westus.api.cognitive.microsoft.com/vision/v1.0/analyze?visualFeatures=Adult<span class="pl-pds">"</span></span>,
method<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>POST<span class="pl-pds">'</span></span>,
body<span class="pl-k">:</span> image,
headers<span class="pl-k">:</span> {
<span class="pl-s"><span class="pl-pds">'</span>Content-Type<span class="pl-pds">'</span></span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>application/octet-stream<span class="pl-pds">'</span></span>,
<span class="pl-s"><span class="pl-pds">'</span>Ocp-Apim-Subscription-Key<span class="pl-pds">'</span></span><span class="pl-k">:</span> subscriptionKey
}
}
};
<span class="pl-k">function</span> <span class="pl-en">analyzeAndProcessImage</span>(<span class="pl-smi">context</span>, <span class="pl-smi">options</span>) {
<span class="pl-en">request</span>(options)
.<span class="pl-en">then</span>((<span class="pl-smi">response</span>) <span class="pl-k">=></span> {
response <span class="pl-k">=</span> <span class="pl-c1">JSON</span>.<span class="pl-c1">parse</span>(response);
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(<span class="pl-s"><span class="pl-pds">"</span>Is Adult: <span class="pl-pds">"</span></span>, <span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">isAdultContent</span>);
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(<span class="pl-s"><span class="pl-pds">"</span>Adult Score: <span class="pl-pds">"</span></span>, <span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">adultScore</span>);
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(<span class="pl-s"><span class="pl-pds">"</span>Is Racy: <span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">isRacyContent</span>);
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(<span class="pl-s"><span class="pl-pds">"</span>Racy Score: <span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">racyScore</span>);
<span class="pl-k">var</span> fileName <span class="pl-k">=</span> <span class="pl-smi">context</span>.<span class="pl-smi">bindingData</span>.<span class="pl-c1">name</span>;
<span class="pl-k">var</span> targetContainer <span class="pl-k">=</span> ((<span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">isRacyContent</span>) <span class="pl-k">?</span> <span class="pl-s"><span class="pl-pds">'</span>rejected<span class="pl-pds">'</span></span> <span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>accepted<span class="pl-pds">'</span></span>);
<span class="pl-k">var</span> blobService <span class="pl-k">=</span> <span class="pl-smi">azure</span>.<span class="pl-en">createBlobService</span>(<span class="pl-c1">process</span>.<span class="pl-smi">env</span>.<span class="pl-smi">AzureWebJobsStorage</span>);
<span class="pl-smi">blobService</span>.<span class="pl-en">startCopyBlob</span>(<span class="pl-en">getStoragePath</span>(<span class="pl-s"><span class="pl-pds">"</span>uploaded<span class="pl-pds">"</span></span>, fileName), targetContainer, fileName, <span class="pl-k">function</span> (<span class="pl-smi">error</span>, <span class="pl-smi">s</span>, <span class="pl-smi">r</span>) {
<span class="pl-k">if</span>(error) <span class="pl-smi">context</span>.<span class="pl-en">log</span>(error);
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(fileName <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span> created in <span class="pl-pds">"</span></span> <span class="pl-k">+</span> targetContainer <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>.<span class="pl-pds">"</span></span>);
<span class="pl-smi">blobService</span>.<span class="pl-en">setBlobMetadata</span>(targetContainer, fileName,
{
<span class="pl-s"><span class="pl-pds">"</span>isAdultContent<span class="pl-pds">"</span></span> <span class="pl-k">:</span> <span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">isAdultContent</span>,
<span class="pl-s"><span class="pl-pds">"</span>adultScore<span class="pl-pds">"</span></span> <span class="pl-k">:</span> (<span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">adultScore</span> <span class="pl-k">*</span> <span class="pl-c1">100</span>).<span class="pl-en">toFixed</span>(<span class="pl-c1">0</span>) <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>%<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>isRacyContent<span class="pl-pds">"</span></span> <span class="pl-k">:</span> <span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">isRacyContent</span>,
<span class="pl-s"><span class="pl-pds">"</span>racyScore<span class="pl-pds">"</span></span> <span class="pl-k">:</span> (<span class="pl-smi">response</span>.<span class="pl-smi">adult</span>.<span class="pl-smi">racyScore</span> <span class="pl-k">*</span> <span class="pl-c1">100</span>).<span class="pl-en">toFixed</span>(<span class="pl-c1">0</span>) <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>%<span class="pl-pds">"</span></span>
},
<span class="pl-k">function</span>(<span class="pl-smi">error</span>,<span class="pl-smi">s</span>,<span class="pl-smi">r</span>) {
<span class="pl-k">if</span>(error) <span class="pl-smi">context</span>.<span class="pl-en">log</span>(error);
<span class="pl-smi">context</span>.<span class="pl-en">log</span>(fileName <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span> metadata added successfully.<span class="pl-pds">"</span></span>);
});
});
})
.<span class="pl-en">catch</span>((<span class="pl-smi">error</span>) <span class="pl-k">=></span> <span class="pl-smi">context</span>.<span class="pl-en">log</span>(error))
.<span class="pl-en">finally</span>(() <span class="pl-k">=></span> <span class="pl-smi">context</span>.<span class="pl-en">done</span>());
};
<span class="pl-k">function</span> <span class="pl-en">getStoragePath</span>(<span class="pl-smi">container</span>,<span class="pl-smi">fileName</span>) {
<span class="pl-k">var</span> storageConnection <span class="pl-k">=</span> (<span class="pl-c1">process</span>.<span class="pl-smi">env</span>.<span class="pl-c1">WEBSITE_CONTENTAZUREFILECONNECTIONSTRING</span>).<span class="pl-c1">split</span>(<span class="pl-s"><span class="pl-pds">'</span>;<span class="pl-pds">'</span></span>);
<span class="pl-k">var</span> accountName <span class="pl-k">=</span> storageConnection[<span class="pl-c1">1</span>].<span class="pl-c1">split</span>(<span class="pl-s"><span class="pl-pds">'</span>=<span class="pl-pds">'</span></span>)[<span class="pl-c1">1</span>];
<span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>https://<span class="pl-pds">"</span></span> <span class="pl-k">+</span> accountName <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>.blob.core.windows.net/<span class="pl-pds">"</span></span> <span class="pl-k">+</span> container <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>/<span class="pl-pds">"</span></span> <span class="pl-k">+</span> fileName <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>.jpg<span class="pl-pds">"</span></span>;
};
};</pre></div>
<blockquote>
<p>In Node.js, <strong>module.exports</strong> is the method used to export a function to be executed. The <strong>getAnalysisOptions</strong> function in this code prepares a request for each blob added to the "uploaded" container, and then it calls the <strong>analyzeAndProcessImage</strong> function to call the Computer Vision API to analyze the image and create a copy of the blob in either the "accepted" container or the "rejected" container, depending on the scores returned by the Computer Vision API.</p>
</blockquote>
</li>
<li>
<p>Click the <strong>Save</strong> button at the top of the code editor to save your changes. Then click <strong>View files</strong>.</p>
<p><a href="Images/js-save-index-js.png" target="_blank"><img src="Images/js-save-index-js.png" alt="Saving the function" style="max-width:100%;"></a></p>
<p><em>Saving the function</em></p>
</li>
<li>
<p>Click <strong>function.json</strong> to open that file for editing.</p>
<p><a href="Images/js-open-function-json.png" target="_blank"><img src="Images/js-open-function-json.png" alt="Opening function.json" style="max-width:100%;"></a></p>
<p><em>Opening function.json</em></p>
</li>
<li>
<p>Replace the JSON shown in the code editor with the following JSON:</p>
<div class="highlight highlight-source-json"><pre>{
<span class="pl-s"><span class="pl-pds">"</span>bindings<span class="pl-pds">"</span></span>: [
{
<span class="pl-s"><span class="pl-pds">"</span>name<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>myBlob<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>type<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>blobTrigger<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>path<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>uploaded/{name}.jpg<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>connection<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>AzureWebJobsStorage<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>dataType<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>binary<span class="pl-pds">"</span></span>,
<span class="pl-s"><span class="pl-pds">"</span>direction<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>in<span class="pl-pds">"</span></span>
}
],
<span class="pl-s"><span class="pl-pds">"</span>disabled<span class="pl-pds">"</span></span>: <span class="pl-c1">false</span>
}</pre></div>
<blockquote>
<p>The statement that sets "dataType" to "binary" improves performance when processing blobs that are images.</p>
</blockquote>
</li>
<li>
<p>Click <strong>Save</strong> to save the changes to the file.</p>
<p><a href="Images/js-save-function-file.png" target="_blank"><img src="Images/js-save-function-file.png" alt="Saving function.json" style="max-width:100%;"></a></p>
<p><em>Saving function.json</em></p>
</li>
<li>
<p>The function has been written, but you must install some dependency packages in order for it to work. The dependencies come from the two <code>require</code> statements at the beginning of the JavaScript code that you added. To resolve these dependencies, begin by clicking the function name in the ribbon on the left. Then click <strong>Platform features</strong>, followed by <strong>Console</strong> to open a developer console.</p>
<p><a href="Images/js-open-dev-console.png" target="_blank"><img src="Images/js-open-dev-console.png" alt="Opening the developer console" style="max-width:100%;"></a></p>
<p><em>Opening the developer console</em></p>
</li>
<li>
<p>Execute the following command in the console to navigate to the function directory:</p>
<pre><code>cd BlobImageAnalyis
</code></pre>
</li>
<li>
<p>Execute the following commands in the console to install the packages used by the function. Ignore any warning messages that are displayed, and note that each command may take a minute or more to run:</p>
<pre><code>npm install request
npm install request-promise
npm install azure-storage
</code></pre>
</li>
</ol>
<p>An Azure Function written in JavaScript has been created and configured and the packages that the function relies upon have been installed. The next step is to add an application setting that the Azure Function relies on.</p>
<p><a name="Exercise3"></a></p>
<h2>Exercise 3: Add a subscription key to application settings</h2>
<p>The Azure Function you created in <a href="#Exercise2">Exercise 2</a> loads a subscription key for the Microsoft Cognitive Services Computer Vision API from application settings. This key is required in order for your code to call the Computer Vision API, and is transmitted in an HTTP header in each call. In this exercise, you will subscribe to the Computer Vision API, and then add an access key for the subscription to application settings.</p>
<ol>
<li>
<p>In the Azure Portal, click <strong>+ New</strong>, followed by <strong>AI + Cognitive Services</strong> and <strong>Computer Vision API</strong>.</p>
<p><a href="Images/new-vision-api.png" target="_blank"><img src="Images/new-vision-api.png" alt="Creating a new Computer Vision API subscription" style="max-width:100%;"></a></p>
<p><em>Creating a new Computer Vision API subscription</em></p>
</li>
<li>
<p>Enter "VisionAPI" into the <strong>Name</strong> box and select <strong>F0</strong> as the <strong>Pricing tier</strong>. Under <strong>Resource Group</strong>, select <strong>Use existing</strong> and select the "FunctionsLabResourceGroup" that you created for the Function App in Exercise 1. Check the <strong>I confirm</strong> box, and then click <strong>Create</strong>.</p>
<p><a href="Images/create-vision-api.png" target="_blank"><img src="Images/create-vision-api.png" alt="Subcribing to the Computer Vision API" style="max-width:100%;"></a></p>
<p><em>Subcribing to the Computer Vision API</em></p>
</li>
<li>
<p>Return to the blade for the "FunctionsLabResourceGroup" resource group and click the Computer Vision API subscription that you just created.</p>
<p><a href="Images/open-vision-api.png" target="_blank"><img src="Images/open-vision-api.png" alt="Opening the Computer Vision API subscription" style="max-width:100%;"></a></p>
<p><em>Opening the Computer Vision API subscription</em></p>
</li>
<li>
<p>Click <strong>Show access keys</strong>.</p>
<p><a href="Images/show-access-keys.png" target="_blank"><img src="Images/show-access-keys.png" alt="Viewing the access keys" style="max-width:100%;"></a></p>
<p><em>Viewing the access keys</em></p>
</li>
<li>
<p>Click the <strong>Copy</strong> button to the right of <strong>KEY 1</strong> to copy the access key to the clipboard.</p>
<p><a href="Images/copy-access-key.png" target="_blank"><img src="Images/copy-access-key.png" alt="Copying the access key" style="max-width:100%;"></a></p>
<p><em>Copying the access key</em></p>
</li>
<li>
<p>Return to the Function App in the Azure Portal and click the function name in the ribbon on the left. Then click <strong>Platform features</strong>, followed by <strong>Application settings</strong>.</p>
<p><a href="Images/open-app-settings.png" target="_blank"><img src="Images/open-app-settings.png" alt="Viewing application settings" style="max-width:100%;"></a></p>
<p><em>Viewing application settings</em></p>
</li>
<li>
<p>Scroll down to the "App settings" section. Add a new app setting named "SubscriptionKey" (without quotation marks), and paste the subscription key that is on the clipboard into the <strong>Value</strong> box. Then click <strong>Save</strong> at the top of the blade.</p>
<p><a href="Images/add-key.png" target="_blank"><img src="Images/add-key.png" alt="Adding a subscription key" style="max-width:100%;"></a></p>
<p><em>Adding a subscription key</em></p>
</li>
</ol>
<p>The work of writing and configuring the Azure Function is complete. Now comes the fun part: testing it out.</p>
<p><a name="Exercise4"></a></p>
<h2>Exercise 4: Test the Azure Function</h2>
<p>Your function is configured to listen for changes to the blob container named "uploaded" that you created in <a href="#Exercise1">Exercise 1</a>. Each time an image appears in the container, the function executes and passes the image to the Computer Vision API for analysis. To test the function, you simply upload images to the container. In this exercise, you will use the Azure Portal to upload images to the "uploaded" container and verify that copies of the images are placed in the "accepted" and "rejected" containers.</p>
<ol>
<li>
<p>In the Azure Portal, go to the resource group created for your Function App. Then click the storage account that was created for it.</p>
<p><a href="Images/open-storage-account.png" target="_blank"><img src="Images/open-storage-account.png" alt="Opening the storage account" style="max-width:100%;"></a></p>
<p><em>Opening the storage account</em></p>
</li>
<li>
<p>Click <strong>Blobs</strong> to view the contents of blob storage.</p>
<p><a href="Images/open-blob-storage.png" target="_blank"><img src="Images/open-blob-storage.png" alt="Opening blob storage" style="max-width:100%;"></a></p>
<p><em>Opening blob storage</em></p>
</li>
<li>
<p>Click <strong>uploaded</strong> to open the "uploaded" container.</p>
<p><a href="Images/open-uploaded-container.png" target="_blank"><img src="Images/open-uploaded-container.png" alt="Opening the "uploaded" container" style="max-width:100%;"></a></p>
<p><em>Opening the "uploaded" container</em></p>
</li>
<li>
<p>Click <strong>Upload</strong>.</p>
<p><a href="Images/upload-images-1.png" target="_blank"><img src="Images/upload-images-1.png" alt="Uploading images to the "uploaded" container" style="max-width:100%;"></a></p>
<p><em>Uploading images to the "uploaded" container</em></p>
</li>
<li>
<p>Click the button with the folder icon to the right of the <strong>Files</strong> box. Select all of the files in this lab's "Resources" folder. Then click the <strong>Upload</strong> button to upload the files to the "uploaded" container.</p>
<p><a href="Images/upload-images-2.png" target="_blank"><img src="Images/upload-images-2.png" alt="Uploading images to the "uploaded" container" style="max-width:100%;"></a></p>
<p><em>Uploading images to the "uploaded" container</em></p>
</li>
<li>
<p>Return to the blade for the "uploaded" container and verify that eight images were uploaded.</p>
<p><a href="Images/uploaded-images.png" target="_blank"><img src="Images/uploaded-images.png" alt="Images uploaded to the "uploaded" container" style="max-width:100%;"></a></p>
<p><em>Images uploaded to the "uploaded" container</em></p>
</li>
<li>
<p>Close the blade for the "uploaded" container and open the "accepted" container.</p>
<p><a href="Images/open-accepted-container.png" target="_blank"><img src="Images/open-accepted-container.png" alt="Opening the "accepted" container" style="max-width:100%;"></a></p>
<p><em>Opening the "accepted" container</em></p>
</li>
<li>
<p>Verify that the "accepted" container holds seven images. <strong>These are the images that were classified as neither adult nor racy by the Computer Vision API</strong>.</p>
<blockquote>
<p>It may take a minute or more for all of the images to appear in the container. If necessary, click <strong>Refresh</strong> every few seconds until you see all seven images.</p>
</blockquote>
<p><a href="Images/accepted-images.png" target="_blank"><img src="Images/accepted-images.png" alt="Images in the "accepted" container" style="max-width:100%;"></a></p>
<p><em>Images in the "accepted" container</em></p>
</li>
<li>
<p>Close the blade for the "accepted" container and open the blade for the "rejected" container. Verify that the "rejected" container holds one image. <strong>This image was classified as adult or racy (or both) by the Computer Vision API</strong>.</p>
<p><a href="Images/js-rejected-images.png" target="_blank"><img src="Images/js-rejected-images.png" alt="Images in the "rejected" container" style="max-width:100%;"></a></p>
<p><em>Images in the "rejected" container</em></p>
</li>
</ol>
<p>The presence of seven images in the "accepted" container and one in the "rejected" container is proof that your Azure Function executed each time an image was uploaded to the "uploaded" container. If you would like, return to the BlobImageAnalysis function in the portal and click <strong>Monitor</strong>. You will see a log detailing each time the function executed.</p>
<p><a name="Exercise5"></a></p>
<h2>Exercise 5: View blob metadata (optional)</h2>
<p>What if you would like to view the scores for adult content and raciness returned by the Computer Vision API for each image uploaded to the "uploaded" container? The scores are stored in blob metadata for the images in the "accepted" and "rejected" containers, but blob metadata can't be viewed through the Azure Portal.</p>
<p>In this exercise, you will use the cross-platform <a href="http://storageexplorer.com">Microsoft Azure Storage Explorer</a> to view blob metadata and see how the Computer Vision API scored the images you uploaded.</p>
<ol>
<li>
<p>If you haven't installed the Microsoft Azure Storage Explorer, go to <a href="http://storageexplorer.com">http://storageexplorer.com</a> and install it now. Versions are available for Windows, macOS, and Linux.</p>
</li>
<li>
<p>Start Storage Explorer. If you are asked to log in, do so using the same account you used to log in to the Azure Portal.</p>
</li>
<li>
<p>Find the storage account that was created for your Azure Function App in <a href="#Exercise1">Exercise 1</a> and expand the list of blob containers underneath it. Then click the container named "rejected."</p>
<blockquote>
<p>If this is the first time you have run Storage Explorer, you may have to click the person icon and tell it which Azure subscription or subscriptions you want it to display.</p>
</blockquote>
<p><a href="Images/explorer-open-rejected-container.png" target="_blank"><img src="Images/explorer-open-rejected-container.png" alt="Opening the "rejected" container" style="max-width:100%;"></a></p>
<p><em>Opening the "rejected" container</em></p>
</li>
<li>
<p>Right-click (on a Mac, Command-click) the image in the "rejected" container and select <strong>Properties</strong> from the context menu.</p>
<p><a href="Images/explorer-view-blob-metadata.png" target="_blank"><img src="Images/explorer-view-blob-metadata.png" alt="Viewing blob metadata" style="max-width:100%;"></a></p>
<p><em>Viewing blob metadata</em></p>
</li>
<li>
<p>Inspect the blob's metadata. <em>IsAdultContent</em> and <em>isRacyContent</em> are Boolean values that indicate whether the Computer Vision API detected adult or racy content in the image. <em>adultScore</em> and <em>racyScore</em> are the computed probabilities.</p>
<p><a href="Images/explorer-metadata-values.png" target="_blank"><img src="Images/explorer-metadata-values.png" alt="Scores returned by the Computer Vision API" style="max-width:100%;"></a></p>
<p><em>Scores returned by the Computer Vision API</em></p>
</li>
<li>
<p>Open the "accepted" container and inspect the metadata for some of the blobs stored there. How do these metadata values differ from the ones attached to the blob in the "rejected" container?</p>
</li>
</ol>
<p>You can probably imagine how this might be used in the real world. Suppose you were building a photo-sharing site and wanted to prevent adult images from being stored. You could easily write an Azure Function that inspects each image that is uploaded and deletes it from storage if it contains adult content.</p>
<p><a name="Summary"></a></p>
<h2>Summary</h2>
<p>In this hands-on lab you learned how to:</p>
<ul>
<li>Create an Azure Function App</li>
<li>Write an Azure Function that uses a blob trigger</li>
<li>Add application settings to an Azure Function App</li>
<li>Use Microsoft Cognitive Services to analyze images and store the results in blob metadata</li>
</ul>
<p>This is just one example of how you can leverage Azure Functions to automate repetitive tasks. Experiment with other Azure Function templates to learn more about Azure Functions and to identify additional ways in which they can aid your research or business.</p>
<hr>
<p>Copyright 2016 Microsoft Corporation. All rights reserved. Except where otherwise noted, these materials are licensed under the terms of the MIT License. You may use them according to the license as is most appropriate for your project. The terms of this license can be found at <a href="https://opensource.org/licenses/MIT">https://opensource.org/licenses/MIT</a>.</p>
</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->