├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── api-connection-scale.md ├── api-management-actions ├── powershell │ ├── clean-up.ps1 │ ├── full-deploy.ps1 │ ├── logic-app-deploy.ps1 │ └── shared-deploy.ps1 ├── readme.md ├── sample-function │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── AwesomeFunction.cs │ ├── SampleFunction.csproj │ ├── host.json │ └── local.settings.json └── templates │ ├── connectors-template.json │ ├── logic-app-definition-parameters.json │ ├── logic-app-definition.json │ ├── logic-app-template.json │ └── shared-template.json ├── concepts-review.md ├── file-definitions.md ├── function-app-actions ├── powershell │ ├── azure-pipelines.yml │ ├── clean-up.ps1 │ ├── full-deploy.ps1 │ ├── logic-app-deploy.ps1 │ └── shared-deploy.ps1 ├── readme.md ├── sample-function │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── AwesomeFunction.cs │ ├── SampleFunction.csproj │ ├── host.json │ └── local.settings.json └── templates │ ├── connectors-template.json │ ├── logic-app-definition-parameters.json │ ├── logic-app-definition.json │ ├── logic-app-template.json │ └── shared-template.json ├── images ├── create-pipeline.gif ├── developer-workflow.png ├── double-sb-connection-single-instance.png ├── double-sb-connection-three-instance-one-cosmos.png ├── double-sb-connection-three-instance-two-cosmos.png ├── double-sb-connection-two-instance-one-cosmos.png ├── double-sb-connection-two-instance.png ├── dup-dr.png ├── dup-environments.png ├── dup-none.png ├── dup-scale.png ├── function-app-sample.png ├── sample-integration-act.png ├── scaled-logic-app-resources.png ├── scaled-resource-group.png ├── script-flow.png ├── servicebus-sample.png ├── single-sb-connection-single-instance.png └── storage-sample.png ├── integration-account-connections ├── powershell │ ├── azure-pipelines.yml │ ├── clean-up.ps1 │ ├── full-deploy.ps1 │ ├── logic-app-deploy.ps1 │ └── shared-deploy.ps1 ├── readme.md └── templates │ ├── connectors-template.json │ ├── logic-app-definition-parameters.json │ ├── logic-app-definition.json │ ├── logic-app-template.json │ └── shared-template.json ├── naming-convention.md ├── readme.md ├── service-bus-connections ├── powershell │ ├── azure-pipelines.yml │ ├── clean-up.ps1 │ ├── full-deploy.ps1 │ ├── logic-app-deploy.ps1 │ └── shared-deploy.ps1 ├── readme.md └── templates │ ├── connectors-template.json │ ├── logic-app-definition-parameters.json │ ├── logic-app-definition.json │ ├── logic-app-template.json │ └── shared-template.json └── storage-account-connections ├── powershell ├── azure-pipelines.yml ├── clean-up.ps1 ├── full-deploy.ps1 ├── logic-app-deploy.ps1 └── shared-deploy.ps1 ├── readme.md └── templates ├── connectors-template.json ├── logic-app-definition-parameters.json ├── logic-app-definition.json ├── logic-app-template.json └── shared-template.json /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /.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, but generally, the file is 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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [project-title] Changelog 2 | 3 | 4 | # x.y.z (yyyy-mm-dd) 5 | 6 | *Features* 7 | * ... 8 | 9 | *Bug Fixes* 10 | * ... 11 | 12 | *Breaking Changes* 13 | * ... 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to [project-title] 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! 77 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /api-connection-scale.md: -------------------------------------------------------------------------------- 1 | # Why consider scaling? 2 | 3 | This topic provides context about the scaling approaches used by the Azure Logic Apps samples for continuous integration (CI) and continuous deployment (CD) pipelines. Logic Apps include built-in scaling, so why create duplicate instances for your logic apps within a single environment? 4 | 5 | Some nonnative API connections use built-in throttling, which might prevent your logic apps from reaching the high-scale performance benchmarks that those apps could otherwise achieve. To resolve this scenario, you can create multiple instances for a logic app, which also means creating multiple instances for the API connections used by that logic app. 6 | 7 | Now, you could put all those instances in a single resource group. However, having all or a large number of logic apps and API connections in the same group makes some tasks more difficult, for example: 8 | 9 | * Troubleshooting logic apps that depend on multiple API connections requires that you know which API connections belong to each logic app. Troubleshooting gets more complicated when you have multiple API connections with the same type. 10 | 11 | * Scaling down requires that you cherry pick resources that are all grouped together. 12 | 13 | By using a separate resource group for each logic app instance, you get these benefits: 14 | 15 | * You gain greater clarity about the API connections that are used by each logic app. 16 | 17 | * You make your solution easier to scale up and down. 18 | 19 | ## How does scaling work? 20 | 21 | Suppose you have a logic app that reads messages from one Azure Service Bus queue, runs some tasks, and then posts a message to another Service Bus queue by using a single Service Bus API connection. From a resources perspective, your resource group for these resources looks like this example in the Azure portal: 22 | 23 | ![Single instance, single implementation](./images/single-sb-connection-single-instance.png) 24 | 25 | The Service Bus API connection currently has built-in throttling. So, if you use a single connection to send messages *and* to read messages, *but* you also require high throughput from the logic app, this connection most likely creates a bottleneck. Here are two ways to address this problem by scaling up: 26 | 27 | * [Single resource group](#single-group) 28 | * [Multiple resource groups](#multiple-groups) 29 | 30 | 31 | 32 | ### Single resource group 33 | 34 | These steps describe an approach and the results from trying to fix the bottleneck problem by using a single resource group. 35 | 36 | 1. You separate the API connections so that one connection reads messages while the other connection sends messages. Your resource group looks like this example: 37 | 38 | ![Single instance, double implementation](./images/double-sb-connection-single-instance.png) 39 | 40 | However, you still get throttling from these connections, and you're nowhere near your throughput requirements. 41 | 42 | 1. You create another instance for your logic app along with its own API connections, and your resource group now looks like this example: 43 | 44 | ![Double instance, double implementation](./images/double-sb-connection-two-instance.png) 45 | 46 | 1. Suppose you have to add a different logic app that reads from the second Service Bus queue and runs some tasks that populate an Azure Cosmos database. You don't want to share the API connections from the first logic app because you risk reducing that logic app's throughput. 47 | 48 | So, you create the second logic app, which has its own instances for the Service Bus API connection. Now, your resource group looks like this example: 49 | 50 | ![Double instance, double implementation, single Azure Cosmos DB logic app](./images/double-sb-connection-two-instance-one-cosmos.png) 51 | 52 | 1. As this situation gets more complicated, you discover that the first logic app no longer meets the throughput requirements. You create a third instance for the first logic app, and your resource group looks like this example: 53 | 54 | ![Triple instance, double implementation, single Azure Cosmos DB logic app](./images/double-sb-connection-three-instance-one-cosmos.png) 55 | 56 | 1. Now, you find that the same throttling happens on the second logic app. So, you have to create another instance for the second logic app and its connections. 57 | 58 | ![Triple instance, double implementation, double Azure Cosmos DB logic app](./images/double-sb-connection-three-instance-two-cosmos.png) 59 | 60 | So, here are some considerations that result from using a single resource group: 61 | 62 | * When did you lose track of which Service Bus API connection belonged to which logic app? 63 | 64 | * What happens if you need to continue scaling up both logic apps? 65 | 66 | * What happens if you have to remove an instance from either logic app or both? Which connection belongs to which logic app? 67 | 68 | * What happens when you have to replicate this solution in multiple environments, such as test, staging, and production *plus* provide instructions to the operations team so that they can support this complexity? 69 | 70 | 71 | 72 | ### Multiple resource groups 73 | 74 | To make the resources created in the [single resource group approach](#single-group) easier to manage and troubleshoot, you can put the resources for each logic app instance and their API connection dependencies into their own resource groups. This makes it very clear which API connections are used by that logic app. For resources that don't require scaling with logic apps, which are the Service Bus namespace and Azure Cosmos DB account in this example, you can put them in their own "shared" resource group. 75 | 76 | ![Triple instance, double implementation, single Azure Cosmos DB logic app, scaled resource group](./images/scaled-resource-group.png) 77 | 78 | Now, the resource group for each logic app instance has the same resources and looks like this example: 79 | 80 | ![Resource group for each logic app instance](./images/scaled-logic-app-resources.png) 81 | 82 | The CI/CD samples show how you can scale up by using an `instanceCount` parameter that specifies the number of logic app instances. These samples also explain why scaling up is implemented by keeping a logic app and its API connections in their own resource group. While these samples provide the capability to scale, no scaling is also an acceptable use case. To use a single logic app instance, specify an `instanceCount` of 1. 83 | -------------------------------------------------------------------------------- /api-management-actions/powershell/clean-up.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deletes all resource groups where their names start with the "groupId" that's passed as input. 4 | 5 | .PARAMETER groupId 6 | The value that identifies the resource group to delete 7 | #> 8 | 9 | param( 10 | [Parameter(Mandatory = $True)] 11 | [string] 12 | $groupId 13 | ) 14 | 15 | $resourceGroups = Get-AzResourceGroup; 16 | 17 | foreach ($resourceGroup in $resourceGroups) { 18 | if ($resourceGroup.ResourceGroupName.StartsWith($groupId, 'CurrentCultureIgnoreCase')) { 19 | Remove-AzResourceGroup -Name $resourceGroup.ResourceGroupName -Force -AsJob 20 | Write-Host "Deleted resource group $($resourceGroup.ResourceGroupName)" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /api-management-actions/powershell/full-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploys a template for a logic app to Azure. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template for the separate logic app definition in the directory. 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER instanceCount 24 | The number of resource group instances to create 25 | 26 | .PARAMETER overrideExistingLogicApp 27 | If true, the script runs the Azure Resource Manager template deployment for the logic app, even when the logic app already exists. 28 | #> 29 | 30 | param( 31 | 32 | [Parameter(Mandatory=$True)] 33 | [string] 34 | $groupId, 35 | 36 | [Parameter(Mandatory=$True)] 37 | [string] 38 | $location, 39 | 40 | [Parameter(Mandatory=$True)] 41 | [string] 42 | $environment, 43 | 44 | [Parameter(Mandatory=$False)] 45 | [string] 46 | $abbrevLocationName = $location, 47 | 48 | [Parameter(Mandatory=$False)] 49 | [string] 50 | $deploymentName = [guid]::NewGuid(), 51 | 52 | [Parameter(Mandatory=$False)] 53 | [int] 54 | $instanceCount = 1, 55 | 56 | [Parameter(Mandatory = $False)] 57 | [bool] 58 | $overrideExistingLogicApp = $True 59 | ) 60 | 61 | Write-Host "Running shared deployment script" 62 | 63 | $result = & "${PSScriptRoot}\shared-deploy.ps1" -groupId $groupId ` 64 | -location $location ` 65 | -environment $environment ` 66 | -abbrevLocationName $abbrevLocationName ` 67 | -deploymentName $deploymentName; 68 | 69 | Write-Host "Deploying Function App" 70 | 71 | Push-Location "${$PSScriptRoot}../sample-function" 72 | 73 | $deployCommand = "func azure functionapp publish {0}" -f $result.Outputs.functionAppName.Value 74 | 75 | Invoke-Expression $deployCommand 76 | 77 | Pop-Location 78 | 79 | Write-Host "Running logic app deployment script" 80 | 81 | & "${PSScriptRoot}\logic-app-deploy.ps1" -groupId $groupId ` 82 | -location $location ` 83 | -environment $environment ` 84 | -abbrevLocationName $abbrevLocationName ` 85 | -deploymentName $deploymentName ` 86 | -identifier "sample-api-management" ` 87 | -instanceCount $instanceCount ` 88 | -overrideExisting $overrideExistingLogicApp; -------------------------------------------------------------------------------- /api-management-actions/powershell/shared-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploy the shared resources. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | OPTIONAL: The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | OPTIONAL: The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER identifier 24 | OPTIONAL: Value appended to the end of the resource group and the logic app within the resource group 25 | 26 | .PARAMETER parametersFilePath 27 | OPTIONAL: Path to the parameter file. Defaults to the file contained within the local directory 28 | 29 | .PARAMETER templateFilePath 30 | OPTIONAL: Path to the template file. Defaults to the file contained within the local directory 31 | #> 32 | 33 | param( 34 | [Parameter(Mandatory = $True)] 35 | [string] 36 | $groupId, 37 | 38 | [Parameter(Mandatory = $True)] 39 | [string] 40 | $location, 41 | 42 | [Parameter(Mandatory = $True)] 43 | [string] 44 | $environment, 45 | 46 | [Parameter(Mandatory = $False)] 47 | [string] 48 | $abbrevLocationName = $location, 49 | 50 | [Parameter(Mandatory = $False)] 51 | [string] 52 | $deploymentName = [guid]::NewGuid(), 53 | 54 | [Parameter(Mandatory = $False)] 55 | [string] 56 | $identifier = "shared", 57 | 58 | [Parameter(Mandatory = $False)] 59 | [string] 60 | $parametersFilePath = "../templates/shared-parameters.json", 61 | 62 | [Parameter(Mandatory = $False)] 63 | [string] 64 | $templateFilePath = "../templates/shared-template.json" 65 | ) 66 | 67 | Function RegisterRP { 68 | <# 69 | .SYNOPSIS 70 | Register the Azure resource provider. 71 | #> 72 | Param( 73 | [Parameter(Mandatory = $True, HelpMessage = "The name of the resource provider to register")] 74 | [string]$ResourceProviderNamespace 75 | ) 76 | 77 | Write-Host "Registering resource provider '$ResourceProviderNamespace'"; 78 | $registered = Register-AzResourceProvider -ProviderNamespace $ResourceProviderNamespace; 79 | } 80 | 81 | Function Set-RelativeFilePaths { 82 | <# 83 | .SYNOPSIS 84 | Fix relative paths that can't be evaluated as given. 85 | .DESCRIPTION 86 | Validate whether the file paths can be evaluated as they're passed as input. 87 | If the paths are relative and can't be found, try to fix by prepending the 88 | root path for script execution to the given path. 89 | #> 90 | Param() 91 | $parametersFilePath = Set-RelativeFilePath -filePath $parametersFilePath 92 | $templateFilePath = Set-RelativeFilePath -filePath $templateFilePath 93 | } 94 | 95 | Function Set-RelativeFilePath { 96 | <# 97 | .SYNOPSIS 98 | Validate a single file path and prepend the root path for script execution when the path fails. 99 | #> 100 | Param( 101 | [Parameter(Mandatory = $True, HelpMessage = "The path of the file to test whether the file exists")] 102 | [string]$filePath 103 | ) 104 | 105 | if (Test-Path $filePath) { 106 | return $filePath; 107 | } 108 | else { 109 | return "{0}\{1}" -f $PSScriptRoot, $filePath; 110 | } 111 | } 112 | 113 | Function Set-ResourceGroup { 114 | <# 115 | .SYNOPSIS 116 | Check whether the resource group exists. If not, create the resource group. 117 | #> 118 | Param( 119 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to create, if not already existing")] 120 | [string]$resourceGroupName 121 | ) 122 | 123 | Write-Host "Working with resource group name: '$resourceGroupName'" 124 | 125 | Write-Host "Checking whether the resource group exists" 126 | $resourceGroup = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue 127 | if (!$resourceGroup) { 128 | Write-Host "Resource group '$resourceGroupName' does not exist. Creating new resource group"; 129 | 130 | Write-Host "Creating resource group '$resourceGroupName' in location '$location'"; 131 | New-AzResourceGroup -Name $resourceGroupName -Location $location 132 | } 133 | else { 134 | Write-Host "Using existing resource group '$resourceGroupName'"; 135 | } 136 | } 137 | 138 | Function New-ResourceManagerTemplateDeployment { 139 | <# 140 | .SYNOPSIS 141 | Create an Azure Resource Manager template deployment. 142 | .OUTPUTS 143 | The results from the Azure Resource Manager template deployment 144 | #> 145 | Param( 146 | [Parameter(Mandatory = $True, HelpMessage = "The path for the parameter file to use for deployment")] 147 | [string]$parametersFilePath, 148 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to use for deployment")] 149 | [string]$resourceGroupName, 150 | [Parameter(Mandatory = $True, HelpMessage = "The path to the template file to use for the deployment")] 151 | [string]$templateFilePath, 152 | [Parameter(Mandatory = $True, HelpMessage = "The values to append to the parameters before deployment")] 153 | [hashtable]$armParameters 154 | ) 155 | if (Test-Path $parametersFilePath) { 156 | Write-Host "Loading parameters from file"; 157 | $parametersFromFile = Get-Content -Raw -Encoding UTF8 -Path $parametersFilePath | ConvertFrom-Json 158 | 159 | Write-Host "Adding script parameters"; 160 | New-Parameter -parameters $parametersFromFile -name "groupId" -value $groupId 161 | New-Parameter -parameters $parametersFromFile -name "environment" -value $environment 162 | New-Parameter -parameters $parametersFromFile -name "locationName" -value $locationName 163 | 164 | Write-Host "Starting deployment..."; 165 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $parametersFromFile; 166 | } 167 | else { 168 | Write-Host "Starting deployment..."; 169 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $armParameters; 170 | } 171 | } 172 | 173 | Function New-Parameter { 174 | <# 175 | .SYNOPSIS 176 | Append a parameter to the hash table for parameters that are passed as input. 177 | #> 178 | Param ( 179 | [Parameter(Mandatory = $True, HelpMessage = "The hash table where to add the parameter")] 180 | [hashtable]$parameters, 181 | [Parameter(Mandatory = $True, HelpMessage = "The name for the parameter to add")] 182 | [string]$name, 183 | [Parameter(Mandatory = $True, HelpMessage = "The value for the parameter to add")] 184 | [string]$value 185 | ) 186 | $parameters | Add-Member -MemberType NoteProperty -Name $name -Value @{value = $value } 187 | } 188 | 189 | #****************************************************************************** 190 | # Script body 191 | # Execution begins here 192 | #****************************************************************************** 193 | 194 | if (!$PSScriptRoot) { $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } 195 | 196 | Set-RelativeFilePaths; 197 | 198 | $resourceProviders = @("Microsoft.Web","Microsoft.Insights","Microsoft.Storage"); 199 | if ($resourceProviders.length) { 200 | Write-Host "Registering resource providers" 201 | foreach ($resourceProvider in $resourceProviders) { 202 | RegisterRP($resourceProvider); 203 | } 204 | } 205 | 206 | $resourceGroupName = "{0}{1}rgp{2}-{3}" -f $groupId, $environment, $location, $identifier; 207 | 208 | Set-ResourceGroup -resourceGroupName $resourceGroupName; 209 | 210 | $armParameters = @{ 211 | groupId = $groupId 212 | environment = $environment 213 | locationName = $abbrevLocationName 214 | }; 215 | 216 | $result = New-ResourceManagerTemplateDeployment -parametersFilePath $parametersFilePath ` 217 | -resourceGroupName $resourceGroupName ` 218 | -templateFilePath $templateFilePath ` 219 | -armParameters $armParameters; 220 | 221 | return $result; -------------------------------------------------------------------------------- /api-management-actions/readme.md: -------------------------------------------------------------------------------- 1 | # Set up an Azure API Management action for Azure Logic Apps 2 | 3 | **This sample is a work in progress. Work remaining:** 4 | 5 | * Automate import of function app into API Management. 6 | * Update logic app definition to use API Management action instead of function app action. 7 | * Update readme.md with relevant information about the sample. Most of the content will be the same as the function app sample. 8 | 9 | Differences are: 10 | 11 | * Automate the function app's import. 12 | * Secure the function app from traffic not routed through API Management. 13 | 14 | ## Prerequisites 15 | 16 | * Install [Azure PowerShell 2.4.0](https://docs.microsoft.com/powershell/azure/install-az-ps?view=azps-2.4.0) on your platform. 17 | * Install [Azure Functions Core Tools](https://docs.microsoft.com/azure/azure-functions/functions-run-local#v2) on your platform. 18 | 19 | ## Description 20 | 21 | TODO: Add content here 22 | 23 | ## Set up sample 24 | 25 | To set up, deploy, and run this sample, you can use the command line or set up an Azure DevOps pipeline. 26 | 27 | ### Command line 28 | 29 | 1. Clone or download this sample repository. 30 | 31 | 1. Sign in to Azure by running this command from any command line tool that you want. 32 | 33 | ```powershell 34 | Connect-AzAccount 35 | ``` 36 | 37 | 1. To target your deployment, select the appropriate [Azure context](https://docs.microsoft.com/powershell/module/az.accounts/Select-AzContext?view=azps-2.4.0) to use. 38 | 39 | 1. To push a full deployment for this sample to Azure, run this command from the PowerShell directory that contains this sample: 40 | 41 | ```powershell 42 | ./full-deploy.ps1 -groupId -environment -location 43 | ``` 44 | 45 | ### Azure DevOps 46 | 47 | This sample uses [multi-stage YAML pipelines](https://docs.microsoft.com/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml). To set up the sample pipeline, follow these steps: 48 | 49 | 1. Make sure that the [multi-stage pipeline preview feature](https://docs.microsoft.com/azure/devops/project/navigation/preview-features?view=azure-devops) is enabled. 50 | 51 | 1. Clone or fork the samples repository into your own repository. 52 | 53 | 1. Choose one of these steps: 54 | 55 | * Create an [Azure Resource Manager service connection](https://docs.microsoft.com/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-azure-rm) that has the name "Azure Samples Subscription" in your project that points to the Azure subscription that you want to use for deployment. 56 | 57 | * Edit all instances of `azureSubscription: 'Azure Samples Subscription'` in the `./powershell/azure-pipelines.yml` file by using the name for an existing Azure Resource Manager service connection in your project. 58 | 59 | > [!NOTE] 60 | > To use the Azure Resource Manager service connection, make sure that the connection has selected the **Allow all pipelines to use this connection** checkbox. Otherwise, you must authorize the pipeline that you create in the next step. 61 | 62 | 1. Update these `./pipeline/azure-pipelines.yml` variables: 63 | 64 | * `groupId`: A value that's unique to you or your organization and is used to start the names for all resources and resource groups that are created 65 | 66 | * `location`: The name for the Azure region where you want to deploy the resources 67 | 68 | * `abbrevLocationName`: The abbreviated region name that's used in resource names 69 | 70 | 1. Create a new pipeline in your project that uses the `./powershell/azure-pipelines.yml` file from this sample. 71 | 72 | ![Animated walkthrough for creating a new pipeline](../images/create-pipeline.gif) 73 | 74 | ## Supporting documentation 75 | 76 | To learn more about the different parts in these samples, review these topics: 77 | 78 | * [Concepts](../concept-review.md) introduces the main concepts that underlie these samples. 79 | 80 | * [Naming convention](../naming-convention.md) describes the naming convention to use when creating the resources in these samples. 81 | 82 | * [Samples file structure and definitions](../file-definitions.md) explains the purpose for each file in these samples. 83 | 84 | * [Scaling](../api-connection-scale.md) expands on the reasons why these samples provide the capability to scale by increasing the number of copies for the logic apps deployed and organizing resources into separate resource groups. 85 | 86 | ## Created resources 87 | 88 | This sample creates these resources: 89 | 90 | TODO: add API management resource to the diagram 91 | 92 | ![Resources created and deployed by this sample](../images/function-app-sample.png) 93 | 94 | To learn about the scripts in this sample and how they work, review [Samples file structure and definitions](../file-definitions.md). 95 | 96 | 97 | TODO: Update descriptions to include the API management bits. 98 | 99 | This sample also implements these template and definition files: 100 | 101 | | File name | Description | 102 | |-----------|-------------| 103 | | `shared-template.json` | This template deploys the Azure function app and all its dependent resources.

The `full-deploy.ps1` script not only creates Azure resources for the function app but can publish that app's source from `./sample=function`. You must publish the app first before the logic app definition can work. To run this script, you must have the Azure Function CLI installed on your computer. Remember that the `full-deploy.ps1` script is used for local execution from a development computer. The idea behind this deployment is that the function app's code gets its own CI/CD pipeline. | 104 | | `connectors-template.json` | This template provides the parameters to inject into the logic app definition but doesn't actually deploy anything. This approach is consistent with the method for injecting parameters into a logic app across the other samples. | 105 | | `logic-app-template.json` | This template creates a shell for a logic app definition, which is blank to support separating the template from the definition. | 106 | | `logic-app-definition.json` | This file defines a basic logic app that uses a timer as a trigger and calls the provided `AwesomeFunction` each time that the logic app gets triggered. | 107 | ||| 108 | 109 | ## Clean up 110 | 111 | When you're done with the sample, delete the resource groups that were created by the sample. To remove all the resource groups with names that start with a specific `groupId` value, run this command from the PowerShell directory that contains this sample: 112 | 113 | ```powershell 114 | ./clean-up.ps1 -groupId 115 | ``` 116 | -------------------------------------------------------------------------------- /api-management-actions/sample-function/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | # local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary, but generally, the file is regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /api-management-actions/sample-function/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions" 4 | ] 5 | } -------------------------------------------------------------------------------- /api-management-actions/sample-function/AwesomeFunction.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 | 11 | namespace SampleFunction 12 | { 13 | public static class AwesomeFunction 14 | { 15 | [FunctionName("AwesomeFunction")] 16 | public static async Task Run( 17 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, 18 | ILogger log) 19 | { 20 | log.LogInformation("C# HTTP trigger function processed a request."); 21 | 22 | string name = req.Query["name"]; 23 | 24 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 25 | dynamic data = JsonConvert.DeserializeObject(requestBody); 26 | name = name ?? data?.name; 27 | 28 | return name != null 29 | ? (ActionResult)new OkObjectResult($"Hello, {name}") 30 | : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api-management-actions/sample-function/SampleFunction.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | PreserveNewest 12 | 13 | 14 | PreserveNewest 15 | Never 16 | 17 | 18 | -------------------------------------------------------------------------------- /api-management-actions/sample-function/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /api-management-actions/sample-function/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "UseDevelopmentStorage=true", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet" 6 | } 7 | } -------------------------------------------------------------------------------- /api-management-actions/templates/connectors-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "sharedResourceGroupName": "[toLower(concat(variables('baseName'), 'rgp', variables('location'), '-shared'))]", 42 | "functionAppName": "[concat(variables('baseName'), 'fa', parameters('locationName'))]", 43 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 44 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 45 | }, 46 | "resources": [ 47 | 48 | ], 49 | "outputs": { 50 | "functionAppName": { 51 | "type": "string", 52 | "value": "[variables('functionAppName')]" 53 | }, 54 | "functionAppResourceGroup": { 55 | "type": "string", 56 | "value": "[variables('sharedResourceGroupName')]" 57 | }, 58 | "subscriptionId": { 59 | "type": "string", 60 | "value": "[subscription().subscriptionId]" 61 | }, 62 | "logicAppName": { 63 | "type": "string", 64 | "value": "[variables('logicAppName')]" 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /api-management-actions/templates/logic-app-definition-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$armValues": { 3 | "value": { 4 | "functionAppName": "{functionAppName}", 5 | "functionAppResourceGroup": "{functionAppResourceGroup}", 6 | "subscriptionId": "{subscriptionId}" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /api-management-actions/templates/logic-app-definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 3 | "actions": { 4 | "AwesomeFunction": { 5 | "inputs": { 6 | "function": { 7 | "id": "/subscriptions/{subscriptionId}/resourceGroups/{functionAppResourceGroup}/providers/Microsoft.Web/sites/{functionAppName}/functions/AwesomeFunction" 8 | }, 9 | "method": "GET", 10 | "queries": { 11 | "name": "sample" 12 | } 13 | }, 14 | "runAfter": {}, 15 | "type": "Function" 16 | } 17 | }, 18 | "contentVersion": "1.0.0.0", 19 | "outputs": {}, 20 | "parameters": { 21 | "$armValues": { 22 | "defaultValue": {}, 23 | "type": "Object" 24 | } 25 | }, 26 | "triggers": { 27 | "Sliding_Window": { 28 | "recurrence": { 29 | "frequency": "Minute", 30 | "interval": 3 31 | }, 32 | "type": "SlidingWindow" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /api-management-actions/templates/logic-app-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 42 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 43 | }, 44 | "resources": [ 45 | { 46 | "name": "[variables('logicAppName')]", 47 | "type": "Microsoft.Logic/workflows", 48 | "location": "[variables('location')]", 49 | "apiVersion": "2016-06-01", 50 | "properties": { 51 | "definition": { 52 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 53 | "contentVersion": "1.0.0.0", 54 | "parameters": {}, 55 | "triggers": {}, 56 | "actions": {}, 57 | "outputs": {} 58 | } 59 | } 60 | } 61 | ], 62 | "outputs": { 63 | "logicAppName": { 64 | "type": "string", 65 | "value": "[variables('logicAppName')]" 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /concepts-review.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | This topic introduces concepts that are followed by the Azure Logic Apps samples that show how to set up a continuous integration (CI) and continuous deployment (CD) pipeline. 4 | 5 | ## Developer workflow 6 | 7 | These samples show how you can streamline both the developer workflow and the release pipeline. A developer's typical workflow looks like this example: 8 | 9 | ![Developer workflow](./images/developer-workflow.png) 10 | 11 | 1. The developer makes their changes in their development environment by using Visual Studio Code, Visual Studio, or the Azure portal. They run all the validation steps in that environment and are ready to integrate. 12 | 13 | 2. The developer downloads the logic app's definition by using Visual Studio Code. 14 | 15 | 3. The developer reviews the logic app's definition, parameters, and connections to make sure that any environment-specific components are provided dynamically, such as parameters passed into the definition, API connections that might need to be added to the connectors template, resources added to the shared template, or updates to the Azure Resource Manager template that's used to deploy the logic app. For examples, see the samples in this repository. 16 | 17 | 4. The developer commits the updated templates to source control. 18 | 19 | 5. The release pipeline automation propagates the changes to all the environments based on the implemented checkpoints. 20 | 21 | ## Duplicate resources 22 | 23 | A typical enterprise scenario usually duplicates resources for these reasons: 24 | 25 | * Development teams 26 | 27 | Most enterprise development shops have more than one developer on the team. This factor might require that different developers have separate instances. Often, one developer's impact on the resources in the environment can slow down or even halt the development efforts of other team members. When these developers have their own instances, they can work on their areas in a silo, and then integrate that work into the shared environments when other team members are ready to use that work. So, each developer has an environment that looks like this example: 28 | 29 | ![Single developer view](./images/dup-none.png) 30 | 31 | * Scaling 32 | 33 | Duplicate resources can make scaling up and down easier, depending on your approach. For more information, see the [Scaling considerations](./scaling.md) topic. 34 | 35 | ![Scaled application view](./images/dup-scale.png) 36 | 37 | * Disaster Recovery (DR) 38 | 39 | DR usually involves setting up a primary region and a secondary region. Some Azure resources have built-in geo-redundant features, but not all do. These resources require duplication across multiple regions and are usually set up to integrate with other resources in those regions. While the production environment is the only environment that actually requires DR capabilities, you must still develop and test the DR resources, which means that these resources require duplication in every environment for DR. 40 | 41 | ![Disaster recovery view](./images/dup-dr.png) 42 | 43 | * Multiple environments 44 | 45 | An enterprise usually needs more than one environment, for example, dev, test, staging, and production. 46 | 47 | ![Multiple environment view](./images/dup-environments.png) 48 | 49 | Some might argue the merits for these reasons to duplicate resources, for example: 50 | 51 | * Your throughput requirements or API connections don't need the scaling solution. 52 | * You don't have that many environments. 53 | * Your workload is not critical and doesn't require DR. 54 | 55 | However, most workloads have at least one of these reasons to duplicate resources. These samples show how you can meet these duplication needs. 56 | 57 | ## Predictable naming 58 | 59 | When you have duplicate resources, you need a consistent way to assign resource names that are unique to their environment but also easily readable by humans. A GUID assigned to each resource name provides uniqueness but not human readability. You need a [naming convention](./naming-convention.md) that meets both needs. 60 | 61 | ## Separation of concerns 62 | 63 | Each sample uses an almost identical [file structure](./file-definitions.md), not only for consistency but also to separate concerns along these boundaries: 64 | 65 | * Shared resources 66 | 67 | These resources are often used across the solution space, for example, by different logic apps, function apps, and web apps. Examples include databases, API Management resources, and Log Analytics workspaces. 68 | 69 | * Logic app dependencies 70 | 71 | Your logic app depends on specific resources, typically API connections, that aren't shared by other logic apps or areas of the solution. These resources have the same lifespan as your logic app, so they also reside in the same Azure resource group as your logic app. 72 | 73 | * Logic app definition 74 | 75 | These samples separate the logic app's definition from the Azure Resource Manager template, which is used in deployment, for these reasons: 76 | 77 | * The logic app's definition, which is the "application code", is the most frequently updated part in the CI/CD pipeline. To streamline the development process by expediting these changes over other changes, these samples pull out just the logic app definition from a development environment by using the [Visual Studio Code extension for Azure Logic Apps](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-logicapps). 78 | 79 | * The separation between definition and template is at the heart of implementing a CI/CD pipeline for Azure Logic Apps. By separating these pieces, you can focus on building the logic app's workflow, while reducing the effort to deploy these resources across multiple environments. 80 | 81 | * Logic app deployment template 82 | 83 | Likewise, these samples separate the Resource Manager template, which is used in deployment, from the logic app's definition for these reasons: 84 | 85 | * The template isn't the same as the logic app's definition. 86 | 87 | * The rate of change in the template is much slower than in the logic app definition. 88 | -------------------------------------------------------------------------------- /file-definitions.md: -------------------------------------------------------------------------------- 1 | # Samples file structure and definitions 2 | 3 | All the samples that create a continuous integration (CI) and continuous delivery (CD) pipeline for Azure Logic Apps follow a similar file structure. Although some minor differences exist, each sample contains the files described in this topic. 4 | 5 | ## PowerShell scripts 6 | 7 | The scripts in each sample follow the workflow that's described by this diagram: 8 | 9 | ![Script flow](./images/script-flow.png) 10 | 11 | | Script file name | Description | 12 | |------------------|-------------| 13 | | `full-deploy.ps1` | Runs both the `logic-app-deploy.ps1` and `shared-deploy.ps1` scripts for a sample.

This script is provided for convenience during deployment and isn't meant for use in a CI/CD pipeline. However, the deployments for the shared resources and logic app resource happen inside different CI/CD pipelines. | 14 | | `logic-app-deploy.ps1` | Brings all the logic app pieces together and performs these actions:

1. Deploy the templates for the API connectors.

2. Take the output from the connectors deployment and inject the values into both the logic app definition template and logic app definition's parameters file.

3. Deploy the number of logic app instances that's defined by the input parameters.

The parameters that pass into this script are also used for naming your logic app resources, based on the [naming convention](./naming-convention.md) used by these samples. | 15 | | `shared-deploy.ps1` | Deploys the `shared-template.json` template and is a variation on the standard PowerShell script that you get when you export from an Azure resource.

This script differs by using the parameters that pass into the script's execution to construct the parameters that pass into resource deployment. These parameters are used to implement the [naming convention](./naming-convention.md) used throughout these samples. | 16 | | `clean-up.ps1` | Removes all the resources that are deployed by a sample. Examines your subscription, finds all the resource groups that start with the group ID that's passed in, and deletes those groups.

**Caution**: Proceed carefully when you run this script. | 17 | ||| 18 | 19 | ## Templates 20 | 21 | | Template file name | Description | 22 | |--------------------|-------------| 23 | | `connectors-template.json` | This Azure Resource Manager template sets up the resources that deploy to the same resource group as the logic app.

Generally, these resources include the API connections used by the logic app. This template must provide the outputs that get injected into the `logic-app-definition.json` file and `logic-app-definition-parameters.json` file. | 24 | | `connectors-parameters.json` (optional) | This file contains parameters that deploy with the `connectors-template.json` file.

- If this file is provided, the parameters that pass in during script execution get appended to the parameters defined in this file.

- If this file isn't provided, the parameters that pass in during script execution get passed along as a hash table to the connectors template during deployment. | 25 | | `logic-app-definition.json` | This file contains your logic app's workflow definition, or "code". The separation between the logic app's definition file and the Resource Manager template used for deployment is critical to supporting multiple environments.

These samples are designed to use the [Visual Studio Code extension for Azure Logic Apps](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-logicapps) so that you can download the logic app's current workflow definition, add that definition to source control, and set up a CI/CD pipeline that deploys to multiple environments. | 26 | | `logic-app-definition-parameters.json` | This file contains parameters that deploy with the `logic-app-definition.json` file.

**Important**: Make sure that the parameters that are defined in this file are also defined in the `logic-app-definition.json` file.

The `logic-app-deploy.ps1` script replaces tokens in both the `logic-app-definition.json` file and the `logic-app-definition-parameters.json` file. The replaced tokens are defined by the output from the `connectors-template.json` file. | 27 | | `logic-app-template.json` | This Azure Resource Manager template sets up the logic app but is mostly generic without much content.

**Note**: Some samples require that the template run one time to succeed. In other words, this template only deploys the first time when the logic app deploys to an environment. When this requirement exists, the `overrideExisting` parameters in the `logic-app-deploy.ps1` script must be set to `false`. | 28 | | `logic-app-template-parameters.json` (optional) | This file contains parameters that deploy with the `logic-app-template.json` file.

- If this file is provided, the parameters that pass in during script execution get appended to the parameters defined in this file.

- If this file isn't provided, the parameters that pass in during script execution get passed along as a hash table to the logic app template during deployment. | 29 | | `shared-template.json` | This Azure Resource Manager template sets up the shared resources used by the logic app and deploys those resources to a separate resource group for these reasons:

- **Separate concerns**: These resources, such as databases, storage accounts, and event hubs, are most likely used by resources other than just the logic app.

- **Scaling**: These samples provide the capability to scale the logic app's implementations, or more specifically, the API connection implementations. The shared resources in this resource group don't require the scaling capability that other resources require. For more information, see the [Scaling](./api-connection-scale.md) topic.

- **Lifecycle management**: The lifespan for the shared resources in this resource group likely differ from the logic app. For example, future demands might require that you replace the logic app with an alternative solution. Separate resource groups gives you the flexibility to replace logic apps without having to cherry pick resources from a single resource group that stores all the resources together.

- **Rate of change**: Usually, the rate of change in shared resources templates is much lower than the rate of change in logic apps. | 30 | | `shared-template-parameters.json` (optional) | This file contains the parameters that deploy with the `shared-template.json` file.

- If this file is provided, the parameters that pass in during script execution get appended to the parameters defined in this file.

- If this file isn't provided, the parameters that pass in during script execution get passed along as a hash table to the shared resources template during deployment. | 31 | ||| 32 | -------------------------------------------------------------------------------- /function-app-actions/powershell/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | location: westus 3 | groupId: cse01 4 | abbrevLocationName: westus 5 | instanceCount: 1 6 | identifier: sample-function-app 7 | sampleDirectory: function-app-actions 8 | sharedResourceGroupIdentifier: shared 9 | 10 | trigger: 11 | - master 12 | 13 | stages: 14 | - stage: BuildFunctionApp 15 | variables: 16 | project: '**/$(sampleDirectory)/sample-function/*.csproj' 17 | buildPlatform: 'Any CPU' 18 | buildConfiguration: 'Release' 19 | jobs: 20 | - job: BuildFunctionApp 21 | pool: 22 | vmImage: 'windows-2019' 23 | steps: 24 | - task: DotNetCoreCLI@2 25 | displayName: Build Project 26 | inputs: 27 | command: 'build' 28 | projects: '$(project)' 29 | arguments: '--configuration $(buildConfiguration)' 30 | - task: ArchiveFiles@2 31 | displayName: 'Archive files' 32 | inputs: 33 | rootFolderOrFile: '$(System.DefaultWorkingDirectory)/$(sampleDirectory)/sample-function/bin/$(buildConfiguration)/netcoreapp2.1/' 34 | includeRootFolder: false 35 | archiveType: zip 36 | archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip 37 | replaceExistingArchive: true 38 | - task: PublishPipelineArtifact@1 39 | inputs: 40 | path: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip' 41 | ArtifactName: 'function-app' 42 | - stage: Development 43 | jobs: 44 | - job: DeployDevelopmentEnvironment 45 | variables: 46 | environment: d 47 | pool: 48 | vmImage: 'windows-2019' 49 | steps: 50 | - script: | 51 | echo ##vso[task.setvariable variable=resourceGroupName]$(groupId)$(environment)rgp$(location)-$(sharedResourceGroupIdentifier) 52 | echo ##vso[task.setvariable variable=templateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\shared-template.json 53 | echo ##vso[task.setvariable variable=logicAppDeployScript]$(Build.Repository.LocalPath)\$(sampleDirectory)\powershell\logic-app-deploy.ps1 54 | echo ##vso[task.setvariable variable=functionAppName]$(groupId)$(environment)fa$(abbrevLocationName) 55 | name: setDynamicVariableNames 56 | - powershell: | 57 | Write-Host "templateFilePath: $(templateFilePath)" 58 | Write-Host "resourceGroupName: $(resourceGroupName)" 59 | Write-Host "logicAppDeployScript: $(logicAppDeployScript)" 60 | name: printOutVariables 61 | - task: AzureResourceGroupDeployment@2 62 | name: SharedResourcesDeployment 63 | inputs: 64 | azureSubscription: 'Azure Samples Subscription' 65 | action: 'Create Or Update Resource Group' 66 | resourceGroupName: $(resourceGroupName) 67 | location: $(location) 68 | templateLocation: Linked artifact 69 | csmFile: $(templateFilePath) 70 | deploymentMode: 'Incremental' 71 | deploymentName: $(Build.BuildNumber) 72 | overrideParameters: -groupId $(groupId) -environment $(environment) -locationName $(abbrevLocationName) 73 | - task: DownloadPipelineArtifact@2 74 | displayName: Download Function App 75 | inputs: 76 | artifactName: function-app 77 | - task: AzureFunctionApp@1 78 | name: DeployFunctionApp 79 | inputs: 80 | azureSubscription: 'Azure Samples Subscription' 81 | appType: 'functionApp' 82 | appName: $(functionAppName) 83 | package: '$(Pipeline.Workspace)/**/$(Build.BuildId).zip' 84 | deploymentMethod: 'auto' 85 | - script: | 86 | echo ##vso[task.setvariable variable=connectorsTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\connectors-template.json 87 | echo ##vso[task.setvariable variable=logicAppTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-template.json 88 | echo ##vso[task.setvariable variable=logicAppDefinitionPath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition.json 89 | echo ##vso[task.setvariable variable=logicAppDefinitionParametersFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition-parameters.json 90 | - task: AzurePowerShell@4 91 | name: DeployLogicAppSample 92 | inputs: 93 | azureSubscription: 'Azure Samples Subscription' 94 | ScriptType: 'FilePath' 95 | ScriptPath: '$(logicAppDeployScript)' 96 | FailOnStandardError: true 97 | azurePowerShellVersion: LatestVersion 98 | ScriptArguments: >- 99 | -groupId $(groupId) 100 | -location $(location) 101 | -abbrevLocationName $(abbrevLocationName) 102 | -environment $(environment) 103 | -instanceCount $(instanceCount) 104 | -identifier $(identifier) 105 | -connectorsTemplateFilePath $(connectorsTemplateFilePath) 106 | -logicAppTemplateFilePath $(logicAppTemplateFilePath) 107 | -logicAppDefinitionPath $(logicAppDefinitionPath) 108 | -logicAppDefinitionParametersFilePath $(logicAppDefinitionParametersFilePath) -------------------------------------------------------------------------------- /function-app-actions/powershell/clean-up.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deletes all resource groups where their names start with the "groupId" that's passed as input. 4 | 5 | .PARAMETER groupId 6 | The value that identifies the resource group to delete 7 | #> 8 | 9 | param( 10 | [Parameter(Mandatory = $True)] 11 | [string] 12 | $groupId 13 | ) 14 | 15 | $resourceGroups = Get-AzResourceGroup; 16 | 17 | foreach ($resourceGroup in $resourceGroups) { 18 | if ($resourceGroup.ResourceGroupName.StartsWith($groupId, 'CurrentCultureIgnoreCase')) { 19 | Remove-AzResourceGroup -Name $resourceGroup.ResourceGroupName -Force -AsJob 20 | Write-Host "Deleted resource group $($resourceGroup.ResourceGroupName)" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /function-app-actions/powershell/full-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploys a template for a logic app to Azure. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER instanceCount 24 | The number of resource group instances to create 25 | 26 | .PARAMETER overrideExistingLogicApp 27 | If true, the script runs the Azure Resource Manager template deployment for the logic app, even when the logic app already exists. 28 | #> 29 | 30 | param( 31 | 32 | [Parameter(Mandatory=$True)] 33 | [string] 34 | $groupId, 35 | 36 | [Parameter(Mandatory=$True)] 37 | [string] 38 | $location, 39 | 40 | [Parameter(Mandatory=$True)] 41 | [string] 42 | $environment, 43 | 44 | [Parameter(Mandatory=$False)] 45 | [string] 46 | $abbrevLocationName = $location, 47 | 48 | [Parameter(Mandatory=$False)] 49 | [string] 50 | $deploymentName = [guid]::NewGuid(), 51 | 52 | [Parameter(Mandatory=$False)] 53 | [int] 54 | $instanceCount = 1, 55 | 56 | [Parameter(Mandatory = $False)] 57 | [bool] 58 | $overrideExistingLogicApp = $True 59 | ) 60 | 61 | Write-Host "Running shared deployment script" 62 | 63 | $result = & "${PSScriptRoot}\shared-deploy.ps1" -groupId $groupId ` 64 | -location $location ` 65 | -environment $environment ` 66 | -abbrevLocationName $abbrevLocationName ` 67 | -deploymentName $deploymentName; 68 | 69 | Write-Host "Deploying Function App" 70 | 71 | Push-Location "${$PSScriptRoot}../sample-function" 72 | 73 | $deployCommand = "func azure functionapp publish {0}" -f $result.Outputs.functionAppName.Value 74 | 75 | Invoke-Expression $deployCommand 76 | 77 | Pop-Location 78 | 79 | Write-Host "Running logic app deployment script" 80 | 81 | & "${PSScriptRoot}\logic-app-deploy.ps1" -groupId $groupId ` 82 | -location $location ` 83 | -environment $environment ` 84 | -abbrevLocationName $abbrevLocationName ` 85 | -deploymentName $deploymentName ` 86 | -identifier "sample-function-app" ` 87 | -instanceCount $instanceCount ` 88 | -overrideExisting $overrideExistingLogicApp; -------------------------------------------------------------------------------- /function-app-actions/powershell/shared-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploy the shared resources. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | OPTIONAL: The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | OPTIONAL: The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER identifier 24 | OPTIONAL: Value appended to the end of the resource group and the logic app within the resource group 25 | 26 | .PARAMETER parametersFilePath 27 | OPTIONAL: Path to the parameter file. Defaults to the file contained within the local directory 28 | 29 | .PARAMETER templateFilePath 30 | OPTIONAL: Path to the template file. Defaults to the file contained within the local directory 31 | #> 32 | 33 | param( 34 | [Parameter(Mandatory = $True)] 35 | [string] 36 | $groupId, 37 | 38 | [Parameter(Mandatory = $True)] 39 | [string] 40 | $location, 41 | 42 | [Parameter(Mandatory = $True)] 43 | [string] 44 | $environment, 45 | 46 | [Parameter(Mandatory = $False)] 47 | [string] 48 | $abbrevLocationName = $location, 49 | 50 | [Parameter(Mandatory = $False)] 51 | [string] 52 | $deploymentName = [guid]::NewGuid(), 53 | 54 | [Parameter(Mandatory = $False)] 55 | [string] 56 | $identifier = "shared", 57 | 58 | [Parameter(Mandatory = $False)] 59 | [string] 60 | $parametersFilePath = "../templates/shared-parameters.json", 61 | 62 | [Parameter(Mandatory = $False)] 63 | [string] 64 | $templateFilePath = "../templates/shared-template.json" 65 | ) 66 | 67 | Function RegisterRP { 68 | <# 69 | .SYNOPSIS 70 | Register the Azure resource provider. 71 | #> 72 | Param( 73 | [Parameter(Mandatory = $True, HelpMessage = "The name of the resource provider to register")] 74 | [string]$ResourceProviderNamespace 75 | ) 76 | 77 | Write-Host "Registering resource provider '$ResourceProviderNamespace'"; 78 | $registered = Register-AzResourceProvider -ProviderNamespace $ResourceProviderNamespace; 79 | } 80 | 81 | Function Set-RelativeFilePaths { 82 | <# 83 | .SYNOPSIS 84 | Fix relative paths that can't be evaluated as given. 85 | .DESCRIPTION 86 | Validate whether the file paths can be evaluated as they're passed as input. 87 | If the paths are relative and can't be found, try to fix by prepending the 88 | root path for script execution to the given path. 89 | #> 90 | Param() 91 | $parametersFilePath = Set-RelativeFilePath -filePath $parametersFilePath 92 | $templateFilePath = Set-RelativeFilePath -filePath $templateFilePath 93 | } 94 | 95 | Function Set-RelativeFilePath { 96 | <# 97 | .SYNOPSIS 98 | Validate a single file path and prepend the root path for script execution when the path fails. 99 | #> 100 | Param( 101 | [Parameter(Mandatory = $True, HelpMessage = "The path of the file to test whether the file exists")] 102 | [string]$filePath 103 | ) 104 | 105 | if (Test-Path $filePath) { 106 | return $filePath; 107 | } 108 | else { 109 | return "{0}\{1}" -f $PSScriptRoot, $filePath; 110 | } 111 | } 112 | 113 | Function Set-ResourceGroup { 114 | <# 115 | .SYNOPSIS 116 | Check whether the resource group exists. If not, create the resource group. 117 | #> 118 | Param( 119 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to create, if not already existing")] 120 | [string]$resourceGroupName 121 | ) 122 | 123 | Write-Host "Working with resource group name: '$resourceGroupName'" 124 | 125 | Write-Host "Checking whether the resource group exists" 126 | $resourceGroup = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue 127 | if (!$resourceGroup) { 128 | Write-Host "Resource group '$resourceGroupName' does not exist. Creating new resource group"; 129 | 130 | Write-Host "Creating resource group '$resourceGroupName' in location '$location'"; 131 | New-AzResourceGroup -Name $resourceGroupName -Location $location 132 | } 133 | else { 134 | Write-Host "Using existing resource group '$resourceGroupName'"; 135 | } 136 | } 137 | 138 | Function New-ResourceManagerTemplateDeployment { 139 | <# 140 | .SYNOPSIS 141 | Create an Azure Resource Manager template deployment. 142 | .OUTPUTS 143 | The results from the Azure Resource Manager template deployment 144 | #> 145 | Param( 146 | [Parameter(Mandatory = $True, HelpMessage = "The path for the parameter file to use for deployment")] 147 | [string]$parametersFilePath, 148 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to use for deployment")] 149 | [string]$resourceGroupName, 150 | [Parameter(Mandatory = $True, HelpMessage = "The path to the template file to use for the deployment")] 151 | [string]$templateFilePath, 152 | [Parameter(Mandatory = $True, HelpMessage = "The values to append to the parameters before deployment")] 153 | [hashtable]$armParameters 154 | ) 155 | if (Test-Path $parametersFilePath) { 156 | Write-Host "Loading parameters from file"; 157 | $parametersFromFile = Get-Content -Raw -Encoding UTF8 -Path $parametersFilePath | ConvertFrom-Json 158 | 159 | Write-Host "Adding script parameters"; 160 | New-Parameter -parameters $parametersFromFile -name "groupId" -value $groupId 161 | New-Parameter -parameters $parametersFromFile -name "environment" -value $environment 162 | New-Parameter -parameters $parametersFromFile -name "locationName" -value $locationName 163 | 164 | Write-Host "Starting deployment..."; 165 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $parametersFromFile; 166 | } 167 | else { 168 | Write-Host "Starting deployment..."; 169 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $armParameters; 170 | } 171 | } 172 | 173 | Function New-Parameter { 174 | <# 175 | .SYNOPSIS 176 | Append a parameter to the hash table for parameters that are passed as input. 177 | #> 178 | Param ( 179 | [Parameter(Mandatory = $True, HelpMessage = "The hash table where to add the parameter")] 180 | [hashtable]$parameters, 181 | [Parameter(Mandatory = $True, HelpMessage = "The name for the parameter to add")] 182 | [string]$name, 183 | [Parameter(Mandatory = $True, HelpMessage = "The value for the parameter to add")] 184 | [string]$value 185 | ) 186 | $parameters | Add-Member -MemberType NoteProperty -Name $name -Value @{value = $value } 187 | } 188 | 189 | #****************************************************************************** 190 | # Script body 191 | # Execution begins here 192 | #****************************************************************************** 193 | 194 | if (!$PSScriptRoot) { $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } 195 | 196 | Set-RelativeFilePaths; 197 | 198 | $resourceProviders = @("Microsoft.Web","Microsoft.Insights","Microsoft.Storage"); 199 | if ($resourceProviders.length) { 200 | Write-Host "Registering resource providers" 201 | foreach ($resourceProvider in $resourceProviders) { 202 | RegisterRP($resourceProvider); 203 | } 204 | } 205 | 206 | $resourceGroupName = "{0}{1}rgp{2}-{3}" -f $groupId, $environment, $location, $identifier; 207 | 208 | Set-ResourceGroup -resourceGroupName $resourceGroupName; 209 | 210 | $armParameters = @{ 211 | groupId = $groupId 212 | environment = $environment 213 | locationName = $abbrevLocationName 214 | }; 215 | 216 | $result = New-ResourceManagerTemplateDeployment -parametersFilePath $parametersFilePath ` 217 | -resourceGroupName $resourceGroupName ` 218 | -templateFilePath $templateFilePath ` 219 | -armParameters $armParameters; 220 | 221 | return $result; -------------------------------------------------------------------------------- /function-app-actions/sample-function/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | # local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary, but generally, the file is regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /function-app-actions/sample-function/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions" 4 | ] 5 | } -------------------------------------------------------------------------------- /function-app-actions/sample-function/AwesomeFunction.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 | 11 | namespace SampleFunction 12 | { 13 | public static class AwesomeFunction 14 | { 15 | [FunctionName("AwesomeFunction")] 16 | public static async Task Run( 17 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, 18 | ILogger log) 19 | { 20 | log.LogInformation("C# HTTP trigger function processed a request."); 21 | 22 | string name = req.Query["name"]; 23 | 24 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 25 | dynamic data = JsonConvert.DeserializeObject(requestBody); 26 | name = name ?? data?.name; 27 | 28 | return name != null 29 | ? (ActionResult)new OkObjectResult($"Hello, {name}") 30 | : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /function-app-actions/sample-function/SampleFunction.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | PreserveNewest 12 | 13 | 14 | PreserveNewest 15 | Never 16 | 17 | 18 | -------------------------------------------------------------------------------- /function-app-actions/sample-function/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /function-app-actions/sample-function/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "UseDevelopmentStorage=true", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet" 6 | } 7 | } -------------------------------------------------------------------------------- /function-app-actions/templates/connectors-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "sharedResourceGroupName": "[toLower(concat(variables('baseName'), 'rgp', variables('location'), '-shared'))]", 42 | "functionAppName": "[concat(variables('baseName'), 'fa', parameters('locationName'))]", 43 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 44 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 45 | }, 46 | "resources": [ 47 | 48 | ], 49 | "outputs": { 50 | "functionAppName": { 51 | "type": "string", 52 | "value": "[variables('functionAppName')]" 53 | }, 54 | "functionAppResourceGroup": { 55 | "type": "string", 56 | "value": "[variables('sharedResourceGroupName')]" 57 | }, 58 | "subscriptionId": { 59 | "type": "string", 60 | "value": "[subscription().subscriptionId]" 61 | }, 62 | "logicAppName": { 63 | "type": "string", 64 | "value": "[variables('logicAppName')]" 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /function-app-actions/templates/logic-app-definition-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$armValues": { 3 | "value": { 4 | "functionAppName": "{functionAppName}", 5 | "functionAppResourceGroup": "{functionAppResourceGroup}", 6 | "subscriptionId": "{subscriptionId}" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /function-app-actions/templates/logic-app-definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 3 | "actions": { 4 | "AwesomeFunction": { 5 | "inputs": { 6 | "function": { 7 | "id": "/subscriptions/{subscriptionId}/resourceGroups/{functionAppResourceGroup}/providers/Microsoft.Web/sites/{functionAppName}/functions/AwesomeFunction" 8 | }, 9 | "method": "GET", 10 | "queries": { 11 | "name": "sample" 12 | } 13 | }, 14 | "runAfter": {}, 15 | "type": "Function" 16 | } 17 | }, 18 | "contentVersion": "1.0.0.0", 19 | "outputs": {}, 20 | "parameters": { 21 | "$armValues": { 22 | "defaultValue": {}, 23 | "type": "Object" 24 | } 25 | }, 26 | "triggers": { 27 | "Sliding_Window": { 28 | "recurrence": { 29 | "frequency": "Minute", 30 | "interval": 3 31 | }, 32 | "type": "SlidingWindow" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /function-app-actions/templates/logic-app-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 42 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 43 | }, 44 | "resources": [ 45 | { 46 | "name": "[variables('logicAppName')]", 47 | "type": "Microsoft.Logic/workflows", 48 | "location": "[variables('location')]", 49 | "apiVersion": "2016-06-01", 50 | "properties": { 51 | "definition": { 52 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 53 | "contentVersion": "1.0.0.0", 54 | "parameters": {}, 55 | "triggers": {}, 56 | "actions": {}, 57 | "outputs": {} 58 | } 59 | } 60 | } 61 | ], 62 | "outputs": { 63 | "logicAppName": { 64 | "type": "string", 65 | "value": "[variables('logicAppName')]" 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /function-app-actions/templates/shared-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "appInsightsRegion": { 25 | "defaultValue": "westus2", 26 | "type": "string", 27 | "metadata": { 28 | "description": "The region where to deploy the Application Insights resource" 29 | } 30 | } 31 | }, 32 | "variables": { 33 | "location": "[resourceGroup().location]", 34 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 35 | "appServicePlanName": "[concat(variables('baseName'), 'asp', parameters('locationName'))]", 36 | "functionAppName": "[concat(variables('baseName'), 'fa', parameters('locationName'))]", 37 | "storageAccountName": "[concat(variables('baseName'), 'sa2', parameters('locationName'))]", 38 | "appInsightsName": "[toLower(concat(variables('baseName'), 'ai', parameters('locationName')))]" 39 | }, 40 | "resources": [ 41 | { 42 | "type": "Microsoft.Web/serverfarms", 43 | "apiVersion": "2016-09-01", 44 | "name": "[variables('appServicePlanName')]", 45 | "location": "[variables('location')]", 46 | "sku": { 47 | "name": "Y1", 48 | "tier": "Dynamic", 49 | "size": "Y1", 50 | "family": "Y", 51 | "capacity": 0 52 | }, 53 | "tags": { 54 | "groupdId": "[parameters('groupId')]" 55 | }, 56 | "properties": { 57 | "name": "[variables('appServicePlanName')]" 58 | }, 59 | "kind": "functionapp" 60 | }, 61 | { 62 | "name": "[variables('storageAccountName')]", 63 | "type": "Microsoft.Storage/storageAccounts", 64 | "apiVersion": "2019-04-01", 65 | "tags": { 66 | "groupdId": "[parameters('groupId')]" 67 | }, 68 | "sku": { 69 | "name": "Standard_LRS" 70 | }, 71 | "kind": "StorageV2", 72 | "location": "[variables('location')]", 73 | "properties": { 74 | "accessTier": "Hot" 75 | }, 76 | "resources": [] 77 | }, 78 | { 79 | "type": "Microsoft.Insights/components", 80 | "apiVersion": "2015-05-01", 81 | "name": "[variables('appInsightsName')]", 82 | "location": "[parameters('appInsightsRegion')]", 83 | "kind": "web", 84 | "properties": { 85 | "Application_Type": "web" 86 | } 87 | }, 88 | { 89 | "type": "Microsoft.Web/sites", 90 | "apiVersion": "2016-08-01", 91 | "name": "[variables('functionAppName')]", 92 | "location": "[variables('location')]", 93 | "tags": { 94 | "groupdId": "[parameters('groupId')]" 95 | }, 96 | "dependsOn": [ 97 | "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]", 98 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]", 99 | "[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]" 100 | ], 101 | "kind": "functionapp", 102 | "properties": { 103 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]", 104 | "httpsOnly": true, 105 | "siteConfig": { 106 | "appSettings": [ 107 | { 108 | "name": "AzureWebJobsDashboard", 109 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(concat(resourceGroup().id, '/providers/', 'Microsoft.Storage/storageAccounts/', variables('storageAccountName')),'2015-05-01-preview').key1)]" 110 | }, 111 | { 112 | "name": "AzureWebJobsStorage", 113 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(concat(resourceGroup().id, '/providers/', 'Microsoft.Storage/storageAccounts/', variables('storageAccountName')),'2015-05-01-preview').key1)]" 114 | }, 115 | { 116 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", 117 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(concat(resourceGroup().id, '/providers/', 'Microsoft.Storage/storageAccounts/', variables('storageAccountName')),'2015-05-01-preview').key1)]" 118 | }, 119 | { 120 | "name": "WEBSITE_CONTENTSHARE", 121 | "value": "[variables('functionAppName')]" 122 | }, 123 | { 124 | "name": "FUNCTIONS_EXTENSION_VERSION", 125 | "value": "~2" 126 | }, 127 | { 128 | "name": "FUNCTIONS_WORKER_RUNTIME", 129 | "value": "dotnet" 130 | }, 131 | { 132 | "name": "WEBSITE_NODE_DEFAULT_VERSION", 133 | "value": "10.14.1" 134 | }, 135 | { 136 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY", 137 | "value": "[reference(resourceId('Microsoft.Insights/components/', variables('appInsightsName'))).InstrumentationKey]" 138 | } 139 | ] 140 | } 141 | } 142 | } 143 | ], 144 | "outputs": { 145 | "functionAppName": { 146 | "type": "string", 147 | "value": "[variables('functionAppName')]" 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /images/create-pipeline.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/create-pipeline.gif -------------------------------------------------------------------------------- /images/developer-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/developer-workflow.png -------------------------------------------------------------------------------- /images/double-sb-connection-single-instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/double-sb-connection-single-instance.png -------------------------------------------------------------------------------- /images/double-sb-connection-three-instance-one-cosmos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/double-sb-connection-three-instance-one-cosmos.png -------------------------------------------------------------------------------- /images/double-sb-connection-three-instance-two-cosmos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/double-sb-connection-three-instance-two-cosmos.png -------------------------------------------------------------------------------- /images/double-sb-connection-two-instance-one-cosmos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/double-sb-connection-two-instance-one-cosmos.png -------------------------------------------------------------------------------- /images/double-sb-connection-two-instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/double-sb-connection-two-instance.png -------------------------------------------------------------------------------- /images/dup-dr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/dup-dr.png -------------------------------------------------------------------------------- /images/dup-environments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/dup-environments.png -------------------------------------------------------------------------------- /images/dup-none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/dup-none.png -------------------------------------------------------------------------------- /images/dup-scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/dup-scale.png -------------------------------------------------------------------------------- /images/function-app-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/function-app-sample.png -------------------------------------------------------------------------------- /images/sample-integration-act.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/sample-integration-act.png -------------------------------------------------------------------------------- /images/scaled-logic-app-resources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/scaled-logic-app-resources.png -------------------------------------------------------------------------------- /images/scaled-resource-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/scaled-resource-group.png -------------------------------------------------------------------------------- /images/script-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/script-flow.png -------------------------------------------------------------------------------- /images/servicebus-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/servicebus-sample.png -------------------------------------------------------------------------------- /images/single-sb-connection-single-instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/single-sb-connection-single-instance.png -------------------------------------------------------------------------------- /images/storage-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-logic-apps-deployment-samples/a8eebaf9ceea8b05b0fced59a1ef423210bd8494/images/storage-sample.png -------------------------------------------------------------------------------- /integration-account-connections/powershell/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | location: westus 3 | groupId: cse01 4 | abbrevLocationName: westus 5 | instanceCount: 1 6 | identifier: sample-int-account 7 | sampleDirectory: integration-account-connections 8 | sharedResourceGroupIdentifier: shared 9 | 10 | trigger: 11 | - master 12 | 13 | stages: 14 | - stage: Development 15 | jobs: 16 | - job: DeployDevelopmentEnvironment 17 | variables: 18 | environment: d 19 | pool: 20 | vmImage: 'windows-2019' 21 | steps: 22 | - script: | 23 | echo ##vso[task.setvariable variable=resourceGroupName]$(groupId)$(environment)rgp$(location)-$(sharedResourceGroupIdentifier) 24 | echo ##vso[task.setvariable variable=templateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\shared-template.json 25 | echo ##vso[task.setvariable variable=logicAppDeployScript]$(Build.Repository.LocalPath)\$(sampleDirectory)\powershell\logic-app-deploy.ps1 26 | name: setDynamicVariableNames 27 | - powershell: | 28 | Write-Host "templateFilePath: $(templateFilePath)" 29 | Write-Host "resourceGroupName: $(resourceGroupName)" 30 | Write-Host "logicAppDeployScript: $(logicAppDeployScript)" 31 | name: printOutVariables 32 | - task: AzureResourceGroupDeployment@2 33 | name: SharedResourcesDeployment 34 | inputs: 35 | azureSubscription: 'Azure Samples Subscription' 36 | action: 'Create Or Update Resource Group' 37 | resourceGroupName: $(resourceGroupName) 38 | location: $(location) 39 | templateLocation: Linked artifact 40 | csmFile: $(templateFilePath) 41 | deploymentMode: 'Incremental' 42 | deploymentName: $(Build.BuildNumber) 43 | overrideParameters: -groupId $(groupId) -environment $(environment) -locationName $(abbrevLocationName) 44 | - script: | 45 | echo ##vso[task.setvariable variable=connectorsTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\connectors-template.json 46 | echo ##vso[task.setvariable variable=logicAppTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-template.json 47 | echo ##vso[task.setvariable variable=logicAppDefinitionPath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition.json 48 | echo ##vso[task.setvariable variable=logicAppDefinitionParametersFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition-parameters.json 49 | - task: AzurePowerShell@4 50 | name: DeployLogicAppSample 51 | inputs: 52 | azureSubscription: 'Azure Samples Subscription' 53 | ScriptType: 'FilePath' 54 | ScriptPath: '$(logicAppDeployScript)' 55 | FailOnStandardError: true 56 | azurePowerShellVersion: LatestVersion 57 | ScriptArguments: >- 58 | -groupId $(groupId) 59 | -location $(location) 60 | -abbrevLocationName $(abbrevLocationName) 61 | -environment $(environment) 62 | -instanceCount $(instanceCount) 63 | -identifier $(identifier) 64 | -connectorsTemplateFilePath $(connectorsTemplateFilePath) 65 | -logicAppTemplateFilePath $(logicAppTemplateFilePath) 66 | -logicAppDefinitionPath $(logicAppDefinitionPath) 67 | -logicAppDefinitionParametersFilePath $(logicAppDefinitionParametersFilePath) -------------------------------------------------------------------------------- /integration-account-connections/powershell/clean-up.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deletes all resource groups where their names start with the "groupId" that's passed as input. 4 | 5 | .PARAMETER groupId 6 | The value that identifies the resource group to delete 7 | #> 8 | 9 | param( 10 | [Parameter(Mandatory = $True)] 11 | [string] 12 | $groupId 13 | ) 14 | 15 | $resourceGroups = Get-AzResourceGroup; 16 | 17 | foreach ($resourceGroup in $resourceGroups) { 18 | if ($resourceGroup.ResourceGroupName.StartsWith($groupId, 'CurrentCultureIgnoreCase')) { 19 | Remove-AzResourceGroup -Name $resourceGroup.ResourceGroupName -Force -AsJob 20 | Write-Host "Deleted resource group $($resourceGroup.ResourceGroupName)" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /integration-account-connections/powershell/full-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploys a template for a logic app to Azure. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER instanceCount 24 | The number of resource group instances to create 25 | 26 | .PARAMETER overrideExistingLogicApp 27 | If true, the script runs the Azure Resource Manager template deployment for the logic app, even when the logic app already exists. 28 | #> 29 | 30 | param( 31 | 32 | [Parameter(Mandatory=$True)] 33 | [string] 34 | $groupId, 35 | 36 | [Parameter(Mandatory=$True)] 37 | [string] 38 | $location, 39 | 40 | [Parameter(Mandatory=$True)] 41 | [string] 42 | $environment, 43 | 44 | [Parameter(Mandatory=$False)] 45 | [string] 46 | $abbrevLocationName = $location, 47 | 48 | [Parameter(Mandatory=$False)] 49 | [string] 50 | $deploymentName = [guid]::NewGuid(), 51 | 52 | [Parameter(Mandatory=$False)] 53 | [int] 54 | $instanceCount = 1, 55 | 56 | [Parameter(Mandatory = $False)] 57 | [bool] 58 | $overrideExistingLogicApp = $True 59 | ) 60 | 61 | Write-Host "Running shared deployment script" 62 | 63 | & "${PSScriptRoot}\shared-deploy.ps1" -groupId $groupId ` 64 | -location $location ` 65 | -environment $environment ` 66 | -abbrevLocationName $abbrevLocationName ` 67 | -deploymentName $deploymentName; 68 | 69 | Write-Host "Running logic app deployment script" 70 | 71 | & "${PSScriptRoot}\logic-app-deploy.ps1" -groupId $groupId ` 72 | -location $location ` 73 | -environment $environment ` 74 | -abbrevLocationName $abbrevLocationName ` 75 | -deploymentName $deploymentName ` 76 | -identifier "sample-int-account" ` 77 | -instanceCount $instanceCount ` 78 | -overrideExisting $overrideExistingLogicApp; -------------------------------------------------------------------------------- /integration-account-connections/powershell/shared-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploy the shared resources. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | OPTIONAL: The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | OPTIONAL: The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER identifier 24 | OPTIONAL: Value appended to the end of the resource group and the logic app within the resource group 25 | 26 | .PARAMETER parametersFilePath 27 | OPTIONAL: Path to the parameter file. Defaults to the file contained within the local directory 28 | 29 | .PARAMETER templateFilePath 30 | OPTIONAL: Path to the template file. Defaults to the file contained within the local directory 31 | #> 32 | 33 | param( 34 | [Parameter(Mandatory = $True)] 35 | [string] 36 | $groupId, 37 | 38 | [Parameter(Mandatory = $True)] 39 | [string] 40 | $location, 41 | 42 | [Parameter(Mandatory = $True)] 43 | [string] 44 | $environment, 45 | 46 | [Parameter(Mandatory = $False)] 47 | [string] 48 | $abbrevLocationName = $location, 49 | 50 | [Parameter(Mandatory = $False)] 51 | [string] 52 | $deploymentName = [guid]::NewGuid(), 53 | 54 | [Parameter(Mandatory = $False)] 55 | [string] 56 | $identifier = "shared", 57 | 58 | [Parameter(Mandatory = $False)] 59 | [string] 60 | $parametersFilePath = "../templates/shared-parameters.json", 61 | 62 | [Parameter(Mandatory = $False)] 63 | [string] 64 | $templateFilePath = "../templates/shared-template.json" 65 | ) 66 | 67 | Function RegisterRP { 68 | <# 69 | .SYNOPSIS 70 | Register the Azure resource provider. 71 | #> 72 | Param( 73 | [Parameter(Mandatory = $True, HelpMessage = "The name of the resource provider to register")] 74 | [string]$ResourceProviderNamespace 75 | ) 76 | 77 | Write-Host "Registering resource provider '$ResourceProviderNamespace'"; 78 | Register-AzResourceProvider -ProviderNamespace $ResourceProviderNamespace; 79 | } 80 | 81 | Function Set-RelativeFilePaths { 82 | <# 83 | .SYNOPSIS 84 | Fix relative paths that can't be evaluated as given. 85 | .DESCRIPTION 86 | Validate whether the file paths can be evaluated as they're passed as input. 87 | If the paths are relative and can't be found, try to fix by prepending the 88 | root path for script execution to the given path. 89 | #> 90 | Param() 91 | $parametersFilePath = Set-RelativeFilePath -filePath $parametersFilePath 92 | $templateFilePath = Set-RelativeFilePath -filePath $templateFilePath 93 | } 94 | 95 | Function Set-RelativeFilePath { 96 | <# 97 | .SYNOPSIS 98 | Validate a single file path and prepend the root path for script execution when the path fails. 99 | #> 100 | Param( 101 | [Parameter(Mandatory = $True, HelpMessage = "The path of the file to test whether the file exists")] 102 | [string]$filePath 103 | ) 104 | 105 | if (Test-Path $filePath) { 106 | return $filePath; 107 | } 108 | else { 109 | return "{0}\{1}" -f $PSScriptRoot, $filePath; 110 | } 111 | } 112 | 113 | Function Set-ResourceGroup { 114 | <# 115 | .SYNOPSIS 116 | Check whether the resource group exists. If not, create the resource group. 117 | #> 118 | Param( 119 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to create, if not already existing")] 120 | [string]$resourceGroupName 121 | ) 122 | 123 | Write-Host "Working with resource group name: '$resourceGroupName'" 124 | 125 | Write-Host "Checking whether the resource group exists" 126 | $resourceGroup = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue 127 | if (!$resourceGroup) { 128 | Write-Host "Resource group '$resourceGroupName' does not exist. Creating new resource group"; 129 | 130 | Write-Host "Creating resource group '$resourceGroupName' in location '$location'"; 131 | New-AzResourceGroup -Name $resourceGroupName -Location $location 132 | } 133 | else { 134 | Write-Host "Using existing resource group '$resourceGroupName'"; 135 | } 136 | } 137 | 138 | Function New-ResourceManagerTemplateDeployment { 139 | <# 140 | .SYNOPSIS 141 | Create an Azure Resource Manager template deployment. 142 | .OUTPUTS 143 | The results from the Azure Resource Manager template deployment 144 | #> 145 | Param( 146 | [Parameter(Mandatory = $True, HelpMessage = "The path for the parameter file to use for deployment")] 147 | [string]$parametersFilePath, 148 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to use for deployment")] 149 | [string]$resourceGroupName, 150 | [Parameter(Mandatory = $True, HelpMessage = "The path to the template file to use for the deployment")] 151 | [string]$templateFilePath, 152 | [Parameter(Mandatory = $True, HelpMessage = "The values to append to the parameters before deployment")] 153 | [hashtable]$armParameters 154 | ) 155 | if (Test-Path $parametersFilePath) { 156 | Write-Host "Loading parameters from file"; 157 | $parametersFromFile = Get-Content -Raw -Encoding UTF8 -Path $parametersFilePath | ConvertFrom-Json 158 | 159 | Write-Host "Adding script parameters"; 160 | New-Parameter -parameters $parametersFromFile -name "groupId" -value $groupId 161 | New-Parameter -parameters $parametersFromFile -name "environment" -value $environment 162 | New-Parameter -parameters $parametersFromFile -name "locationName" -value $locationName 163 | 164 | Write-Host "Starting deployment..."; 165 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $parametersFromFile; 166 | } 167 | else { 168 | Write-Host "Starting deployment..."; 169 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $armParameters; 170 | } 171 | } 172 | 173 | Function New-Parameter { 174 | <# 175 | .SYNOPSIS 176 | Append a parameter to the hash table for parameters that are passed as input. 177 | #> 178 | Param ( 179 | [Parameter(Mandatory = $True, HelpMessage = "The hash table where to add the parameter")] 180 | [hashtable]$parameters, 181 | [Parameter(Mandatory = $True, HelpMessage = "The name for the parameter to add")] 182 | [string]$name, 183 | [Parameter(Mandatory = $True, HelpMessage = "The value for the parameter to add")] 184 | [string]$value 185 | ) 186 | $parameters | Add-Member -MemberType NoteProperty -Name $name -Value @{value = $value } 187 | } 188 | 189 | #****************************************************************************** 190 | # Script body 191 | # Execution begins here 192 | #****************************************************************************** 193 | 194 | if (!$PSScriptRoot) { $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } 195 | 196 | Set-RelativeFilePaths 197 | 198 | $resourceProviders = @("Microsoft.Logic"); 199 | if ($resourceProviders.length) { 200 | Write-Host "Registering resource providers" 201 | foreach ($resourceProvider in $resourceProviders) { 202 | RegisterRP($resourceProvider); 203 | } 204 | } 205 | 206 | $resourceGroupName = "{0}{1}rgp{2}-{3}" -f $groupId, $environment, $location, $identifier 207 | 208 | Set-ResourceGroup -resourceGroupName $resourceGroupName 209 | 210 | $armParameters = @{ 211 | groupId = $groupId 212 | environment = $environment 213 | locationName = $abbrevLocationName 214 | }; 215 | 216 | New-ResourceManagerTemplateDeployment -parametersFilePath $parametersFilePath ` 217 | -resourceGroupName $resourceGroupName ` 218 | -templateFilePath $templateFilePath ` 219 | -armParameters $armParameters 220 | -------------------------------------------------------------------------------- /integration-account-connections/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - azurepowershell 5 | products: 6 | - azure 7 | - logic-apps 8 | - azure-resource-manager 9 | --- 10 | 11 | # Connect to an integration account from Azure Logic Apps and deploy by using Azure DevOps Pipelines 12 | 13 | This sample shows how to create a logic app that handles X12 messages by connecting to an integration account and deploy the app by using Azure DevOps Pipelines. 14 | 15 | In this topic, you complete the following tasks: 16 | 17 | * Set up the logic app with a connection to an integration account, which is used by most of the integration account's related actions. 18 | 19 | * Set up an X12 API connection, which is used by all the X12-related actions to work with the integration account. 20 | 21 | To learn more about the template and definition files in this sample and how they work, review [Samples file structure and definitions](../file-definitions.md). 22 | 23 | ## How this sample works 24 | 25 | This sample uses the outputs from creating the X12 connection and defines these output variables in the `connectors-template.json` file: 26 | 27 | ```json 28 | "outputs": { 29 | "x12ManagedApiId": { 30 | "type": "string", 31 | "value": "[variables('x12ConnectionId')]" 32 | }, 33 | "x12ConnId": { 34 | "type": "string", 35 | "value": "[resourceId('Microsoft.Web/connections', variables('x12ConnectionName'))]" 36 | }, 37 | "logicAppName": { 38 | "type": "string", 39 | "value": "[variables('logicAppName')]" 40 | } 41 | } 42 | ``` 43 | 44 | The `logic-app-definition-parameters.json` file replaces the `x12ConnId` and `x12ManagedApiId` values and updates the logic app's definition with the resulting values: 45 | 46 | ```json 47 | { 48 | "$connections": { 49 | "value": { 50 | "x12": { 51 | "connectionId": "{x12ConnId}", 52 | "connectionName": "x12", 53 | "id": "{x12ManagedApiId}" 54 | } 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | In the `logic-app-template` file, the following code sets up the logic app to use the integration account that's defined in the `shared-template. json` file: 61 | 62 | ``` json 63 | "integrationAccount": { 64 | "id": "[resourceId(subscription().subscriptionId, variables('sharedResourceGroupName'), 'Microsoft.Logic/integrationAccounts', variables('integrationAccountName'))]" 65 | } 66 | ``` 67 | 68 | ## Prerequisites 69 | 70 | * Install [Azure PowerShell 2.4.0](https://docs.microsoft.com/powershell/azure/install-az-ps?view=azps-2.4.0) on your platform. 71 | 72 | ## Set up sample 73 | 74 | To set up, deploy, and run this sample, you can use the command line or set up an Azure DevOps pipeline. 75 | 76 | ### Command line 77 | 78 | To run this sample from the command line, follow these steps. 79 | 80 | 1. Clone or download this sample repository. 81 | 82 | 1. Sign in to Azure by running this command from any command line tool that you want. 83 | 84 | ```powershell 85 | Connect-AzAccount 86 | ``` 87 | 88 | 1. To target your deployment, select the appropriate [Azure context](https://docs.microsoft.com/powershell/module/az.accounts/Select-AzContext?view=azps-2.4.0) to use. 89 | 90 | 1. To push a full deployment for this sample to Azure, run this command from the PowerShell directory that contains this sample: 91 | 92 | ```powershell 93 | ./full-deploy.ps1 -groupId -environment -location 94 | ``` 95 | 96 | ### Azure DevOps 97 | 98 | This sample uses [multi-stage YAML pipelines](https://docs.microsoft.com/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml). To set up the sample pipeline, follow these steps: 99 | 100 | 1. Make sure that the [multi-stage pipeline preview feature](https://docs.microsoft.com/azure/devops/project/navigation/preview-features?view=azure-devops) is enabled. 101 | 102 | 1. Clone or fork the samples repository into your own repository. 103 | 104 | 1. Choose one of these steps: 105 | 106 | * Create an [Azure Resource Manager service connection](https://docs.microsoft.com/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-azure-rm) that has the name "Azure Samples Subscription" in your project that points to the Azure subscription that you want to use for deployment. 107 | 108 | * Edit all instances of `azureSubscription: 'Azure Samples Subscription'` in the `./powershell/azure-pipelines.yml` file by using the name for an existing Azure Resource Manager service connection in your project. 109 | 110 | > [!NOTE] 111 | > To use the Azure Resource Manager service connection, make sure that the connection has selected the **Allow all pipelines to use this connection** checkbox. Otherwise, you must authorize the pipeline that you create in the next step. 112 | 113 | 1. Update these `./pipeline/azure-pipelines.yml` variables: 114 | 115 | * `groupId`: A value that's unique to you or your organization and is used to start the names for all resources and resource groups that are created 116 | 117 | * `location`: The name for the Azure region where you want to deploy the resources 118 | 119 | * `abbrevLocationName`: The abbreviated region name that's used in resource names 120 | 121 | 1. Create a new pipeline in your project that uses the `./powershell/azure-pipelines.yml` file from this sample. 122 | 123 | ![Animated walkthrough for creating a new pipeline](../images/create-pipeline.gif) 124 | 125 | ## Supporting documentation 126 | 127 | To learn more about the different parts in these samples, review these topics: 128 | 129 | * [Concepts](../concept-review.md) introduces the main concepts that underlie these samples. 130 | 131 | * [Naming convention](../naming-convention.md) describes the naming convention to use when creating the resources in these samples. 132 | 133 | * [Samples file structure and definitions](../file-definitions.md) explains the purpose for each file in these samples. 134 | 135 | * [Scaling](../api-connection-scale.md) expands on the reasons why these samples provide the capability to scale by increasing the number of copies for the logic apps deployed and organizing resources into separate resource groups. 136 | 137 | ## Resources 138 | 139 | This sample creates these resources: 140 | 141 | ![Resources created and deployed by this sample](../images/sample-integration-act.png) 142 | 143 | To learn about the scripts in this sample and how they work, review [Samples file structure and definitions](../file-definitions.md). 144 | 145 | This sample also implements these template and definition files: 146 | 147 | | File name | Description | 148 | |-----------|-------------| 149 | | `shared-template.json` | This template creates and deploys an integration account. | 150 | | `connectors-template.json` | This template creates an X12 API connection to the integration account that's created by the `shared-template.json` file. | 151 | | `logic-app-template.json` | This template creates a shell for a logic app definition, which is blank to support separating the template from the definition. The logic app's integration account property is set up to point at the integration account that's created by the `shared-template.json` file. | 152 | | `logic-app-definition.json` | This file in this sample is blank. Any sample that's created for an integration account requires uploading and setting up the integration account with partners, agreements, schemas, and maps. This task is outside the scope for this specific sample. | 153 | ||| 154 | 155 | ## Clean up 156 | 157 | When you're done with the sample, delete the resource groups that were created by the sample. To remove all the resource groups with names that start with a specific `groupId` value, run this command from the PowerShell directory that contains this sample: 158 | 159 | ```powershell 160 | ./clean-up.ps1 -groupId 161 | ``` 162 | -------------------------------------------------------------------------------- /integration-account-connections/templates/connectors-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "sharedResourceGroupName": "[toLower(concat(variables('baseName'), 'rgp', variables('location'), '-shared'))]", 42 | "baseConnectionsId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('location'), '/managedApis/')]", 43 | "integrationAccountName": "[toLower(concat(variables('baseName'), 'iact', parameters('locationName')))]", 44 | "integrationAccountId": "[resourceId(subscription().subscriptionId, variables('sharedResourceGroupName'), 'Microsoft.Logic/integrationAccounts', variables('integrationAccountName'))]", 45 | "x12ConnectionName": "x12", 46 | "x12ConnectionId": "[concat(variables('baseConnectionsId'), 'x12')]", 47 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 48 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 49 | }, 50 | "resources": [ 51 | { 52 | "type": "Microsoft.Web/connections", 53 | "apiVersion": "2016-06-01", 54 | "name": "[variables('x12ConnectionName')]", 55 | "location": "[variables('location')]", 56 | "tags": { 57 | "groupdId": "[parameters('groupId')]" 58 | }, 59 | "properties": { 60 | "displayName": "integrationAccount", 61 | "customParameterValues": {}, 62 | "api": { 63 | "id": "[variables('x12ConnectionId')]" 64 | }, 65 | "parameterValues": { 66 | "integrationAccountId": "[variables('integrationAccountId')]", 67 | "integrationAccountUrl": "[listCallbackURL(variables('integrationAccountId'), '2016-06-01').value]" 68 | } 69 | } 70 | } 71 | ], 72 | "outputs": { 73 | "x12ManagedApiId": { 74 | "type": "string", 75 | "value": "[variables('x12ConnectionId')]" 76 | }, 77 | "x12ConnId": { 78 | "type": "string", 79 | "value": "[resourceId('Microsoft.Web/connections', variables('x12ConnectionName'))]" 80 | }, 81 | "logicAppName": { 82 | "type": "string", 83 | "value": "[variables('logicAppName')]" 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /integration-account-connections/templates/logic-app-definition-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$connections": { 3 | "value": { 4 | "x12": { 5 | "connectionId": "{x12ConnId}", 6 | "connectionName": "x12", 7 | "id": "{x12ManagedApiId}" 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /integration-account-connections/templates/logic-app-definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "$connections": { 6 | "defaultValue": {}, 7 | "type": "Object" 8 | } 9 | }, 10 | "triggers": {}, 11 | "actions": {}, 12 | "outputs": {} 13 | } -------------------------------------------------------------------------------- /integration-account-connections/templates/logic-app-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "sharedResourceGroupName": "[toLower(concat(variables('baseName'), 'rgp', variables('location'), '-shared'))]", 42 | "integrationAccountName": "[toLower(concat(variables('baseName'), 'iact', parameters('locationName')))]", 43 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 44 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 45 | }, 46 | "resources": [ 47 | { 48 | "name": "[variables('logicAppName')]", 49 | "type": "Microsoft.Logic/workflows", 50 | "location": "[variables('location')]", 51 | "apiVersion": "2016-06-01", 52 | "properties": { 53 | "integrationAccount": { 54 | "id": "[resourceId(subscription().subscriptionId, variables('sharedResourceGroupName'), 'Microsoft.Logic/integrationAccounts', variables('integrationAccountName'))]" 55 | }, 56 | "definition": { 57 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 58 | "contentVersion": "1.0.0.0", 59 | "parameters": {}, 60 | "triggers": {}, 61 | "actions": {}, 62 | "outputs": {} 63 | } 64 | } 65 | } 66 | ], 67 | "outputs": { 68 | "logicAppName": { 69 | "type": "string", 70 | "value": "[variables('logicAppName')]" 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /integration-account-connections/templates/shared-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "integrationAccountSku": { 25 | "type": "string", 26 | "allowedValues": [ 27 | "Free", 28 | "Standard" 29 | ], 30 | "defaultValue": "Free", 31 | "metadata": { 32 | "description": "The SKU for the integration account" 33 | } 34 | } 35 | }, 36 | "variables": { 37 | "location": "[resourceGroup().location]", 38 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 39 | "integrationAccountName": "[toLower(concat(variables('baseName'), 'iact', parameters('locationName')))]" 40 | }, 41 | "resources": [ 42 | { 43 | "name": "[variables('integrationAccountName')]", 44 | "type": "Microsoft.Logic/integrationAccounts", 45 | "apiVersion": "2016-06-01", 46 | "location": "[variables('location')]", 47 | "tags": { 48 | "groupdId": "[parameters('groupId')]" 49 | }, 50 | "properties": {}, 51 | "sku": { 52 | "name": "[parameters('integrationAccountSku')]" 53 | }, 54 | "resources": [] 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /naming-convention.md: -------------------------------------------------------------------------------- 1 | # Naming conventions for Azure resources 2 | 3 | This topic describes the naming convention that these samples use when creating the resources in the Azure Logic Apps samples for continuous integration (CI) and continuous deployment (CD) pipelines. This convention follows this format: 4 | 5 | `-` 6 | 7 | Based on each identifier that's described in this topic, here are some examples: 8 | 9 | | Group ID | Environment | Resource type | Region | Optional info | Resource name | 10 | |----------|-------------|---------------|--------|---------------|---------------| 11 | | cse01 | Development | Storage account V2 | West US | Ingest | cse01dsa2westusingest | 12 | | cse01 | Test | Logic app | Central US | Sample Service Bus connection | cse01tlacentralus-sample-sb-conn | 13 | | cse01 | Development | Resource group | Central US | Shared resources | cse01drgpentralus-shared | 14 | ||||||| 15 | 16 | ## Group ID 17 | 18 | This label describes the higher-level functionality for a resource group because you can define multiple resource groups for a specific business solution. 19 | 20 | ## Environment 21 | 22 | This character represents the environment where the resource is used, for example: 23 | 24 | | Environment | Character | 25 | |-------------|-----------| 26 | | Development | d | 27 | | Test | t | 28 | | Integration | i | 29 | | Production | p | 30 | ||| 31 | 32 | ## Resource type 33 | 34 | These characters are an acronym for the resource type, for example: 35 | 36 | | Resource type | Acronym | 37 | |---------------|---------| 38 | | Storage account | sa | 39 | | Storage account V2 | sa2 | 40 | | Function app | fa | 41 | | Logic app | la | 42 | | Integration account | iact | 43 | | Service Bus | sb | 44 | | Network security group | nsg | 45 | | Resource group | rgp | 46 | | Event hub | eh | 47 | | Event Grid subscription | egs | 48 | | Log Analytics workspace | law | 49 | | Log Analytics solution | las | 50 | ||| 51 | 52 | ## Region 53 | 54 | The region where to deploy the resource as defined by the resource definition's `.location` property. For regions with long names, such as North Central US, you can use the designated abbreviation, for example, "West US" is `westus` and "Central US" is `centralus`. 55 | 56 | ## Optional information or identification 57 | 58 | If a solution uses more than one resource that has the same type, such as multiple storage accounts or function apps, those resources might need more information to identify its purpose. If the resource name permits, add a hyphen and a suitable label for this information. 59 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Azure Logic Apps: Set up a continuous integration (CI) and continuous delivery (CD) pipeline 2 | 3 | These samples show some strategies that you can use to implement a continuous integration (CI) and continuous delivery (CD) pipeline for Azure Logic Apps. 4 | 5 | ## Supporting documents 6 | 7 | To learn more about the different parts in these samples, review these topics: 8 | 9 | 1. [Concepts](concepts-review.md) introduces the main concepts that underlie these samples. 10 | 11 | 1. [Naming conventions](naming-convention.md) describes the naming convention to use when creating the resources in these samples. 12 | 13 | 1. [Samples file structure and definitions](file-definitions.md) explains the purpose for each file in these samples. 14 | 15 | 1. [Scaling](api-connection-scale.md) expands on the reasons why these samples provide the capability to scale by increasing the number of copies for the logic apps deployed and organizing resources into separate resource groups. 16 | 17 | ## Samples inventory 18 | 19 | * [Azure function app actions](./function-app-actions/) 20 | * [Azure Service Bus connections](./service-bus-connections/) 21 | * [Azure storage account connections](./storage-account-connections/) 22 | * [Integration account connections](./integration-account-connections/) 23 | * [API Management actions](./api-management-actions/) 24 | -------------------------------------------------------------------------------- /service-bus-connections/powershell/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | location: westus 3 | groupId: cse01 4 | abbrevLocationName: westus 5 | instanceCount: 1 6 | identifier: sample-sb-conn 7 | sampleDirectory: service-bus-connections 8 | sharedResourceGroupIdentifier: shared 9 | 10 | trigger: 11 | - master 12 | 13 | stages: 14 | - stage: Development 15 | jobs: 16 | - job: DeployDevelopmentEnvironment 17 | variables: 18 | environment: d 19 | pool: 20 | vmImage: 'windows-2019' 21 | steps: 22 | - script: | 23 | echo ##vso[task.setvariable variable=resourceGroupName]$(groupId)$(environment)rgp$(location)-$(sharedResourceGroupIdentifier) 24 | echo ##vso[task.setvariable variable=templateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\shared-template.json 25 | echo ##vso[task.setvariable variable=logicAppDeployScript]$(Build.Repository.LocalPath)\$(sampleDirectory)\powershell\logic-app-deploy.ps1 26 | name: setDynamicVariableNames 27 | - powershell: | 28 | Write-Host "templateFilePath: $(templateFilePath)" 29 | Write-Host "resourceGroupName: $(resourceGroupName)" 30 | Write-Host "logicAppDeployScript: $(logicAppDeployScript)" 31 | name: printOutVariables 32 | - task: AzureResourceGroupDeployment@2 33 | name: SharedResourcesDeployment 34 | inputs: 35 | azureSubscription: 'Azure Samples Subscription' 36 | action: 'Create Or Update Resource Group' 37 | resourceGroupName: $(resourceGroupName) 38 | location: $(location) 39 | templateLocation: Linked artifact 40 | csmFile: $(templateFilePath) 41 | deploymentMode: 'Incremental' 42 | deploymentName: $(Build.BuildNumber) 43 | overrideParameters: -groupId $(groupId) -environment $(environment) -locationName $(abbrevLocationName) 44 | - script: | 45 | echo ##vso[task.setvariable variable=connectorsTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\connectors-template.json 46 | echo ##vso[task.setvariable variable=logicAppTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-template.json 47 | echo ##vso[task.setvariable variable=logicAppDefinitionPath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition.json 48 | echo ##vso[task.setvariable variable=logicAppDefinitionParametersFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition-parameters.json 49 | - task: AzurePowerShell@4 50 | name: DeployLogicAppSample 51 | inputs: 52 | azureSubscription: 'Azure Samples Subscription' 53 | ScriptType: 'FilePath' 54 | ScriptPath: '$(logicAppDeployScript)' 55 | FailOnStandardError: true 56 | azurePowerShellVersion: LatestVersion 57 | ScriptArguments: >- 58 | -groupId $(groupId) 59 | -location $(location) 60 | -abbrevLocationName $(abbrevLocationName) 61 | -environment $(environment) 62 | -instanceCount $(instanceCount) 63 | -identifier $(identifier) 64 | -connectorsTemplateFilePath $(connectorsTemplateFilePath) 65 | -logicAppTemplateFilePath $(logicAppTemplateFilePath) 66 | -logicAppDefinitionPath $(logicAppDefinitionPath) 67 | -logicAppDefinitionParametersFilePath $(logicAppDefinitionParametersFilePath) -------------------------------------------------------------------------------- /service-bus-connections/powershell/clean-up.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deletes all resource groups where their names start with the "groupId" that's passed as input. 4 | 5 | .PARAMETER groupId 6 | The value that identifies the resource group to delete 7 | #> 8 | 9 | param( 10 | [Parameter(Mandatory = $True)] 11 | [string] 12 | $groupId 13 | ) 14 | 15 | $resourceGroups = Get-AzResourceGroup; 16 | 17 | foreach ($resourceGroup in $resourceGroups) { 18 | if ($resourceGroup.ResourceGroupName.StartsWith($groupId, 'CurrentCultureIgnoreCase')) { 19 | Remove-AzResourceGroup -Name $resourceGroup.ResourceGroupName -Force -AsJob 20 | Write-Host "Deleted resource group $($resourceGroup.ResourceGroupName)" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service-bus-connections/powershell/full-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploys a template for a logic app to Azure. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER instanceCount 24 | The number of resource group instances to create 25 | 26 | .PARAMETER overrideExistingLogicApp 27 | If true, the script runs the Azure Resource Manager template deployment for the logic app, even when the logic app already exists. 28 | #> 29 | 30 | param( 31 | 32 | [Parameter(Mandatory=$True)] 33 | [string] 34 | $groupId, 35 | 36 | [Parameter(Mandatory=$True)] 37 | [string] 38 | $location, 39 | 40 | [Parameter(Mandatory=$True)] 41 | [string] 42 | $environment, 43 | 44 | [Parameter(Mandatory=$False)] 45 | [string] 46 | $abbrevLocationName = $location, 47 | 48 | [Parameter(Mandatory=$False)] 49 | [string] 50 | $deploymentName = [guid]::NewGuid(), 51 | 52 | [Parameter(Mandatory=$False)] 53 | [int] 54 | $instanceCount = 1, 55 | 56 | [Parameter(Mandatory = $False)] 57 | [bool] 58 | $overrideExistingLogicApp = $True 59 | ) 60 | 61 | Write-Host "Running shared deployment script" 62 | 63 | & "${PSScriptRoot}\shared-deploy.ps1" -groupId $groupId ` 64 | -location $location ` 65 | -environment $environment ` 66 | -abbrevLocationName $abbrevLocationName ` 67 | -deploymentName $deploymentName; 68 | 69 | Write-Host "Running logic app deployment script" 70 | 71 | & "${PSScriptRoot}\logic-app-deploy.ps1" -groupId $groupId ` 72 | -location $location ` 73 | -environment $environment ` 74 | -abbrevLocationName $abbrevLocationName ` 75 | -deploymentName $deploymentName ` 76 | -identifier "sample-sb-conn" ` 77 | -instanceCount $instanceCount ` 78 | -overrideExisting $overrideExistingLogicApp; -------------------------------------------------------------------------------- /service-bus-connections/powershell/shared-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploy the shared resources. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | OPTIONAL: The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | OPTIONAL: The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER identifier 24 | OPTIONAL: Value appended to the end of the resource group and the logic app within the resource group 25 | 26 | .PARAMETER parametersFilePath 27 | OPTIONAL: Path to the parameter file. Defaults to the file contained within the local directory 28 | 29 | .PARAMETER templateFilePath 30 | OPTIONAL: Path to the template file. Defaults to the file contained within the local directory 31 | #> 32 | 33 | param( 34 | [Parameter(Mandatory = $True)] 35 | [string] 36 | $groupId, 37 | 38 | [Parameter(Mandatory = $True)] 39 | [string] 40 | $location, 41 | 42 | [Parameter(Mandatory = $True)] 43 | [string] 44 | $environment, 45 | 46 | [Parameter(Mandatory = $False)] 47 | [string] 48 | $abbrevLocationName = $location, 49 | 50 | [Parameter(Mandatory = $False)] 51 | [string] 52 | $deploymentName = [guid]::NewGuid(), 53 | 54 | [Parameter(Mandatory = $False)] 55 | [string] 56 | $identifier = "shared", 57 | 58 | [Parameter(Mandatory = $False)] 59 | [string] 60 | $parametersFilePath = "../templates/shared-parameters.json", 61 | 62 | [Parameter(Mandatory = $False)] 63 | [string] 64 | $templateFilePath = "../templates/shared-template.json" 65 | ) 66 | 67 | Function RegisterRP { 68 | <# 69 | .SYNOPSIS 70 | Register the Azure resource provider. 71 | #> 72 | Param( 73 | [Parameter(Mandatory = $True, HelpMessage = "The name of the resource provider to register")] 74 | [string]$ResourceProviderNamespace 75 | ) 76 | 77 | Write-Host "Registering resource provider '$ResourceProviderNamespace'"; 78 | Register-AzResourceProvider -ProviderNamespace $ResourceProviderNamespace; 79 | } 80 | 81 | Function Set-RelativeFilePaths { 82 | <# 83 | .SYNOPSIS 84 | Fix relative paths that can't be evaluated as given. 85 | .DESCRIPTION 86 | Validate whether the file paths can be evaluated as they're passed as input. 87 | If the paths are relative and can't be found, try to fix by prepending the 88 | root path for script execution to the given path. 89 | #> 90 | Param() 91 | $parametersFilePath = Set-RelativeFilePath -filePath $parametersFilePath 92 | $templateFilePath = Set-RelativeFilePath -filePath $templateFilePath 93 | } 94 | 95 | Function Set-RelativeFilePath { 96 | <# 97 | .SYNOPSIS 98 | Validate a single file path and prepend the root path for script execution when the path fails. 99 | #> 100 | Param( 101 | [Parameter(Mandatory = $True, HelpMessage = "The path of the file to test whether the file exists")] 102 | [string]$filePath 103 | ) 104 | 105 | if (Test-Path $filePath) { 106 | return $filePath; 107 | } 108 | else { 109 | return "{0}\{1}" -f $PSScriptRoot, $filePath; 110 | } 111 | } 112 | 113 | Function Set-ResourceGroup { 114 | <# 115 | .SYNOPSIS 116 | Check whether the resource group exists. If not, create the resource group. 117 | #> 118 | Param( 119 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to create, if not already existing")] 120 | [string]$resourceGroupName 121 | ) 122 | 123 | Write-Host "Working with resource group name: '$resourceGroupName'" 124 | 125 | Write-Host "Checking whether the resource group exists" 126 | $resourceGroup = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue 127 | if (!$resourceGroup) { 128 | Write-Host "Resource group '$resourceGroupName' does not exist. Creating new resource group"; 129 | 130 | Write-Host "Creating resource group '$resourceGroupName' in location '$location'"; 131 | New-AzResourceGroup -Name $resourceGroupName -Location $location 132 | } 133 | else { 134 | Write-Host "Using existing resource group '$resourceGroupName'"; 135 | } 136 | } 137 | 138 | Function New-ResourceManagerTemplateDeployment { 139 | <# 140 | .SYNOPSIS 141 | Create an Azure Resource Manager template deployment. 142 | .OUTPUTS 143 | The results from the Azure Resource Manager template deployment 144 | #> 145 | Param( 146 | [Parameter(Mandatory = $True, HelpMessage = "The path for the parameter file to use for deployment")] 147 | [string]$parametersFilePath, 148 | [Parameter(Mandatory = $True, HelpMessage = "The name for the resource group to use for deployment")] 149 | [string]$resourceGroupName, 150 | [Parameter(Mandatory = $True, HelpMessage = "The path to the template file to use for the deployment")] 151 | [string]$templateFilePath, 152 | [Parameter(Mandatory = $True, HelpMessage = "The values to append to the parameters before deployment")] 153 | [hashtable]$armParameters 154 | ) 155 | if (Test-Path $parametersFilePath) { 156 | Write-Host "Loading parameters from file"; 157 | $parametersFromFile = Get-Content -Raw -Encoding UTF8 -Path $parametersFilePath | ConvertFrom-Json 158 | 159 | Write-Host "Adding script parameters"; 160 | New-Parameter -parameters $parametersFromFile -name "groupId" -value $groupId 161 | New-Parameter -parameters $parametersFromFile -name "environment" -value $environment 162 | New-Parameter -parameters $parametersFromFile -name "locationName" -value $locationName 163 | 164 | Write-Host "Starting deployment..."; 165 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $parametersFromFile; 166 | } 167 | else { 168 | Write-Host "Starting deployment..."; 169 | return New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath -TemplateParameterObject $armParameters; 170 | } 171 | } 172 | 173 | Function New-Parameter { 174 | <# 175 | .SYNOPSIS 176 | Append a parameter to the hash table for parameters that are passed as input. 177 | #> 178 | Param ( 179 | [Parameter(Mandatory = $True, HelpMessage = "The hash table where to add the parameter")] 180 | [hashtable]$parameters, 181 | [Parameter(Mandatory = $True, HelpMessage = "The name for the parameter to add")] 182 | [string]$name, 183 | [Parameter(Mandatory = $True, HelpMessage = "The value for the parameter to add")] 184 | [string]$value 185 | ) 186 | $parameters | Add-Member -MemberType NoteProperty -Name $name -Value @{value = $value } 187 | } 188 | 189 | #****************************************************************************** 190 | # Script body 191 | # Execution begins here 192 | #****************************************************************************** 193 | 194 | if (!$PSScriptRoot) { $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } 195 | 196 | Set-RelativeFilePaths 197 | 198 | $resourceProviders = @("microsoft.servicebus"); 199 | if ($resourceProviders.length) { 200 | Write-Host "Registering resource providers" 201 | foreach ($resourceProvider in $resourceProviders) { 202 | RegisterRP($resourceProvider); 203 | } 204 | } 205 | 206 | $resourceGroupName = "{0}{1}rgp{2}-{3}" -f $groupId, $environment, $location, $identifier 207 | 208 | Set-ResourceGroup -resourceGroupName $resourceGroupName 209 | 210 | $armParameters = @{ 211 | groupId = $groupId 212 | environment = $environment 213 | locationName = $abbrevLocationName 214 | }; 215 | 216 | New-ResourceManagerTemplateDeployment -parametersFilePath $parametersFilePath ` 217 | -resourceGroupName $resourceGroupName ` 218 | -templateFilePath $templateFilePath ` 219 | -armParameters $armParameters 220 | -------------------------------------------------------------------------------- /service-bus-connections/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - azurepowershell 5 | products: 6 | - azure 7 | - logic-apps 8 | - azure-resource-manager 9 | - azure-service-bus 10 | --- 11 | 12 | # Connect to Azure Service Bus queues from Azure Logic Apps and deploy with Azure DevOps Pipelines 13 | 14 | This sample shows how to create a logic app that handles messages in an Azure Service Bus queue and deploy the app by using Azure DevOps Pipelines. The logic app creates Azure Service Bus API connections: one connection reads messages from the queue while the other connection sends messages to the queue. To learn more about the template and definition files in this sample and how they work, review [Samples file structure and definitions](../file-definitions.md). 15 | 16 | ## How this sample works 17 | 18 | This sample uses the outputs from creating Azure Service Bus connections and defines these output variables in the `connectors-template.json` file: 19 | 20 | ```json 21 | "outputs": { 22 | "serviceBusManagedApiId": { 23 | "type": "string", 24 | "value": "[variables('serviceBusConnectionId')]" 25 | }, 26 | "serviceBusConnId1": { 27 | "type": "string", 28 | "value": "[resourceId('Microsoft.Web/connections', concat(variables('serviceBusConnectionName'), '-1'))]" 29 | }, 30 | "serviceBusConnId2": { 31 | "type": "string", 32 | "value": "[resourceId('Microsoft.Web/connections', concat(variables('serviceBusConnectionName'), '-2'))]" 33 | }, 34 | "logicAppName": { 35 | "type": "string", 36 | "value": "[variables('logicAppName')]" 37 | } 38 | } 39 | ``` 40 | 41 | The `logic-app-definition-parameters.json` file replaces the `serviceBusConnId1`, `serviceBusConnId2`, and `serviceBusManagedApiId` values and updates the logic app's definition with the resulting values: 42 | 43 | ``` json 44 | { 45 | "$connections": { 46 | "value": { 47 | "servicebus-1": { 48 | "connectionId": "{serviceBusConnId1}", 49 | "connectionName": "servicebus-1", 50 | "id": "{serviceBusManagedApiId}" 51 | }, 52 | "servicebus-2": { 53 | "connectionId": "{serviceBusConnId2}", 54 | "connectionName": "servicebus-2", 55 | "id": "{serviceBusManagedApiId}" 56 | } 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | ## Prerequisites 63 | 64 | * Install [Azure PowerShell 2.4.0](https://docs.microsoft.com/powershell/azure/install-az-ps?view=azps-2.4.0) on your platform. 65 | 66 | ## Set up sample 67 | 68 | To set up, deploy, and run this sample, you can use the command line or set up an Azure DevOps pipeline. 69 | 70 | ### Command line 71 | 72 | To run this sample from the command line, follow these steps. 73 | 74 | 1. Clone or download this sample repository. 75 | 76 | 1. Sign in to Azure by running this command from any command line tool that you want. 77 | 78 | ```powershell 79 | Connect-AzAccount 80 | ``` 81 | 82 | 1. To target your deployment, select the appropriate [Azure context](https://docs.microsoft.com/powershell/module/az.accounts/Select-AzContext?view=azps-2.4.0) to use. 83 | 84 | 1. To push a full deployment for this sample to Azure, run this command from the PowerShell directory that contains this sample: 85 | 86 | ```powershell 87 | ./full-deploy.ps1 -groupId -environment -location 88 | ``` 89 | 90 | ### Azure DevOps 91 | 92 | This sample uses [multi-stage YAML pipelines](https://docs.microsoft.com/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml). To set up the sample pipeline, follow these steps: 93 | 94 | 1. Make sure that the [multi-stage pipeline preview feature](https://docs.microsoft.com/azure/devops/project/navigation/preview-features?view=azure-devops) is enabled. 95 | 96 | 1. Clone or fork the samples repository into your own repository. 97 | 98 | 1. Choose one of these steps: 99 | 100 | * Create an [Azure Resource Manager service connection](https://docs.microsoft.com/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-azure-rm) that has the name "Azure Samples Subscription" in your project that points to the Azure subscription that you want to use for deployment. 101 | 102 | * Edit all instances of `azureSubscription: 'Azure Samples Subscription'` in the `./powershell/azure-pipelines.yml` file by using the name for an existing Azure Resource Manager service connection in your project. 103 | 104 | > [!NOTE] 105 | > To use the Azure Resource Manager service connection, make sure that the connection has selected the **Allow all pipelines to use this connection** checkbox. Otherwise, you must authorize the pipeline that you create in the next step. 106 | 107 | 1. Update these `./pipeline/azure-pipelines.yml` variables: 108 | 109 | * `groupId`: A value that's unique to you or your organization and is used to start the names for all resources and resource groups that are created 110 | 111 | * `location`: The name for the Azure region where you want to deploy the resources 112 | 113 | * `abbrevLocationName`: The abbreviated region name that's used in resource names 114 | 115 | 1. Create a new pipeline in your project that uses the `./powershell/azure-pipelines.yml` file from this sample. 116 | 117 | ![Animated walkthrough for creating a new pipeline](../images/create-pipeline.gif) 118 | 119 | ## Supporting documentation 120 | 121 | To learn more about the different parts in these samples, review these topics: 122 | 123 | * [Concepts](../concept-review.md) introduces the main concepts that underlie these samples. 124 | 125 | * [Naming convention](../naming-convention.md) describes the naming convention to use when creating the resources in these samples. 126 | 127 | * [Samples file structure and definitions](../file-definitions.md) explains the purpose for each file in these samples. 128 | 129 | * [Scaling](../api-connection-scale.md) expands on the reasons why these samples provide the capability to scale by increasing the number of copies for the logic apps deployed and organizing resources into separate resource groups. 130 | 131 | ## Resources 132 | 133 | This sample creates these resources: 134 | 135 | ![Resources created and deployed by this sample](../images/servicebus-sample.png) 136 | 137 | To learn about the scripts in this sample and how they work, review [Samples file structure and definitions](../file-definitions.md). 138 | 139 | This sample also implements these template and definition files: 140 | 141 | | File name | Description | 142 | |-----------|-------------| 143 | | `shared-template.json` | This template creates a single service bus resource that sets up the `queue_1` and `queue_2` queues. | 144 | | `connectors-template.json` | This template creates two Service Bus API connections. Both connections are set up with the connection strings for the service bus that's created by the `shared_template.json` file. | 145 | | `logic-app-template.json` | This template creates a shell for a logic app definition, which is blank to support separating the template from the definition. | 146 | | `logic-app-definition.json` | This file defines a basic logic app that gets a message from `queue_1` by using one API connection and sends the message to `queue_2` by using the other API connection. | 147 | | `logic-app-definition-parameters.json` | This file contains the setup information for the Azure Service Bus connectors. | 148 | ||| 149 | 150 | ## Clean up 151 | 152 | When you're done with the sample, delete the resource groups that were created by the sample. To remove all the resource groups with names that start with a specific `groupId` value, run this command from the PowerShell directory that contains this sample: 153 | 154 | ```powershell 155 | ./clean-up.ps1 -groupId 156 | ``` 157 | -------------------------------------------------------------------------------- /service-bus-connections/templates/connectors-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "sharedResourceGroupName": "[toLower(concat(variables('baseName'), 'rgp', variables('location'), '-shared'))]", 42 | "servicesBusName": "[toLower(concat(variables('baseName'), 'sb', parameters('locationName')))]", 43 | "serviceBusAuthRuleId": "[resourceId(subscription().subscriptionId, variables('sharedResourceGroupName'), 'Microsoft.ServiceBus/namespaces/authorizationRules', variables('servicesBusName'), 'RootManageSharedAccessKey')]", 44 | "baseConnectionsId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('location'), '/managedApis/')]", 45 | "serviceBusConnectionName": "servicebus", 46 | "serviceBusConnectionId": "[concat(variables('baseConnectionsId'), 'servicebus')]", 47 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 48 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 49 | }, 50 | "resources": [ 51 | { 52 | "type": "Microsoft.Web/connections", 53 | "apiVersion": "2016-06-01", 54 | "name": "[concat(variables('serviceBusConnectionName'), '-1')]", 55 | "location": "[variables('location')]", 56 | "tags": { 57 | "groupdId": "[parameters('groupId')]" 58 | }, 59 | "properties": { 60 | "displayName": "[variables('serviceBusConnectionName')]", 61 | "customParameterValues": {}, 62 | "api": { 63 | "id": "[variables('serviceBusConnectionId')]" 64 | }, 65 | "parameterValues": { 66 | "connectionString": "[listKeys(variables('ServiceBusAuthRuleId'), '2015-08-01').primaryConnectionString]" 67 | } 68 | } 69 | }, 70 | { 71 | "type": "Microsoft.Web/connections", 72 | "apiVersion": "2016-06-01", 73 | "name": "[concat(variables('serviceBusConnectionName'), '-2')]", 74 | "location": "[variables('location')]", 75 | "tags": { 76 | "groupdId": "[parameters('groupId')]" 77 | }, 78 | "properties": { 79 | "displayName": "[variables('serviceBusConnectionName')]", 80 | "customParameterValues": {}, 81 | "api": { 82 | "id": "[variables('serviceBusConnectionId')]" 83 | }, 84 | "parameterValues": { 85 | "connectionString": "[listKeys(variables('ServiceBusAuthRuleId'), '2015-08-01').primaryConnectionString]" 86 | } 87 | } 88 | } 89 | ], 90 | "outputs": { 91 | "serviceBusManagedApiId": { 92 | "type": "string", 93 | "value": "[variables('serviceBusConnectionId')]" 94 | }, 95 | "serviceBusConnId1": { 96 | "type": "string", 97 | "value": "[resourceId('Microsoft.Web/connections', concat(variables('serviceBusConnectionName'), '-1'))]" 98 | }, 99 | "serviceBusConnId2": { 100 | "type": "string", 101 | "value": "[resourceId('Microsoft.Web/connections', concat(variables('serviceBusConnectionName'), '-2'))]" 102 | }, 103 | "logicAppName": { 104 | "type": "string", 105 | "value": "[variables('logicAppName')]" 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /service-bus-connections/templates/logic-app-definition-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$connections": { 3 | "value": { 4 | "servicebus-1": { 5 | "connectionId": "{serviceBusConnId1}", 6 | "connectionName": "servicebus-1", 7 | "id": "{serviceBusManagedApiId}" 8 | }, 9 | "servicebus-2": { 10 | "connectionId": "{serviceBusConnId2}", 11 | "connectionName": "servicebus-2", 12 | "id": "{serviceBusManagedApiId}" 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /service-bus-connections/templates/logic-app-definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "$connections": { 6 | "defaultValue": {}, 7 | "type": "Object" 8 | } 9 | }, 10 | "triggers": { 11 | "When_a_message_is_received_in_a_queue_(peek-lock)": { 12 | "recurrence": { 13 | "frequency": "Minute", 14 | "interval": 1 15 | }, 16 | "type": "ApiConnection", 17 | "inputs": { 18 | "host": { 19 | "connection": { 20 | "name": "@parameters('$connections')['servicebus-1']['connectionId']" 21 | } 22 | }, 23 | "method": "get", 24 | "path": "/@{encodeURIComponent(encodeURIComponent('queue_1'))}/messages/head/peek", 25 | "queries": { 26 | "queueType": "Main", 27 | "sessionId": "None" 28 | } 29 | } 30 | } 31 | }, 32 | "actions": { 33 | "Complete_the_message_in_a_queue": { 34 | "runAfter": { 35 | "Send_message": [ 36 | "Succeeded" 37 | ] 38 | }, 39 | "type": "ApiConnection", 40 | "inputs": { 41 | "host": { 42 | "connection": { 43 | "name": "@parameters('$connections')['servicebus-1']['connectionId']" 44 | } 45 | }, 46 | "method": "delete", 47 | "path": "/@{encodeURIComponent(encodeURIComponent('queue_1'))}/messages/complete", 48 | "queries": { 49 | "lockToken": "@triggerBody()?['LockToken']", 50 | "queueType": "Main", 51 | "sessionId": "" 52 | } 53 | } 54 | }, 55 | "Initialize_variable": { 56 | "runAfter": {}, 57 | "type": "InitializeVariable", 58 | "inputs": { 59 | "variables": [ 60 | { 61 | "name": "awesomeStuff", 62 | "type": "String", 63 | "value": "HappensHere" 64 | } 65 | ] 66 | } 67 | }, 68 | "Send_message": { 69 | "runAfter": { 70 | "Initialize_variable": [ 71 | "Succeeded" 72 | ] 73 | }, 74 | "type": "ApiConnection", 75 | "inputs": { 76 | "body": { 77 | "ContentData": "@{triggerBody()?['ContentData']}", 78 | "ContentType": "@triggerBody()?['ContentType']", 79 | "CorrelationId": "@triggerBody()?['CorrelationId']", 80 | "Properties": "@triggerBody()?['Properties']" 81 | }, 82 | "host": { 83 | "connection": { 84 | "name": "@parameters('$connections')['servicebus-2']['connectionId']" 85 | } 86 | }, 87 | "method": "post", 88 | "path": "/@{encodeURIComponent(encodeURIComponent('queue_2'))}/messages", 89 | "queries": { 90 | "systemProperties": "None" 91 | } 92 | } 93 | } 94 | }, 95 | "outputs": {} 96 | } -------------------------------------------------------------------------------- /service-bus-connections/templates/logic-app-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 42 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 43 | }, 44 | "resources": [ 45 | { 46 | "name": "[variables('logicAppName')]", 47 | "type": "Microsoft.Logic/workflows", 48 | "location": "[variables('location')]", 49 | "apiVersion": "2016-06-01", 50 | "properties": { 51 | "definition": { 52 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 53 | "contentVersion": "1.0.0.0", 54 | "parameters": {}, 55 | "triggers": {}, 56 | "actions": {}, 57 | "outputs": {} 58 | } 59 | } 60 | } 61 | ], 62 | "outputs": { 63 | "logicAppName": { 64 | "type": "string", 65 | "value": "[variables('logicAppName')]" 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /service-bus-connections/templates/shared-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "queues": { 25 | "type": "array", 26 | "defaultValue": [ 27 | "queue_1", 28 | "queue_2" 29 | ], 30 | "metadata": { 31 | "description": "The array of queues to create" 32 | } 33 | }, 34 | "serviceBusSku": { 35 | "type": "string", 36 | "allowedValues": [ 37 | "Standard", 38 | "Premium" 39 | ], 40 | "defaultValue": "Standard", 41 | "metadata": { 42 | "description": "The SKU for the Azure Service Bus namespace" 43 | } 44 | }, 45 | "serviceBusCapacity": { 46 | "type": "int", 47 | "allowedValues": [ 48 | 1, 49 | 2, 50 | 4 51 | ], 52 | "defaultValue": 1, 53 | "metadata": { 54 | "description": "The specified messaging units for the Azure Service Bus namespace" 55 | } 56 | } 57 | }, 58 | "variables": { 59 | "location": "[resourceGroup().location]", 60 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 61 | "servicesBusName": "[toLower(concat(variables('baseName'), 'sb', parameters('locationName')))]" 62 | }, 63 | "resources": [ 64 | { 65 | "name": "[variables('servicesBusName')]", 66 | "type": "Microsoft.ServiceBus/namespaces", 67 | "apiVersion": "2017-04-01", 68 | "tags": { 69 | "groupdId": "[parameters('groupId')]" 70 | }, 71 | "location": "[variables('location')]", 72 | "sku": { 73 | "name": "[parameters('serviceBusSku')]", 74 | "tier": "[parameters('serviceBusSku')]", 75 | "capacity": "[parameters('serviceBusCapacity')]" 76 | }, 77 | "properties": {}, 78 | "resources": [] 79 | }, 80 | { 81 | "name": "[concat(variables('servicesBusName'), '/',parameters('queues')[copyindex()])]", 82 | "type": "Microsoft.ServiceBus/namespaces/queues", 83 | "apiVersion": "2017-04-01", 84 | "tags": { 85 | "groupdId": "[parameters('groupId')]" 86 | }, 87 | "dependsOn": [ 88 | "[resourceId('Microsoft.ServiceBus/namespaces', variables('servicesBusName'))]" 89 | ], 90 | "copy": { 91 | "name": "queueCopy", 92 | "count": "[length(parameters('queues'))]" 93 | }, 94 | "properties": { 95 | "requiresDuplicateDetection": true, 96 | "requiresSession": true, 97 | "enableBatchedOperations": true 98 | }, 99 | "resources": [] 100 | }, 101 | { 102 | "type": "Microsoft.ServiceBus/namespaces/AuthorizationRules", 103 | "name": "[concat(variables('servicesBusName'), '/', 'RootManageSharedAccessKey')]", 104 | "apiVersion": "2017-04-01", 105 | "location": "[variables('location')]", 106 | "dependsOn": [ 107 | "[resourceId('Microsoft.ServiceBus/namespaces', variables('servicesBusName'))]" 108 | ], 109 | "properties": { 110 | "rights": [ 111 | "Listen", 112 | "Manage", 113 | "Send" 114 | ] 115 | } 116 | } 117 | ] 118 | } -------------------------------------------------------------------------------- /storage-account-connections/powershell/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | location: westus 3 | groupId: cse01 4 | abbrevLocationName: westus 5 | instanceCount: 1 6 | identifier: sample-storage-conn 7 | sampleDirectory: storage-account-connections 8 | sharedResourceGroupIdentifier: shared 9 | 10 | trigger: 11 | - master 12 | 13 | stages: 14 | - stage: Development 15 | jobs: 16 | - job: DeployDevelopmentEnvironment 17 | variables: 18 | environment: d 19 | pool: 20 | vmImage: 'windows-2019' 21 | steps: 22 | - script: | 23 | echo ##vso[task.setvariable variable=resourceGroupName]$(groupId)$(environment)rgp$(location)-$(sharedResourceGroupIdentifier) 24 | echo ##vso[task.setvariable variable=templateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\shared-template.json 25 | echo ##vso[task.setvariable variable=logicAppDeployScript]$(Build.Repository.LocalPath)\$(sampleDirectory)\powershell\logic-app-deploy.ps1 26 | name: setDynamicVariableNames 27 | - powershell: | 28 | Write-Host "templateFilePath: $(templateFilePath)" 29 | Write-Host "resourceGroupName: $(resourceGroupName)" 30 | Write-Host "logicAppDeployScript: $(logicAppDeployScript)" 31 | name: printOutVariables 32 | - task: AzureResourceGroupDeployment@2 33 | name: SharedResourcesDeployment 34 | inputs: 35 | azureSubscription: 'Azure Samples Subscription' 36 | action: 'Create Or Update Resource Group' 37 | resourceGroupName: $(resourceGroupName) 38 | location: $(location) 39 | templateLocation: Linked artifact 40 | csmFile: $(templateFilePath) 41 | deploymentMode: 'Incremental' 42 | deploymentName: $(Build.BuildNumber) 43 | overrideParameters: -groupId $(groupId) -environment $(environment) -locationName $(abbrevLocationName) 44 | - script: | 45 | echo ##vso[task.setvariable variable=connectorsTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\connectors-template.json 46 | echo ##vso[task.setvariable variable=logicAppTemplateFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-template.json 47 | echo ##vso[task.setvariable variable=logicAppDefinitionPath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition.json 48 | echo ##vso[task.setvariable variable=logicAppDefinitionParametersFilePath]$(Build.Repository.LocalPath)\$(sampleDirectory)\templates\logic-app-definition-parameters.json 49 | - task: AzurePowerShell@4 50 | name: DeployLogicAppSample 51 | inputs: 52 | azureSubscription: 'Azure Samples Subscription' 53 | ScriptType: 'FilePath' 54 | ScriptPath: '$(logicAppDeployScript)' 55 | FailOnStandardError: true 56 | azurePowerShellVersion: LatestVersion 57 | ScriptArguments: >- 58 | -groupId $(groupId) 59 | -location $(location) 60 | -abbrevLocationName $(abbrevLocationName) 61 | -environment $(environment) 62 | -instanceCount $(instanceCount) 63 | -identifier $(identifier) 64 | -connectorsTemplateFilePath $(connectorsTemplateFilePath) 65 | -logicAppTemplateFilePath $(logicAppTemplateFilePath) 66 | -logicAppDefinitionPath $(logicAppDefinitionPath) 67 | -logicAppDefinitionParametersFilePath $(logicAppDefinitionParametersFilePath) -------------------------------------------------------------------------------- /storage-account-connections/powershell/clean-up.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deletes all resource groups where their names start with the "groupId" that's passed as input. 4 | 5 | .PARAMETER groupId 6 | The value that identifies the resource group to delete 7 | #> 8 | 9 | param( 10 | [Parameter(Mandatory = $True)] 11 | [string] 12 | $groupId 13 | ) 14 | 15 | $resourceGroups = Get-AzResourceGroup; 16 | 17 | foreach ($resourceGroup in $resourceGroups) { 18 | if ($resourceGroup.ResourceGroupName.StartsWith($groupId, 'CurrentCultureIgnoreCase')) { 19 | Remove-AzResourceGroup -Name $resourceGroup.ResourceGroupName -Force -AsJob 20 | Write-Host "Deleted resource group $($resourceGroup.ResourceGroupName)" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /storage-account-connections/powershell/full-deploy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploys a template for a logic app to Azure. 4 | 5 | .DESCRIPTION 6 | Deploys an Azure Resource Manager template with the LogicApp definition seperated within the directory 7 | 8 | .PARAMETER groupId 9 | The value used for constructing resources in the resource group and identifies the resources that are used together in a specific solution 10 | 11 | .PARAMETER location 12 | The region or location name to use for the resource group and the resources in that group 13 | 14 | .PARAMETER environment 15 | The alphabetical character that identifies the deployment environment to use in the name for each resource that's created in the resource group. For example, values include "d" for development, "t" for test, "s" for staging, and "p" for production. 16 | 17 | .PARAMETER abbrevLocationName 18 | The abbreviated region name that's used in resource names due to character limitations on some resource types. Defaults to the "location" parameter value. 19 | 20 | .PARAMETER deploymentName 21 | The name used for the deployment. If not given, a GUID is assigned as the deployment name. 22 | 23 | .PARAMETER instanceCount 24 | The number of resource group instances to create 25 | 26 | .PARAMETER overrideExistingLogicApp 27 | If true, the script runs the Azure Resource Manager template deployment for the logic app, even when the logic app already exists. 28 | #> 29 | 30 | param( 31 | 32 | [Parameter(Mandatory=$True)] 33 | [string] 34 | $groupId, 35 | 36 | [Parameter(Mandatory=$True)] 37 | [string] 38 | $location, 39 | 40 | [Parameter(Mandatory=$True)] 41 | [string] 42 | $environment, 43 | 44 | [Parameter(Mandatory=$False)] 45 | [string] 46 | $abbrevLocationName = $location, 47 | 48 | [Parameter(Mandatory=$False)] 49 | [string] 50 | $deploymentName = [guid]::NewGuid(), 51 | 52 | [Parameter(Mandatory=$False)] 53 | [int] 54 | $instanceCount = 1, 55 | 56 | [Parameter(Mandatory = $False)] 57 | [bool] 58 | $overrideExistingLogicApp = $True 59 | ) 60 | 61 | Write-Host "Running shared deployment script" 62 | 63 | & "${PSScriptRoot}\shared-deploy.ps1" -groupId $groupId ` 64 | -location $location ` 65 | -environment $environment ` 66 | -abbrevLocationName $abbrevLocationName ` 67 | -deploymentName $deploymentName; 68 | 69 | Write-Host "Running logic app deployment script" 70 | 71 | & "${PSScriptRoot}\logic-app-deploy.ps1" -groupId $groupId ` 72 | -location $location ` 73 | -environment $environment ` 74 | -abbrevLocationName $abbrevLocationName ` 75 | -deploymentName $deploymentName ` 76 | -identifier "sample-storage-conn" ` 77 | -instanceCount $instanceCount ` 78 | -overrideExisting $overrideExistingLogicApp; -------------------------------------------------------------------------------- /storage-account-connections/templates/connectors-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "sharedResourceGroupName": "[toLower(concat(variables('baseName'), 'rgp', variables('location'), '-shared'))]", 42 | "baseConnectionsId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('location'), '/managedApis/')]", 43 | "storageAccountName": "[toLower(concat(variables('baseName'), 'sa2', parameters('locationName')))]", 44 | "storageAccountId": "[resourceId(subscription().subscriptionId, variables('sharedResourceGroupName'), 'Microsoft.Storage/storageAccounts', variables('storageAccountName'))]", 45 | "tablesConnectionName": "azuretables", 46 | "tablesConnectionId": "[concat(variables('baseConnectionsId'), 'azuretables')]", 47 | "blobConnectionName": "azureblob", 48 | "blobConnectionId": "[concat(variables('baseConnectionsId'), 'azureblob')]", 49 | "fileConnectionName": "azurefile", 50 | "fileConnectionId": "[concat(variables('baseConnectionsId'), 'azurefile')]", 51 | "queuesConnectionName": "azurequeues", 52 | "queuesConnectionId": "[concat(variables('baseConnectionsId'), 'azurequeues')]", 53 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 54 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 55 | }, 56 | "resources": [ 57 | { 58 | "type": "Microsoft.Web/connections", 59 | "apiVersion": "2016-06-01", 60 | "name": "[variables('tablesConnectionName')]", 61 | "location": "[variables('location')]", 62 | "tags": { 63 | "groupdId": "[parameters('groupId')]" 64 | }, 65 | "properties": { 66 | "displayName": "storageAccount", 67 | "customParameterValues": {}, 68 | "api": { 69 | "id": "[variables('tablesConnectionId')]" 70 | }, 71 | "parameterValues": { 72 | "storageaccount": "[variables('storageAccountName')]", 73 | "sharedkey": "[listKeys(variables('storageAccountId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]" 74 | } 75 | } 76 | }, 77 | { 78 | "type": "Microsoft.Web/connections", 79 | "apiVersion": "2016-06-01", 80 | "name": "[variables('queuesConnectionName')]", 81 | "location": "[variables('location')]", 82 | "tags": { 83 | "groupdId": "[parameters('groupId')]" 84 | }, 85 | "properties": { 86 | "displayName": "storageAccount", 87 | "customParameterValues": {}, 88 | "api": { 89 | "id": "[variables('queuesConnectionId')]" 90 | }, 91 | "parameterValues": { 92 | "storageaccount": "[variables('storageAccountName')]", 93 | "sharedkey": "[listKeys(variables('storageAccountId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]" 94 | } 95 | } 96 | }, 97 | { 98 | "type": "Microsoft.Web/connections", 99 | "apiVersion": "2016-06-01", 100 | "name": "[variables('blobConnectionName')]", 101 | "location": "[variables('location')]", 102 | "tags": { 103 | "groupdId": "[parameters('groupId')]" 104 | }, 105 | "properties": { 106 | "displayName": "storageAccount", 107 | "customParameterValues": {}, 108 | "api": { 109 | "id": "[variables('blobConnectionId')]" 110 | }, 111 | "parameterValues": { 112 | "accountName": "[variables('storageAccountName')]", 113 | "accessKey": "[listKeys(variables('storageAccountId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]" 114 | } 115 | } 116 | }, 117 | { 118 | "type": "Microsoft.Web/connections", 119 | "apiVersion": "2016-06-01", 120 | "name": "[variables('fileConnectionName')]", 121 | "location": "[variables('location')]", 122 | "tags": { 123 | "groupdId": "[parameters('groupId')]" 124 | }, 125 | "properties": { 126 | "displayName": "storageAccount", 127 | "customParameterValues": {}, 128 | "api": { 129 | "id": "[variables('fileConnectionId')]" 130 | }, 131 | "parameterValues": { 132 | "accountName": "[variables('storageAccountName')]", 133 | "accessKey": "[listKeys(variables('storageAccountId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]" 134 | } 135 | } 136 | } 137 | ], 138 | "outputs": { 139 | "tablesManagedApiId": { 140 | "type": "string", 141 | "value": "[variables('tablesConnectionId')]" 142 | }, 143 | "tablesConnId": { 144 | "type": "string", 145 | "value": "[resourceId('Microsoft.Web/connections', variables('tablesConnectionName'))]" 146 | }, 147 | "fileManagedApiId": { 148 | "type": "string", 149 | "value": "[variables('fileConnectionId')]" 150 | }, 151 | "fileConnId": { 152 | "type": "string", 153 | "value": "[resourceId('Microsoft.Web/connections', variables('fileConnectionName'))]" 154 | }, 155 | "blobManagedApiId": { 156 | "type": "string", 157 | "value": "[variables('blobConnectionId')]" 158 | }, 159 | "blobConnId": { 160 | "type": "string", 161 | "value": "[resourceId('Microsoft.Web/connections', variables('blobConnectionName'))]" 162 | }, 163 | "queuesManagedApiId": { 164 | "type": "string", 165 | "value": "[variables('queuesConnectionId')]" 166 | }, 167 | "queuesConnId": { 168 | "type": "string", 169 | "value": "[resourceId('Microsoft.Web/connections', variables('queuesConnectionName'))]" 170 | }, 171 | "logicAppName": { 172 | "type": "string", 173 | "value": "[variables('logicAppName')]" 174 | } 175 | } 176 | } -------------------------------------------------------------------------------- /storage-account-connections/templates/logic-app-definition-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$connections": { 3 | "value": { 4 | "azuretables": { 5 | "connectionId": "{tablesConnId}", 6 | "connectionName": "azuretables", 7 | "id": "{tablesManagedApiId}" 8 | }, 9 | "azureblob": { 10 | "connectionId": "{blobConnId}", 11 | "connectionName": "azureblob", 12 | "id": "{blobManagedApiId}" 13 | }, 14 | "azurefile": { 15 | "connectionId": "{fileConnId}", 16 | "connectionName": "azurefile", 17 | "id": "{fileManagedApiId}" 18 | }, 19 | "azurequeues": { 20 | "connectionId": "{queuesConnId}", 21 | "connectionName": "azurequeues", 22 | "id": "{queuesManagedApiId}" 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /storage-account-connections/templates/logic-app-definition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 3 | "actions": { 4 | "Create_blob": { 5 | "inputs": { 6 | "body": "@triggerBody()?['MessageText']", 7 | "host": { 8 | "connection": { 9 | "name": "@parameters('$connections')['azureblob']['connectionId']" 10 | } 11 | }, 12 | "method": "post", 13 | "path": "/datasets/default/files", 14 | "queries": { 15 | "folderPath": "/samplecontainer1", 16 | "name": "@triggerBody()?['MessageId']", 17 | "queryParametersSingleEncoded": true 18 | } 19 | }, 20 | "runAfter": {}, 21 | "runtimeConfiguration": { 22 | "contentTransfer": { 23 | "transferMode": "Chunked" 24 | } 25 | }, 26 | "type": "ApiConnection" 27 | }, 28 | "Create_file": { 29 | "inputs": { 30 | "body": "@triggerBody()?['MessageText']", 31 | "host": { 32 | "connection": { 33 | "name": "@parameters('$connections')['azurefile']['connectionId']" 34 | } 35 | }, 36 | "method": "post", 37 | "path": "/datasets/default/files", 38 | "queries": { 39 | "folderPath": "/sampleshare1", 40 | "name": "@triggerBody()?['MessageId']", 41 | "queryParametersSingleEncoded": true 42 | } 43 | }, 44 | "runAfter": { 45 | "Insert_Entity": [ 46 | "Succeeded" 47 | ] 48 | }, 49 | "runtimeConfiguration": { 50 | "contentTransfer": { 51 | "transferMode": "Chunked" 52 | } 53 | }, 54 | "type": "ApiConnection" 55 | }, 56 | "Insert_Entity": { 57 | "inputs": { 58 | "body": "@triggerBody()", 59 | "host": { 60 | "connection": { 61 | "name": "@parameters('$connections')['azuretables']['connectionId']" 62 | } 63 | }, 64 | "method": "post", 65 | "path": "/Tables/@{encodeURIComponent('sampleTable1')}/entities" 66 | }, 67 | "runAfter": { 68 | "Create_blob": [ 69 | "Succeeded" 70 | ] 71 | }, 72 | "type": "ApiConnection" 73 | } 74 | }, 75 | "contentVersion": "1.0.0.0", 76 | "outputs": {}, 77 | "parameters": { 78 | "$connections": { 79 | "defaultValue": {}, 80 | "type": "Object" 81 | } 82 | }, 83 | "triggers": { 84 | "When_there_are_messages_in_a_queue": { 85 | "inputs": { 86 | "host": { 87 | "connection": { 88 | "name": "@parameters('$connections')['azurequeues']['connectionId']" 89 | } 90 | }, 91 | "method": "get", 92 | "path": "/@{encodeURIComponent('samplequeue1')}/message_trigger" 93 | }, 94 | "recurrence": { 95 | "frequency": "Minute", 96 | "interval": 3 97 | }, 98 | "splitOn": "@triggerBody()?['QueueMessagesList']?['QueueMessage']", 99 | "type": "ApiConnection" 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /storage-account-connections/templates/logic-app-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "identifier": { 25 | "type": "string", 26 | "metadata": { 27 | "description": "The value that's appended to the logic app name and identifies the logic app's purpose or function." 28 | } 29 | }, 30 | "instance": { 31 | "type": "int", 32 | "defaultValue": 1, 33 | "metadata": { 34 | "description": "The value that tracks the number of resource group instances" 35 | } 36 | } 37 | }, 38 | "variables": { 39 | "location": "[resourceGroup().location]", 40 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 41 | "instance": "[if(equals(parameters('instance'), 1), '', concat('-', sub(parameters('instance'), 1)))]", 42 | "logicAppName": "[toLower(concat(variables('baseName'), 'la', parameters('locationName'), '-', parameters('identifier'), variables('instance')))]" 43 | }, 44 | "resources": [ 45 | { 46 | "name": "[variables('logicAppName')]", 47 | "type": "Microsoft.Logic/workflows", 48 | "location": "[variables('location')]", 49 | "apiVersion": "2016-06-01", 50 | "properties": { 51 | "definition": { 52 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 53 | "contentVersion": "1.0.0.0", 54 | "parameters": {}, 55 | "triggers": {}, 56 | "actions": {}, 57 | "outputs": {} 58 | } 59 | } 60 | } 61 | ], 62 | "outputs": { 63 | "logicAppName": { 64 | "type": "string", 65 | "value": "[variables('logicAppName')]" 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /storage-account-connections/templates/shared-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The group name to use for creating resource names" 9 | } 10 | }, 11 | "environment": { 12 | "type": "string", 13 | "defaultValue": "d", 14 | "metadata": { 15 | "description": "The alphabetical character that identifies the deployment environment to use in the name for each created resource. For example, values include 'd' for development, 't' for test, 's' for staging, and 'p' for production." 16 | } 17 | }, 18 | "locationName": { 19 | "type": "string", 20 | "metadata": { 21 | "description": "The name that identifies the region or location in resource names and is usually the shortened version of that name" 22 | } 23 | }, 24 | "containers": { 25 | "type": "array", 26 | "defaultValue": [ 27 | "samplecontainer1", 28 | "samplecontainer2" 29 | ], 30 | "metadata": { 31 | "description": "The array of containers to create" 32 | } 33 | } 34 | }, 35 | "variables": { 36 | "location": "[resourceGroup().location]", 37 | "baseName": "[concat(parameters('groupId'), parameters('environment'))]", 38 | "storageAccountName": "[toLower(concat(variables('baseName'), 'sa2', parameters('locationName')))]" 39 | }, 40 | "resources": [ 41 | { 42 | "name": "[variables('storageAccountName')]", 43 | "type": "Microsoft.Storage/storageAccounts", 44 | "apiVersion": "2019-04-01", 45 | "tags": { 46 | "groupdId": "[parameters('groupId')]" 47 | }, 48 | "sku": { 49 | "name": "Standard_LRS" 50 | }, 51 | "kind": "StorageV2", 52 | "location": "[variables('location')]", 53 | "properties": { 54 | "accessTier": "Hot" 55 | }, 56 | "resources": [] 57 | }, 58 | { 59 | "name": "[concat(variables('storageAccountName'), '/default/',toLower(parameters('containers')[copyindex()]))]", 60 | "type": "Microsoft.Storage/storageAccounts/blobServices/containers", 61 | "apiVersion": "2019-04-01", 62 | "tags": { 63 | "groupdId": "[parameters('groupId')]" 64 | }, 65 | "dependsOn": [ 66 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" 67 | ], 68 | "copy": { 69 | "name": "containerCopy", 70 | "count": "[length(parameters('containers'))]" 71 | }, 72 | "properties": { 73 | "publicAccess": "Container" 74 | }, 75 | "resources": [] 76 | } 77 | ], 78 | "outputs": { 79 | "storageAccountName": { 80 | "type": "string", 81 | "value": "[variables('storageAccountName')]" 82 | } 83 | } 84 | } --------------------------------------------------------------------------------