├── .gitignore
├── AzureFunctionConsumer.sln
├── AzureFunctionConsumer
├── AzureFunctionConsumer.csproj
├── Program.cs
└── c-sharp.ico
├── README.md
├── images
├── AFC001.PNG
├── AFC002.PNG
├── AFC003.PNG
├── AFC004.PNG
├── AFC005.PNG
└── AFC006.PNG
└── labFiles
├── AppService.Dev.Specialist.Functions.1
├── README.md
└── azuredeploy.json
├── AppService.Dev.Specialist.Functions.2
├── README.md
└── azuredeploy.json
├── AppService.Dev.Specialist.Functions.3
├── README.md
└── azuredeploy.json
├── AppService.Dev.Specialist.Functions.4
├── README.md
└── azuredeploy.json
├── AppService.Dev.Specialist.Functions.5
├── README.md
└── azuredeploy.json
├── README.md
├── functionApp.json
└── source
├── x.cs
├── y.cs
└── z.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/AzureFunctionConsumer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2035
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureFunctionConsumer", "AzureFunctionConsumer\AzureFunctionConsumer.csproj", "{EE6C9F3A-3CBD-413F-AAC7-4D2DC334C8D1}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {EE6C9F3A-3CBD-413F-AAC7-4D2DC334C8D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {EE6C9F3A-3CBD-413F-AAC7-4D2DC334C8D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {EE6C9F3A-3CBD-413F-AAC7-4D2DC334C8D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {EE6C9F3A-3CBD-413F-AAC7-4D2DC334C8D1}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {44D1541A-3E5C-4823-990D-C5AB2FBAA291}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/AzureFunctionConsumer/AzureFunctionConsumer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | c-sharp.ico
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/AzureFunctionConsumer/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Text;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using System.Collections.Generic;
7 |
8 | using static System.Console;
9 | using static System.Convert;
10 |
11 | using Microsoft.Azure.Documents;
12 | using Microsoft.Azure.Documents.Client;
13 | using Microsoft.Azure.EventHubs;
14 | using Microsoft.Azure.ServiceBus;
15 |
16 | using Microsoft.WindowsAzure.Storage;
17 | using Microsoft.WindowsAzure.Storage.Blob;
18 | using Microsoft.WindowsAzure.Storage.Queue;
19 | using Microsoft.WindowsAzure.Storage.Table;
20 |
21 | using Newtonsoft.Json;
22 | using System.Linq;
23 |
24 | namespace AzureFunctionConsumer
25 | {
26 | class Program
27 | {
28 | private static CloudBlobClient blobClient;
29 | private static CloudQueue storageAccountQueue;
30 | private static EventHubClient eventHubClient;
31 | private static HttpClient httpClient;
32 | private static IQueueClient queueClient;
33 | private static DocumentClient documentClient;
34 | private static CloudTableClient tableStorageClient;
35 | static Program()
36 | {
37 | httpClient = new HttpClient();
38 | }
39 | static void Main(string[] args)
40 | {
41 | bool keepGoing = true;
42 | try
43 | {
44 | do
45 | {
46 | var selection = DisplayMenu();
47 | switch (selection)
48 | {
49 | case 1:
50 | WriteLine("You selected Event Hub.");
51 | MainEventHubAsync(args).GetAwaiter().GetResult();
52 | break;
53 | case 2:
54 | WriteLine("You selected Storage Queue.");
55 | MainStorageQueueAsync(args).GetAwaiter().GetResult();
56 | break;
57 | case 3:
58 | WriteLine("You selected Blob Storage.");
59 | MainBlobStorageAsync(args).GetAwaiter().GetResult();
60 | break;
61 | case 4:
62 | WriteLine("You selected Service Bus.");
63 | MainServiceBusAsync(args).GetAwaiter().GetResult();
64 | break;
65 | case 5:
66 | WriteLine("You selected Cosomos DB.");
67 | MainCosmosDBAsync(args).GetAwaiter().GetResult();
68 | break;
69 | case 6:
70 | WriteLine("You selected HTTP Trigger.");
71 | MainHTTPTriggerAsync(args).GetAwaiter().GetResult();
72 | break;
73 | case 7:
74 | WriteLine("You selected Event Grid.");
75 | WriteLine("NOT YET IMPLEMENTED.");
76 | break;
77 | case 8:
78 | WriteLine("You selected Table Storage.");
79 | MainStorageTableAsync(args).GetAwaiter().GetResult();
80 | break;
81 | case 9:
82 | WriteLine("You selected Microsoft Graph.");
83 | WriteLine("You need to run this one from a browser and send your AAD credentials.");
84 | break;
85 | case 10:
86 | WriteLine("You selected SendGrid.");
87 | WriteLine("NOT YET IMPLEMENTED.");
88 | break;
89 | case 11:
90 | WriteLine("You selected SignalR.");
91 | WriteLine("NOT YET IMPLEMENTED.");
92 | break;
93 | case 12:
94 | WriteLine("You selected Timer.");
95 | WriteLine("This one is triggered with using a CRON schedule.");
96 | break;
97 | case 13:
98 | WriteLine("Bye.");
99 | keepGoing = false;
100 | break;
101 | default:
102 | throw new InvalidOperationException("You entered an invalid option. Bye.");
103 | }
104 | } while (keepGoing);
105 | }
106 | catch (Exception ex)
107 | {
108 | WriteLine($"Well...something happend that wasn't expected, specifically: {ex.Message}");
109 | }
110 | }
111 | static public int DisplayMenu()
112 | {
113 | WriteLine();
114 | WriteLine("1. Event Hub");
115 | WriteLine("2. Storage Queue");
116 | WriteLine("3. Blob Storage");
117 | WriteLine("4. Service Bus");
118 | WriteLine("5. Cosomos DB");
119 | WriteLine("6. HTTP Trigger");
120 | WriteLine("7. Event Grid");
121 | WriteLine("8. Table Storage");
122 | WriteLine("9. Microsoft Graph");
123 | WriteLine("10. SendGrid");
124 | WriteLine("11. SignalR");
125 | WriteLine("12. Timer");
126 | WriteLine("13. Exit");
127 | WriteLine("Which would you like to trigger? Enter '13' to exit.");
128 | var result = ReadLine();
129 | return ToInt32(result);
130 | }
131 | #region Blob Storage
132 | //Use the Event Grid trigger instead of the Blob storage trigger for high consumption blob-only storage accounts
133 | private static async Task MainBlobStorageAsync(string[] args)
134 | {
135 | WriteLine("Enter your Blob Storage connection string:");
136 | var BlobStorageConnectionString = ReadLine();
137 | while (BlobStorageConnectionString.Length == 0)
138 | {
139 | WriteLine("Try again, this value must have a length > 0");
140 | WriteLine("Enter your Blob Storage connection string:");
141 | BlobStorageConnectionString = ReadLine();
142 | }
143 | WriteLine("Enter the Blob Container name:");
144 | var BlobContainerName = ReadLine();
145 | while (BlobContainerName.Length == 0)
146 | {
147 | WriteLine("Try again, this value must have a length > 0");
148 | WriteLine("Enter the Blob Container name: (lowercase only letters!)");
149 | BlobContainerName = ReadLine().ToLower();
150 | }
151 | CloudStorageAccount storageAccount = CloudStorageAccount.Parse(BlobStorageConnectionString);
152 | blobClient = storageAccount.CreateCloudBlobClient();
153 | CloudBlobContainer blobContainer = blobClient.GetContainerReference(BlobContainerName);
154 |
155 | WriteLine("Enter A to add blobs or S to search the blob container:");
156 | var AddOrSearch = ReadLine();
157 | while (AddOrSearch != "A" && AddOrSearch != "S")
158 | {
159 | WriteLine("Try again, this value must be either A or S");
160 | WriteLine("Enter A to add blobs or S to search the blob container:");
161 | AddOrSearch = ReadLine().ToUpper();
162 | }
163 | if (AddOrSearch == "S")
164 | {
165 | await SearchBlobsAsync(blobContainer);
166 | }
167 | else
168 | {
169 | WriteLine("Enter number of blobs to add: ");
170 | int BlobsToSend = 0;
171 | while (!int.TryParse(ReadLine(), out BlobsToSend))
172 | {
173 | WriteLine("Try again, this value must be numeric.");
174 | }
175 | WriteLine("Creating blob container...");
176 | //CloudBlobContainer blobContainer = blobClient.GetContainerReference(BlobContainerName);
177 | if (await blobContainer.CreateIfNotExistsAsync())
178 | {
179 | WriteLine($"Blob container '{blobContainer.Name}' Created.");
180 | }
181 | else
182 | {
183 | WriteLine($"Blob container '{blobContainer.Name}' Exists.");
184 | }
185 |
186 | await SendBlobsToBlobStorage(BlobsToSend, blobContainer);
187 |
188 | WriteLine("Press Y to delete the blobs.");
189 |
190 | if (ReadLine() == "Y")
191 | {
192 | await DeleteBlobs(BlobsToSend, blobContainer);
193 | }
194 | else
195 | {
196 | WriteLine("No blobs deleted.");
197 | }
198 | }
199 | }
200 | private static async Task SendBlobsToBlobStorage(int numBlobsToSend, CloudBlobContainer container)
201 | {
202 | Random r = new Random();
203 | var number = r.Next(1, 10);
204 | string blobContent = string.Empty;
205 |
206 | for (var i = 0; i < numBlobsToSend; i++)
207 | {
208 | try
209 | {
210 | var blob = $"Blob {i}";
211 | WriteLine($"Sending blob: {blob} named helloworld{i}.txt to {container.Name}");
212 | CloudBlockBlob blockBlob = container.GetBlockBlobReference($"helloworld{i}.txt");
213 |
214 | for (int n = 0; n < number; n++)
215 | {
216 | blobContent = blobContent + Guid.NewGuid().ToString("N");
217 | }
218 |
219 | await blockBlob.UploadTextAsync(blobContent);
220 | }
221 | catch (StorageException se)
222 | {
223 | WriteLine($"StorageException: {se.Message}");
224 | }
225 | catch (Exception ex)
226 | {
227 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
228 | }
229 |
230 | await Task.Delay(10);
231 | }
232 |
233 | WriteLine($"{numBlobsToSend} blobs sent.");
234 | }
235 | private static async Task DeleteBlobs(int numBlobsToDelete, CloudBlobContainer container)
236 | {
237 | for (var i = 0; i < numBlobsToDelete; i++)
238 | {
239 | try
240 | {
241 | var blob = $"Blob {i}";
242 | WriteLine($"Deleting blob: {blob} named helloworld{i}.txt from {container.Name}");
243 | CloudBlockBlob blockBlob = container.GetBlockBlobReference($"helloworld{i}.txt");
244 | await blockBlob.DeleteIfExistsAsync();
245 | }
246 | catch (StorageException se)
247 | {
248 | WriteLine($"StorageException: {se.Message}");
249 | }
250 | catch (Exception ex)
251 | {
252 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
253 | }
254 |
255 | await Task.Delay(10);
256 | }
257 |
258 | WriteLine($"{numBlobsToDelete} blobs deleted.");
259 | }
260 | private static async Task SearchBlobsAsync(CloudBlobContainer container)
261 | {
262 | if (await container.ExistsAsync())
263 | {
264 | WriteLine($"Enter the path to search, leave blank to search the entire '{container.Name}' container:");
265 | WriteLineWithGreenColor(true);
266 | WriteLine($"Ex: blobreceipts/hostId/namespace.functionName.Run");
267 | WriteLineWithGreenColor(false);
268 | var prefix = @ReadLine();
269 |
270 | WriteLine($"Enter the start date by counting the number of days from today on which the search should start.");
271 | WriteLineWithGreenColor(true);
272 | WriteLine($"Ex: 1 is {DateTime.Now.AddDays(-1)} (yesterday) and 5 is {DateTime.Now.AddDays(-5)} (5 days ago)");
273 | WriteLineWithGreenColor(false);
274 | int startDate = (ToInt32(ReadLine()) * -1);
275 |
276 | WriteLine($"Enter the end date by counting the number of days from today on which the search should end.");
277 | WriteLineWithGreenColor(true);
278 | WriteLine($"Enter 0 for now {DateTime.Now}");
279 | WriteLineWithGreenColor(false);
280 | int endDate = (ToInt32(ReadLine()) * -1);
281 | if (endDate == 0) endDate = 1; //Seems this logic didn't work when endDate was 0, but nothing can already exist which is added tomorrow...
282 |
283 | if (startDate > endDate)
284 | {
285 | WriteLine($"Start date {DateTime.Now.AddDays(startDate)} " +
286 | $"cannot come before end date {DateTime.Now.AddDays(endDate)}, start over.");
287 | return;
288 | }
289 | WriteLineWithGreenColor(true);
290 | WriteLine($"Searching '{container.Name} -> {prefix}' from {DateTime.Now.AddDays(startDate)} " +
291 | $"to {DateTime.Now.AddDays(endDate)}");
292 | WriteLineWithGreenColor(false);
293 |
294 | try
295 | {
296 | int maxResults = 500;
297 | BlobContinuationToken continuationToken = null;
298 | CloudBlob blob;
299 | IEnumerable blobList;
300 |
301 | do
302 | {
303 | BlobResultSegment resultSegment = await container.ListBlobsSegmentedAsync(prefix,
304 | true, BlobListingDetails.Metadata, maxResults, continuationToken, null, null);
305 |
306 | blobList = resultSegment.Results.OfType()
307 | .Where(b => b.Properties.Created >= DateTime.Today.AddDays(startDate) &&
308 | b.Properties.Created <= DateTime.Today.AddDays(endDate))
309 | .Select(b => b);
310 |
311 | foreach (var blobItem in blobList)
312 | {
313 | blob = (CloudBlob)blobItem;
314 | await blob.FetchAttributesAsync();
315 | WriteLine($"Blob name: {blob.Name} - last modified on {blob.Properties.LastModified}");
316 | }
317 | continuationToken = resultSegment.ContinuationToken;
318 |
319 | } while (continuationToken != null);
320 |
321 | if (blobList.Count() > 0)
322 | {
323 | WriteLine("Would you like to remove/reprocess a blob? Y/N ");
324 | var delete = ReadLine();
325 | while (delete == "Y")
326 | {
327 | //should repopulate blobList and check there are blobs to delete
328 | WriteLine("Enter the path and blob name you would like to remove/reprocess: ");
329 | WriteLineWithGreenColor(true);
330 | WriteLine($"Ex: {((CloudBlob)blobList.First()).Name}");
331 | WriteLineWithGreenColor(false);
332 | var path = ReadLine();
333 |
334 | CloudBlockBlob blockBlob = container.GetBlockBlobReference(path);
335 | await blockBlob.DeleteIfExistsAsync();
336 | WriteLine($"Deleted {path} from {container.Name}");
337 |
338 | WriteLine("Would you like to remove/reprocess another blob? Y/N ");
339 | delete = ReadLine();
340 | }
341 | }
342 | }
343 | catch (StorageException e)
344 | {
345 | WriteLine(e.Message);
346 | ReadLine();
347 | }
348 |
349 | if (container.Name.ToLower() != "azure-webjobs-hosts")
350 | {
351 | WriteLineWithYellowColor(true);
352 | WriteLine($"NOTE: you searched '{container.Name} -> {prefix}'. You need to search in the " +
353 | "azure-webjobs-hosts container if you want to reprocess a blob.");
354 | WriteLineWithYellowColor(false);
355 | }
356 | }
357 | else
358 | {
359 | WriteLine($"Blob container '{container.Name}' doesn't exist. Please start over.");
360 | }
361 | }
362 | public static void WriteLineWithGreenColor(bool enable)
363 | {
364 | if (enable)
365 | {
366 | Console.BackgroundColor = ConsoleColor.DarkGreen;
367 | Console.ForegroundColor = ConsoleColor.Black;
368 | }
369 | else
370 | {
371 | Console.ResetColor();
372 | }
373 | }
374 | public static void WriteLineWithYellowColor(bool enable)
375 | {
376 | if (enable)
377 | {
378 | Console.BackgroundColor = ConsoleColor.DarkYellow;
379 | Console.ForegroundColor = ConsoleColor.Black;
380 | }
381 | else
382 | {
383 | Console.ResetColor();
384 | }
385 | }
386 | #endregion
387 | #region Event Hubs
388 | private static async Task MainEventHubAsync(string[] args)
389 | {
390 | WriteLine("Enter your Event Hub connection string:");
391 | var EventHubConnectionString = ReadLine();
392 | while (EventHubConnectionString.Length == 0)
393 | {
394 | WriteLine("Try again, this value must have a length > 0");
395 | WriteLine("Enter your Event Hub connection string:");
396 | EventHubConnectionString = ReadLine();
397 | }
398 | WriteLine("Enter your Event Hub name:");
399 | var EventHubName = ReadLine();
400 | while (EventHubName.Length == 0)
401 | {
402 | WriteLine("Try again, this value must have a length > 0");
403 | WriteLine("Enter your Event Hub name:");
404 | EventHubName = ReadLine();
405 | }
406 | WriteLine("Enter number of events to add: ");
407 | int EventHubEventsToSend = 0;
408 | while (!int.TryParse(ReadLine(), out EventHubEventsToSend))
409 | {
410 | WriteLine("Try again, this value must be numeric.");
411 | }
412 |
413 | var connectionStringBuilder = new EventHubsConnectionStringBuilder(EventHubConnectionString)
414 | {
415 | EntityPath = EventHubName
416 | };
417 | eventHubClient = EventHubClient.CreateFromConnectionString(connectionStringBuilder.ToString());
418 | await SendMessagesToEventHub(EventHubEventsToSend);
419 | await eventHubClient.CloseAsync();
420 | }
421 | private static async Task SendMessagesToEventHub(int numMessagesToSend)
422 | {
423 | for (var i = 0; i < numMessagesToSend; i++)
424 | {
425 | try
426 | {
427 | var message = $"Message-{i}";
428 |
429 | var json = "{\"message\":\"" + message + "\"}";
430 | WriteLine($"Sending message: {json}");
431 | await eventHubClient.SendAsync(new EventData(Encoding.UTF8.GetBytes(json)));
432 | }
433 | catch (EventHubsCommunicationException ehce)
434 | {
435 | WriteLine($"EventHubsCommunicationException: {ehce.Message}");
436 | }
437 | catch (EventHubsException ehe)
438 | {
439 | WriteLine($"EventHubsException: {ehe.Message}");
440 | }
441 | catch (Exception ex)
442 | {
443 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
444 | }
445 |
446 | await Task.Delay(10);
447 | }
448 |
449 | WriteLine($"{numMessagesToSend} messages sent.");
450 | }
451 | #endregion
452 | #region Azure Queue
453 | private static async Task MainStorageQueueAsync(string[] args)
454 | {
455 | WriteLine("Enter your Storage Queue connection string:");
456 | var StorageQueueConnectionString = ReadLine();
457 | while (StorageQueueConnectionString.Length == 0)
458 | {
459 | WriteLine("Try again, this value must have a length > 0");
460 | WriteLine("Enter your Storage Queue connection string:");
461 | StorageQueueConnectionString = ReadLine();
462 | }
463 | WriteLine("Enter your Storage Queue name:");
464 | var StorageQueueName = ReadLine();
465 | while (StorageQueueName.Length == 0)
466 | {
467 | WriteLine("Try again, this value must have a length > 0");
468 | WriteLine("Enter your Storage Queue name:");
469 | StorageQueueName = ReadLine();
470 | }
471 | WriteLine("Enter number of messages to add: ");
472 | int StorageMessagesToSend = 0;
473 | while (!int.TryParse(ReadLine(), out StorageMessagesToSend))
474 | {
475 | WriteLine("Try again, this value must be numeric.");
476 | }
477 | CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageQueueConnectionString);
478 | CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
479 | storageAccountQueue = queueClient.GetQueueReference(StorageQueueName);
480 | if (await storageAccountQueue.CreateIfNotExistsAsync())
481 | {
482 | WriteLine($"Queue '{storageAccountQueue.Name}' Created.");
483 | }
484 | else
485 | {
486 | WriteLine($"Queue '{storageAccountQueue.Name}' Exists.");
487 | }
488 | await SendMessagesToStorageAccount(StorageMessagesToSend);
489 | }
490 | private static async Task SendMessagesToStorageAccount(int numMessagesToSend)
491 | {
492 | for (var i = 0; i < numMessagesToSend; i++)
493 | {
494 | try
495 | {
496 | var message = $"Message {i} - {Guid.NewGuid().ToString()}";
497 | WriteLine($"Sending message: {message}");
498 | await storageAccountQueue.AddMessageAsync(new CloudQueueMessage(message));
499 | }
500 | catch (StorageException se)
501 | {
502 | WriteLine($"StorageException: {se.Message}");
503 | }
504 | catch (Exception ex)
505 | {
506 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
507 | }
508 |
509 | await Task.Delay(10);
510 | }
511 |
512 | WriteLine($"{numMessagesToSend} messages sent.");
513 | }
514 | #endregion
515 | #region HTTP Trigger
516 | private static async Task MainHTTPTriggerAsync(string[] args)
517 | {
518 | WriteLine("Enter your HTTP Trigger URL:");
519 | var HTTPTriggerUrl = ReadLine();
520 | while (HTTPTriggerUrl.Length == 0)
521 | {
522 | WriteLine("Try again, this value must have a length > 0");
523 | WriteLine("Enter your HTTP Trigger URL:");
524 | HTTPTriggerUrl = ReadLine();
525 | }
526 | WriteLine("Enter HTTP Trigger function key or enter: anonymous");
527 | var HTTPTriggerFunctionKey = ReadLine();
528 | while (HTTPTriggerFunctionKey.Length == 0)
529 | {
530 | WriteLine("Try again, this value must have a length > 0");
531 | WriteLine("Enter HTTP Trigger function key:");
532 | HTTPTriggerFunctionKey = ReadLine();
533 | }
534 | WriteLine("Enter POST or GET:");
535 | var Method = ReadLine();
536 | while (Method.Length == 0)
537 | {
538 | WriteLine("Try again, this value must have a length > 0");
539 | WriteLine("Enter POST or GET:");
540 | HTTPTriggerFunctionKey = ReadLine();
541 | }
542 | string Name = String.Empty;
543 | if (Method == "POST")
544 | {
545 | WriteLine("Enter your name or any name:");
546 | Name = ReadLine();
547 | while (Name.Length == 0)
548 | {
549 | WriteLine("Try again, this value must have a length > 0");
550 | WriteLine("Enter any value for Query String usage:");
551 | Name = ReadLine();
552 | }
553 | }
554 | WriteLine("Enter number of requests to send: ");
555 | int HTTPRequestsToSend = 0;
556 | while (!int.TryParse(ReadLine(), out HTTPRequestsToSend))
557 | {
558 | WriteLine("Try again, this value must be numeric.");
559 | }
560 | await SendHTTPRequests(HTTPRequestsToSend, HTTPTriggerUrl, HTTPTriggerFunctionKey, Method, Name);
561 | }
562 | private static async Task SendHTTPRequests(int numHTTPRequestsToSend, string Url, string FunctionKey, string Method, string Name)
563 | {
564 | HttpResponseMessage response = new HttpResponseMessage();
565 | for (var i = 0; i < numHTTPRequestsToSend; i++)
566 | {
567 | try
568 | {
569 | var httpRequest = $"HTTP request {i}";
570 | WriteLine($"Sending request: {httpRequest}");
571 |
572 | if (Method == "POST")
573 | {
574 | var json = "{\"name\":\"" + Name + "\"}";
575 | var encodedContent = new StringContent(json, Encoding.UTF8, "application/json");
576 | if (FunctionKey == "anonymous")
577 | {
578 | response = await httpClient.PostAsync(Url, encodedContent).ConfigureAwait(false);
579 | }
580 | else
581 | {
582 | response = await httpClient.PostAsync(Url + "?code=" + FunctionKey, encodedContent).ConfigureAwait(false);
583 | }
584 | }
585 | else if (Method == "GET")
586 | {
587 | WriteLine("If you execute an anonymous GET, then the number of sent requests will burst 10 concurrent requests that many times.");
588 | if (FunctionKey == "anonymous")
589 | {
590 | response = await httpClient.GetAsync(Url);
591 | for (int t = 0; t < 10; t++)
592 | {
593 | Thread rThread = new Thread(() => httpClient.GetAsync(Url));
594 | rThread.Start();
595 | //This is a burst mode code segment, be careful if you do this, costs, ddos and latency
596 | //if (t % 10 == 0)
597 | //{
598 | // System.Threading.Thread.Sleep(2000);
599 | // WriteLine($"Sleeping...");
600 | //}
601 | WriteLine($"Thread {t} is sending an anonymous GET to {Url} now: {DateTime.Now}");
602 | }
603 | }
604 | else
605 | {
606 | response = await httpClient.GetAsync(Url + "?code=" + FunctionKey);
607 | }
608 | }
609 | else
610 | {
611 | throw new Exception("Method must be POST or GET");
612 | }
613 |
614 | WriteLine($"The response code is: {response.StatusCode}");
615 | response.EnsureSuccessStatusCode();
616 | var resultContent = await response.Content.ReadAsStringAsync();
617 | WriteLine(resultContent);
618 | }
619 | catch (HttpRequestException hre)
620 | {
621 | WriteLine($"HttpRequestException: {hre.Message}");
622 | }
623 | catch (Exception ex)
624 | {
625 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
626 | }
627 |
628 | await Task.Delay(10);
629 | }
630 |
631 | WriteLine($"{numHTTPRequestsToSend} requests sent.");
632 | }
633 | #endregion
634 | #region Service Bus
635 | private static async Task MainServiceBusAsync(string[] args)
636 | {
637 | WriteLine("Enter your Service Bus connection string:");
638 | var ServiceBusConnectionString = ReadLine();
639 | while (ServiceBusConnectionString.Length == 0)
640 | {
641 | WriteLine("Try again, this value must have a length > 0");
642 | WriteLine("Enter your Service Bus connection string:");
643 | ServiceBusConnectionString = ReadLine();
644 | }
645 | WriteLine("Enter your Queue name:");
646 | var QueueName = ReadLine();
647 | while (QueueName.Length == 0)
648 | {
649 | WriteLine("Try again, this value must have a length > 0");
650 | WriteLine("Enter yourQueue name:");
651 | QueueName = ReadLine();
652 | }
653 | WriteLine("Enter number of messages to add: ");
654 | int ServiceBusMessagesToSend = 0;
655 | while (!int.TryParse(ReadLine(), out ServiceBusMessagesToSend))
656 | {
657 | WriteLine("Try again, this value must be numeric.");
658 | }
659 | queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
660 | await SendMessagesToServicebus(ServiceBusMessagesToSend);
661 | await queueClient.CloseAsync();
662 | }
663 | private static async Task SendMessagesToServicebus(int numMessagesToSend)
664 | {
665 | for (var i = 0; i < numMessagesToSend; i++)
666 | {
667 | try
668 | {
669 | var messageBody = $"Message-{i}-{Guid.NewGuid().ToString("N")}-{DateTime.Now.Minute}";
670 | WriteLine($"Sending message: {messageBody}");
671 | var message = new Message(Encoding.UTF8.GetBytes(messageBody));
672 | await queueClient.SendAsync(message);
673 | }
674 | catch (ServiceBusTimeoutException sbte)
675 | {
676 | WriteLine($"ServiceBusTimeoutException: {sbte.Message}");
677 | }
678 | catch (ServiceBusException sbe)
679 | {
680 | WriteLine($"ServiceBusException: {sbe.Message}");
681 | }
682 | catch (Exception ex)
683 | {
684 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
685 | }
686 |
687 | await Task.Delay(10);
688 | }
689 |
690 | WriteLine($"{numMessagesToSend} messages sent.");
691 | }
692 | #endregion
693 | #region CosmosDB
694 | private static async Task MainCosmosDBAsync(string[] args)
695 | {
696 | WriteLine("Enter your Cosmos database name:");
697 | var CosmosDatabaseName = ReadLine();
698 | while (CosmosDatabaseName.Length == 0)
699 | {
700 | WriteLine("Try again, this value must have a length > 0");
701 | WriteLine("Enter your Cosmos database name:");
702 | CosmosDatabaseName = ReadLine();
703 | }
704 | WriteLine("Enter your Cosmos collection name:");
705 | var CosmosCollectionName = ReadLine();
706 | while (CosmosCollectionName.Length == 0)
707 | {
708 | WriteLine("Try again, this value must have a length > 0");
709 | WriteLine("Enter your Cosmos collection name:");
710 | CosmosCollectionName = ReadLine();
711 | }
712 | WriteLine("Enter your Cosmos endpoint url name:");
713 | var CosmosEndpointUrl = ReadLine();
714 | while (CosmosEndpointUrl.Length == 0)
715 | {
716 | WriteLine("Try again, this value must have a length > 0");
717 | WriteLine("Enter your Cosmos endpoint url name:");
718 | CosmosEndpointUrl = ReadLine();
719 | }
720 | WriteLine("Enter your Cosmos account key:");
721 | var CosmosAccountKey = ReadLine();
722 | while (CosmosAccountKey.Length == 0)
723 | {
724 | WriteLine("Try again, this value must have a length > 0");
725 | WriteLine("Enter your Cosmos account key:");
726 | CosmosAccountKey = ReadLine();
727 | }
728 | WriteLine("Enter number of documents to add: ");
729 | int CosmosDocumentsToSend = 0;
730 | while (!int.TryParse(ReadLine(), out CosmosDocumentsToSend))
731 | {
732 | WriteLine("Try again, this value must be numeric.");
733 | }
734 |
735 | try
736 | {
737 | using (documentClient = new DocumentClient(new Uri(CosmosEndpointUrl), CosmosAccountKey))
738 | {
739 | Uri collectionUri = UriFactory.CreateDocumentCollectionUri(CosmosDatabaseName, CosmosCollectionName);
740 | await SendDocumentsToCosmos(CosmosDocumentsToSend, collectionUri);
741 | WriteLine($"Press Y to delete **ALL** documents in the '{CosmosCollectionName}' container.");
742 | if (ReadLine() == "Y")
743 | {
744 | await DeleteCosmosDocuments(CosmosDatabaseName, CosmosCollectionName, collectionUri);
745 | }
746 | else
747 | {
748 | WriteLine("No documents deleted.");
749 | }
750 | }
751 | }
752 | catch (DocumentClientException dce)
753 | {
754 | WriteLine($"{dce.StatusCode} error occurred: {dce.Message}");
755 | }
756 | catch (Exception ex)
757 | {
758 | WriteLine($"Error occurred: {ex.Message}");
759 | }
760 | }
761 | private static async Task SendDocumentsToCosmos(int numDocumentsToSend, Uri collectionUri)
762 | {
763 | Random r = new Random();
764 |
765 | for (var i = 0; i < numDocumentsToSend; i++)
766 | {
767 | try
768 | {
769 | var Id = r.Next(1, 2147483647).ToString();
770 | CosmosDocument cosmosDocument = CreateCosmosDocument(Id);
771 | WriteLine($"Sending document with Id = : {Id}");
772 | await documentClient.CreateDocumentAsync(collectionUri, cosmosDocument);
773 | }
774 | catch (DocumentClientException dce)
775 | {
776 | WriteLine($"{dce.StatusCode} error occurred: {dce.Message}");
777 | }
778 | catch (Exception ex)
779 | {
780 | WriteLine($"Error occurred: {ex.Message}");
781 | }
782 |
783 | await Task.Delay(10);
784 | }
785 |
786 | WriteLine($"{numDocumentsToSend} documents sent.");
787 | }
788 | private static async Task DeleteCosmosDocuments(string cosmosDatabaseName, string cosmosCollectionName, Uri collectionUri)
789 | {
790 | FeedOptions fe = new FeedOptions();
791 | var documents =
792 | from d in documentClient.CreateDocumentQuery(collectionUri, fe).ToList()
793 | select d;
794 |
795 | int i = 0;
796 | foreach (var item in documents)
797 | {
798 | try
799 | {
800 | ResourceResponse response = await documentClient.DeleteDocumentAsync(
801 | UriFactory.CreateDocumentUri(cosmosDatabaseName, cosmosCollectionName, item.Id),
802 | new RequestOptions() { PartitionKey = new PartitionKey(item.Id) }); //Undefined.Value
803 |
804 | WriteLine($"Deleted document with Id: {item.Id}");
805 | }
806 | catch (DocumentClientException dce)
807 | {
808 | if (dce.StatusCode == System.Net.HttpStatusCode.NotFound)
809 | {
810 | WriteLine($"The Partition Key for the container must be '/id' for the delete to work.");
811 | }
812 | else
813 | {
814 | WriteLine($"{dce.StatusCode} error occurred: {dce.Message}");
815 | }
816 | }
817 | catch (Exception ex)
818 | {
819 | WriteLine($"Error occurred: {ex.Message}");
820 | }
821 | await Task.Delay(10);
822 | i++;
823 | }
824 | WriteLine($"{i.ToString()} documents deleted.");
825 | }
826 | private static CosmosDocument CreateCosmosDocument(string documentId)
827 | {
828 | CosmosDocument cosmosDocument = new CosmosDocument()
829 | {
830 | Id = documentId,
831 | CreateDate = DateTime.Now,
832 | AccountNumber = $"Account{documentId}",
833 | Freight = 472.3108m,
834 | TotalDue = 985.018m,
835 | Items = new CosmosDocumentDetail[]
836 | {
837 | new CosmosDocumentDetail
838 | {
839 | OrderQty = ToInt32(documentId),
840 | ProductId = ToInt32(documentId) + ToInt32(documentId),
841 | UnitPrice = 419.4589m
842 | }
843 | },
844 | };
845 | return cosmosDocument;
846 | }
847 | public class CosmosDocument
848 | {
849 | [JsonProperty(PropertyName = "id")]
850 | public string Id { get; set; }
851 | public DateTime CreateDate { get; set; }
852 | public string AccountNumber { get; set; }
853 | public decimal Freight { get; set; }
854 | public decimal TotalDue { get; set; }
855 | public CosmosDocumentDetail[] Items { get; set; }
856 | }
857 | public class CosmosDocumentDetail
858 | {
859 | public int OrderQty { get; set; }
860 | public int ProductId { get; set; }
861 | public decimal UnitPrice { get; set; }
862 | }
863 | #endregion
864 | #region Azure Table
865 | private static async Task MainStorageTableAsync(string[] args)
866 | {
867 | WriteLine("Enter your Table Storage connection string:");
868 | var TableStorageConnectionString = ReadLine();
869 | while (TableStorageConnectionString.Length == 0)
870 | {
871 | WriteLine("Try again, this value must have a length > 0");
872 | WriteLine("Enter your Table Storage connection string:");
873 | TableStorageConnectionString = ReadLine();
874 | }
875 | WriteLine("Enter your Table name:");
876 | var TableName = ReadLine();
877 | while (TableName.Length == 0)
878 | {
879 | WriteLine("Try again, this value must have a length > 0");
880 | WriteLine("Enter your Table name:");
881 | TableName = ReadLine();
882 | }
883 | WriteLine("Enter number of rows to add: ");
884 | int StorageRowsToSend = 0;
885 | while (!int.TryParse(ReadLine(), out StorageRowsToSend))
886 | {
887 | WriteLine("Try again, this value must be numeric.");
888 | }
889 | CloudStorageAccount TableStorageAccount = CloudStorageAccount.Parse(TableStorageConnectionString);
890 | tableStorageClient = TableStorageAccount.CreateCloudTableClient();
891 | CloudTable table = tableStorageClient.GetTableReference(TableName);
892 | if (await table.CreateIfNotExistsAsync())
893 | {
894 | WriteLine($"Queue '{table.Name}' Created.");
895 | }
896 | else
897 | {
898 | WriteLine($"Queue '{table.Name}' Exists.");
899 | }
900 | await SendMessagesToTableStorageAccount(StorageRowsToSend, table);
901 |
902 | WriteLine($"Press Y to delete table {TableName}.");
903 |
904 | if (ReadLine() == "Y")
905 | {
906 | await DeleteTableRows(table);
907 | }
908 | else
909 | {
910 | WriteLine("No rows deleted.");
911 | }
912 | }
913 | private static async Task SendMessagesToTableStorageAccount(int numRowToInsert, CloudTable table)
914 | {
915 | var pKey = Guid.NewGuid().ToString();
916 |
917 | for (var i = 0; i < numRowToInsert; i++)
918 | {
919 | try
920 | {
921 | var row = $"Row #{i} - {Guid.NewGuid().ToString()}";
922 |
923 | TableStorageRowEntity tsre = new TableStorageRowEntity(pKey, Guid.NewGuid().ToString())
924 | { message = row, dateTime = DateTime.Now.ToString() };
925 |
926 | TableOperation insertOperation = TableOperation.InsertOrMerge(tsre);
927 | TableResult result = table.ExecuteAsync(insertOperation).Result;
928 | WriteLine($"Inserted: {row}");
929 | }
930 | catch (StorageException se)
931 | {
932 | WriteLine($"StorageException: {se.Message}");
933 | }
934 | catch (Exception ex)
935 | {
936 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
937 | }
938 |
939 | await Task.Delay(10);
940 | }
941 |
942 | WriteLine($"{numRowToInsert} rows inserted.");
943 | }
944 | private static async Task DeleteTableRows(CloudTable table)
945 | {
946 | int counter = 0;
947 | try
948 | {
949 | TableContinuationToken token = null;
950 | TableQuery query = new TableQuery()
951 | .Select(new List { "PartitionKey" });
952 | var rows = await table.ExecuteQuerySegmentedAsync(query, token);
953 |
954 | foreach (var item in rows)
955 | {
956 | WriteLine($"Deleting row: {item.RowKey} ");
957 | TableOperation deleteOperation = TableOperation.Delete(item);
958 | TableResult result = await table.ExecuteAsync(deleteOperation);
959 | counter++;
960 | }
961 | }
962 | catch (StorageException se)
963 | {
964 | WriteLine($"StorageException: {se.Message}");
965 | }
966 | catch (Exception ex)
967 | {
968 | WriteLine($"{DateTime.Now} > Exception: {ex.Message}");
969 | }
970 |
971 | await Task.Delay(10);
972 |
973 | WriteLine($"{counter.ToString()} rows deleted.");
974 | }
975 | #endregion
976 | }
977 | public class TableStorageRowEntity : TableEntity
978 | {
979 | public string message { get; set; }
980 | public string dateTime { get; set; }
981 |
982 | public TableStorageRowEntity() { }
983 |
984 | public TableStorageRowEntity(string pKey, string rKey)
985 | {
986 | PartitionKey = pKey;
987 | RowKey = rKey;
988 | }
989 | }
990 | }
991 |
--------------------------------------------------------------------------------
/AzureFunctionConsumer/c-sharp.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/AzureFunctionConsumer/c-sharp.ico
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Azure Function Consumer
2 | The Azure Function Consumer is a .NET Core console app to consume Azure Functions. This program let's you send blobs, messages or documents to Azure resource which are bound to an Azure Function.
3 |
4 | There are Labs available [here][LINK1] that will walk you through the **creation** of Azure Functions bound to numerous Azure products. When you send data to an Azure product (i.e. Azure Blob Storage, Event Hub, Service Bus, etc.) that is bound to an Azure Function, the Azure Function is triggered and the code within your Azure Function is executed.
5 |
6 | Sometimes the code doesn't work as expected, here are some labs which can help you learn how to **debug, troubleshoot and resolve** badly behaving Azure Functions issues.
7 |
8 | The Azure Function consumer requires the dotnet runtime, which can be downoaded from [here][LINK2] and configured following the provided instructions.
9 |
10 | To see which version of the .NET Core runtime you have installed, enter **dotnet --version**
11 |
12 | 
13 |
14 | Start the program by runnung: **dotnet AzureFunctionConsumer.dll**
15 |
16 | 
17 |
18 | The Azure Function Consumer currently supports sending data transmissions to:
19 | + Event Hub
20 | + Storage Queue
21 | + Blob Storage
22 | + Service Bus Queue
23 | + Cosmos DB
24 | + HTTP Trigger
25 |
26 | ## Download the Azure Function Consumer program ##
27 | Download the AzureFunctionConsumer.dll and its dependencies from [here][LINK3]. Take the most current version.
28 |
29 | ## Examples ##
30 | The following examples illustrate how to trigger an HTTP, an Event Hub, Storage Queue and an Azure Cosmos DB bound Azure Function
31 |
32 | ### HTTP Triggered Azure Function ###
33 | 
34 |
35 | ### Event Hub Triggered Function ###
36 | 
37 |
38 | ### Storge Queue Triggered Function ###
39 | 
40 |
41 | ### Azure Cosmos DB Triggered Function ###
42 | 
43 |
44 | ## Report an issue or have an improvment idea ##
45 | If you experience an unexpected exceptiion or have an awkward experience, log it [here][LINK4]. If you would like to recommend a new feature log it [here][LINK4] as well.
46 |
47 | [LINK1]: https://www.thebestcsharpprogrammerintheworld.com/2020/07/06/azure-functions-labs-information-and-setup-instructions/
48 | [LINK2]: https://github.com/dotnet/core
49 | [LINK3]: https://github.com/benperk/AzureFunctionConsumer/releases
50 | [LINK4]: https://github.com/benperk/AzureFunctionConsumer/issues
51 |
--------------------------------------------------------------------------------
/images/AFC001.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/images/AFC001.PNG
--------------------------------------------------------------------------------
/images/AFC002.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/images/AFC002.PNG
--------------------------------------------------------------------------------
/images/AFC003.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/images/AFC003.PNG
--------------------------------------------------------------------------------
/images/AFC004.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/images/AFC004.PNG
--------------------------------------------------------------------------------
/images/AFC005.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/images/AFC005.PNG
--------------------------------------------------------------------------------
/images/AFC006.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benperk/AzureFunctionConsumer/30df577d303e469a98f3fd3ec4d02eb1460fd1dd/images/AFC006.PNG
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.1/README.md:
--------------------------------------------------------------------------------
1 | # AppService.Dev.Specialist.Functions.1
2 | ARM Template for deploying lab for AppService DEV Specialist Certification - Azure Functions Lab #1
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fbenperk%2FAzureFunctionConsumer%2Fmaster%2FlabFiles%2FAppService.Dev.Specialist.Functions.1%2Fazuredeploy.json)
5 |
6 | ## This arm deployment will provision:
7 |
8 | 1. An Azure Storage Account with a container named elx
9 | 2. An Azure Function Dynamic App Sevice Plan
10 | 3. An Azure Function App
11 | 4. An Application Insights Resource
12 | 5. Uses the MSDEPLOY extension to deploy Function source code
13 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.1/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "appName": {
6 | "type": "string",
7 | "defaultValue": "[concat('elx', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the function app that you wish to create."
10 | }
11 | },
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "[resourceGroup().location]"
15 | },
16 | "runtime": {
17 | "type": "string",
18 | "defaultValue": "dotnet"
19 | },
20 | "msdeployPackageUrl": {
21 | "defaultValue": "https://csharpguitar.blob.core.windows.net/elx/csharpguitar-elx.zip",
22 | "type": "string"
23 | }
24 | },
25 | "variables": {
26 | "functionAppName": "[parameters('appName')]",
27 | "hostingPlanName": "[parameters('appName')]",
28 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'elx')]",
29 | "applicationInsightsName": "[parameters('appName')]",
30 | "functionWorkerRuntime": "[parameters('runtime')]",
31 | "storageContainer": "[concat('default/', 'elx')]"
32 | },
33 | "resources": [
34 | {
35 | "type": "Microsoft.Storage/storageAccounts",
36 | "name": "[variables('storageAccountName')]",
37 | "apiVersion": "2021-02-01",
38 | "location": "[parameters('location')]",
39 | "kind": "Storage",
40 | "sku": {
41 | "name": "Standard_LRS"
42 | },
43 | "resources": [
44 | {
45 | "type": "blobServices/containers",
46 | "apiVersion": "2021-02-01",
47 | "name": "[variables('storageContainer')]",
48 | "dependsOn": [
49 | "[variables('storageAccountName')]"
50 | ]
51 | }
52 | ]
53 | },
54 | {
55 | "type": "Microsoft.Web/serverfarms",
56 | "apiVersion": "2020-12-01",
57 | "name": "[variables('hostingPlanName')]",
58 | "location": "[parameters('location')]",
59 | "sku": {
60 | "name": "Y1",
61 | "tier": "Dynamic"
62 | },
63 | "properties": {
64 | "name": "[variables('hostingPlanName')]",
65 | "computeMode": "Dynamic"
66 | }
67 | },
68 | {
69 | "type": "microsoft.insights/components",
70 | "apiVersion": "2020-02-02-preview",
71 | "name": "[variables('applicationInsightsName')]",
72 | "location": "[parameters('location')]",
73 | "tags": {
74 | "[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
75 | },
76 | "properties": {
77 | "ApplicationId": "[variables('applicationInsightsName')]",
78 | "Request_Source": "IbizaWebAppExtensionCreate"
79 | }
80 | },
81 | {
82 | "apiVersion": "2020-12-01",
83 | "type": "Microsoft.Web/sites",
84 | "name": "[variables('functionAppName')]",
85 | "location": "[parameters('location')]",
86 | "kind": "functionapp",
87 | "dependsOn": [
88 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
89 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
90 | ],
91 | "properties": {
92 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
93 | "siteConfig": {
94 | "appSettings": [
95 | {
96 | "name": "BLOB_FUNCTION_GO",
97 | "value": "true"
98 | },
99 | {
100 | "name": "AzureWebJobsStorage",
101 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
102 | },
103 | {
104 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
105 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
106 | },
107 | {
108 | "name": "WEBSITE_CONTENTSHARE",
109 | "value": "[toLower(variables('functionAppName'))]"
110 | },
111 | {
112 | "name": "FUNCTIONS_EXTENSION_VERSION",
113 | "value": "~3"
114 | },
115 | {
116 | "name": "BLOB_CONNECTION",
117 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=x', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
118 | },
119 | {
120 | "name": "TIMER_FUNCTION_GO",
121 | "value": "true"
122 | },
123 | {
124 | "name": "AzureFunctionsWebHost__hostid",
125 | "value": "[concat(variables('functionAppName'), '-hostid')]"
126 | },
127 | {
128 | "name": "AzureWebJobsDisableHomepage",
129 | "value": "true"
130 | },
131 | {
132 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
133 | "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]"
134 | },
135 | {
136 | "name": "FUNCTIONS_WORKER_RUNTIME",
137 | "value": "[variables('functionWorkerRuntime')]"
138 | }
139 | ]
140 | }
141 | },
142 | "resources": [
143 | {
144 | "name": "MSDeploy",
145 | "type": "Extensions",
146 | "apiVersion": "2018-11-01",
147 | "dependsOn": [
148 | "[resourceId('Microsoft.Web/Sites', variables('functionAppName'))]"
149 | ],
150 | "properties": {
151 | "packageUri": "[parameters('msdeployPackageUrl')]"
152 | }
153 | },
154 | {
155 | "apiVersion": "2015-08-01",
156 | "name": "appsettings",
157 | "type": "config",
158 | "dependsOn": [
159 | "[concat('Microsoft.Web/Sites/', variables('functionAppName'))]",
160 | "[concat('Microsoft.Web/Sites/', variables('functionAppName'), '/Extensions/MSDeploy')]",
161 | "[concat('microsoft.insights/components/', variables('applicationInsightsName'))]"
162 | ],
163 | "properties": {
164 | "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]",
165 | "AzureFunctionsWebHost__hostid": "[concat(variables('functionAppName'), '-hostid')]",
166 | "AzureWebJobsDisableHomepage": "true",
167 | "BLOB_CONNECTION": "[concat('DefaultEndpointsProtocol=https;AccountName=x', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]",
168 | "TIMER_FUNCTION_GO": "true",
169 | "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]",
170 | "WEBSITE_CONTENTSHARE": "[toLower(variables('functionAppName'))]",
171 | "FUNCTIONS_EXTENSION_VERSION": "~3",
172 | "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]",
173 | "FUNCTIONS_WORKER_RUNTIME": "[variables('functionWorkerRuntime')]",
174 | "WEBSITE_RUN_FROM_PACKAGE": "1"
175 | }
176 | }
177 | ]
178 | }
179 | ]
180 | }
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.2/README.md:
--------------------------------------------------------------------------------
1 | # AppService.Dev.Specialist.Functions.2
2 | ARM Template for deploying lab for AppService DEV Specialist Certification - Azure Functions Lab #2
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fbenperk%2FAzureFunctionConsumer%2Fmaster%2FlabFiles%2FAppService.Dev.Specialist.Functions.2%2Fazuredeploy.json)
5 |
6 | ## This arm deployment will provision:
7 |
8 | 1. An Azure Storage Account with a container named elx
9 | 2. An Azure Function Dynamic App Sevice Plan
10 | 3. An Azure Function App
11 | 4. An Application Insights Resource
12 | 5. Uses the ZIPDEPLOY extension to deploy Function source code
13 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.2/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "appName": {
6 | "type": "string",
7 | "defaultValue": "[concat('elx', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the function app that you wish to create."
10 | }
11 | },
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "[resourceGroup().location]"
15 | },
16 | "runtime": {
17 | "type": "string",
18 | "defaultValue": "dotnet"
19 | },
20 | "zipdeployPackageUrl": {
21 | "defaultValue": "https://csharpguitar.blob.core.windows.net/elx/csharpguitar-elx.zip",
22 | "type": "string"
23 | }
24 | },
25 | "variables": {
26 | "functionAppName": "[parameters('appName')]",
27 | "hostingPlanName": "[parameters('appName')]",
28 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'elx')]",
29 | "applicationInsightsName": "[parameters('appName')]",
30 | "functionWorkerRuntime": "[parameters('runtime')]",
31 | "storageContainer": "[concat('default/', 'elx')]"
32 | },
33 | "resources": [
34 | {
35 | "type": "Microsoft.Storage/storageAccounts",
36 | "name": "[variables('storageAccountName')]",
37 | "apiVersion": "2021-02-01",
38 | "location": "[parameters('location')]",
39 | "kind": "Storage",
40 | "sku": {
41 | "name": "Standard_LRS"
42 | },
43 | "resources": [
44 | {
45 | "type": "blobServices/containers",
46 | "apiVersion": "2021-02-01",
47 | "name": "[variables('storageContainer')]",
48 | "dependsOn": [
49 | "[variables('storageAccountName')]"
50 | ]
51 | }
52 | ]
53 | },
54 | {
55 | "type": "Microsoft.Web/serverfarms",
56 | "apiVersion": "2020-12-01",
57 | "name": "[variables('hostingPlanName')]",
58 | "location": "[parameters('location')]",
59 | "sku": {
60 | "name": "Y1",
61 | "tier": "Dynamic"
62 | },
63 | "properties": {
64 | "name": "[variables('hostingPlanName')]",
65 | "computeMode": "Dynamic"
66 | }
67 | },
68 | {
69 | "type": "microsoft.insights/components",
70 | "apiVersion": "2020-02-02-preview",
71 | "name": "[variables('applicationInsightsName')]",
72 | "location": "[parameters('location')]",
73 | "tags": {
74 | "[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
75 | },
76 | "properties": {
77 | "ApplicationId": "[variables('applicationInsightsName')]",
78 | "Request_Source": "IbizaWebAppExtensionCreate"
79 | }
80 | },
81 | {
82 | "apiVersion": "2020-12-01",
83 | "type": "Microsoft.Web/sites",
84 | "name": "[variables('functionAppName')]",
85 | "location": "[parameters('location')]",
86 | "kind": "functionapp",
87 | "dependsOn": [
88 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
89 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
90 | ],
91 | "properties": {
92 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
93 | "siteConfig": {
94 | "appSettings": [
95 | {
96 | "name": "BLOB_FUNCTION_GO",
97 | "value": "true"
98 | },
99 | {
100 | "name": "AzureWebJobsStorage",
101 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
102 | },
103 | {
104 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
105 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
106 | },
107 | {
108 | "name": "WEBSITE_CONTENTSHARE",
109 | "value": "[toLower(variables('functionAppName'))]"
110 | },
111 | {
112 | "name": "WEBSITE_RUN_FROM_PACKAGE",
113 | "value": "1"
114 | },
115 | {
116 | "name": "FUNCTIONS_EXTENSION_VERSION",
117 | "value": "~3"
118 | },
119 | {
120 | "name": "BLOB_CONNECTION",
121 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=x', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
122 | },
123 | {
124 | "name": "TIMER_FUNCTION_GO",
125 | "value": "true"
126 | },
127 | {
128 | "name": "AzureFunctionsWebHost__hostid",
129 | "value": "[concat(variables('functionAppName'), '-hostid')]"
130 | },
131 | {
132 | "name": "AzureWebJobsDisableHomepage",
133 | "value": "true"
134 | },
135 | {
136 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
137 | "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]"
138 | },
139 | {
140 | "name": "FUNCTIONS_WORKER_RUNTIME",
141 | "value": "[variables('functionWorkerRuntime')]"
142 | }
143 | ]
144 | }
145 | },
146 | "resources": [
147 | {
148 | "name": "zipdeploy",
149 | "type": "Extensions",
150 | "apiVersion": "2019-08-01",
151 | "dependsOn": [
152 | "[resourceId('Microsoft.Web/Sites', variables('functionAppName'))]"
153 | ],
154 | "properties": {
155 | "packageUri": "[parameters('zipdeployPackageUrl')]"
156 | }
157 | }
158 | ]
159 | }
160 | ]
161 | }
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.3/README.md:
--------------------------------------------------------------------------------
1 | # AppService.Dev.Specialist.Functions.3
2 | ARM Template for deploying lab for AppService DEV Specialist Certification - Azure Functions Lab #3
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fbenperk%2FAzureFunctionConsumer%2Fmaster%2FlabFiles%2FAppService.Dev.Specialist.Functions.3%2Fazuredeploy.json)
5 |
6 | ## This arm deployment will provision:
7 |
8 | 1. An Azure Storage Account
9 | 2. An Azure Function Dynamic App Sevice Plan running Linux
10 | 3. An Azure Function App targeting the Python runtime
11 | 4. An Application Insights Resource
12 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.3/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "appName": {
6 | "type": "string",
7 | "defaultValue": "[concat('elx', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the function app that you wish to create."
10 | }
11 | },
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "[resourceGroup().location]"
15 | },
16 | "runtime": {
17 | "type": "string",
18 | "defaultValue": "python"
19 | },
20 | "eventHubSku": {
21 | "type": "string",
22 | "defaultValue": "Standard",
23 | "allowedValues": [ "Basic", "Standard" ],
24 | "metadata": {
25 | "description": "Specifies the messaging tier for Event Hub Namespace."
26 | }
27 | },
28 | "use32BitWorkerProcess": {
29 | "type": "bool",
30 | "defaultValue": "true"
31 | },
32 | "linuxFxVersion": {
33 | "type": "string",
34 | "defaultValue": "Python|3.9"
35 | }
36 | },
37 | "variables": {
38 | "functionAppName": "[parameters('appName')]",
39 | "hostingPlanName": "[parameters('appName')]",
40 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'elx')]",
41 | "applicationInsightsName": "[parameters('appName')]",
42 | "functionWorkerRuntime": "[parameters('runtime')]",
43 | "eventHubNamespaceName": "[concat(parameters('appName'), 'ns')]",
44 | "eventHubName": "elx"
45 | },
46 | "resources": [
47 | {
48 | "type": "Microsoft.Storage/storageAccounts",
49 | "name": "[variables('storageAccountName')]",
50 | "apiVersion": "2021-02-01",
51 | "location": "[parameters('location')]",
52 | "kind": "Storage",
53 | "sku": {
54 | "name": "Standard_LRS"
55 | }
56 | },
57 | {
58 | "type": "Microsoft.Web/serverfarms",
59 | "apiVersion": "2018-02-01",
60 | "name": "[variables('hostingPlanName')]",
61 | "location": "[parameters('location')]",
62 | "kind": "functionapp",
63 | "sku": {
64 | "name": "Y1",
65 | "tier": "Dynamic",
66 | "size": "Y1",
67 | "family": "Y",
68 | "capacity": 0
69 | },
70 | "properties": {
71 | "name": "[variables('hostingPlanName')]",
72 | "computeMode": "Dynamic",
73 | "perSiteScaling": false,
74 | "maximumElasticWorkerCount": 1,
75 | "isSpot": false,
76 | "reserved": true,
77 | "isXenon": false,
78 | "hyperV": false,
79 | "targetWorkerCount": 0,
80 | "targetWorkerSizeId": 0
81 | }
82 | },
83 | {
84 | "type": "microsoft.insights/components",
85 | "apiVersion": "2020-02-02-preview",
86 | "name": "[variables('applicationInsightsName')]",
87 | "location": "[parameters('location')]",
88 | "tags": {
89 | "[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
90 | },
91 | "properties": {
92 | "ApplicationId": "[variables('applicationInsightsName')]",
93 | "Request_Source": "IbizaWebAppExtensionCreate"
94 | }
95 | },
96 | {
97 | "apiVersion": "2018-11-01",
98 | "type": "Microsoft.Web/sites",
99 | "name": "[variables('functionAppName')]",
100 | "location": "[parameters('location')]",
101 | "kind": "functionapp,linux",
102 | "dependsOn": [
103 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
104 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
105 | ],
106 | "properties": {
107 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
108 | "siteConfig": {
109 | "appSettings": [
110 | {
111 | "name": "FUNCTIONS_EXTENSION_VERSION",
112 | "value": "~3"
113 | },
114 | {
115 | "name": "FUNCTIONS_WORKER_RUNTIME",
116 | "value": "[variables('functionWorkerRuntime')]"
117 | },
118 | {
119 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
120 | "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]"
121 | },
122 | {
123 | "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
124 | "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').ConnectionString]"
125 | },
126 | {
127 | "name": "AzureWebJobsStorage",
128 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
129 | },
130 | {
131 | "name": "EH_CONNECTION",
132 | "value": "[listkeys(resourceId('Microsoft.Eventhub/namespaces/authorizationRules', variables('eventHubNamespaceName'), 'RootManageSharedAccessKey'), '2017-04-01').primaryConnectionString]"
133 | },
134 | {
135 | "name": "WEBSITE_RUN_FROM_PACKAGE",
136 | "value": "1"
137 | },
138 | {
139 | "name": "AzureWebJobsDisableHomepage",
140 | "value": "true"
141 | }
142 | ],
143 | "use32BitWorkerProcess": "[parameters('use32BitWorkerProcess')]",
144 | "numberOfWorkers": 1,
145 | "linuxFxVersion": "Python|3.9"
146 | }
147 | }
148 | },
149 | {
150 | "type": "Microsoft.EventHub/namespaces",
151 | "apiVersion": "2018-01-01-preview",
152 | "name": "[variables('eventHubNamespaceName')]",
153 | "location": "[parameters('location')]",
154 | "sku": {
155 | "name": "[parameters('eventHubSku')]",
156 | "tier": "[parameters('eventHubSku')]",
157 | "capacity": 1
158 | },
159 | "properties": {
160 | "isAutoInflateEnabled": false,
161 | "maximumThroughputUnits": 0
162 | }
163 | },
164 | {
165 | "type": "Microsoft.EventHub/namespaces/eventhubs",
166 | "apiVersion": "2017-04-01",
167 | "name": "[concat(variables('eventHubNamespaceName'), '/', variables('eventHubName'))]",
168 | "location": "[parameters('location')]",
169 | "dependsOn": [
170 | "[resourceId('Microsoft.EventHub/namespaces', variables('eventHubNamespaceName'))]"
171 | ],
172 | "properties": {
173 | "messageRetentionInDays": 7,
174 | "partitionCount": 2
175 | }
176 | }
177 | ]
178 | }
179 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.4/README.md:
--------------------------------------------------------------------------------
1 | # AppService.Dev.Specialist.Functions.4
2 | ARM Template for deploying lab for AppService DEV Specialist Certification - Azure Functions Lab #4
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fbenperk%2FAzureFunctionConsumer%2Fmaster%2FlabFiles%2FAppService.Dev.Specialist.Functions.4%2Fazuredeploy.json)
5 |
6 | ## This arm deployment will provision:
7 |
8 | 1. An Azure Storage Account
9 | 2. An Azure Function Dynamic App Sevice Plan
10 | 3. An Azure Function App
11 | 4. An Application Insights Resource
12 | 5. Uses the ZIPDEPLOY extension to deploy Function source code
13 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.4/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "appName": {
6 | "type": "string",
7 | "defaultValue": "[concat('elx', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the function app that you wish to create."
10 | }
11 | },
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "[resourceGroup().location]"
15 | },
16 | "runtime": {
17 | "type": "string",
18 | "defaultValue": "dotnet"
19 | },
20 | "zipdeployPackageUrl": {
21 | "defaultValue": "https://csharpguitar.blob.core.windows.net/elx/csharpguitar-elx-http.zip",
22 | "type": "string"
23 | }
24 | },
25 | "variables": {
26 | "functionAppName": "[parameters('appName')]",
27 | "hostingPlanName": "[parameters('appName')]",
28 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'elx')]",
29 | "applicationInsightsName": "[parameters('appName')]",
30 | "functionWorkerRuntime": "[parameters('runtime')]",
31 | "storageContainer": "[concat('default/', 'elx')]"
32 | },
33 | "resources": [
34 | {
35 | "type": "Microsoft.Storage/storageAccounts",
36 | "name": "[variables('storageAccountName')]",
37 | "apiVersion": "2021-02-01",
38 | "location": "[parameters('location')]",
39 | "kind": "Storage",
40 | "sku": {
41 | "name": "Standard_LRS"
42 | }
43 | },
44 | {
45 | "type": "Microsoft.Web/serverfarms",
46 | "apiVersion": "2020-12-01",
47 | "name": "[variables('hostingPlanName')]",
48 | "location": "[parameters('location')]",
49 | "sku": {
50 | "name": "Y1",
51 | "tier": "Dynamic"
52 | },
53 | "properties": {
54 | "name": "[variables('hostingPlanName')]",
55 | "computeMode": "Dynamic"
56 | }
57 | },
58 | {
59 | "type": "microsoft.insights/components",
60 | "apiVersion": "2020-02-02-preview",
61 | "name": "[variables('applicationInsightsName')]",
62 | "location": "[parameters('location')]",
63 | "tags": {
64 | "[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
65 | },
66 | "properties": {
67 | "ApplicationId": "[variables('applicationInsightsName')]",
68 | "Request_Source": "IbizaWebAppExtensionCreate"
69 | }
70 | },
71 | {
72 | "apiVersion": "2020-12-01",
73 | "type": "Microsoft.Web/sites",
74 | "name": "[variables('functionAppName')]",
75 | "location": "[parameters('location')]",
76 | "kind": "functionapp",
77 | "dependsOn": [
78 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
79 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
80 | ],
81 | "properties": {
82 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
83 | "siteConfig": {
84 | "appSettings": [
85 | {
86 | "name": "BLOB_FUNCTION_GO",
87 | "value": "true"
88 | },
89 | {
90 | "name": "AzureWebJobsStorage",
91 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
92 | },
93 | {
94 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
95 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
96 | },
97 | {
98 | "name": "WEBSITE_CONTENTSHARE",
99 | "value": "[toLower(variables('functionAppName'))]"
100 | },
101 | {
102 | "name": "WEBSITE_RUN_FROM_PACKAGE",
103 | "value": "0"
104 | },
105 | {
106 | "name": "FUNCTIONS_EXTENSION_VERSION",
107 | "value": "~3"
108 | },
109 | {
110 | "name": "TIMER_FUNCTION_GO",
111 | "value": "true"
112 | },
113 | {
114 | "name": "HTTP_TRIGGER_CANCELLATION",
115 | "value": "false"
116 | },
117 | {
118 | "name": "AzureFunctionsWebHost__hostid",
119 | "value": "[concat(variables('functionAppName'), '-hostid')]"
120 | },
121 | {
122 | "name": "AzureWebJobsDisableHomepage",
123 | "value": "true"
124 | },
125 | {
126 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
127 | "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]"
128 | },
129 | {
130 | "name": "FUNCTIONS_WORKER_RUNTIME",
131 | "value": "[variables('functionWorkerRuntime')]"
132 | }
133 | ]
134 | }
135 | },
136 | "resources": [
137 | {
138 | "name": "zipdeploy",
139 | "type": "Extensions",
140 | "apiVersion": "2019-08-01",
141 | "dependsOn": [
142 | "[resourceId('Microsoft.Web/Sites', variables('functionAppName'))]"
143 | ],
144 | "properties": {
145 | "packageUri": "[parameters('zipdeployPackageUrl')]"
146 | }
147 | }
148 | ]
149 | }
150 | ]
151 | }
152 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.5/README.md:
--------------------------------------------------------------------------------
1 | # AppService.Dev.Specialist.Functions.5
2 | ARM Template for deploying lab for AppService DEV Specialist Certification - Azure Functions Lab #5
3 |
4 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fbenperk%2FAzureFunctionConsumer%2Fmaster%2FlabFiles%2FAppService.Dev.Specialist.Functions.5%2Fazuredeploy.json)
5 |
6 | ## This arm deployment will provision:
7 |
8 | 1. An Azure Storage Account
9 | 2. An Azure Function Dynamic App Sevice Plan
10 | 3. An Azure Function App
11 | 4. An Application Insights Resource
12 | 5. Uses the ZIPDEPLOY extension to deploy Function source code
13 |
--------------------------------------------------------------------------------
/labFiles/AppService.Dev.Specialist.Functions.5/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "appName": {
6 | "type": "string",
7 | "defaultValue": "[concat('elx', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the function app that you wish to create."
10 | }
11 | },
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "[resourceGroup().location]"
15 | },
16 | "runtime": {
17 | "type": "string",
18 | "defaultValue": "dotnet"
19 | },
20 | "zipdeployPackageUrl": {
21 | "defaultValue": "https://csharpguitar.blob.core.windows.net/elx/csharpguitar-elx-http.zip",
22 | "type": "string"
23 | }
24 | },
25 | "variables": {
26 | "functionAppName": "[parameters('appName')]",
27 | "hostingPlanName": "[parameters('appName')]",
28 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'elx')]",
29 | "applicationInsightsName": "[parameters('appName')]",
30 | "functionWorkerRuntime": "[parameters('runtime')]",
31 | "storageContainer": "[concat('default/', 'elx')]"
32 | },
33 | "resources": [
34 | {
35 | "type": "Microsoft.Storage/storageAccounts",
36 | "name": "[variables('storageAccountName')]",
37 | "apiVersion": "2021-02-01",
38 | "location": "[parameters('location')]",
39 | "kind": "Storage",
40 | "sku": {
41 | "name": "Standard_LRS"
42 | }
43 | },
44 | {
45 | "type": "Microsoft.Web/serverfarms",
46 | "apiVersion": "2020-12-01",
47 | "name": "[variables('hostingPlanName')]",
48 | "location": "[parameters('location')]",
49 | "sku": {
50 | "name": "Y1",
51 | "tier": "Dynamic"
52 | },
53 | "properties": {
54 | "name": "[variables('hostingPlanName')]",
55 | "computeMode": "Dynamic"
56 | }
57 | },
58 | {
59 | "type": "microsoft.insights/components",
60 | "apiVersion": "2020-02-02-preview",
61 | "name": "[variables('applicationInsightsName')]",
62 | "location": "[parameters('location')]",
63 | "tags": {
64 | "[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
65 | },
66 | "properties": {
67 | "ApplicationId": "[variables('applicationInsightsName')]",
68 | "Request_Source": "IbizaWebAppExtensionCreate"
69 | }
70 | },
71 | {
72 | "apiVersion": "2020-12-01",
73 | "type": "Microsoft.Web/sites",
74 | "name": "[variables('functionAppName')]",
75 | "location": "[parameters('location')]",
76 | "kind": "functionapp",
77 | "dependsOn": [
78 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
79 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
80 | ],
81 | "properties": {
82 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
83 | "siteConfig": {
84 | "appSettings": [
85 | {
86 | "name": "BLOB_FUNCTION_GO",
87 | "value": "true"
88 | },
89 | {
90 | "name": "AzureWebJobsStorage",
91 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
92 | },
93 | {
94 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
95 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
96 | },
97 | {
98 | "name": "WEBSITE_CONTENTSHARE",
99 | "value": "[toLower(variables('functionAppName'))]"
100 | },
101 | {
102 | "name": "WEBSITE_RUN_FROM_PACKAGE",
103 | "value": "1"
104 | },
105 | {
106 | "name": "FUNCTIONS_EXTENSION_VERSION",
107 | "value": "~3"
108 | },
109 | {
110 | "name": "TIMER_FUNCTION_GO",
111 | "value": "true"
112 | },
113 | {
114 | "name": "TIMER_FUNCTION_SCHEDULE",
115 | "value": "0 */1 * * * *"
116 | },
117 | {
118 | "name": "HTTP_TRIGGER_CANCELLATION",
119 | "value": "false"
120 | },
121 | {
122 | "name": "AzureFunctionsWebHost__hostid",
123 | "value": "[concat(variables('functionAppName'), '-hostid')]"
124 | },
125 | {
126 | "name": "AzureWebJobsDisableHomepage",
127 | "value": "true"
128 | },
129 | {
130 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
131 | "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02-preview').InstrumentationKey]"
132 | },
133 | {
134 | "name": "FUNCTIONS_WORKER_RUNTIME",
135 | "value": "[variables('functionWorkerRuntime')]"
136 | }
137 | ]
138 | }
139 | },
140 | "resources": [
141 | {
142 | "name": "zipdeploy",
143 | "type": "Extensions",
144 | "apiVersion": "2019-08-01",
145 | "dependsOn": [
146 | "[resourceId('Microsoft.Web/Sites', variables('functionAppName'))]"
147 | ],
148 | "properties": {
149 | "packageUri": "[parameters('zipdeployPackageUrl')]"
150 | }
151 | }
152 | ]
153 | }
154 | ]
155 | }
--------------------------------------------------------------------------------
/labFiles/README.md:
--------------------------------------------------------------------------------
1 | **Warning**: DO NOT use the content in these folders as examples for real use scenarios, they are intentionally broken and are used for debugging and training purposes. If you want, deploy them and try to figure out what is wrong. Have fun with it!
2 |
--------------------------------------------------------------------------------
/labFiles/functionApp.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "appName": {
6 | "type": "string",
7 | "defaultValue": "[concat('elx', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the function app that you wish to create."
10 | }
11 | },
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "[resourceGroup().location]"
15 | },
16 | "runtime": {
17 | "type": "string",
18 | "defaultValue": "dotnet"
19 | },
20 | "msdeployPackageUrl": {
21 | "defaultValue": "https://.blob.core.windows.net/elx/csharpguitar-blob.zip",
22 | "type": "string"
23 | }
24 | },
25 | "variables": {
26 | "functionAppName": "[parameters('appName')]",
27 | "hostingPlanName": "[parameters('appName')]",
28 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'elx')]",
29 | "functionWorkerRuntime": "[parameters('runtime')]"
30 | },
31 | "resources": [
32 | {
33 | "type": "Microsoft.Storage/storageAccounts",
34 | "name": "[variables('storageAccountName')]",
35 | "apiVersion": "2019-06-01",
36 | "location": "[parameters('location')]",
37 | "kind": "Storage",
38 | "sku": {
39 | "name": "Standard_LRS"
40 | }
41 | },
42 | {
43 | "type": "Microsoft.Web/serverfarms",
44 | "apiVersion": "2019-08-01",
45 | "name": "[variables('hostingPlanName')]",
46 | "location": "[parameters('location')]",
47 | "sku": {
48 | "name": "Y1",
49 | "tier": "Dynamic"
50 | },
51 | "properties": {
52 | "name": "[variables('hostingPlanName')]",
53 | "computeMode": "Dynamic"
54 | }
55 | },
56 | {
57 | "apiVersion": "2019-08-01",
58 | "type": "Microsoft.Web/sites",
59 | "name": "[variables('functionAppName')]",
60 | "location": "[parameters('location')]",
61 | "kind": "functionapp",
62 | "dependsOn": [
63 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
64 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
65 | ],
66 | "properties": {
67 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
68 | "siteConfig": {
69 | "appSettings": [
70 | {
71 | "name": "AzureWebJobsStorage",
72 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
73 | },
74 | {
75 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
76 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
77 | },
78 | {
79 | "name": "WEBSITE_CONTENTSHARE",
80 | "value": "[toLower(variables('functionAppName'))]"
81 | },
82 | {
83 | "name": "FUNCTIONS_EXTENSION_VERSION",
84 | "value": "~3"
85 | },
86 | {
87 | "name": "AzureWebJobsDashboard",
88 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
89 | },
90 | {
91 | "name": "BLOB_CONTAINER_CONNECTION_STRING",
92 | "value": "DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net"
93 | },
94 | {
95 | "name": "AzureWebJobsDisableHomepage",
96 | "value": "true"
97 | },
98 | {
99 | "name": "FUNCTIONS_WORKER_RUNTIME",
100 | "value": "[variables('functionWorkerRuntime')]"
101 | }
102 | ]
103 | }
104 | },
105 | "resources": [
106 | {
107 | "name": "MSDeploy",
108 | "type": "Extensions",
109 | "apiVersion": "2018-11-01",
110 | "dependsOn": [
111 | "[resourceId('Microsoft.Web/Sites', variables('functionAppName'))]"
112 | ],
113 | "properties": {
114 | "packageUri": "[parameters('msdeployPackageUrl')]"
115 | }
116 | }
117 | ]
118 | }
119 | ]
120 | }
121 |
--------------------------------------------------------------------------------
/labFiles/source/x.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Microsoft.Azure.WebJobs;
4 | using Microsoft.Azure.WebJobs.Host;
5 | using Microsoft.Extensions.Logging;
6 | using System.Reflection;
7 | using System.Collections.Generic;
8 |
9 | using Azure.Storage.Blobs;
10 |
11 | namespace dev_assessment
12 | {
13 | // The HTTP trigger must return successfully by default when package csharpguitar-elx.zip is created
14 | // The 'not default' (as coded now) code in the HTTP trigger (Function 'z') is used to create csharpguitar-elx-http.zip
15 | // What this means is that if you need to create a new csharpguitar-elx.zip, modify Function 'z' so it always returns a 200
16 | public static class x
17 | {
18 | [Disable("BLOB_FUNCTION_GO")]
19 | [FunctionName("x")]
20 | public static void Run([BlobTrigger("elx/{name}", Connection = "BLOB_CONNECTION")]Stream myBlob, string name, ILogger log, Uri uri, IDictionary metadata)
21 | {
22 | log.LogInformation($"C# Blob trigger function processed blob named: {name} with a size of: {myBlob.Length} bytes");
23 |
24 | var connectionString = Environment.GetEnvironmentVariable("BLOB_CONNECTION");
25 | var container = new BlobContainerClient(connectionString, "elx");
26 | var blob = container.GetBlobClient(name);
27 |
28 | log.LogInformation($"************************* blob properties for: {name} *************************");
29 | log.LogInformation($"Blob: {name} has an ETAG of {blob.GetProperties().Value.ETag}");
30 | log.LogInformation($"Blob: {name} has a creation time of {blob.GetProperties().Value.CreatedOn}");
31 | log.LogInformation($"Blob: {name} has a last modified value of {blob.GetProperties().Value.LastModified}");
32 | log.LogInformation($"*******************************************************************************");
33 |
34 | Type uriType = typeof(Uri);
35 | PropertyInfo[] properties = uriType.GetProperties();
36 | foreach (PropertyInfo uriProp in properties)
37 | {
38 | log.LogInformation($"File Property Name: {uriProp.Name} Value: {uriProp.GetValue(uri, null)}");
39 | }
40 |
41 | foreach (KeyValuePair data in metadata)
42 | {
43 | log.LogInformation($"User-Defined Metadata Key = { data.Key }");
44 | log.LogInformation($"User-Defined Metadata Value = { data.Value }");
45 | }
46 | if (metadata.Count == 0)
47 | {
48 | log.LogInformation("No user-defined metadata was found.");
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/labFiles/source/y.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Azure.WebJobs;
3 | using Microsoft.Azure.WebJobs.Host;
4 | using Microsoft.Extensions.Logging;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using System.Diagnostics;
8 |
9 | namespace dev_assessment
10 | {
11 | public static class y
12 | {
13 | // This code is the same for (lab 4 and lab 5), no dependencies.
14 | // This code does not exist in csharpguitar-elx.zip (lab 1), no problem to add it, but not necessary
15 | // if added it would require retesting the labs
16 | // This code is included in csharpguitar-elx-http.zip, as per lab instructions
17 | public static string _globalString = "*** Begin process ***";
18 | public static object thisLock = new object();
19 |
20 | [Disable("TIMER_FUNCTION_GO")]
21 | [FunctionName("y")]
22 | public static async void Run([TimerTrigger("%TIMER_FUNCTION_SCHEDULE%")]TimerInfo myTimer, ILogger log, CancellationToken cancellationToken)
23 | {
24 | Random r = new Random();
25 | var number = r.Next(1, 100);
26 | if (number % 4 == 0)
27 | {
28 | log.LogInformation($"C# Timer trigger function started execution at: {DateTime.Now} for scenario 'alpha'");
29 | //runs long (72 seconds), helpful when you want to trigger a cancellation token
30 | int length = 24;
31 | for (int i = 0; i < length; i++)
32 | {
33 | if (cancellationToken.IsCancellationRequested)
34 | {
35 | if (cancellationToken.CanBeCanceled)
36 | {
37 | log.LogInformation($"Function invocation was successfully cancelled at: {DateTime.Now}.");
38 | log.LogInformation($"cancellationToken.CanBeCanceled had a value of: {cancellationToken.CanBeCanceled}");
39 | log.LogInformation($"The unique identifier: {Guid.NewGuid()}");
40 | log.LogInformation($"***NOTE*** this is where you can do code clean up, just before being shutdown!");
41 | log.LogInformation($"***NOTE*** the invocation did not complete, it was cancelled while executing, the code was on iteration {i} of {length}.");
42 | break;
43 | }
44 | else
45 | {
46 | log.LogInformation($"Function invocation cancellation was requested at: {DateTime.Now}.");
47 | log.LogInformation($"cancellationToken.CanBeCanceled had a value of: {cancellationToken.CanBeCanceled} and therefore could not be cancelled.");
48 | log.LogInformation($"The unique identifier: {Guid.NewGuid()}");
49 | log.LogInformation($"***NOTE*** although the code received a cancellation request, the invocation could not be cancelled.");
50 | }
51 | }
52 | log.LogInformation($"This Function Invocation will loop {length} times. Current iteration is: {i}");
53 | Thread.Sleep(5000);
54 | }
55 | log.LogInformation($"C# Timer trigger function completed execution at: {DateTime.Now} for scenario 'alpha'");
56 | }
57 | else
58 | {
59 | number = r.Next(1, 100);
60 | if (number % 4 == 0)
61 | {
62 | log.LogInformation($"C# Timer trigger function started execution at: {DateTime.Now} for scenario 'beta'");
63 | //tip: the implementation of the async/await pattern is wrong and causes big problems
64 | Stopwatch timer = new Stopwatch();
65 | timer.Start();
66 | /* Incorrect */
67 | log.LogInformation($"Calling InsertAsync()");
68 | await InsertAsync();
69 | log.LogInformation($"Calling UpdateAsync()");
70 | await UpdateAsync();
71 | /* --------- */
72 | /* Correct */
73 | //Task globalInsertStatus = InsertAsync();
74 | //var globalUpdateStatus = UpdateAsync();
75 | /* ------- */
76 | timer.Stop();
77 | log.LogInformation($"Calling InsertAsync() and UpdateAsync() methods took {timer.Elapsed}");
78 | log.LogInformation("****************************************************************************");
79 | log.LogInformation($"The time interval for this timer function is: {Environment.GetEnvironmentVariable("TIMER_FUNCTION_SCHEDULE")}");
80 | log.LogInformation($"TimerInfo PastDue: {myTimer.IsPastDue}.");
81 | log.LogInformation($"ScheduleStatus Last: {myTimer.ScheduleStatus.Last}");
82 | log.LogInformation($"ScheduleStatus LastUpdated: {myTimer.ScheduleStatus.LastUpdated}");
83 | log.LogInformation($"ScheduleStatus Next: {myTimer.ScheduleStatus.Next}");
84 | log.LogInformation("****************************************************************************");
85 | log.LogInformation($"Timer execution interval is: {Environment.GetEnvironmentVariable("TIMER_FUNCTION_SCHEDULE")} but took {timer.Elapsed} to complete.");
86 | log.LogInformation("****************************************************************************");
87 | log.LogInformation($"C# Timer trigger function completed execution at: {DateTime.Now} for scenario 'beta'");
88 | /* Correct */
89 | //log.LogInformation($"The InsertAsync() value of _globalString is {await globalInsertStatus} for scenario 'beta'");
90 | //log.LogInformation($"The UpdateAsync() value of _globalString was {await globalUpdateStatus} for scenario 'beta'");
91 | /* ------- */
92 | }
93 | number = r.Next(1, 100);
94 | if (number % 2 == 0)
95 | {
96 | log.LogInformation($"C# Timer trigger function started execution at: {DateTime.Now} for scenario 'gamma'");
97 | Thread.Sleep(2000);
98 | log.LogInformation($"C# Timer trigger function completed execution at: {DateTime.Now} for scenario 'gamma'");
99 | }
100 | else
101 | {
102 | number = r.Next(1, 100);
103 | if (number % 2 == 0)
104 | {
105 | log.LogInformation($"C# Timer trigger function started execution at: {DateTime.Now} for scenario 'delta'");
106 | try
107 | {
108 | Thread.Sleep(5000);
109 | throw new FunctionInvocationException("Explain what can cause a stack overflow exception");
110 | }
111 | catch (FunctionInvocationException fie)
112 | {
113 | log.LogInformation($"A {fie.GetType()} was thrown. Was this a hanlded or unhandled exception?");
114 | log.LogInformation($"C# Timer trigger function completed execution at: {DateTime.Now} for scenario 'delta'");
115 | }
116 | }
117 | else
118 | {
119 | log.LogInformation($"C# Timer trigger function started execution at: {DateTime.Now} for scenario 'epsilon'");
120 | Thread.Sleep(10000);
121 | throw new FunctionInvocationException("Ouch! Was this a handled or unhandled exception?");
122 | //code execution will cease before logging this, why?
123 | log.LogInformation($"C# Timer trigger function completed execution at: {DateTime.Now} for scenario 'epsilon'");
124 | }
125 | }
126 | }
127 | }
128 | public static async Task InsertAsync()
129 | {
130 | lock (_globalString)
131 | {
132 | _globalString = $"'InsertAsync() begin {DateTime.Now}'";
133 | //The Sleep() is a simulated Insert into a very busy data repository
134 | System.Threading.Thread.Sleep(41000);
135 | _globalString = $"'InsertAsync() end {DateTime.Now}'";
136 | }
137 |
138 | await Task.Delay(1000);
139 | return _globalString;
140 | }
141 | public static async Task UpdateAsync()
142 | {
143 | lock (_globalString)
144 | {
145 | _globalString = $"'UpdateAsync() begin {DateTime.Now}'";
146 | //The Sleep() is a simulated Update on a very busy data repository
147 | System.Threading.Thread.Sleep(49000);
148 | _globalString = $"'UpdateAsync() end {DateTime.Now}'";
149 | }
150 |
151 | await Task.Delay(1000);
152 | return _globalString;
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/labFiles/source/z.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.Azure.WebJobs;
6 | using Microsoft.Azure.WebJobs.Extensions.Http;
7 | using Microsoft.AspNetCore.Http;
8 | using Microsoft.Extensions.Logging;
9 | using Newtonsoft.Json;
10 | using System.Threading;
11 |
12 | namespace dev_assessment
13 | {
14 | public static class z
15 | {
16 | // The code in this function must return successfully by default when package csharpguitar-elx.zip is created
17 | // This code is to be included in the csharpguitar-elx-http.zip (lab 4) but not in csharpguitar-elx.zip (lab 1)
18 | // This code is the same for (lab 4 and lab 5), no dependencies.
19 | [FunctionName("z")]
20 | public static async Task Run(
21 | [HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "csharpguitar")] HttpRequest req,
22 | ILogger log, CancellationToken cancellationToken)
23 | {
24 | log.LogInformation("C# HTTP trigger function processed a request.");
25 |
26 | int length = 40;
27 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
28 | dynamic data = JsonConvert.DeserializeObject(requestBody);
29 | string cancel = data?["cancel"];
30 |
31 | if (cancel == "yes")
32 | {
33 | for (int i = 0; i < length; i++)
34 | {
35 | if (cancellationToken.IsCancellationRequested)
36 | {
37 | if (cancellationToken.CanBeCanceled)
38 | {
39 | log.LogInformation($"Function invocation was successfully cancelled at: {DateTime.Now} using the '{req.Method}' method.");
40 | log.LogInformation($"cancellationToken.CanBeCanceled had a value of: {cancellationToken.CanBeCanceled}");
41 | log.LogInformation($"The unique identifier: {Guid.NewGuid()}");
42 | break;
43 | }
44 | else
45 | {
46 | log.LogInformation($"Function invocation cancellation was requested at: {DateTime.Now} using the '{req.Method}' method.");
47 | log.LogInformation($"cancellationToken.CanBeCanceled had a value of: {cancellationToken.CanBeCanceled} and therefore could not be cancelled.");
48 | log.LogInformation($"The unique identifier: {Guid.NewGuid()}");
49 | }
50 | }
51 | log.LogInformation($"This Function Invocation will loop {length} times. Current iteration is: {i}");
52 | Thread.Sleep(5000);
53 | }
54 | }
55 |
56 | string responseMessage = string.IsNullOrEmpty(cancel)
57 | ? $"HTTP triggered function executed successfully using the '{req.Method}' method. {Guid.NewGuid()}"
58 | : $"A value of {cancel} was recevied by this HTTP triggered function using the '{req.Method}' method.";
59 |
60 | return new OkObjectResult(responseMessage);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------