├── .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 | ![checking .net code version](images/AFC001.PNG) 13 | 14 | Start the program by runnung: **dotnet AzureFunctionConsumer.dll** 15 | 16 | ![starting the Azure Function Consumer](images/AFC002.PNG) 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 | ![Calling an HTTP triggered Azure Function](images/AFC003.PNG) 34 | 35 | ### Event Hub Triggered Function ### 36 | ![Invoking an Event Hub triggered Azure Function](images/AFC004.PNG) 37 | 38 | ### Storge Queue Triggered Function ### 39 | ![Invoking a Storage Queue triggered Azure Function](images/AFC005.PNG) 40 | 41 | ### Azure Cosmos DB Triggered Function ### 42 | ![Invoking an Azure Cosmos DB triggered Azure Function](images/AFC006.PNG) 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 | [![Deploy To Azure](https://aka.ms/deploytoazurebutton)](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 | [![Deploy To Azure](https://aka.ms/deploytoazurebutton)](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 | [![Deploy To Azure](https://aka.ms/deploytoazurebutton)](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 | [![Deploy To Azure](https://aka.ms/deploytoazurebutton)](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 | [![Deploy To Azure](https://aka.ms/deploytoazurebutton)](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 | --------------------------------------------------------------------------------